Skip to content
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
12 changes: 5 additions & 7 deletions audit-trail-rs/src/core/create/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,24 @@
pub(super) struct CreateOps;

impl CreateOps {
pub(super) fn create_trail(
audit_trail_package_id: ObjectID,
tf_components_package_id: ObjectID,
admin: IotaAddress,
initial_data: Option<Data>,
initial_record_metadata: Option<String>,
locking_config: LockingConfig,
trail_metadata: Option<ImmutableMetadata>,
updatable_metadata: Option<String>,
) -> Result<ProgrammableTransaction, Error> {

Check failure on line 25 in audit-trail-rs/src/core/create/operations.rs

View workflow job for this annotation

GitHub Actions / clippy (ubuntu-latest)

this function has too many arguments (8/7)

error: this function has too many arguments (8/7) --> audit-trail-rs/src/core/create/operations.rs:16:5 | 16 | / pub(super) fn create_trail( 17 | | audit_trail_package_id: ObjectID, 18 | | tf_components_package_id: ObjectID, 19 | | admin: IotaAddress, ... | 24 | | updatable_metadata: Option<String>, 25 | | ) -> Result<ProgrammableTransaction, Error> { | |_______________________________________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.94.0/index.html#too_many_arguments = note: `-D clippy::too-many-arguments` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::too_many_arguments)]`
let mut ptb = ProgrammableTransactionBuilder::new();

let initial_data = initial_data.ok_or_else(|| {
Error::InvalidArgument(
"initial_data is required to create the default flexible trail; use `with_initial_record(...)`"
.to_string(),
)
})?;
let data_tag = Data::tag(audit_trail_package_id);
let initial_data_arg = initial_data.to_option_ptb(&mut ptb, audit_trail_package_id)?;
let initial_data_arg = match initial_data {
Some(data) => data.to_option_ptb(&mut ptb, audit_trail_package_id)?,
None => utils::option_to_move(None, data_tag.clone(), &mut ptb)
.map_err(|e| Error::InvalidArgument(format!("failed to build initial_data option: {e}")))?,
};

let initial_record_metadata = utils::ptb_pure(&mut ptb, "initial_record_metadata", initial_record_metadata)?;
let locking_config = locking_config.to_ptb(&mut ptb, audit_trail_package_id, tf_components_package_id)?;
Expand Down
51 changes: 2 additions & 49 deletions audit-trail-rs/src/core/types/record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@
TypeTag::from_str(&format!("{package_id}::record::Data")).expect("should be valid type tag")
}

/// Creates a PTB argument for the default flexible Move `record::Data` type.
/// Creates a PTB argument for the Move `record::Data` type exposed by the Rust SDK.
pub(in crate::core) fn to_ptb(self, ptb: &mut Ptb, package_id: ObjectID) -> Result<Argument, Error> {

Check failure on line 74 in audit-trail-rs/src/core/types/record.rs

View workflow job for this annotation

GitHub Actions / clippy (ubuntu-latest)

methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference

error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference --> audit-trail-rs/src/core/types/record.rs:74:35 | 74 | pub(in crate::core) fn to_ptb(self, ptb: &mut Ptb, package_id: ObjectID) -> Result<Argument, Error> { | ^^^^ | = help: consider choosing a less ambiguous name = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.94.0/index.html#wrong_self_convention
match self {
Data::Bytes(bytes) => {
let bytes = utils::ptb_pure(ptb, "bytes", bytes)?;
Expand All @@ -97,13 +97,13 @@
}

/// Creates a PTB argument for `Option<record::Data>`.
pub(in crate::core) fn to_option_ptb(self, ptb: &mut Ptb, package_id: ObjectID) -> Result<Argument, Error> {

Check failure on line 100 in audit-trail-rs/src/core/types/record.rs

View workflow job for this annotation

GitHub Actions / clippy (ubuntu-latest)

methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference

error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference --> audit-trail-rs/src/core/types/record.rs:100:42 | 100 | pub(in crate::core) fn to_option_ptb(self, ptb: &mut Ptb, package_id: ObjectID) -> Result<Argument, Error> { | ^^^^ | = help: consider choosing a less ambiguous name = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.94.0/index.html#wrong_self_convention
let data = self.to_ptb(ptb, package_id)?;
utils::option_to_move(Some(data), Self::tag(package_id), ptb)
.map_err(|e| Error::InvalidArgument(format!("failed to build record data option: {e}")))
}

/// Validates that the on-chain trail stores the default flexible Move `record::Data` type.
/// Validates that the on-chain trail stores the Move `record::Data` type supported by the Rust SDK.
pub(in crate::core) fn ensure_supported_trail_tag(expected: &TypeTag, package_id: ObjectID) -> Result<(), Error> {
let supported = Self::tag(package_id);

Expand Down Expand Up @@ -175,50 +175,3 @@
Data::Bytes(value.to_vec())
}
}

#[cfg(test)]
mod tests {
use super::Data;
use iota_interaction::types::TypeTag;
use iota_interaction::types::base_types::ObjectID;
use std::str::FromStr;

fn roundtrip(value: &Data) -> Data {
let encoded = bcs::to_bytes(value).expect("failed to bcs encode Data");
bcs::from_bytes::<Data>(&encoded).expect("failed to deserialize Data from bcs payload")
}

#[test]
fn deserialize_text_variant_roundtrips() {
let data = roundtrip(&Data::Text("hello world".to_string()));
assert_eq!(data, Data::Text("hello world".to_string()));
}

#[test]
fn deserialize_bytes_variant_roundtrips() {
let data = roundtrip(&Data::Bytes(vec![0xF0, 0x28, 0x8C, 0x28]));
assert_eq!(data, Data::Bytes(vec![0xF0, 0x28, 0x8C, 0x28]));
}

#[test]
fn supported_trail_tag_accepts_record_data() {
let package_id = ObjectID::from_str("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")
.expect("valid object id");

let expected = Data::tag(package_id);
Data::ensure_supported_trail_tag(&expected, package_id).expect("record::Data should be supported");
}

#[test]
fn supported_trail_tag_rejects_legacy_string_trails() {
let package_id = ObjectID::from_str("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")
.expect("valid object id");
let legacy_string = TypeTag::from_str("0x1::string::String").expect("valid string type tag");

let err = Data::ensure_supported_trail_tag(&legacy_string, package_id).expect_err("legacy tag should fail");
assert!(
err.to_string().contains("unsupported trail record type"),
"unexpected error: {err}"
);
}
}
26 changes: 25 additions & 1 deletion audit-trail-rs/tests/e2e/records.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ async fn add_and_fetch_record_roundtrip() -> anyhow::Result<()> {
}

#[tokio::test]
async fn add_record_accepts_mixed_data_variants_in_default_trail() -> anyhow::Result<()> {
async fn add_record_accepts_text_and_bytes_variants() -> anyhow::Result<()> {
let client = get_funded_test_client().await?;
let trail_id = client.create_test_trail(Data::text("text-trail")).await?;
let records = client.trail(trail_id).records();
Expand All @@ -98,6 +98,30 @@ async fn add_record_accepts_mixed_data_variants_in_default_trail() -> anyhow::Re
Ok(())
}

#[tokio::test]
async fn add_record_to_empty_trail_created_without_initial_record() -> anyhow::Result<()> {
let client = get_funded_test_client().await?;
let trail_id = client.create_trail().finish().build_and_execute(&client).await?.output.trail_id;
let records = client.trail(trail_id).records();

grant_role_capability(&client, trail_id, "RecordWriter", [Permission::AddRecord]).await?;

let added = records
.add(Data::text("first record"), Some("first metadata".to_string()))
.build_and_execute(&client)
.await?
.output;

assert_eq!(added.sequence_number, 0);
assert_eq!(records.record_count().await?, 1);

let record = records.get(0).await?;
assert_eq!(record.metadata, Some("first metadata".to_string()));
assert_text_data(record.data, "first record");

Ok(())
}

#[tokio::test]
async fn get_missing_record_fails() -> anyhow::Result<()> {
let client = get_funded_test_client().await?;
Expand Down
19 changes: 19 additions & 0 deletions audit-trail-rs/tests/e2e/trail.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,25 @@ async fn create_trail_with_default_builder_settings() -> anyhow::Result<()> {
Ok(())
}

#[tokio::test]
async fn create_empty_trail_without_initial_record() -> anyhow::Result<()> {
let client = get_funded_test_client().await?;

let created = client.create_trail().finish().build_and_execute(&client).await?.output;

assert_eq!(created.creator, client.sender_address());

let on_chain = created.fetch_audit_trail(&client).await?;
assert_eq!(on_chain.id.object_id(), &created.trail_id);
assert_eq!(on_chain.creator, client.sender_address());
assert_eq!(on_chain.sequence_number, 0);
assert_eq!(on_chain.locking_config, config_with_window(LockingWindow::None));
assert!(on_chain.immutable_metadata.is_none());
assert!(on_chain.updatable_metadata.is_none());

Ok(())
}

#[tokio::test]
async fn create_trail_with_metadata_and_time_lock() -> anyhow::Result<()> {
let client = get_funded_test_client().await?;
Expand Down
Loading