iotaledger / iotaledger/notarization

[Task]: Making the notarization state flexible

Open
#77 6 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
5
Forks
7
Avg merge
10h 34m
Merged PRs (30d)
11

Description

### Feature description

Currently, our notarization::State object is powerful but limited. It is generic over `T`, but our API effectively constrains T to be the internal Data enum (`String` or `Vec`).

We want to make it flexible so as to be used with complex object and structures from the end-users

### Motivation

To unlock the full potential of our notarization service, we need to allow users to notarize their own custom data structures (e.g., an `Invoice`, `SensorReading` struct). The primary challenge is this: how can our system convert an arbitrary, user-defined type into a Move-compatible Argument to build a programmable transaction, without knowing the type's structure in advance?

### Requirements

- Allow users to use their own custom types to describe the state they want to notarization

### Open questions

_No response_

### The Proposed Solution
The `IntoPtbArgument` Trait
I propose we solve this by introducing a new public trait: `IntoPtbArgument`.
This trait will act as a contract. Any type that wishes to be used as notarization state must implement this trait. By doing so, the type provides the exact logic needed to convert itself into a transaction argument.

## Trait Definition
The trait would be simple, containing a single method:

```rs
/// A contract for types that can be converted into a notarization state argument
/// for a programmable transaction.
pub trait IntoPtbArgument {
/// Defines how this type is converted into a transaction argument.
fn into_ptb(
self,
ptb: &mut ProgrammableTransactionBuilder,
package_id: ObjectID,
metadata: Option
) -> Result;
}
```
## API Modification
We will then update `State` to enforce this contract using a generic bound. The `into_ptb` function becomes a simple, robust delegator.

```rs
// The State struct now requires its generic type T to fulfill our contract.
pub struct State {
pub data: T,
#[serde(default)]
pub metadata: Option,
}

impl State {
/// Creates a new `Argument` from the `State`.
/// The logic is now fully delegated to the data type itself.
pub(in crate::core) fn into_ptb(
self,
ptb: &mut ProgrammableTransactionBuilder,
package_id: ObjectID,
) -> Result {
self.data.into_argument(ptb, package_id, self.metadata)
}
}
```

The user can implement this easily over their complex type if they wanted

```rs
// In user's code
#[derive(Serialize)]
struct DigitalAsset {
id: u64,
owner: String,
uri: String,
}

// User provides the logic to convert their struct into a PTB argument.
impl IntoPtbArgument for DigitalAsset {
fn into_ptb(self, ptb: &mut ProgrammableTransactionBuilder, package_id: ObjectID, metadata: Option) -> Result {
println!("Using custom DigitalAsset implementation to build transaction...");

# User writes their own implementation on how to construct this.
.....
}
}
```
This change will be non-breaking as we can make the Data enum backward compatible; we just implement the IntoPtb ensuring all current functionality remains unchanged

### Are you planning to do it yourself in a pull request?

Yes

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.