diff --git a/CHANGELOG.md b/CHANGELOG.md index 89870a39a1..c14f7389f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file. ### Changes +- Serviceability + - Gate Device and device-interface instructions on `NETWORK_ADMIN` (and `HEALTH_ORACLE` for sethealth) or the contributor owner via `authorize()`; internal foundation-only sub-gates now also accept NETWORK_ADMIN holders. (#3980) - 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 diff --git a/smartcontract/programs/doublezero-serviceability/src/processors/device/create.rs b/smartcontract/programs/doublezero-serviceability/src/processors/device/create.rs index d6b4a526a8..d07e840e9f 100644 --- a/smartcontract/programs/doublezero-serviceability/src/processors/device/create.rs +++ b/smartcontract/programs/doublezero-serviceability/src/processors/device/create.rs @@ -1,4 +1,5 @@ use crate::{ + authorize::authorize, error::DoubleZeroError, pda::get_device_pda, processors::resource::create_resource, @@ -7,7 +8,7 @@ use crate::{ serializer::{try_acc_create, try_acc_write}, state::{ accounttype::AccountType, contributor::Contributor, device::*, exchange::Exchange, - globalstate::GlobalState, location::Location, + globalstate::GlobalState, location::Location, permission::permission_flags, }, }; use borsh::BorshSerialize; @@ -127,8 +128,17 @@ pub fn process_create_device( let mut contributor = Contributor::try_from(contributor_account)?; + // Authorization: the contributor owner, or NETWORK_ADMIN (Permission account) / + // foundation (legacy). if contributor.owner != *payer_account.key - && !globalstate.foundation_allowlist.contains(payer_account.key) + && authorize( + program_id, + accounts_iter, + payer_account.key, + &globalstate, + permission_flags::NETWORK_ADMIN, + ) + .is_err() { return Err(DoubleZeroError::InvalidOwnerPubkey.into()); } diff --git a/smartcontract/programs/doublezero-serviceability/src/processors/device/delete.rs b/smartcontract/programs/doublezero-serviceability/src/processors/device/delete.rs index 3e4b1fbc8f..7ed760322b 100644 --- a/smartcontract/programs/doublezero-serviceability/src/processors/device/delete.rs +++ b/smartcontract/programs/doublezero-serviceability/src/processors/device/delete.rs @@ -1,10 +1,11 @@ use crate::{ + authorize::authorize, error::DoubleZeroError, processors::validation::validate_program_account, serializer::{try_acc_close, try_acc_write}, state::{ accounttype::AccountType, contributor::Contributor, device::*, exchange::Exchange, - globalstate::GlobalState, location::Location, + globalstate::GlobalState, location::Location, permission::permission_flags, }, }; use borsh::BorshSerialize; @@ -104,8 +105,17 @@ pub fn process_delete_device( let mut contributor = Contributor::try_from(contributor_account)?; + // Authorization: the contributor owner, or NETWORK_ADMIN (Permission account) / + // foundation (legacy). if contributor.owner != *payer_account.key - && !globalstate.foundation_allowlist.contains(payer_account.key) + && authorize( + program_id, + accounts_iter, + payer_account.key, + &globalstate, + permission_flags::NETWORK_ADMIN, + ) + .is_err() { return Err(DoubleZeroError::NotAllowed.into()); } diff --git a/smartcontract/programs/doublezero-serviceability/src/processors/device/interface/create.rs b/smartcontract/programs/doublezero-serviceability/src/processors/device/interface/create.rs index eedde19e71..6f47fc02fd 100644 --- a/smartcontract/programs/doublezero-serviceability/src/processors/device/interface/create.rs +++ b/smartcontract/programs/doublezero-serviceability/src/processors/device/interface/create.rs @@ -1,4 +1,5 @@ use crate::{ + authorize::authorize, error::DoubleZeroError, pda::get_resource_extension_pda, processors::{ @@ -16,6 +17,7 @@ use crate::{ Interface, InterfaceCYOA, InterfaceDIA, InterfaceStatus, InterfaceType, LoopbackType, RoutingMode, CURRENT_INTERFACE_SCHEMA_VERSION, CYOA_DIA_INTERFACE_MTU, INTERFACE_MTU, }, + permission::permission_flags, topology::FlexAlgoNodeSegment, }, }; @@ -136,9 +138,18 @@ pub fn process_create_device_interface( let contributor = Contributor::try_from(contributor_account)?; - if contributor.owner != *payer_account.key - && !globalstate.foundation_allowlist.contains(payer_account.key) - { + // Authorization: the contributor owner, or NETWORK_ADMIN (Permission account) / + // foundation (legacy). Privileged callers also bypass the device-contributor + // binding checked below. + let is_privileged = authorize( + program_id, + accounts_iter, + payer_account.key, + &globalstate, + permission_flags::NETWORK_ADMIN, + ) + .is_ok(); + if contributor.owner != *payer_account.key && !is_privileged { return Err(DoubleZeroError::InvalidOwnerPubkey.into()); } @@ -184,9 +195,7 @@ pub fn process_create_device_interface( // The supplied contributor must be the one the device belongs to, // unless the payer is on the foundation allowlist. - if !globalstate.foundation_allowlist.contains(payer_account.key) - && device.contributor_pk != *contributor_account.key - { + if !is_privileged && device.contributor_pk != *contributor_account.key { return Err(DoubleZeroError::InvalidContributorPubkey.into()); } diff --git a/smartcontract/programs/doublezero-serviceability/src/processors/device/interface/delete.rs b/smartcontract/programs/doublezero-serviceability/src/processors/device/interface/delete.rs index 8754f0b3fb..bd09e50aa0 100644 --- a/smartcontract/programs/doublezero-serviceability/src/processors/device/interface/delete.rs +++ b/smartcontract/programs/doublezero-serviceability/src/processors/device/interface/delete.rs @@ -1,4 +1,5 @@ use crate::{ + authorize::authorize, error::DoubleZeroError, pda::get_resource_extension_pda, processors::{ @@ -13,6 +14,7 @@ use crate::{ device::*, globalstate::GlobalState, interface::{InterfaceStatus, InterfaceType, LoopbackType}, + permission::permission_flags, }, }; use borsh::BorshSerialize; @@ -105,19 +107,26 @@ pub fn process_delete_device_interface( let contributor = Contributor::try_from(contributor_account)?; - if contributor.owner != *payer_account.key - && !globalstate.foundation_allowlist.contains(payer_account.key) - { + // Authorization: the contributor owner, or NETWORK_ADMIN (Permission account) / + // foundation (legacy). Privileged callers also bypass the device-contributor + // binding below. + let is_privileged = authorize( + program_id, + accounts_iter, + payer_account.key, + &globalstate, + permission_flags::NETWORK_ADMIN, + ) + .is_ok(); + if contributor.owner != *payer_account.key && !is_privileged { return Err(DoubleZeroError::NotAllowed.into()); } let mut device: Device = Device::try_from(device_account)?; - // The supplied contributor must be the one the device belongs to, - // unless the payer is on the foundation allowlist. - if !globalstate.foundation_allowlist.contains(payer_account.key) - && device.contributor_pk != *contributor_account.key - { + // The supplied contributor must be the one the device belongs to, unless the + // caller is privileged (foundation or NETWORK_ADMIN). + if !is_privileged && device.contributor_pk != *contributor_account.key { return Err(DoubleZeroError::InvalidContributorPubkey.into()); } diff --git a/smartcontract/programs/doublezero-serviceability/src/processors/device/interface/update.rs b/smartcontract/programs/doublezero-serviceability/src/processors/device/interface/update.rs index 9d0330fe31..6ad1f76703 100644 --- a/smartcontract/programs/doublezero-serviceability/src/processors/device/interface/update.rs +++ b/smartcontract/programs/doublezero-serviceability/src/processors/device/interface/update.rs @@ -1,4 +1,5 @@ use crate::{ + authorize::{authorize, split_trailing_permission}, error::{DoubleZeroError, Validate}, format_option, helper::format_option_displayable, @@ -18,6 +19,7 @@ use crate::{ InterfaceCYOA, InterfaceDIA, InterfaceStatus, InterfaceType, LoopbackType, RoutingMode, CYOA_DIA_INTERFACE_MTU, INTERFACE_MTU, }, + permission::permission_flags, topology::FlexAlgoNodeSegment, }, }; @@ -110,21 +112,25 @@ pub fn process_update_device_interface( // The presence of update_topologies forces seg_ext consumption; otherwise // fall back to the legacy account-count heuristic so callers that set // node_segment_idx without onchain allocation enabled still work. - let segment_routing_ids_ext = if value.update_topologies || accounts.len() > 5 { - Some(next_account_info(accounts_iter)?) + // Remaining tail: [segment_routing_ids?, topology_0..N?, payer, system, permission?]. + // 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 it never + // inflates the optional-account detection. What's left (`mid`) is the optional + // SegmentRoutingIds account followed by the optional topology union. + let rest: Vec<&AccountInfo> = accounts_iter.collect(); + let (payer_account, _system_program, mid, permission_account) = + split_trailing_permission(program_id, &rest)?; + let seg_ext_present = value.update_topologies || !mid.is_empty(); + let segment_routing_ids_ext = if seg_ext_present { + mid.first().copied() } else { None }; - - let mut topology_accounts = Vec::new(); - if value.update_topologies { - for _ in 0..value.topology_count { - topology_accounts.push(next_account_info(accounts_iter)?); - } - } - - let payer_account = next_account_info(accounts_iter)?; - let _system_program = next_account_info(accounts_iter)?; + let topology_accounts: Vec<&AccountInfo> = if seg_ext_present { + mid.get(1..).unwrap_or(&[]).to_vec() + } else { + Vec::new() + }; #[cfg(test)] msg!("process_update_device_interface({:?})", value); @@ -152,19 +158,26 @@ pub fn process_update_device_interface( let contributor = Contributor::try_from(contributor_account)?; - if contributor.owner != *payer_account.key - && !globalstate.foundation_allowlist.contains(payer_account.key) - { + // Authorization: the contributor owner, or NETWORK_ADMIN (Permission account) / + // foundation (legacy). Privileged callers also bypass the device-contributor + // binding and the foundation-only gates below. + let is_privileged = authorize( + program_id, + &mut permission_account.into_iter(), + payer_account.key, + &globalstate, + permission_flags::NETWORK_ADMIN, + ) + .is_ok(); + if contributor.owner != *payer_account.key && !is_privileged { return Err(DoubleZeroError::NotAllowed.into()); } let mut device: Device = Device::try_from(device_account)?; - // The supplied contributor must be the one the device belongs to, - // unless the payer is on the foundation allowlist. - if !globalstate.foundation_allowlist.contains(payer_account.key) - && device.contributor_pk != *contributor_account.key - { + // The supplied contributor must be the one the device belongs to, unless the + // caller is privileged (foundation or NETWORK_ADMIN). + if !is_privileged && device.contributor_pk != *contributor_account.key { return Err(DoubleZeroError::InvalidContributorPubkey.into()); } @@ -229,7 +242,7 @@ pub fn process_update_device_interface( iface.ip_net = ip_net; } if let Some(node_segment_idx) = value.node_segment_idx { - if !globalstate.foundation_allowlist.contains(payer_account.key) { + if !is_privileged { return Err(DoubleZeroError::NotAllowed.into()); } @@ -263,9 +276,7 @@ pub fn process_update_device_interface( // for removed topologies have their SR ID deallocated; new topologies get a // freshly allocated SR ID. if value.update_topologies { - if contributor.owner != *payer_account.key - && !globalstate.foundation_allowlist.contains(payer_account.key) - { + if contributor.owner != *payer_account.key && !is_privileged { return Err(DoubleZeroError::NotAllowed.into()); } diff --git a/smartcontract/programs/doublezero-serviceability/src/processors/device/resume.rs b/smartcontract/programs/doublezero-serviceability/src/processors/device/resume.rs index 064d445d5d..d64601b80b 100644 --- a/smartcontract/programs/doublezero-serviceability/src/processors/device/resume.rs +++ b/smartcontract/programs/doublezero-serviceability/src/processors/device/resume.rs @@ -1,8 +1,10 @@ use crate::{ + authorize::authorize, error::DoubleZeroError, serializer::try_acc_write, state::{ accounttype::AccountType, contributor::Contributor, device::*, globalstate::GlobalState, + permission::permission_flags, }, }; use borsh::BorshSerialize; @@ -74,8 +76,17 @@ pub fn process_resume_device( let contributor = Contributor::try_from(contributor_account)?; + // Authorization: the contributor owner, or NETWORK_ADMIN (Permission account) / + // foundation (legacy). if contributor.owner != *payer_account.key - && !globalstate.foundation_allowlist.contains(payer_account.key) + && authorize( + program_id, + accounts_iter, + payer_account.key, + &globalstate, + permission_flags::NETWORK_ADMIN, + ) + .is_err() { return Err(DoubleZeroError::NotAllowed.into()); } diff --git a/smartcontract/programs/doublezero-serviceability/src/processors/device/sethealth.rs b/smartcontract/programs/doublezero-serviceability/src/processors/device/sethealth.rs index f6750a1913..ef68850741 100644 --- a/smartcontract/programs/doublezero-serviceability/src/processors/device/sethealth.rs +++ b/smartcontract/programs/doublezero-serviceability/src/processors/device/sethealth.rs @@ -1,9 +1,11 @@ use core::fmt; use crate::{ - error::DoubleZeroError, + authorize::authorize, serializer::try_acc_write, - state::{accounttype::AccountType, device::*, globalstate::GlobalState}, + state::{ + accounttype::AccountType, device::*, globalstate::GlobalState, permission::permission_flags, + }, }; use borsh::BorshSerialize; use borsh_incremental::BorshDeserializeIncremental; @@ -62,11 +64,16 @@ pub fn process_set_health_device( let globalstate = GlobalState::try_from(globalstate_account)?; assert_eq!(globalstate.account_type, AccountType::GlobalState); - if globalstate.health_oracle_pk != *payer_account.key - && !globalstate.foundation_allowlist.contains(payer_account.key) - { - return Err(DoubleZeroError::NotAllowed.into()); - } + // Authorization: HEALTH_ORACLE or foundation, via a Permission account or the + // legacy health_oracle_pk / foundation_allowlist (HEALTH_ORACLE covers the + // oracle key, NETWORK_ADMIN covers foundation). + authorize( + program_id, + accounts_iter, + payer_account.key, + &globalstate, + permission_flags::HEALTH_ORACLE | permission_flags::NETWORK_ADMIN, + )?; let mut device: Device = Device::try_from(device_account)?; device.device_health = value.health; diff --git a/smartcontract/programs/doublezero-serviceability/src/processors/device/update.rs b/smartcontract/programs/doublezero-serviceability/src/processors/device/update.rs index 12931774df..7ccccac60a 100644 --- a/smartcontract/programs/doublezero-serviceability/src/processors/device/update.rs +++ b/smartcontract/programs/doublezero-serviceability/src/processors/device/update.rs @@ -1,4 +1,5 @@ use crate::{ + authorize::authorize, error::DoubleZeroError, pda::get_resource_extension_pda, processors::resource::create_resource, @@ -6,7 +7,8 @@ use crate::{ serializer::{try_acc_close, try_acc_write}, state::{ accounttype::AccountType, contributor::Contributor, device::*, globalstate::GlobalState, - location::Location, resource_extension::ResourceExtensionBorrowed, + location::Location, permission::permission_flags, + resource_extension::ResourceExtensionBorrowed, }, }; use borsh::BorshSerialize; @@ -218,24 +220,32 @@ pub fn process_update_device( let contributor = Contributor::try_from(contributor_account)?; - if contributor.owner != *payer_account.key - && !globalstate.foundation_allowlist.contains(payer_account.key) - { + // Authorization: the contributor owner, or NETWORK_ADMIN (Permission account) / + // foundation (legacy). The permission is the optional trailing account (payer and + // system_program were already consumed above). Privileged callers bypass the + // contributor binding and may edit the privileged fields / set any status below. + let is_privileged = authorize( + program_id, + accounts_iter, + payer_account.key, + &globalstate, + permission_flags::NETWORK_ADMIN, + ) + .is_ok(); + if contributor.owner != *payer_account.key && !is_privileged { return Err(DoubleZeroError::NotAllowed.into()); } let mut device: Device = Device::try_from(device_account)?; - // The supplied contributor must be the one the device belongs to, - // unless the payer is on the foundation allowlist. - if !globalstate.foundation_allowlist.contains(payer_account.key) - && device.contributor_pk != *contributor_account.key - { + // The supplied contributor must be the one the device belongs to, unless the + // caller is privileged (foundation or NETWORK_ADMIN). + if !is_privileged && device.contributor_pk != *contributor_account.key { return Err(DoubleZeroError::InvalidContributorPubkey.into()); } - // Only allow updates from the foundation allowlist - if globalstate.foundation_allowlist.contains(payer_account.key) { + // Only allow these updates from privileged callers (foundation or NETWORK_ADMIN) + if is_privileged { if let Some(contributor_pk) = value.contributor_pk { device.contributor_pk = contributor_pk; } @@ -356,8 +366,8 @@ pub fn process_update_device( } if let Some(status) = value.status { - // Foundation members can set any status - if globalstate.foundation_allowlist.contains(payer_account.key) { + // Privileged callers (foundation or NETWORK_ADMIN) can set any status + if is_privileged { device.status = status; } else { // Contributors can only transition between Activated <-> Drained states, diff --git a/smartcontract/sdk/rs/src/commands/device/create.rs b/smartcontract/sdk/rs/src/commands/device/create.rs index 80be30d00e..6916eb5457 100644 --- a/smartcontract/sdk/rs/src/commands/device/create.rs +++ b/smartcontract/sdk/rs/src/commands/device/create.rs @@ -65,7 +65,7 @@ impl CreateDeviceCommand { })?; client - .execute_transaction( + .execute_authorized_transaction( DoubleZeroInstruction::CreateDevice(DeviceCreateArgs { code, device_type: self.device_type, @@ -167,7 +167,7 @@ mod tests { let pubmetrics_publisher = Pubkey::default(); client - .expect_execute_transaction() + .expect_execute_authorized_transaction() .with( predicate::eq(DoubleZeroInstruction::CreateDevice(DeviceCreateArgs { code: "test_device".to_string(), diff --git a/smartcontract/sdk/rs/src/commands/device/delete.rs b/smartcontract/sdk/rs/src/commands/device/delete.rs index 80b299ee74..cb65e26a82 100644 --- a/smartcontract/sdk/rs/src/commands/device/delete.rs +++ b/smartcontract/sdk/rs/src/commands/device/delete.rs @@ -52,7 +52,7 @@ impl DeleteDeviceCommand { if resource_accounts.is_empty() { // Legacy path - client.execute_transaction( + client.execute_authorized_transaction( DoubleZeroInstruction::DeleteDevice(DeviceDeleteArgs::default()), vec![ AccountMeta::new(self.pubkey, false), @@ -74,7 +74,7 @@ impl DeleteDeviceCommand { accounts.extend(owner_accounts); accounts.push(AccountMeta::new(device.owner, false)); - client.execute_transaction( + client.execute_authorized_transaction( DoubleZeroInstruction::DeleteDevice(DeviceDeleteArgs { resource_count }), accounts, ) @@ -171,7 +171,7 @@ mod tests { .returning(|_| Err(eyre::eyre!("not found"))); client - .expect_execute_transaction() + .expect_execute_authorized_transaction() .with( predicate::eq(DoubleZeroInstruction::DeleteDevice( DeviceDeleteArgs::default(), @@ -272,7 +272,7 @@ mod tests { }); client - .expect_execute_transaction() + .expect_execute_authorized_transaction() .with( predicate::eq(DoubleZeroInstruction::DeleteDevice(DeviceDeleteArgs { resource_count: 2, diff --git a/smartcontract/sdk/rs/src/commands/device/interface/create.rs b/smartcontract/sdk/rs/src/commands/device/interface/create.rs index 45fba8f9c9..81f0eb4ba5 100644 --- a/smartcontract/sdk/rs/src/commands/device/interface/create.rs +++ b/smartcontract/sdk/rs/src/commands/device/interface/create.rs @@ -72,7 +72,7 @@ impl CreateDeviceInterfaceCommand { } client - .execute_transaction( + .execute_authorized_transaction( DoubleZeroInstruction::CreateDeviceInterface(DeviceInterfaceCreateArgs { name: self.name.clone(), loopback_type: self.loopback_type, @@ -162,7 +162,7 @@ mod tests { .returning(move |_| Ok(AccountData::Device(device.clone()))); client - .expect_execute_transaction() + .expect_execute_authorized_transaction() .with( predicate::eq(DoubleZeroInstruction::CreateDeviceInterface( DeviceInterfaceCreateArgs { diff --git a/smartcontract/sdk/rs/src/commands/device/interface/delete.rs b/smartcontract/sdk/rs/src/commands/device/interface/delete.rs index 4f64644ce3..a8c85f510b 100644 --- a/smartcontract/sdk/rs/src/commands/device/interface/delete.rs +++ b/smartcontract/sdk/rs/src/commands/device/interface/delete.rs @@ -37,7 +37,7 @@ impl DeleteDeviceInterfaceCommand { AccountMeta::new(segment_routing_ids_ext, false), ]; - client.execute_transaction( + client.execute_authorized_transaction( DoubleZeroInstruction::DeleteDeviceInterface(DeviceInterfaceDeleteArgs { name: self.name.clone(), use_onchain_deallocation: true, @@ -115,7 +115,7 @@ mod tests { .returning(move |_| Ok(AccountData::Device(device.clone()))); client - .expect_execute_transaction() + .expect_execute_authorized_transaction() .with( predicate::eq(DoubleZeroInstruction::DeleteDeviceInterface( DeviceInterfaceDeleteArgs { diff --git a/smartcontract/sdk/rs/src/commands/device/interface/update.rs b/smartcontract/sdk/rs/src/commands/device/interface/update.rs index 5861114ebe..8525ce0299 100644 --- a/smartcontract/sdk/rs/src/commands/device/interface/update.rs +++ b/smartcontract/sdk/rs/src/commands/device/interface/update.rs @@ -77,7 +77,7 @@ impl UpdateDeviceInterfaceCommand { } } - client.execute_transaction( + client.execute_authorized_transaction( DoubleZeroInstruction::UpdateDeviceInterface(DeviceInterfaceUpdateArgs { name: self.name.clone(), loopback_type: self.loopback_type, @@ -163,7 +163,7 @@ mod tests { .returning(move |_| Ok(AccountData::Device(device.clone()))); client - .expect_execute_transaction() + .expect_execute_authorized_transaction() .with( predicate::eq(DoubleZeroInstruction::UpdateDeviceInterface( DeviceInterfaceUpdateArgs { @@ -222,7 +222,7 @@ mod tests { .returning(move |_| Ok(AccountData::Device(device.clone()))); client - .expect_execute_transaction() + .expect_execute_authorized_transaction() .with( predicate::eq(DoubleZeroInstruction::UpdateDeviceInterface( DeviceInterfaceUpdateArgs { @@ -285,7 +285,7 @@ mod tests { let (topo_b, _) = get_topology_pda(&program_id, "TOPO-B"); client - .expect_execute_transaction() + .expect_execute_authorized_transaction() .with( predicate::eq(DoubleZeroInstruction::UpdateDeviceInterface( DeviceInterfaceUpdateArgs { diff --git a/smartcontract/sdk/rs/src/commands/device/sethealth.rs b/smartcontract/sdk/rs/src/commands/device/sethealth.rs index c5ae3b41a5..8d03c43a97 100644 --- a/smartcontract/sdk/rs/src/commands/device/sethealth.rs +++ b/smartcontract/sdk/rs/src/commands/device/sethealth.rs @@ -17,7 +17,7 @@ impl SetDeviceHealthCommand { .execute(client) .map_err(|_err| eyre::eyre!("Globalstate not initialized"))?; - client.execute_transaction( + client.execute_authorized_transaction( DoubleZeroInstruction::SetDeviceHealth(DeviceSetHealthArgs { health: self.health, }), @@ -112,7 +112,7 @@ mod tests { }); client - .expect_execute_transaction() + .expect_execute_authorized_transaction() .with( predicate::eq(DoubleZeroInstruction::UpdateDevice(DeviceUpdateArgs { code: Some("test_device".to_string()), diff --git a/smartcontract/sdk/rs/src/commands/device/update.rs b/smartcontract/sdk/rs/src/commands/device/update.rs index fba38fed37..76e5670d6c 100644 --- a/smartcontract/sdk/rs/src/commands/device/update.rs +++ b/smartcontract/sdk/rs/src/commands/device/update.rs @@ -86,7 +86,7 @@ impl UpdateDeviceCommand { resource_count += max_count + 1; } - client.execute_transaction( + client.execute_authorized_transaction( DoubleZeroInstruction::UpdateDevice(DeviceUpdateArgs { code, contributor_pk: self.contributor_pk, @@ -206,7 +206,7 @@ mod tests { .with(predicate::eq(device_pubkey)) .returning(move |_| Ok(AccountData::Device(device.clone()))); client - .expect_execute_transaction() + .expect_execute_authorized_transaction() .with( predicate::eq(DoubleZeroInstruction::UpdateDevice(DeviceUpdateArgs { code: Some("test_device".to_string()),