1
0

Asset contract mostly implemented

This commit is contained in:
Jon Michael Aanes 2025-04-02 00:01:11 +02:00
parent 6aaabff522
commit e39d48da82
6 changed files with 204 additions and 86 deletions

View File

@ -2,35 +2,16 @@
resolver = "2"
members = [
"voting",
"multi-voting",
"petition",
"ping",
"mia-game",
"nickname",
"access-control",
"dns",
"dns-voting-client",
"zk-second-price-auction",
"zk-struct-open",
"zk-immediate-open",
"zk-voting-simple",
"zk-average-salary",
"zk-statistics",
"zk-multi-functional",
"zk-file-share",
"zk-classification",
"upgradable-v1",
"upgradable-v2",
"upgradable-v3",
"notamon-common",
"notamon-asset-contract",
]
[workspace.package]
version = "5.203.0"
description = "Example contract for the Partisia Blockchain."
homepage = "https://gitlab.com/partisiablockchain/language/example-contracts"
repository = "https://gitlab.com/partisiablockchain/language/example-contracts"
documentation = "https://gitlab.com/partisiablockchain/language/example-contracts"
version = "0.1.0"
description = "TODO"
homepage = "TODO"
repository = "TODO"
documentation = "TODO"
edition = "2021"
license = "MIT"

View File

@ -19,6 +19,8 @@ crate-type = ['rlib', 'cdylib']
zk-compute-path = "src/zk_compute.rs"
[dependencies]
notamon-common = { path = "../notamon-common" }
pbc_contract_common.workspace = true
pbc_traits.workspace = true
pbc_lib.workspace = true

View File

