Asset contract mostly implemented
This commit is contained in:
parent
6aaabff522
commit
e39d48da82
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
21
rust/notamon-common/Cargo.toml
Normal file
21
rust/notamon-common/Cargo.toml
Normal 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
|
3
rust/notamon-common/README.md
Normal file
3
rust/notamon-common/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Notamon common functionality
|
||||
|
||||
TODO
|
72
rust/notamon-common/src/lib.rs
Normal file
72
rust/notamon-common/src/lib.rs
Normal 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:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user