iotaledger / iotaledger/notarization
[Task]: Making the notarization state flexible
- 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
Assessment
This issue has not been assessed yet.