Skip to content
Draft
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ All notable changes to this project will be documented in this file.
- Serviceability
- `Feed` account: a catalog mapping `metro(exchange) → group-set`, managed by a catalog admin (`FEED_AUTHORITY` Permission or `FOUNDATION`) via `CreateFeed`/`UpdateFeed`/`DeleteFeed`. A feed with no metros imposes no restriction. (#1700)
- `SetAccessPassFeeds` provisions feed_keys (SKU seats) onto an EdgeSeat pass; the oracle calls it via its `ACCESS_PASS_ADMIN` Permission. (#1700)
- EdgeSeat multicast connect is metro-gated: a device whose exchange is not covered by any of the pass's feeds is rejected with `MetroMismatch`, and the matching feed's per-feed cap is enforced. (#1700)

### Changes

Expand Down
13 changes: 13 additions & 0 deletions client/doublezero/src/command/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,7 @@ impl ProvisioningCliCommand {
subscriber: sub_group_pks.contains(first_group_pk),
tunnel_endpoint,
owner: None,
feed_pk: None,
});

let user_pk = match res {
Expand Down Expand Up @@ -711,6 +712,8 @@ impl ProvisioningCliCommand {
client_ip: *client_ip,
publisher: pub_group_pks.contains(group_pk),
subscriber: sub_group_pks.contains(group_pk),
device_pk: None,
feed_pk: None,
})?;
}

Expand All @@ -737,6 +740,8 @@ impl ProvisioningCliCommand {
client_ip: *client_ip,
publisher: true,
subscriber: false,
device_pk: None,
feed_pk: None,
});

match res {
Expand Down Expand Up @@ -766,6 +771,8 @@ impl ProvisioningCliCommand {
client_ip: *client_ip,
publisher: false,
subscriber: true,
device_pk: None,
feed_pk: None,
});

match res {
Expand Down Expand Up @@ -819,6 +826,7 @@ impl ProvisioningCliCommand {
subscriber: sub_group_pks.contains(first_group_pk),
tunnel_endpoint,
owner: None,
feed_pk: None,
});

let user_pk = match res {
Expand Down Expand Up @@ -847,6 +855,8 @@ impl ProvisioningCliCommand {
client_ip: *client_ip,
publisher: pub_group_pks.contains(group_pk),
subscriber: sub_group_pks.contains(group_pk),
device_pk: None,
feed_pk: None,
})?;
}

Expand Down Expand Up @@ -1564,6 +1574,7 @@ mod tests {
subscriber,
tunnel_endpoint: user.tunnel_endpoint,
owner: None,
feed_pk: None,
};

let users = self.users.clone();
Expand Down Expand Up @@ -1603,6 +1614,8 @@ mod tests {
client_ip,
publisher,
subscriber,
device_pk: None,
feed_pk: None,
};

