Skip to content
Open
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
13 changes: 13 additions & 0 deletions audit-trail-move/api_mapping.toml
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,19 @@ wasm = [
"WasmTrailRecords::add",
]

[audit_trails.main.correct_record]
rust = [
"CorrectRecord",
"CorrectRecord::new",
"TrailRecords::correct",
]
wasm = [
"WasmCorrectRecord",
"WasmCorrectRecord::build_programmable_transaction",
"WasmCorrectRecord::apply_with_events",
"WasmTrailRecords::correct",
]

[audit_trails.main.delete_record]
rust = [
"DeleteRecord",
Expand Down
104 changes: 103 additions & 1 deletion audit-trail-move/sources/audit_trail.move
Comment thread
itsyaasir marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use audit_trails::{
record::{Self, Record, InitialRecord},
record_tags::{Self, RoleTags, TagRegistry}
};
use iota::{clock::{Self, Clock}, event, linked_table::{Self, LinkedTable}, vec_set::VecSet};
use iota::{clock::{Self, Clock}, event, linked_table::{Self, LinkedTable}, vec_set::{Self, VecSet}};
use std::string::String;
use tf_components::{capability::Capability, role_map::{Self, RoleMap}, timelock::TimeLock};

Expand Down Expand Up @@ -51,6 +51,8 @@ const ERecordTagAlreadyDefined: vector<u8> =
#[error]
const ERecordTagInUse: vector<u8> =
b"The requested tag cannot be removed because it is already used by an existing record or role";
#[error]
const ERecordAlreadyReplaced: vector<u8> = b"The record has already been replaced";

// ===== Constants =====

Expand Down Expand Up @@ -533,6 +535,106 @@ public fun add_record<D: store + copy>(
output
}

/// Adds a correction record that supersedes an existing record.
///
/// The original record remains immutable. The new correction record is appended
/// at the next sequence number with a correction tracker whose `replaces` set
/// contains `sequence_number`. The replaced record receives an `is_replaced_by`
/// back-pointer to the new correction so clients can resolve the current
/// canonical record by following the replacement chain.
///
/// Requires a capability granting the `CorrectRecord` permission. When either
/// the replaced record or the new correction record carries a tag, that same
/// capability must also allow the corresponding tag.
///
/// Aborts with:
/// * `EPackageVersionMismatch` when the trail is at a different package version.
/// * any error documented by `RoleMap::assert_capability_valid` when `cap` fails
/// authorization checks.
/// * `ETrailWriteLocked` while `write_lock` is active.
/// * `ERecordNotFound` when no record exists at `sequence_number`.
/// * `ERecordAlreadyReplaced` when `sequence_number` already points to a newer
/// correction.
/// * `ERecordTagNotDefined` when `record_tag` is not in the trail's tag registry.
/// * `ERecordTagNotAllowed` when `cap`'s role does not allow the replaced
/// record tag or the new correction tag.
///
/// Emits a `RecordAdded` event for the correction record on success.
///
/// Returns the same receipt that is emitted as the `RecordAdded` event.
public fun correct_record<D: store + copy>(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that a CorrectionRecord may be used to correct multiple records and not just one, should we already account for this here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it is accounted here. or did I overlook something ?

self: &mut AuditTrail<D>,
cap: &Capability,
sequence_number: u64,
stored_data: D,
record_metadata: Option<String>,
record_tag: Option<String>,
clock: &Clock,
ctx: &mut TxContext,
): RecordAdded {
assert!(self.version == PACKAGE_VERSION, EPackageVersionMismatch);
self
.roles
.assert_capability_valid(
cap,
&permission::correct_record(),
clock,
ctx,
);
assert!(!self.locking_config.is_write_locked(clock), ETrailWriteLocked);
assert!(self.records.contains(sequence_number), ERecordNotFound);
assert!(
!self.records.borrow(sequence_number).correction().is_replaced(),
ERecordAlreadyReplaced,
);
assert!(
is_record_tag_allowed(
self,
cap,
self.records.borrow(sequence_number).tag(),
),
ERecordTagNotAllowed,
);
assert!(is_record_tag_allowed(self, cap, &record_tag), ERecordTagNotAllowed);

let caller = ctx.sender();
let timestamp = clock.timestamp_ms();
let trail_id = self.id();
let seq = self.sequence_number;

if (record_tag.is_some()) {
self.tags.increment_usage_count(record_tag.borrow());
};

let mut replaces = vec_set::empty();
replaces.insert(sequence_number);

let correction = record::new(
stored_data,
record_metadata,
record_tag,
seq,
caller,
timestamp,
record::with_replaces(replaces),
);

self.records.borrow_mut(sequence_number).correction_mut().set_replaced_by(seq);

linked_table::push_back(&mut self.records, seq, correction);
self.sequence_number = self.sequence_number + 1;

let output = RecordAdded {
trail_id,
sequence_number: seq,
added_by: caller,
timestamp,
};

event::emit(copy output);
output
}

/// Deletes the record at `sequence_number` from the trail.
///
/// When the deleted record carries a tag, the trail's tag-registry usage count for
Expand Down
5 changes: 5 additions & 0 deletions audit-trail-move/sources/record.move
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ public fun correction<D: store + copy>(self: &Record<D>): &RecordCorrection {
&self.correction
}

/// Returns a mutable reference to the record's bidirectional correction tracker.
public(package) fun correction_mut<D: store + copy>(self: &mut Record<D>): &mut RecordCorrection {
&mut self.correction
}

/// Destroys a `Record` by destructuring it.
public(package) fun destroy<D: store + copy + drop>(self: Record<D>) {
let Record {
Expand Down
Loading
Loading