diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 77c88d4..73e4006 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -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" diff --git a/rust/notamon-asset-contract/Cargo.toml b/rust/notamon-asset-contract/Cargo.toml index 55b4dd1..67f236f 100644 --- a/rust/notamon-asset-contract/Cargo.toml +++ b/rust/notamon-asset-contract/Cargo.toml @@ -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 diff --git a/rust/notamon-asset-contract/src/lib.rs b/rust/notamon-asset-contract/src/lib.rs index ce8d438..1bb45d9 100644 --- a/rust/notamon-asset-contract/src/lib.rs +++ b/rust/notamon-asset-contract/src/lib.rs @@ -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, + permissions: Permissions, +} /// Initializes contract with empty state. #[init(zk = true)] -pub fn initialize(ctx: ContractContext, zk_state: ZkState) -> CollectionState { - CollectionState {} +pub fn initialize(ctx: ContractContext, zk_state: ZkState) -> 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, - file_length: u32, + state: AssetContractState, + zk_state: ZkState, + asset_id: AssetId, + asset_length: u32, ) -> ( - CollectionState, + AssetContractState, Vec, - ZkInputDef>, + ZkInputDef>, ) { - 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, - file_id: u32, - new_owner: Address, -) -> (CollectionState, Vec, Vec) { - 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, + 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, - file_id: u32, -) -> (CollectionState, Vec, Vec) { - 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, + asset_id: AssetId, +) -> (AssetContractState, Vec, Vec) { + 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, + asset_id: AssetId, +) -> (AssetContractState, Vec, Vec) { + 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, + role: Role, + permission: Permission, +) -> AssetContractState { + state.permissions.assert_has_permission(&context.sender, Role::ADMIN {}); + state.permissions.set_permission(role, permission); + state +} diff --git a/rust/notamon-common/Cargo.toml b/rust/notamon-common/Cargo.toml new file mode 100644 index 0000000..da3b0aa --- /dev/null +++ b/rust/notamon-common/Cargo.toml @@ -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 diff --git a/rust/notamon-common/README.md b/rust/notamon-common/README.md new file mode 100644 index 0000000..e2f8ac7 --- /dev/null +++ b/rust/notamon-common/README.md @@ -0,0 +1,3 @@ +# Notamon common functionality + +TODO diff --git a/rust/notamon-common/src/lib.rs b/rust/notamon-common/src/lib.rs new file mode 100644 index 0000000..1fc5adc --- /dev/null +++ b/rust/notamon-common/src/lib.rs @@ -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
}, +} + +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 { + permissions: AvlTreeMap, +} + +impl Permissions { + + 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 Permissions { + 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:?}"); + } + } +} +