let users = self.users.clone();
Expand Down
5 changes: 4 additions & 1 deletion client/doublezero/src/command/disconnect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,10 @@ impl DecommissioningCliCommand {

spinner.inc(1);
spinner.println(format!("⚡ Removing account: {pubkey}"));
let res = client.delete_user(DeleteUserCommand { pubkey: *pubkey });
let res = client.delete_user(DeleteUserCommand {
pubkey: *pubkey,
feed_pk: None,
});
match res {
Ok(_) => {
spinner.println(" Account deletion submitted");
Expand Down
8 changes: 8 additions & 0 deletions client/doublezero/src/command/multicast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ impl MulticastUnsubscribeCliCommand {
client_ip,
publisher: carry_pub,
subscriber: false,
device_pk: None,
feed_pk: None,
}) {
Ok(_) => spinner.println(format!(" unsubscribed from {code}")),
Err(e) => {
Expand Down Expand Up @@ -219,6 +221,8 @@ impl MulticastUnpublishCliCommand {
client_ip,
publisher: false,
subscriber: carry_sub,
device_pk: None,
feed_pk: None,
}) {
Ok(_) => spinner.println(format!(" unpublished from {code}")),
Err(e) => {
Expand Down Expand Up @@ -261,6 +265,8 @@ impl MulticastSubscribeCliCommand {
client_ip,
publisher: carry_pub,
subscriber: true,
device_pk: None,
feed_pk: None,
}) {
Ok(_) => spinner.println(format!(" subscribed to {code}")),
Err(e) => {
Expand Down Expand Up @@ -303,6 +309,8 @@ impl MulticastPublishCliCommand {
client_ip,
publisher: true,
subscriber: carry_sub,
device_pk: None,
feed_pk: None,
}) {
Ok(_) => spinner.println(format!(" publishing to {code}")),
Err(e) => {
Expand Down
5 changes: 4 additions & 1 deletion smartcontract/cli/src/tenant/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ impl DeleteTenantCliCommand {

for user_pk in &tenant_users {
spinner.set_message(format!("Deleting user {user_pk}"));
client.delete_user(DeleteUserCommand { pubkey: *user_pk })?;
client.delete_user(DeleteUserCommand {
pubkey: *user_pk,
feed_pk: None,
})?;
spinner.inc(1);
}

Expand Down
2 changes: 2 additions & 0 deletions smartcontract/cli/src/user/create_subscribe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ impl CreateSubscribeUserCliCommand {
.ok_or(eyre::eyre!("Subscriber is required if publisher is not"))?,
tunnel_endpoint: Ipv4Addr::UNSPECIFIED,
owner: owner_pk,
feed_pk: None,
})?;
writeln!(out, "Signature: {signature}",)?;

Expand Down Expand Up @@ -234,6 +235,7 @@ mod tests {
mgroup_pk: mgroup_pubkey,
tunnel_endpoint: Ipv4Addr::UNSPECIFIED,
owner: None,
feed_pk: None,
}))
.times(1)
.returning(move |_| Ok((signature, pda_pubkey)));
Expand Down
10 changes: 8 additions & 2 deletions smartcontract/cli/src/user/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ impl DeleteUserCliCommand {
client.check_requirements(CHECK_ID_JSON | CHECK_BALANCE)?;

let pubkey = Pubkey::from_str(&self.pubkey)?;
let signature = client.delete_user(DeleteUserCommand { pubkey })?;
let signature = client.delete_user(DeleteUserCommand {
pubkey,
feed_pk: None,
})?;
writeln!(out, "Signature: {signature}",)?;

Ok(())
Expand Down Expand Up @@ -100,7 +103,10 @@ mod tests {

client
.expect_delete_user()
.with(predicate::eq(DeleteUserCommand { pubkey: pda_pubkey }))
.with(predicate::eq(DeleteUserCommand {
pubkey: pda_pubkey,
feed_pk: None,
}))
.returning(move |_| Ok(signature));

/*****************************************************************************************************/
Expand Down
5 changes: 4 additions & 1 deletion smartcontract/cli/src/user/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,10 @@ mod tests {

client
.expect_delete_user()
.with(predicate::eq(DeleteUserCommand { pubkey: pda_pubkey }))
.with(predicate::eq(DeleteUserCommand {
pubkey: pda_pubkey,
feed_pk: None,
}))
.returning(move |_| Ok(signature));

// Expected success (table)
Expand Down
5 changes: 4 additions & 1 deletion smartcontract/cli/src/user/request_ban.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,10 @@ mod tests {

client
.expect_delete_user()
.with(predicate::eq(DeleteUserCommand { pubkey: pda_pubkey }))
.with(predicate::eq(DeleteUserCommand {
pubkey: pda_pubkey,
feed_pk: None,
}))
.returning(move |_| Ok(signature));
client
.expect_list_foundation_allowlist()
Expand Down
8 changes: 8 additions & 0 deletions smartcontract/cli/src/user/subscribe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ impl SubscribeUserCliCommand {
client_ip: user.client_ip,
publisher,
subscriber,
device_pk: None,
feed_pk: None,
})?;
writeln!(out, "Updated roles for {group_pk}: {signature}")?;
}
Expand Down Expand Up @@ -218,6 +220,8 @@ mod tests {
client_ip,
publisher: false,
subscriber: true,
device_pk: None,
feed_pk: None,
}))
.times(1)
.returning(move |_| Ok(signature));
Expand Down Expand Up @@ -436,6 +440,8 @@ mod tests {
client_ip,
publisher: false,
subscriber: true,
device_pk: None,
feed_pk: None,
}))
.times(1)
.returning(move |_| Ok(signature));
Expand Down Expand Up @@ -535,6 +541,8 @@ mod tests {
client_ip,
publisher: true,
subscriber: false,
device_pk: None,
feed_pk: None,
}))
.times(1)
.returning(move |_| Ok(signature));
Expand Down
5 changes: 4 additions & 1 deletion smartcontract/cli/src/user/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,10 @@ mod tests {

client
.expect_delete_user()
.with(predicate::eq(DeleteUserCommand { pubkey: pda_pubkey }))
.with(predicate::eq(DeleteUserCommand {
pubkey: pda_pubkey,
feed_pk: None,
}))
.returning(move |_| Ok(signature));
client
.expect_update_user()
Expand Down
16 changes: 16 additions & 0 deletions smartcontract/programs/doublezero-serviceability/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,14 @@ pub enum DoubleZeroError {
AccessPassMaxUnicastUsersExceeded, // variant 89
#[error("Access pass max multicast users exceeded")]
AccessPassMaxMulticastUsersExceeded, // variant 90
#[error("Device exchange (metro) is not covered by any feed on the access pass")]
MetroMismatch, // variant 91
#[error("A Feed account is required for this EdgeSeat access pass")]
FeedAccountRequired, // variant 92
#[error("Feed is not provisioned on the access pass")]
FeedNotOnAccessPass, // variant 93
#[error("Target multicast group is not joinable via the feed for this exchange")]
GroupNotInFeed, // variant 94
}

impl From<DoubleZeroError> for ProgramError {
Expand Down Expand Up @@ -282,6 +290,10 @@ impl From<DoubleZeroError> for ProgramError {
DoubleZeroError::InvalidDeviceTunnelBlock => ProgramError::Custom(88),
DoubleZeroError::AccessPassMaxUnicastUsersExceeded => ProgramError::Custom(89),
DoubleZeroError::AccessPassMaxMulticastUsersExceeded => ProgramError::Custom(90),
DoubleZeroError::MetroMismatch => ProgramError::Custom(91),
DoubleZeroError::FeedAccountRequired => ProgramError::Custom(92),
DoubleZeroError::FeedNotOnAccessPass => ProgramError::Custom(93),
DoubleZeroError::GroupNotInFeed => ProgramError::Custom(94),
}
}
}
Expand Down Expand Up @@ -379,6 +391,10 @@ impl From<u32> for DoubleZeroError {
88 => DoubleZeroError::InvalidDeviceTunnelBlock,
89 => DoubleZeroError::AccessPassMaxUnicastUsersExceeded,
90 => DoubleZeroError::AccessPassMaxMulticastUsersExceeded,
91 => DoubleZeroError::MetroMismatch,
92 => DoubleZeroError::FeedAccountRequired,
93 => DoubleZeroError::FeedNotOnAccessPass,
94 => DoubleZeroError::GroupNotInFeed,
_ => DoubleZeroError::Custom(e),
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,17 @@ pub fn process_set_access_pass(

// Update fields. The max caps are overwritten from args; the live counts are left
// untouched so an in-flight pass keeps its current seat usage.
accesspass.accesspass_type = value.accesspass_type.clone();
//
// EdgeSeat feed seats are owned by SetAccessPassFeeds (the oracle), not this instruction.
// SetAccessPassArgs carries no feed payload, so when both the stored and incoming types are
// EdgeSeat we preserve the provisioned seat vector instead of clobbering it (and its live
// current_users) with the incoming empty vec.
accesspass.accesspass_type = match (&accesspass.accesspass_type, &value.accesspass_type) {
(AccessPassType::EdgeSeat(existing), AccessPassType::EdgeSeat(_)) => {
AccessPassType::EdgeSeat(existing.clone())
}
_ => value.accesspass_type.clone(),
};
accesspass.last_access_epoch = value.last_access_epoch;
accesspass.flags = flags;
accesspass.max_unicast_users = value.max_unicast_users;
Expand Down
Loading
Loading