@ -3,8 +3,6 @@
#[macro_use]
extern crate pbc_contract_codegen;
extern crate pbc_contract_common;
extern crate pbc_lib;
use pbc_contract_common::address::Address;
use pbc_contract_common::context::ContractContext;
@ -13,94 +11,135 @@ use pbc_contract_common::zk::{SecretVarId, ZkInputDef, ZkState, ZkStateChange};
use pbc_zk::Sbi8;
use read_write_rpc_derive::ReadWriteRPC;
use read_write_state_derive::ReadWriteState;
use pbc_contract_common::avl_tree_map::AvlTreeMap;
use pbc_traits::{ReadRPC, WriteRPC, ReadWriteState};
use create_type_spec_derive::CreateTypeSpec;
use std::fmt::Debug;
use notamon_common::{AssetId, Permission, Permissions};
mod zk_compute;
/// Metadata for secret-shared files.
/// Metadata for secret-shared assets.
#[derive(ReadWriteState, ReadWriteRPC, Debug)]
#[repr(C)]
pub struct SecretVarMetadata {}
pub struct AssetMetadata {
asset_id: AssetId,
}
/// Empty contract state, as all stored files are secret-shared.
#[derive(ReadWriteState, ReadWriteRPC, Debug, CreateTypeSpec, PartialEq, Eq, Clone, Copy)]
pub enum Role {
#[discriminant(1)]
ADMIN {},
#[discriminant(2)]
UPLOADER {},
#[discriminant(3)]
PUBLISHER {},
}
/// Empty contract state, as all stored assets are secret-shared.
#[state]
pub struct CollectionState {}
pub struct AssetContractState {
asset_id_to_variable_id: AvlTreeMap<AssetId, SecretVarId>,
permissions: Permissions<Role>,
}
/// Initializes contract with empty state.
#[init(zk = true)]
pub fn initialize(ctx: ContractContext, zk_state: ZkState<SecretVarMetadata>) -> CollectionState {
CollectionState {}
pub fn initialize(ctx: ContractContext, zk_state: ZkState<AssetMetadata>) -> AssetContractState {
let mut permissions = Permissions::new();
permissions.set_permission(Role::ADMIN { }, Permission::only(ctx.sender));
AssetContractState {
asset_id_to_variable_id: AvlTreeMap::new(),
permissions,
}
}
/// Upload a new file with a specific size of `file_length`.
/// Upload a new asset with a specific size of `asset_length`.
///
/// `file_length` is the size of the file in *bytes*.
/// Fails if the uploaded file has a different size than `file_length`.
/// `asset_length` is the size of the asset in *bytes*.
/// Fails if the uploaded asset has a different size than `asset_length`.
#[zk_on_secret_input(shortname = 0x42)]
pub fn add_file(
pub fn set_asset(
context: ContractContext,
state: CollectionState,
zk_state: ZkState<SecretVarMetadata>,
file_length: u32,
state: AssetContractState,
zk_state: ZkState<AssetMetadata>,
asset_id: AssetId,
asset_length: u32,
) -> (
CollectionState,
AssetContractState,
Vec<EventGroup>,
ZkInputDef<SecretVarMetadata, Vec<Sbi8>>,
ZkInputDef<AssetMetadata, Vec<Sbi8>>,
) {
let input_def = ZkInputDef::with_metadata_and_size(None, SecretVarMetadata {}, file_length * 8);
state.permissions.assert_has_permission(&context.sender, Role::UPLOADER {});
let input_def = ZkInputDef::with_metadata_and_size(Some(SHORTNAME_SET_ASSET_INPUTTED), AssetMetadata { asset_id }, asset_length * 8);
(state, vec![], input_def)
}
/// Changes ownership of the secret-shared file with id `file_id`
/// from the sender to `new_owner`.
///
/// Fails if the sender is not the current owner of the referenced file.
#[action(shortname = 0x03, zk = true)]
pub fn change_file_owner(
ctx: ContractContext,
state: CollectionState,
zk_state: ZkState<SecretVarMetadata>,
file_id: u32,
new_owner: Address,
) -> (CollectionState, Vec<EventGroup>, Vec<ZkStateChange>) {
let file_id = SecretVarId::new(file_id);
let file_owner = zk_state.get_variable(file_id).unwrap().owner;
assert_eq!(
file_owner, ctx.sender,
"Only the owner of the secret file is allowed to change ownership."
);
(
state,
vec![],
vec![ZkStateChange::TransferVariable {
variable: file_id,
new_owner,
}],
)
#[zk_on_variable_inputted(shortname=0x43)]
pub fn set_asset_inputted(
context: ContractContext,
mut state: AssetContractState,
zk_state: ZkState<AssetMetadata>,
variable_id: SecretVarId,
) -> AssetContractState {
let variable = zk_state.get_variable(variable_id).unwrap();
state.asset_id_to_variable_id.insert(variable.metadata.asset_id,
variable_id);
state
}
/// Deletes the secret-shared file with id `file_id`.
/// Deletes the secret-shared asset with id `asset_id`.
///
/// Fails if the sender is not the current owner of the secret file.
/// Fails if the sender is not the current owner of the secret asset.
#[action(shortname = 0x05, zk = true)]
pub fn delete_file(
ctx: ContractContext,
state: CollectionState,
zk_state: ZkState<SecretVarMetadata>,
file_id: u32,
) -> (CollectionState, Vec<EventGroup>, Vec<ZkStateChange>) {
let file_id = SecretVarId::new(file_id);
let file_owner = zk_state.get_variable(file_id).unwrap().owner;
assert_eq!(
file_owner, ctx.sender,
"Only the owner of the secret file is allowed to delete it."
);
pub fn delete_asset(
context: ContractContext,
state: AssetContractState,
zk_state: ZkState<AssetMetadata>,
asset_id: AssetId,
) -> (AssetContractState, Vec<EventGroup>, Vec<ZkStateChange>) {
state.permissions.assert_has_permission(&context.sender, Role::UPLOADER {});
let variable_id = state.asset_id_to_variable_id.get(&asset_id).unwrap();
(
state,
vec![],
vec![ZkStateChange::DeleteVariables {
variables_to_delete: vec![file_id],
variables_to_delete: vec![variable_id],
}],
)
}
#[action(shortname = 0x07, zk = true)]
pub fn publish_asset(
context: ContractContext,
state: AssetContractState,
zk_state: ZkState<AssetMetadata>,
asset_id: AssetId,
) -> (AssetContractState, Vec<EventGroup>, Vec<ZkStateChange>) {
state.permissions.assert_has_permission(&context.sender, Role::PUBLISHER {});
let variable_id = state.asset_id_to_variable_id.get(&asset_id).unwrap();
(
state,
vec![],
vec![ZkStateChange::OpenVariables {
variables: vec![variable_id],
}],
)
}
#[action(shortname = 0x06, zk = true)]
pub fn set_permission(
context: ContractContext,
mut state: AssetContractState,
zk_state: ZkState<AssetMetadata>,
role: Role,
permission: Permission,
) -> AssetContractState {
state.permissions.assert_has_permission(&context.sender, Role::ADMIN {});
state.permissions.set_permission(role, permission);
state
}

View File

@ -0,0 +1,21 @@
[package]
name = "notamon-common"
readme = "README.md"
version.workspace = true
description.workspace = true
homepage.workspace = true
repository.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
[features]
abi = ["pbc_contract_common/abi", "pbc_traits/abi", "create_type_spec_derive/abi", "pbc_lib/abi"]
[dependencies]
pbc_contract_common.workspace = true
pbc_traits.workspace = true
pbc_lib.workspace = true
read_write_rpc_derive.workspace = true
read_write_state_derive.workspace = true
create_type_spec_derive.workspace = true

View File

@ -0,0 +1,3 @@
# Notamon common functionality
TODO

View File

@ -0,0 +1,72 @@
#![doc = include_str!("../README.md")]
#![allow(unused_variables)]
use pbc_contract_common::avl_tree_map::AvlTreeMap;
use pbc_contract_common::address::Address;
use pbc_contract_common::context::ContractContext;
use pbc_contract_common::events::EventGroup;
use pbc_contract_common::zk::{SecretVarId, ZkInputDef, ZkState, ZkStateChange};
use pbc_traits::{ReadRPC, WriteRPC, ReadWriteState};
use read_write_rpc_derive::ReadWriteRPC;
use read_write_state_derive::ReadWriteState;
use create_type_spec_derive::CreateTypeSpec;
use std::fmt::Debug;
#[derive(ReadWriteState, ReadWriteRPC, Debug, CreateTypeSpec, PartialEq, Eq)]
pub struct AssetId{
id: u32,
}
#[derive(ReadWriteState, ReadWriteRPC, Debug, CreateTypeSpec, PartialEq, Eq)]
pub enum Permission {
#[discriminant(0)]
None {},
#[discriminant(1)]
Addresses { addresses: Vec<Address> },
}
impl Permission {
pub const NONE: Permission = Permission::None { };
pub fn only(address: Address) -> Permission {
Permission::Addresses { addresses: vec![address] }
}
pub fn allows(&self, address: &Address) -> bool {
match self {
Permission::None { } => false,
Permission::Addresses { addresses } => addresses.contains(address),
}
}
}
#[derive(ReadWriteState, Debug, CreateTypeSpec)]
pub struct Permissions<KeyT: ReadWriteState> {
permissions: AvlTreeMap<KeyT, Permission>,
}
impl <KeyT: ReadWriteState> Permissions<KeyT> {
pub fn new () -> Self {
Self { permissions: AvlTreeMap::new() }
}
pub fn set_permission(&mut self, permission_key: KeyT, permission: Permission) {
self.permissions.insert(permission_key, permission);
}
pub fn get_permission(&self, permission: &KeyT) -> Permission {
self.permissions.get(permission).unwrap_or(Permission::NONE)
}
}
impl <KeyT: ReadWriteState + Debug> Permissions<KeyT> {
pub fn assert_has_permission(&self, address: &Address, permission: KeyT) {
if !self.get_permission(&permission).allows(address) {
panic!("User {address} does not have permission: {permission:?}");
}
}
}