@@ -14,7 +14,6 @@ use solana_program::{
1414 account_info:: { next_account_info, AccountInfo } ,
1515 entrypoint:: ProgramResult ,
1616 msg,
17- program_error:: ProgramError ,
1817 pubkey:: Pubkey ,
1918} ;
2019use std:: { fmt, net:: Ipv4Addr } ;
@@ -36,90 +35,6 @@ impl fmt::Debug for MulticastGroupSubscribeArgs {
3635 }
3736}
3837
39- pub struct SubscribeUserResult {
40- pub mgroup : MulticastGroup ,
41- /// True if the publisher list transitioned between empty and non-empty
42- /// (gained first publisher or lost last publisher). Callers that need to
43- /// trigger activator reprocessing should check this flag.
44- pub publisher_list_transitioned : bool ,
45- }
46-
47- /// Toggle a user's multicast group subscription.
48- ///
49- /// Handles both create-time subscription (user lists start empty, only adds)
50- /// and post-activation subscription changes (add/remove toggle). The caller is
51- /// responsible for setting `user.status = Updating` when
52- /// `publisher_list_transitioned` is true and the user is already activated.
53- pub fn subscribe_user_to_multicastgroup (
54- mgroup_account : & AccountInfo ,
55- accesspass : & AccessPass ,
56- user : & mut User ,
57- publisher : bool ,
58- subscriber : bool ,
59- ) -> Result < SubscribeUserResult , ProgramError > {
60- let mut mgroup = MulticastGroup :: try_from ( mgroup_account) ?;
61- if mgroup. status != MulticastGroupStatus :: Activated {
62- msg ! ( "MulticastGroupStatus: {:?}" , mgroup. status) ;
63- return Err ( DoubleZeroError :: InvalidStatus . into ( ) ) ;
64- }
65-
66- // Check allowlists for additions
67- if publisher && !accesspass. mgroup_pub_allowlist . contains ( mgroup_account. key ) {
68- msg ! ( "{:?}" , accesspass) ;
69- return Err ( DoubleZeroError :: NotAllowed . into ( ) ) ;
70- }
71- if subscriber && !accesspass. mgroup_sub_allowlist . contains ( mgroup_account. key ) {
72- msg ! ( "{:?}" , accesspass) ;
73- return Err ( DoubleZeroError :: NotAllowed . into ( ) ) ;
74- }
75-
76- let mut publisher_list_transitioned = false ;
77-
78- // Manage the publisher list
79- match publisher {
80- true => {
81- if !user. publishers . contains ( mgroup_account. key ) {
82- let was_empty = user. publishers . is_empty ( ) ;
83- mgroup. publisher_count = mgroup. publisher_count . saturating_add ( 1 ) ;
84- user. publishers . push ( * mgroup_account. key ) ;
85- if was_empty {
86- publisher_list_transitioned = true ;
87- }
88- }
89- }
90- false => {
91- if user. publishers . contains ( mgroup_account. key ) {
92- mgroup. publisher_count = mgroup. publisher_count . saturating_sub ( 1 ) ;
93- user. publishers . retain ( |& x| x != * mgroup_account. key ) ;
94- if user. publishers . is_empty ( ) {
95- publisher_list_transitioned = true ;
96- }
97- }
98- }
99- }
100-
101- // Manage the subscriber list
102- match subscriber {
103- true => {
104- if !user. subscribers . contains ( mgroup_account. key ) {
105- mgroup. subscriber_count = mgroup. subscriber_count . saturating_add ( 1 ) ;
106- user. subscribers . push ( * mgroup_account. key ) ;
107- }
108- }
109- false => {
110- if user. subscribers . contains ( mgroup_account. key ) {
111- mgroup. subscriber_count = mgroup. subscriber_count . saturating_sub ( 1 ) ;
112- user. subscribers . retain ( |& x| x != * mgroup_account. key ) ;
113- }
114- }
115- }
116-
117- Ok ( SubscribeUserResult {
118- mgroup,
119- publisher_list_transitioned,
120- } )
121- }
122-
12338pub fn process_subscribe_multicastgroup (
12439 program_id : & Pubkey ,
12540 accounts : & [ AccountInfo ] ,
@@ -163,7 +78,15 @@ pub fn process_subscribe_multicastgroup(
16378 ) ;
16479 assert ! ( user_account. is_writable, "user account is not writable" ) ;
16580
166- // Parse and validate user
81+ // Parse accounts
82+ let mut mgroup: MulticastGroup = MulticastGroup :: try_from ( mgroup_account) ?;
83+ if mgroup. status != MulticastGroupStatus :: Activated {
84+ #[ cfg( test) ]
85+ msg ! ( "MulticastGroupStatus: {:?}" , mgroup. status) ;
86+
87+ return Err ( DoubleZeroError :: InvalidStatus . into ( ) ) ;
88+ }
89+
16790 let mut user: User = User :: try_from ( user_account) ?;
16891 if user. status != UserStatus :: Activated && user. status != UserStatus :: Updating {
16992 msg ! ( "UserStatus: {:?}" , user. status) ;
@@ -193,22 +116,76 @@ pub fn process_subscribe_multicastgroup(
193116 return Err ( DoubleZeroError :: Unauthorized . into ( ) ) ;
194117 }
195118
196- let result = subscribe_user_to_multicastgroup (
197- mgroup_account,
198- & accesspass,
199- & mut user,
200- value. publisher ,
201- value. subscriber ,
202- ) ?;
203-
204- // Trigger activator reprocessing when publisher list transitions
205- // (gaining first publisher requires dz_ip allocation, losing last means it's no longer needed)
206- if result. publisher_list_transitioned {
207- user. status = UserStatus :: Updating ;
119+ // Check if the user is in the allowlist
120+ if value. publisher && !accesspass. mgroup_pub_allowlist . contains ( mgroup_account. key ) {
121+ msg ! ( "{:?}" , accesspass) ;
122+ return Err ( DoubleZeroError :: NotAllowed . into ( ) ) ;
123+ }
124+ if value. subscriber && !accesspass. mgroup_sub_allowlist . contains ( mgroup_account. key ) {
125+ msg ! ( "{:?}" , accesspass) ;
126+ return Err ( DoubleZeroError :: NotAllowed . into ( ) ) ;
127+ }
128+
129+ // Manage the publisher lists
130+ match value. publisher {
131+ true => {
132+ if !user. publishers . contains ( mgroup_account. key ) {
133+ let was_empty = user. publishers . is_empty ( ) ;
134+ // Increment publisher count
135+ mgroup. publisher_count = mgroup. publisher_count . saturating_add ( 1 ) ;
136+ // Add multicast group to user's publisher list
137+ user. publishers . push ( * mgroup_account. key ) ;
138+ // Only trigger activator reprocessing when gaining first publisher
139+ // (activator needs to allocate dz_ip)
140+ if was_empty {
141+ user. status = UserStatus :: Updating ;
142+ }
143+ }
144+ }
145+ false => {
146+ if user. publishers . contains ( mgroup_account. key ) {
147+ // Decrement publisher count
148+ mgroup. publisher_count = mgroup. publisher_count . saturating_sub ( 1 ) ;
149+ // Remove multicast group from user's publisher list
150+ user. publishers . retain ( |& x| x != * mgroup_account. key ) ;
151+ // Trigger activator reprocessing when losing last publisher
152+ // (dz_ip no longer needed)
153+ if user. publishers . is_empty ( ) {
154+ user. status = UserStatus :: Updating ;
155+ }
156+ }
157+ }
158+ }
159+
160+ // Manage the subscriber lists
161+ match value. subscriber {
162+ true => {
163+ if !user. subscribers . contains ( mgroup_account. key ) {
164+ // Increment subscriber count
165+ mgroup. subscriber_count = mgroup. subscriber_count . saturating_add ( 1 ) ;
166+ // Add multicast group to user's subscriber list
167+ user. subscribers . push ( * mgroup_account. key ) ;
168+ // No activator reprocessing needed for subscriber changes
169+ // (subscriber groups don't affect tunnel or dz_ip config)
170+ }
171+ }
172+ false => {
173+ if user. subscribers . contains ( mgroup_account. key ) {
174+ // Decrement subscriber count
175+ mgroup. subscriber_count = mgroup. subscriber_count . saturating_sub ( 1 ) ;
176+ // Remove multicast group from user's subscriber list
177+ user. subscribers . retain ( |& x| x != * mgroup_account. key ) ;
178+ }
179+ }
208180 }
209181
210- try_acc_write ( & result . mgroup , mgroup_account, payer_account, accounts) ?;
182+ try_acc_write ( & mgroup, mgroup_account, payer_account, accounts) ?;
211183 try_acc_write ( & user, user_account, payer_account, accounts) ?;
212184
185+ #[ cfg( test) ]
186+ msg ! ( "Updated: {:?}" , mgroup) ;
187+ #[ cfg( test) ]
188+ msg ! ( "Updated: {:?}" , user_account) ;
189+
213190 Ok ( ( ) )
214191}
0 commit comments