diff --git a/backend/canisters/community/CHANGELOG.md b/backend/canisters/community/CHANGELOG.md index 53310884a8..5bfbbb4213 100644 --- a/backend/canisters/community/CHANGELOG.md +++ b/backend/canisters/community/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - Add `LifetimeDiamondMembership` access gate ([#5986](https://github.com/open-chat-labs/open-chat/pull/5986)) +- Add `UniquePerson` access gate ([#5993](https://github.com/open-chat-labs/open-chat/pull/5993)) ## [[2.0.1194](https://github.com/open-chat-labs/open-chat/releases/tag/v2.0.1194-community)] - 2024-06-06 diff --git a/backend/canisters/community/api/src/updates/c2c_join_channel.rs b/backend/canisters/community/api/src/updates/c2c_join_channel.rs index 3c77d0a2b3..7ae5f8c00f 100644 --- a/backend/canisters/community/api/src/updates/c2c_join_channel.rs +++ b/backend/canisters/community/api/src/updates/c2c_join_channel.rs @@ -2,7 +2,7 @@ use candid::{CandidType, Principal}; use serde::{Deserialize, Serialize}; use types::{ ChannelId, CommunityCanisterChannelSummary, CommunityCanisterCommunitySummary, GateCheckFailedReason, TimestampMillis, - UserId, VerifiedCredentialGateArgs, + UniquePersonProof, UserId, VerifiedCredentialGateArgs, }; #[derive(CandidType, Serialize, Deserialize, Debug)] @@ -15,6 +15,7 @@ pub struct Args { pub is_bot: bool, pub diamond_membership_expires_at: Option, pub verified_credential_args: Option, + pub unique_person_proof: Option, } #[derive(CandidType, Serialize, Deserialize, Debug)] diff --git a/backend/canisters/community/api/src/updates/c2c_join_community.rs b/backend/canisters/community/api/src/updates/c2c_join_community.rs index 002f656a58..e1a17cfff0 100644 --- a/backend/canisters/community/api/src/updates/c2c_join_community.rs +++ b/backend/canisters/community/api/src/updates/c2c_join_community.rs @@ -1,6 +1,9 @@ use candid::{CandidType, Principal}; use serde::{Deserialize, Serialize}; -use types::{CommunityCanisterCommunitySummary, GateCheckFailedReason, TimestampMillis, UserId, VerifiedCredentialGateArgs}; +use types::{ + CommunityCanisterCommunitySummary, GateCheckFailedReason, TimestampMillis, UniquePersonProof, UserId, + VerifiedCredentialGateArgs, +}; #[derive(CandidType, Serialize, Deserialize, Debug)] pub struct Args { @@ -11,6 +14,7 @@ pub struct Args { pub is_bot: bool, pub diamond_membership_expires_at: Option, pub verified_credential_args: Option, + pub unique_person_proof: Option, } #[derive(CandidType, Serialize, Deserialize, Debug)] diff --git a/backend/canisters/community/impl/src/updates/add_members_to_channel.rs b/backend/canisters/community/impl/src/updates/add_members_to_channel.rs index 1c5ff76a96..11f045f63e 100644 --- a/backend/canisters/community/impl/src/updates/add_members_to_channel.rs +++ b/backend/canisters/community/impl/src/updates/add_members_to_channel.rs @@ -54,6 +54,7 @@ async fn add_members_to_channel(args: Args) -> Response { user_id: *user_id, diamond_membership_expires_at: diamond_membership_expiry_dates.get(user_id).copied(), this_canister: prepare_result.this_canister, + unique_person_proof: None, verified_credential_args: None, now: prepare_result.now_nanos, }) diff --git a/backend/canisters/community/impl/src/updates/c2c_join_channel.rs b/backend/canisters/community/impl/src/updates/c2c_join_channel.rs index 316097b800..c33f94c6d0 100644 --- a/backend/canisters/community/impl/src/updates/c2c_join_channel.rs +++ b/backend/canisters/community/impl/src/updates/c2c_join_channel.rs @@ -14,7 +14,7 @@ use gated_groups::{ CheckVerifiedCredentialGateArgs, }; use group_chat_core::AddResult; -use types::{AccessGate, ChannelId, MemberJoined, TimestampMillis, VerifiedCredentialGateArgs}; +use types::{AccessGate, ChannelId, MemberJoined, TimestampMillis, UniquePersonProof, VerifiedCredentialGateArgs}; #[update_msgpack(guard = "caller_is_user_index_or_local_user_index")] #[trace] @@ -32,6 +32,7 @@ async fn c2c_join_channel(args: Args) -> Response { is_bot: args.is_bot, diamond_membership_expires_at: args.diamond_membership_expires_at, verified_credential_args: args.verified_credential_args.clone(), + unique_person_proof: args.unique_person_proof.clone(), }) .await { @@ -64,8 +65,18 @@ pub(crate) fn join_channel_synchronously( channel_id: ChannelId, user_principal: Principal, diamond_membership_expires_at: Option, + unique_person_proof: Option, ) { - match read_state(|state| is_permitted_to_join(channel_id, user_principal, diamond_membership_expires_at, None, state)) { + match read_state(|state| { + is_permitted_to_join( + channel_id, + user_principal, + diamond_membership_expires_at, + unique_person_proof, + None, + state, + ) + }) { Ok(None) => {} Ok(Some(args)) if args.gate.synchronous() => { if !matches!(check_if_passes_gate_synchronously(args), CheckIfPassesGateResult::Success) { @@ -84,6 +95,7 @@ async fn check_gate_then_join_channel(args: &Args) -> Response { args.channel_id, args.principal, args.diamond_membership_expires_at, + args.unique_person_proof.clone(), args.verified_credential_args.clone(), state, ) @@ -104,6 +116,7 @@ fn is_permitted_to_join( channel_id: ChannelId, user_principal: Principal, diamond_membership_expires_at: Option, + unique_person_proof: Option, verified_credential_args: Option, state: &RuntimeState, ) -> Result, Response> { @@ -135,6 +148,7 @@ fn is_permitted_to_join( user_id: member.user_id, diamond_membership_expires_at, this_canister: state.env.canister_id(), + unique_person_proof, verified_credential_args: verified_credential_args.map(|vc| CheckVerifiedCredentialGateArgs { user_ii_principal: vc.user_ii_principal, credential_jwt: vc.credential_jwt, diff --git a/backend/canisters/community/impl/src/updates/c2c_join_community.rs b/backend/canisters/community/impl/src/updates/c2c_join_community.rs index b835a6d667..1bedf75d35 100644 --- a/backend/canisters/community/impl/src/updates/c2c_join_community.rs +++ b/backend/canisters/community/impl/src/updates/c2c_join_community.rs @@ -32,7 +32,12 @@ pub(crate) async fn join_community(args: Args) -> Response { match mutate_state(|state| join_community_impl(&args, state)) { Ok(public_channel_ids) => { for c in public_channel_ids { - join_channel_synchronously(c, args.principal, args.diamond_membership_expires_at); + join_channel_synchronously( + c, + args.principal, + args.diamond_membership_expires_at, + args.unique_person_proof.clone(), + ); } read_state(|state| { if let Some(member) = state.data.members.get_by_user_id(&args.user_id) { @@ -68,6 +73,7 @@ fn is_permitted_to_join(args: &Args, state: &RuntimeState) -> Result Response { is_bot: true, diamond_membership_expires_at: None, verified_credential_args: None, + unique_person_proof: None, }, state, ) diff --git a/backend/canisters/group/CHANGELOG.md b/backend/canisters/group/CHANGELOG.md index 67ab08ce60..2035e73ea9 100644 --- a/backend/canisters/group/CHANGELOG.md +++ b/backend/canisters/group/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - Add `LifetimeDiamondMembership` access gate ([#5986](https://github.com/open-chat-labs/open-chat/pull/5986)) +- Add `UniquePerson` access gate ([#5993](https://github.com/open-chat-labs/open-chat/pull/5993)) ## [[2.0.1195](https://github.com/open-chat-labs/open-chat/releases/tag/v2.0.1195-group)] - 2024-06-06 diff --git a/backend/canisters/group/api/src/updates/c2c_join_group.rs b/backend/canisters/group/api/src/updates/c2c_join_group.rs index 709e438fa1..52076bfa96 100644 --- a/backend/canisters/group/api/src/updates/c2c_join_group.rs +++ b/backend/canisters/group/api/src/updates/c2c_join_group.rs @@ -1,6 +1,9 @@ use candid::{CandidType, Principal}; use serde::{Deserialize, Serialize}; -use types::{GateCheckFailedReason, GroupCanisterGroupChatSummary, TimestampMillis, UserId, VerifiedCredentialGateArgs}; +use types::{ + GateCheckFailedReason, GroupCanisterGroupChatSummary, TimestampMillis, UniquePersonProof, UserId, + VerifiedCredentialGateArgs, +}; #[derive(CandidType, Serialize, Deserialize, Debug)] pub struct Args { @@ -12,6 +15,7 @@ pub struct Args { pub is_bot: bool, pub diamond_membership_expires_at: Option, pub verified_credential_args: Option, + pub unique_person_proof: Option, } #[derive(CandidType, Serialize, Deserialize, Debug)] diff --git a/backend/canisters/group/impl/src/updates/c2c_join_group.rs b/backend/canisters/group/impl/src/updates/c2c_join_group.rs index efaebf95aa..0edb30882b 100644 --- a/backend/canisters/group/impl/src/updates/c2c_join_group.rs +++ b/backend/canisters/group/impl/src/updates/c2c_join_group.rs @@ -48,6 +48,7 @@ fn is_permitted_to_join(args: &Args, state: &RuntimeState) -> Result self.diamond_membership += 1, AccessGate::LifetimeDiamondMember => self.lifetime_diamond_membership += 1, + AccessGate::UniquePerson => self.unique_person += 1, AccessGate::VerifiedCredential(_) => self.verified_credential += 1, AccessGate::SnsNeuron(_) => self.sns_neuron += 1, AccessGate::Payment(_) => self.payment += 1, diff --git a/backend/canisters/local_user_index/CHANGELOG.md b/backend/canisters/local_user_index/CHANGELOG.md index b207e7f519..974d28da95 100644 --- a/backend/canisters/local_user_index/CHANGELOG.md +++ b/backend/canisters/local_user_index/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - Add `LifetimeDiamondMembership` access gate ([#5986](https://github.com/open-chat-labs/open-chat/pull/5986)) +- Add `UniquePerson` access gate ([#5993](https://github.com/open-chat-labs/open-chat/pull/5993)) ### Changed diff --git a/backend/canisters/local_user_index/api/src/lib.rs b/backend/canisters/local_user_index/api/src/lib.rs index 0f1e1d39aa..0054dd7394 100644 --- a/backend/canisters/local_user_index/api/src/lib.rs +++ b/backend/canisters/local_user_index/api/src/lib.rs @@ -4,7 +4,7 @@ use types::nns::CryptoAmount; use types::{ CanisterId, ChannelLatestMessageIndex, ChatId, ChitEarnedReason, CommunityId, Cryptocurrency, DiamondMembershipPlanDuration, MessageContent, MessageContentInitial, MessageId, MessageIndex, PhoneNumber, ReferralType, - SuspensionDuration, TimestampMillis, UpdateUserPrincipalArgs, User, UserId, + SuspensionDuration, TimestampMillis, UniquePersonProof, UpdateUserPrincipalArgs, User, UserId, }; mod lifecycle; @@ -36,6 +36,7 @@ pub enum Event { UserDeleted(UserDeleted), SecretKeySet(Vec), ChitEarned(ChitEarned), + NotifyUniqueHumanProof(UserId, UniquePersonProof), } #[derive(Serialize, Deserialize, Clone, Debug)] @@ -162,6 +163,7 @@ pub struct GlobalUser { pub is_bot: bool, pub is_platform_moderator: bool, pub diamond_membership_expires_at: Option, + pub unique_person_proof: Option, } #[derive(Serialize, Deserialize, Clone, Debug)] diff --git a/backend/canisters/local_user_index/impl/src/model/global_user_map.rs b/backend/canisters/local_user_index/impl/src/model/global_user_map.rs index 9a366b87b3..aaedcbef86 100644 --- a/backend/canisters/local_user_index/impl/src/model/global_user_map.rs +++ b/backend/canisters/local_user_index/impl/src/model/global_user_map.rs @@ -2,12 +2,14 @@ use candid::Principal; use local_user_index_canister::GlobalUser; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; -use types::{TimestampMillis, UserId}; +use types::{TimestampMillis, UniquePersonProof, UserId}; #[derive(Serialize, Deserialize, Default)] pub struct GlobalUserMap { user_id_to_principal: HashMap, principal_to_user_id: HashMap, + #[serde(default)] + unique_person_proofs: HashMap, platform_moderators: HashSet, bots: HashSet, diamond_membership_expiry_dates: HashMap, @@ -77,6 +79,10 @@ impl GlobalUserMap { } } + pub fn insert_unique_person_proof(&mut self, user_id: UserId, proof: UniquePersonProof) { + self.unique_person_proofs.insert(user_id, proof); + } + pub fn is_bot(&self, user_id: &UserId) -> bool { self.bots.contains(user_id) } @@ -96,6 +102,7 @@ impl GlobalUserMap { is_bot: self.bots.contains(&user_id), is_platform_moderator: self.platform_moderators.contains(&user_id), diamond_membership_expires_at: self.diamond_membership_expiry_dates.get(&user_id).copied(), + unique_person_proof: self.unique_person_proofs.get(&user_id).cloned(), } } } diff --git a/backend/canisters/local_user_index/impl/src/updates/c2c_notify_user_index_events.rs b/backend/canisters/local_user_index/impl/src/updates/c2c_notify_user_index_events.rs index f6d94dee5d..abb5351e62 100644 --- a/backend/canisters/local_user_index/impl/src/updates/c2c_notify_user_index_events.rs +++ b/backend/canisters/local_user_index/impl/src/updates/c2c_notify_user_index_events.rs @@ -193,5 +193,8 @@ fn handle_event(event: Event, state: &mut RuntimeState) { })), ); } + Event::NotifyUniqueHumanProof(user_id, proof) => { + state.data.global_users.insert_unique_person_proof(user_id, proof); + } } } diff --git a/backend/canisters/local_user_index/impl/src/updates/join_channel.rs b/backend/canisters/local_user_index/impl/src/updates/join_channel.rs index 6cd2b88913..38b48f05f1 100644 --- a/backend/canisters/local_user_index/impl/src/updates/join_channel.rs +++ b/backend/canisters/local_user_index/impl/src/updates/join_channel.rs @@ -18,6 +18,7 @@ async fn join_channel(args: Args) -> Response { is_bot: user_details.is_bot, diamond_membership_expires_at: user_details.diamond_membership_expires_at, verified_credential_args: args.verified_credential_args.clone(), + unique_person_proof: user_details.unique_person_proof.clone(), }; match community_canister_c2c_client::c2c_join_channel(args.community_id.into(), &c2c_args).await { Ok(response) => match response { diff --git a/backend/canisters/local_user_index/impl/src/updates/join_community.rs b/backend/canisters/local_user_index/impl/src/updates/join_community.rs index 3224efa89f..4416693ce0 100644 --- a/backend/canisters/local_user_index/impl/src/updates/join_community.rs +++ b/backend/canisters/local_user_index/impl/src/updates/join_community.rs @@ -17,6 +17,7 @@ async fn join_community(args: Args) -> Response { is_bot: user_details.is_bot, diamond_membership_expires_at: user_details.diamond_membership_expires_at, verified_credential_args: args.verified_credential_args, + unique_person_proof: user_details.unique_person_proof.clone(), }; match community_canister_c2c_client::c2c_join_community(args.community_id.into(), &c2c_args).await { Ok(response) => match response { diff --git a/backend/canisters/local_user_index/impl/src/updates/join_group.rs b/backend/canisters/local_user_index/impl/src/updates/join_group.rs index 1178acb096..e7628ef7ec 100644 --- a/backend/canisters/local_user_index/impl/src/updates/join_group.rs +++ b/backend/canisters/local_user_index/impl/src/updates/join_group.rs @@ -21,6 +21,7 @@ async fn join_group(args: Args) -> Response { is_bot: user_details.is_bot, diamond_membership_expires_at: user_details.diamond_membership_expires_at, verified_credential_args: args.verified_credential_args.clone(), + unique_person_proof: user_details.unique_person_proof.clone(), }; match group_canister_c2c_client::c2c_join_group(args.chat_id.into(), &c2c_args).await { Ok(response) => match response { diff --git a/backend/canisters/user_index/CHANGELOG.md b/backend/canisters/user_index/CHANGELOG.md index 03b844c47b..fdf21465e1 100644 --- a/backend/canisters/user_index/CHANGELOG.md +++ b/backend/canisters/user_index/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [unreleased] +### Added + +- Store `unique_person_proof` alongside each relevant account ([#5993](https://github.com/open-chat-labs/open-chat/pull/5993)) + ## [[2.0.1225](https://github.com/open-chat-labs/open-chat/releases/tag/v2.0.1225-user_index)] - 2024-07-04 ### Changed diff --git a/backend/canisters/user_index/impl/src/model/user.rs b/backend/canisters/user_index/impl/src/model/user.rs index 11e58cd560..60106aa1f4 100644 --- a/backend/canisters/user_index/impl/src/model/user.rs +++ b/backend/canisters/user_index/impl/src/model/user.rs @@ -4,7 +4,8 @@ use candid::{CandidType, Principal}; use serde::{Deserialize, Serialize}; use types::{ is_default, is_empty_slice, CyclesTopUp, CyclesTopUpInternal, PhoneNumber, RegistrationFee, SuspensionAction, - SuspensionDuration, TimestampMillis, UserId, UserSummary, UserSummaryStable, UserSummaryV2, UserSummaryVolatile, + SuspensionDuration, TimestampMillis, UniquePersonProof, UserId, UserSummary, UserSummaryStable, UserSummaryV2, + UserSummaryVolatile, }; #[derive(Serialize, Deserialize, Clone)] @@ -58,7 +59,9 @@ pub struct User { #[serde(rename = "cu", default)] pub chit_updated: TimestampMillis, #[serde(rename = "lc", default)] - pub lastest_chit_event: TimestampMillis, + pub latest_chit_event: TimestampMillis, + #[serde(rename = "uh", default, skip_serializing_if = "Option::is_none")] + pub unique_person_proof: Option, } impl User { @@ -112,7 +115,8 @@ impl User { chit_updated: now, streak: 0, streak_ends: 0, - lastest_chit_event: 0, + latest_chit_event: 0, + unique_person_proof: None, } } @@ -223,7 +227,8 @@ impl Default for User { streak: 0, streak_ends: 0, chit_updated: 0, - lastest_chit_event: 0, + latest_chit_event: 0, + unique_person_proof: None, } } } diff --git a/backend/canisters/user_index/impl/src/model/user_map.rs b/backend/canisters/user_index/impl/src/model/user_map.rs index 6124db74b9..110d94c330 100644 --- a/backend/canisters/user_index/impl/src/model/user_map.rs +++ b/backend/canisters/user_index/impl/src/model/user_map.rs @@ -202,11 +202,11 @@ impl UserMap { return false; }; - if chit_event_timestamp <= user.lastest_chit_event { + if chit_event_timestamp <= user.latest_chit_event { return false; } - user.lastest_chit_event = chit_event_timestamp; + user.latest_chit_event = chit_event_timestamp; user.chit_balance = chit_balance; user.streak = streak; user.streak_ends = streak_ends; diff --git a/backend/canisters/user_index/impl/src/timer_job_types.rs b/backend/canisters/user_index/impl/src/timer_job_types.rs index 5a733874f0..8454cd029a 100644 --- a/backend/canisters/user_index/impl/src/timer_job_types.rs +++ b/backend/canisters/user_index/impl/src/timer_job_types.rs @@ -268,6 +268,7 @@ impl Job for JoinUserToGroup { .get_by_user_id(&self.user_id) .and_then(|u| u.diamond_membership_details.expires_at()), verified_credential_args: None, + unique_person_proof: None, }) }) { ic_cdk::spawn(join_group(self.group_id, args, self.attempt)); diff --git a/backend/integration_tests/src/client/user.rs b/backend/integration_tests/src/client/user.rs index ab0e97c81e..f231497bd8 100644 --- a/backend/integration_tests/src/client/user.rs +++ b/backend/integration_tests/src/client/user.rs @@ -13,9 +13,9 @@ generate_query_call!(updates); generate_update_call!(accept_p2p_swap); generate_update_call!(add_reaction); generate_update_call!(block_user); -generate_update_call!(claim_daily_chit); generate_update_call!(cancel_message_reminder); generate_update_call!(cancel_p2p_swap); +generate_update_call!(claim_daily_chit); generate_update_call!(create_community); generate_update_call!(create_group); generate_update_call!(delete_community); diff --git a/backend/libraries/gated_groups/src/lib.rs b/backend/libraries/gated_groups/src/lib.rs index 8093ae0289..d377e7792d 100644 --- a/backend/libraries/gated_groups/src/lib.rs +++ b/backend/libraries/gated_groups/src/lib.rs @@ -4,8 +4,8 @@ use icrc_ledger_types::icrc2::transfer_from::TransferFromArgs; use sns_governance_canister::types::neuron::DissolveState; use sns_governance_canister::types::Neuron; use types::{ - AccessGate, CanisterId, GateCheckFailedReason, PaymentGate, SnsNeuronGate, TimestampMillis, TokenBalanceGate, UserId, - VerifiedCredentialGate, + AccessGate, CanisterId, GateCheckFailedReason, PaymentGate, SnsNeuronGate, TimestampMillis, TokenBalanceGate, + UniquePersonProof, UserId, VerifiedCredentialGate, }; use utils::consts::MEMO_JOINING_FEE; use utils::time::{DAY_IN_MS, NANOS_PER_MILLISECOND}; @@ -23,6 +23,7 @@ pub struct CheckGateArgs { pub user_id: UserId, pub diamond_membership_expires_at: Option, pub this_canister: CanisterId, + pub unique_person_proof: Option, pub verified_credential_args: Option, pub now: TimestampMillis, } @@ -37,9 +38,10 @@ pub struct CheckVerifiedCredentialGateArgs { pub async fn check_if_passes_gate(args: CheckGateArgs) -> CheckIfPassesGateResult { match args.gate { - AccessGate::VerifiedCredential(g) => check_verified_credential_gate(&g, args.verified_credential_args, args.now).await, AccessGate::DiamondMember => check_diamond_member_gate(args.diamond_membership_expires_at, args.now), AccessGate::LifetimeDiamondMember => check_lifetime_diamond_member_gate(args.diamond_membership_expires_at, args.now), + AccessGate::UniquePerson => check_unique_person_gate(args.unique_person_proof), + AccessGate::VerifiedCredential(g) => check_verified_credential_gate(&g, args.verified_credential_args, args.now), AccessGate::SnsNeuron(g) => check_sns_neuron_gate(&g, args.user_id).await, AccessGate::Payment(g) => try_transfer_from(&g, args.user_id, args.this_canister, args.now).await, AccessGate::TokenBalance(g) => check_token_balance_gate(&g, args.user_id).await, @@ -49,6 +51,9 @@ pub async fn check_if_passes_gate(args: CheckGateArgs) -> CheckIfPassesGateResul pub fn check_if_passes_gate_synchronously(args: CheckGateArgs) -> CheckIfPassesGateResult { match args.gate { AccessGate::DiamondMember => check_diamond_member_gate(args.diamond_membership_expires_at, args.now), + AccessGate::LifetimeDiamondMember => check_lifetime_diamond_member_gate(args.diamond_membership_expires_at, args.now), + AccessGate::UniquePerson => check_unique_person_gate(args.unique_person_proof), + AccessGate::VerifiedCredential(g) => check_verified_credential_gate(&g, args.verified_credential_args, args.now), _ => CheckIfPassesGateResult::InternalError("Gate check could not be performed synchronously".to_string()), } } @@ -76,7 +81,15 @@ fn check_lifetime_diamond_member_gate( } } -async fn check_verified_credential_gate( +fn check_unique_person_gate(proof: Option) -> CheckIfPassesGateResult { + if proof.is_some() { + CheckIfPassesGateResult::Success + } else { + CheckIfPassesGateResult::Failed(GateCheckFailedReason::NoUniquePersonProof) + } +} + +fn check_verified_credential_gate( _gate: &VerifiedCredentialGate, args: Option, _now: TimestampMillis, diff --git a/backend/libraries/types/can.did b/backend/libraries/types/can.did index ef8274c6a5..3e746383dd 100644 --- a/backend/libraries/types/can.did +++ b/backend/libraries/types/can.did @@ -1517,6 +1517,7 @@ type DiamondMembershipStatusFull = variant { type AccessGate = variant { DiamondMember; LifetimeDiamondMember; + UniquePerson; VerifiedCredential : VerifiedCredentialGate; SnsNeuron : SnsNeuronGate; Payment : PaymentGate; @@ -1565,6 +1566,7 @@ type TokenBalanceGate = record { type GateCheckFailedReason = variant { NotDiamondMember; NotLifetimeDiamondMember; + NoUniquePersonProof; NoSnsNeuronsFound; NoSnsNeuronsWithRequiredStakeFound; NoSnsNeuronsWithRequiredDissolveDelayFound; diff --git a/backend/libraries/types/src/gated_groups.rs b/backend/libraries/types/src/gated_groups.rs index 13fc274bca..8fc2074e24 100644 --- a/backend/libraries/types/src/gated_groups.rs +++ b/backend/libraries/types/src/gated_groups.rs @@ -10,6 +10,7 @@ pub const SNS_FEE_SHARE_PERCENT: u128 = 2; pub enum AccessGate { DiamondMember, LifetimeDiamondMember, + UniquePerson, VerifiedCredential(VerifiedCredentialGate), SnsNeuron(SnsNeuronGate), Payment(PaymentGate), @@ -20,7 +21,10 @@ impl AccessGate { pub fn synchronous(&self) -> bool { matches!( self, - AccessGate::DiamondMember | AccessGate::LifetimeDiamondMember | AccessGate::VerifiedCredential(_) + AccessGate::DiamondMember + | AccessGate::LifetimeDiamondMember + | AccessGate::UniquePerson + | AccessGate::VerifiedCredential(_) ) } @@ -32,6 +36,7 @@ impl AccessGate { match self { AccessGate::DiamondMember => "diamond", AccessGate::LifetimeDiamondMember => "lifetime_diamond", + AccessGate::UniquePerson => "unique_person", AccessGate::VerifiedCredential(_) => "verified_credential", AccessGate::SnsNeuron(_) => "sns_neuron", AccessGate::Payment(_) => "payment", @@ -79,6 +84,7 @@ pub struct TokenBalanceGate { pub enum GateCheckFailedReason { NotDiamondMember, NotLifetimeDiamondMember, + NoUniquePersonProof, NoSnsNeuronsFound, NoSnsNeuronsWithRequiredStakeFound, NoSnsNeuronsWithRequiredDissolveDelayFound, diff --git a/backend/libraries/types/src/lib.rs b/backend/libraries/types/src/lib.rs index f7b6b67112..6b255aadb5 100644 --- a/backend/libraries/types/src/lib.rs +++ b/backend/libraries/types/src/lib.rs @@ -53,6 +53,7 @@ mod option; mod p2p_swaps; mod phone_number; mod polls; +mod proof_of_uniqueness; mod proposals; mod range_set; mod reactions; @@ -123,6 +124,7 @@ pub use option::*; pub use p2p_swaps::*; pub use phone_number::*; pub use polls::*; +pub use proof_of_uniqueness::*; pub use proposals::*; pub use range_set::*; pub use reactions::*; diff --git a/backend/libraries/types/src/proof_of_uniqueness.rs b/backend/libraries/types/src/proof_of_uniqueness.rs new file mode 100644 index 0000000000..6c1642148a --- /dev/null +++ b/backend/libraries/types/src/proof_of_uniqueness.rs @@ -0,0 +1,14 @@ +use crate::TimestampMillis; +use candid::CandidType; +use serde::{Deserialize, Serialize}; + +#[derive(CandidType, Serialize, Deserialize, Clone, Debug)] +pub struct UniquePersonProof { + pub timestamp: TimestampMillis, + pub provider: UniquePersonProofProvider, +} + +#[derive(CandidType, Serialize, Deserialize, Clone, Debug)] +pub enum UniquePersonProofProvider { + DecideAI, +}