Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file.

### Changes

- Serviceability
- Gate UpdateUser on `USER_ADMIN`, CheckAccessPass on `ACTIVATOR`, and accesspass CheckStatus on `ACTIVATOR|USER_ADMIN` via `authorize()`; user create and set_bgp_status remain owner-authorized (not part of the admin Permission system). (#3984)
- Collector
- Harden ledger writes against a slow/degraded RPC endpoint: bound each RPC request (default 15s, `--ledger-rpc-timeout`), size the connection pool above the submitter concurrency (default 128, `--ledger-rpc-max-conns`), and deadline each submission attempt so it fails fast and retries with a fresh blockhash instead of sending an expired one and failing preflight with `BlockhashNotFound`. (#3973)
- E2E
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
use crate::{
authorize::authorize,
error::DoubleZeroError,
serializer::try_acc_write,
state::{accesspass::AccessPass, globalstate::GlobalState},
state::{accesspass::AccessPass, globalstate::GlobalState, permission::permission_flags},
};
use borsh::BorshSerialize;
use borsh_incremental::BorshDeserializeIncremental;
use core::fmt;

#[cfg(test)]
use solana_program::msg;
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint::ProgramResult,
msg,
pubkey::Pubkey,
};

Expand Down Expand Up @@ -63,19 +65,17 @@ pub fn process_check_status_access_pass(
"PDA Account is not writable"
);

// Parse the global state account & check if the payer is in the allowlist
// Authorization: ACTIVATOR or foundation, via a Permission account or the legacy
// activator_authority_pk / foundation_allowlist (ACTIVATOR covers the activator
// authority, USER_ADMIN covers foundation).
let globalstate = GlobalState::try_from(globalstate_account)?;
if globalstate.activator_authority_pk != *payer_account.key
&& !globalstate.foundation_allowlist.contains(payer_account.key)
{
msg!(
"activator_authority_pk: {} payer: {} foundation_allowlist: {:?}",
globalstate.activator_authority_pk,
payer_account.key,
globalstate.foundation_allowlist
);
return Err(DoubleZeroError::NotAllowed.into());
}
authorize(
program_id,
accounts_iter,
payer_account.key,
&globalstate,
permission_flags::ACTIVATOR | permission_flags::USER_ADMIN,
)?;

let mut accesspass = AccessPass::try_from(accesspass_account)?;
// Update status
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use crate::{
authorize::authorize,
error::DoubleZeroError,
pda::get_accesspass_pda,
processors::validation::validate_program_account,
serializer::try_acc_write,
state::{
accesspass::AccessPass,
globalstate::GlobalState,
permission::permission_flags,
user::{User, UserStatus},
},
};
Expand Down Expand Up @@ -71,10 +73,15 @@ pub fn process_check_access_pass_user(
"Invalid System Program Account Owner"
);

// Authorization: ACTIVATOR (Permission account) or activator authority (legacy).
let globalstate = GlobalState::try_from(globalstate_account)?;
if globalstate.activator_authority_pk != *payer_account.key {
return Err(DoubleZeroError::NotAllowed.into());
}
authorize(
program_id,
accounts_iter,
payer_account.key,
&globalstate,
permission_flags::ACTIVATOR,
)?;

let mut user: User = User::try_from(user_account)?;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{
authorize::{authorize, split_trailing_permission},
error::DoubleZeroError,
format_option,
helper::format_option_displayable,
Expand All @@ -9,7 +10,7 @@ use crate::{
},
resource::ResourceType,
serializer::try_acc_write,
state::{globalstate::GlobalState, tenant::Tenant, user::*},
state::{globalstate::GlobalState, permission::permission_flags, tenant::Tenant, user::*},
};
use borsh::BorshSerialize;
use borsh_incremental::BorshDeserializeIncremental;
Expand Down Expand Up @@ -97,23 +98,19 @@ pub fn process_update_user(
dz_prefix_accounts.push(next_account_info(accounts_iter)?);
}

// Tenant accounts are optional — present when tenant_pk is being updated.
// We compute the expected count of remaining accounts to detect their presence.
// Remaining accounts: [old_tenant?, new_tenant?, payer, system]
// With tenants: 4 remaining. Without tenants: 2 remaining.
let remaining: Vec<_> = accounts_iter.collect();
let has_tenant_accounts = remaining.len() >= 4;
let (old_tenant_account, new_tenant_account, payer_account, _system_program) =
if has_tenant_accounts {
(
Some(remaining[0]),
Some(remaining[1]),
remaining[2],
remaining[3],
)
} else {
(None, None, remaining[0], remaining[1])
};
// Remaining tail: [old_tenant?, new_tenant?, payer, system, permission?]. The
// optional tenant pair is present when tenant_pk is being updated.
// split_trailing_permission peels payer/system — and the optional payer
// Permission PDA the SDK appends when it exists — off the tail by PDA match, so
// the tenant accounts are detected unambiguously via what's left (`leading`).
let remaining: Vec<&AccountInfo> = accounts_iter.collect();
let (payer_account, _system_program, leading, permission_account) =
split_trailing_permission(program_id, &remaining)?;
let (old_tenant_account, new_tenant_account) = if leading.len() >= 2 {
(Some(leading[0]), Some(leading[1]))
} else {
(None, None)
};

#[cfg(test)]
msg!("process_update_user({:?})", value);
Expand All @@ -130,10 +127,16 @@ pub fn process_update_user(
"GlobalState"
);

// Authorization: USER_ADMIN (Permission account) or foundation (legacy). This is
// an administrative operation — the User owner does not update via this path.
let globalstate = GlobalState::try_from(globalstate_account)?;
if !globalstate.foundation_allowlist.contains(payer_account.key) {
return Err(DoubleZeroError::NotAllowed.into());
}
authorize(
program_id,
&mut permission_account.into_iter(),
payer_account.key,
&globalstate,
permission_flags::USER_ADMIN,
)?;

let mut user: User = User::try_from(user_account)?;

Expand Down
4 changes: 2 additions & 2 deletions smartcontract/sdk/rs/src/commands/accesspass/check_status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ impl CheckStatusAccessPassCommand {
let (pda_pubkey, _) =
get_accesspass_pda(&client.get_program_id(), &self.client_ip, &self.user_payer);

client.execute_transaction(
client.execute_authorized_transaction(
DoubleZeroInstruction::CheckStatusAccessPass(CheckStatusAccessPassArgs {}),
vec![
AccountMeta::new(pda_pubkey, false),
Expand Down Expand Up @@ -56,7 +56,7 @@ mod tests {
let (pda_pubkey, _) = get_accesspass_pda(&client.get_program_id(), &client_ip, &payer);

client
.expect_execute_transaction()
.expect_execute_authorized_transaction()
.with(
predicate::eq(DoubleZeroInstruction::CheckStatusAccessPass(
CheckStatusAccessPassArgs {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ impl CheckUserAccessPassCommand {
let (accesspass_pk, _) =
get_accesspass_pda(&client.get_program_id(), &user.client_ip, &user.owner);

client.execute_transaction(
client.execute_authorized_transaction(
DoubleZeroInstruction::CheckUserAccessPass(CheckUserAccessPassArgs {}),
vec![
AccountMeta::new(self.user_pubkey, false),
Expand Down
4 changes: 2 additions & 2 deletions smartcontract/sdk/rs/src/commands/user/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ impl UpdateUserCommand {
accounts.push(AccountMeta::new(new_tenant_pk, false));
}

client.execute_transaction(
client.execute_authorized_transaction(
DoubleZeroInstruction::UpdateUser(UserUpdateArgs {
user_type: self.user_type,
cyoa_type: self.cyoa_type,
Expand Down Expand Up @@ -211,7 +211,7 @@ mod tests {
get_resource_extension_pda(&program_id, ResourceType::DzPrefixBlock(device_pk, 0));

client
.expect_execute_transaction()
.expect_execute_authorized_transaction()
.with(
predicate::eq(DoubleZeroInstruction::UpdateUser(UserUpdateArgs {
user_type: None,
Expand Down
Loading