1use crate::error::MapGroveDbError;
2use crate::types::groups::{GroupActionSigners, GroupActions, Groups};
3use crate::verify::verify_tenderdash_proof;
4use crate::{ContextProvider, Error, FromProof};
5use dapi_grpc::platform::v0::{
6 get_group_action_signers_request, get_group_actions_request, get_group_info_request,
7 get_group_infos_request, GetGroupActionSignersRequest, GetGroupActionSignersResponse,
8 GetGroupActionsRequest, GetGroupActionsResponse, GetGroupInfoRequest, GetGroupInfoResponse,
9 GetGroupInfosRequest, GetGroupInfosResponse, Proof, ResponseMetadata,
10};
11use dapi_grpc::platform::VersionedGrpcResponse;
12use dpp::dashcore::Network;
13use dpp::data_contract::group::{Group, GroupMemberPower};
14use dpp::data_contract::GroupContractPosition;
15use dpp::group::group_action::GroupAction;
16use dpp::group::group_action_status::GroupActionStatus;
17use dpp::identifier::Identifier;
18use dpp::version::PlatformVersion;
19use drive::drive::Drive;
20use indexmap::IndexMap;
21
22impl FromProof<GetGroupInfoRequest> for Group {
23 type Request = GetGroupInfoRequest;
24 type Response = GetGroupInfoResponse;
25
26 fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
27 request: I,
28 response: O,
29 _network: Network,
30 platform_version: &PlatformVersion,
31 provider: &'a dyn ContextProvider,
32 ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
33 where
34 Self: Sized + 'a,
35 {
36 let request: Self::Request = request.into();
37 let response: Self::Response = response.into();
38
39 let (contract_id, group_contract_position) = match request
40 .version
41 .ok_or(Error::EmptyVersion)?
42 {
43 get_group_info_request::Version::V0(v0) => {
44 let contract_id =
45 Identifier::try_from(v0.contract_id).map_err(|error| Error::RequestError {
46 error: format!("can't convert contract_id to identifier: {error}"),
47 })?;
48
49 let group_contract_position = v0.group_contract_position as GroupContractPosition;
50
51 (contract_id, group_contract_position)
52 }
53 };
54
55 let metadata = response
56 .metadata()
57 .or(Err(Error::EmptyResponseMetadata))?
58 .clone();
59
60 let proof = response.proof_owned().or(Err(Error::NoProofInResult))?;
61
62 let (root_hash, result) = Drive::verify_group_info(
63 &proof.grovedb_proof,
64 contract_id,
65 group_contract_position,
66 false,
67 platform_version,
68 )
69 .map_drive_error(&proof, &metadata)?;
70
71 verify_tenderdash_proof(&proof, &metadata, &root_hash, provider)?;
72
73 Ok((result, metadata, proof))
74 }
75}
76
77impl FromProof<GetGroupInfosRequest> for Groups {
78 type Request = GetGroupInfosRequest;
79 type Response = GetGroupInfosResponse;
80
81 fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
82 request: I,
83 response: O,
84 _network: Network,
85 platform_version: &PlatformVersion,
86 provider: &'a dyn ContextProvider,
87 ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
88 where
89 Self: Sized + 'a,
90 {
91 let request: Self::Request = request.into();
92 let response: Self::Response = response.into();
93
94 let (contract_id, start_at_group_contract_position, count) = match request
95 .version
96 .ok_or(Error::EmptyVersion)?
97 {
98 get_group_infos_request::Version::V0(v0) => {
99 let contract_id =
100 Identifier::try_from(v0.contract_id).map_err(|error| Error::RequestError {
101 error: format!("can't convert contract_id to identifier: {error}"),
102 })?;
103
104 let start_group_contract_position =
105 v0.start_at_group_contract_position.map(|start_position| {
106 (
107 start_position.start_group_contract_position as GroupContractPosition,
108 start_position.start_group_contract_position_included,
109 )
110 });
111
112 let count = v0.count.map(|count| count as u16);
113
114 (contract_id, start_group_contract_position, count)
115 }
116 };
117
118 let metadata = response
119 .metadata()
120 .or(Err(Error::EmptyResponseMetadata))?
121 .clone();
122
123 let proof = response.proof_owned().or(Err(Error::NoProofInResult))?;
124
125 let (root_hash, result) = Drive::verify_group_infos_in_contract(
126 &proof.grovedb_proof,
127 contract_id,
128 start_at_group_contract_position,
129 count,
130 false,
131 platform_version,
132 )
133 .map(
135 |(root_hash, result): (_, IndexMap<GroupContractPosition, Group>)| {
136 let optional_value_map = result
137 .into_iter()
138 .map(|(action_id, group_action)| (action_id, Some(group_action)))
139 .collect::<Groups>();
140 (root_hash, optional_value_map)
141 },
142 )
143 .map_drive_error(&proof, &metadata)?;
144
145 verify_tenderdash_proof(&proof, &metadata, &root_hash, provider)?;
146
147 Ok((Some(result), metadata, proof))
148 }
149}
150
151impl FromProof<GetGroupActionsRequest> for GroupActions {
152 type Request = GetGroupActionsRequest;
153 type Response = GetGroupActionsResponse;
154
155 fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
156 request: I,
157 response: O,
158 _network: Network,
159 platform_version: &PlatformVersion,
160 provider: &'a dyn ContextProvider,
161 ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
162 where
163 Self: Sized + 'a,
164 {
165 let request: Self::Request = request.into();
166 let response: Self::Response = response.into();
167
168 let (contract_id, group_contract_position, status, start_at_action_id, count) =
169 match request.version.ok_or(Error::EmptyVersion)? {
170 get_group_actions_request::Version::V0(v0) => {
171 let contract_id = Identifier::try_from(v0.contract_id).map_err(|error| {
172 Error::RequestError {
173 error: format!("can't convert contract_id to identifier: {error}"),
174 }
175 })?;
176
177 let start_at_action_id =
178 v0.start_at_action_id
179 .map(|start_at_action_id| {
180 let start_action_id =
181 Identifier::try_from(start_at_action_id.start_action_id)
182 .map_err(|error| Error::RequestError {
183 error: format!(
184 "can't convert start_action_id to identifier: {error}"
185 ),
186 })?;
187
188 Ok::<_, Error>((
189 start_action_id,
190 start_at_action_id.start_action_id_included,
191 ))
192 })
193 .transpose()?;
194
195 let group_contract_position =
196 v0.group_contract_position as GroupContractPosition;
197
198 let count = v0.count.map(|count| count as u16);
199
200 let status = GroupActionStatus::try_from(v0.status).map_err(|error| {
201 Error::RequestError {
202 error: format!("can't convert status to GroupActionStatus: {error}"),
203 }
204 })?;
205
206 (
207 contract_id,
208 group_contract_position,
209 status,
210 start_at_action_id,
211 count,
212 )
213 }
214 };
215
216 let metadata = response
217 .metadata()
218 .or(Err(Error::EmptyResponseMetadata))?
219 .clone();
220
221 let proof = response.proof_owned().or(Err(Error::NoProofInResult))?;
222
223 let (root_hash, result) = Drive::verify_action_infos_in_contract(
224 &proof.grovedb_proof,
225 contract_id,
226 group_contract_position,
227 status,
228 start_at_action_id,
229 count,
230 false,
231 platform_version,
232 )
233 .map(
235 |(root_hash, result): (_, IndexMap<Identifier, GroupAction>)| {
236 let optional_value_map = result
237 .into_iter()
238 .map(|(action_id, group_action)| (action_id, Some(group_action)))
239 .collect::<GroupActions>();
240 (root_hash, optional_value_map)
241 },
242 )
243 .map_drive_error(&proof, &metadata)?;
244
245 verify_tenderdash_proof(&proof, &metadata, &root_hash, provider)?;
246
247 Ok((Some(result), metadata, proof))
248 }
249}
250
251impl FromProof<GetGroupActionSignersRequest> for GroupActionSigners {
252 type Request = GetGroupActionSignersRequest;
253 type Response = GetGroupActionSignersResponse;
254
255 fn maybe_from_proof_with_metadata<'a, I: Into<Self::Request>, O: Into<Self::Response>>(
256 request: I,
257 response: O,
258 _network: Network,
259 platform_version: &PlatformVersion,
260 provider: &'a dyn ContextProvider,
261 ) -> Result<(Option<Self>, ResponseMetadata, Proof), Error>
262 where
263 Self: Sized + 'a,
264 {
265 let request: Self::Request = request.into();
266 let response: Self::Response = response.into();
267
268 let (contract_id, group_contract_position, status, action_id) = match request
269 .version
270 .ok_or(Error::EmptyVersion)?
271 {
272 get_group_action_signers_request::Version::V0(v0) => {
273 let contract_id =
274 Identifier::try_from(v0.contract_id).map_err(|error| Error::RequestError {
275 error: format!("can't convert contract_id to identifier: {error}"),
276 })?;
277
278 let action_id =
279 Identifier::try_from(v0.action_id).map_err(|error| Error::RequestError {
280 error: format!("can't convert action_id to identifier: {error}"),
281 })?;
282
283 let group_contract_position = v0.group_contract_position as GroupContractPosition;
284
285 let status = GroupActionStatus::try_from(v0.status).map_err(|error| {
286 Error::RequestError {
287 error: format!("can't convert status to GroupActionStatus: {error}"),
288 }
289 })?;
290
291 (contract_id, group_contract_position, status, action_id)
292 }
293 };
294
295 let metadata = response
296 .metadata()
297 .or(Err(Error::EmptyResponseMetadata))?
298 .clone();
299
300 let proof = response.proof_owned().or(Err(Error::NoProofInResult))?;
301
302 let (root_hash, result) = Drive::verify_action_signers(
303 &proof.grovedb_proof,
304 contract_id,
305 group_contract_position,
306 status,
307 action_id,
308 false,
309 platform_version,
310 )
311 .map(
313 |(root_hash, result): (_, IndexMap<Identifier, GroupMemberPower>)| {
314 let optional_value_map = result
315 .into_iter()
316 .map(|(action_id, group_action)| (action_id, Some(group_action)))
317 .collect::<GroupActionSigners>();
318 (root_hash, optional_value_map)
319 },
320 )
321 .map_drive_error(&proof, &metadata)?;
322
323 verify_tenderdash_proof(&proof, &metadata, &root_hash, provider)?;
324
325 Ok((Some(result), metadata, proof))
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use super::*;
332 use dapi_grpc::platform::v0::get_group_action_signers_request::{
333 GetGroupActionSignersRequestV0, Version as SignersReqVersion,
334 };
335 use dapi_grpc::platform::v0::get_group_actions_request::{
336 GetGroupActionsRequestV0, StartAtActionId, Version as ActionsReqVersion,
337 };
338 use dapi_grpc::platform::v0::get_group_actions_response::{
339 get_group_actions_response_v0::Result as ActionsRespResult, GetGroupActionsResponseV0,
340 Version as ActionsRespVersion,
341 };
342 use dapi_grpc::platform::v0::get_group_info_request::{
343 GetGroupInfoRequestV0, Version as InfoReqVersion,
344 };
345 use dapi_grpc::platform::v0::get_group_info_response::{
346 get_group_info_response_v0::Result as InfoRespResult, GetGroupInfoResponseV0,
347 Version as InfoRespVersion,
348 };
349 use dapi_grpc::platform::v0::get_group_infos_request::{
350 GetGroupInfosRequestV0, StartAtGroupContractPosition, Version as InfosReqVersion,
351 };
352 use dapi_grpc::platform::v0::get_group_infos_response::{
353 get_group_infos_response_v0::Result as InfosRespResult, GetGroupInfosResponseV0,
354 Version as InfosRespVersion,
355 };
356 use dash_context_provider::ContextProviderError;
357 use dpp::data_contract::TokenConfiguration;
358 use dpp::prelude::{CoreBlockHeight, DataContract};
359 use std::sync::Arc;
360
361 struct UnreachableProvider;
362
363 impl ContextProvider for UnreachableProvider {
364 fn get_data_contract(
365 &self,
366 _id: &Identifier,
367 _pv: &PlatformVersion,
368 ) -> Result<Option<Arc<DataContract>>, ContextProviderError> {
369 panic!("should not be called")
370 }
371 fn get_token_configuration(
372 &self,
373 _id: &Identifier,
374 ) -> Result<Option<TokenConfiguration>, ContextProviderError> {
375 panic!("should not be called")
376 }
377 fn get_quorum_public_key(
378 &self,
379 _qt: u32,
380 _qh: [u8; 32],
381 _h: u32,
382 ) -> Result<[u8; 48], ContextProviderError> {
383 panic!("should not be called")
384 }
385 fn get_platform_activation_height(&self) -> Result<CoreBlockHeight, ContextProviderError> {
386 panic!("should not be called")
387 }
388 }
389
390 fn pv() -> &'static PlatformVersion {
391 PlatformVersion::latest()
392 }
393
394 #[test]
397 fn group_info_empty_version_on_request_missing() {
398 let request = GetGroupInfoRequest { version: None };
399 let response = GetGroupInfoResponse {
400 version: Some(InfoRespVersion::V0(GetGroupInfoResponseV0 {
401 result: Some(InfoRespResult::Proof(Proof::default())),
402 metadata: Some(ResponseMetadata::default()),
403 })),
404 };
405 let err = <Group as FromProof<_>>::maybe_from_proof(
406 request,
407 response,
408 Network::Testnet,
409 pv(),
410 &UnreachableProvider,
411 )
412 .unwrap_err();
413 assert!(matches!(err, Error::EmptyVersion), "got: {err:?}");
414 }
415
416 #[test]
417 fn group_info_request_error_on_bad_contract_id() {
418 let request = GetGroupInfoRequest {
419 version: Some(InfoReqVersion::V0(GetGroupInfoRequestV0 {
420 contract_id: vec![0u8; 7], group_contract_position: 0,
422 prove: true,
423 })),
424 };
425 let response = GetGroupInfoResponse::default();
426 let err = <Group as FromProof<_>>::maybe_from_proof(
427 request,
428 response,
429 Network::Testnet,
430 pv(),
431 &UnreachableProvider,
432 )
433 .unwrap_err();
434 match err {
435 Error::RequestError { error } => assert!(error.contains("contract_id"), "got: {error}"),
436 other => panic!("expected RequestError, got: {other:?}"),
437 }
438 }
439
440 #[test]
441 fn group_info_empty_response_metadata() {
442 let request = GetGroupInfoRequest {
443 version: Some(InfoReqVersion::V0(GetGroupInfoRequestV0 {
444 contract_id: vec![0u8; 32],
445 group_contract_position: 0,
446 prove: true,
447 })),
448 };
449 let response = GetGroupInfoResponse { version: None };
450 let err = <Group as FromProof<_>>::maybe_from_proof(
451 request,
452 response,
453 Network::Testnet,
454 pv(),
455 &UnreachableProvider,
456 )
457 .unwrap_err();
458 assert!(matches!(err, Error::EmptyResponseMetadata), "got: {err:?}");
459 }
460
461 #[test]
462 fn group_info_no_proof_when_result_missing() {
463 let request = GetGroupInfoRequest {
464 version: Some(InfoReqVersion::V0(GetGroupInfoRequestV0 {
465 contract_id: vec![0u8; 32],
466 group_contract_position: 0,
467 prove: true,
468 })),
469 };
470 let response = GetGroupInfoResponse {
471 version: Some(InfoRespVersion::V0(GetGroupInfoResponseV0 {
472 result: None,
473 metadata: Some(ResponseMetadata::default()),
474 })),
475 };
476 let err = <Group as FromProof<_>>::maybe_from_proof(
477 request,
478 response,
479 Network::Testnet,
480 pv(),
481 &UnreachableProvider,
482 )
483 .unwrap_err();
484 assert!(matches!(err, Error::NoProofInResult), "got: {err:?}");
485 }
486
487 #[test]
490 fn group_infos_empty_version_on_request_missing() {
491 let request = GetGroupInfosRequest { version: None };
492 let response = GetGroupInfosResponse::default();
493 let err = <Groups as FromProof<_>>::maybe_from_proof(
494 request,
495 response,
496 Network::Testnet,
497 pv(),
498 &UnreachableProvider,
499 )
500 .unwrap_err();
501 assert!(matches!(err, Error::EmptyVersion), "got: {err:?}");
502 }
503
504 #[test]
505 fn group_infos_request_error_on_bad_contract_id() {
506 let request = GetGroupInfosRequest {
507 version: Some(InfosReqVersion::V0(GetGroupInfosRequestV0 {
508 contract_id: vec![0u8; 12], start_at_group_contract_position: Some(StartAtGroupContractPosition {
510 start_group_contract_position: 0,
511 start_group_contract_position_included: true,
512 }),
513 count: Some(10),
514 prove: true,
515 })),
516 };
517 let response = GetGroupInfosResponse::default();
518 let err = <Groups as FromProof<_>>::maybe_from_proof(
519 request,
520 response,
521 Network::Testnet,
522 pv(),
523 &UnreachableProvider,
524 )
525 .unwrap_err();
526 match err {
527 Error::RequestError { error } => assert!(error.contains("contract_id"), "got: {error}"),
528 other => panic!("expected RequestError, got: {other:?}"),
529 }
530 }
531
532 #[test]
533 fn group_infos_empty_response_metadata() {
534 let request = GetGroupInfosRequest {
535 version: Some(InfosReqVersion::V0(GetGroupInfosRequestV0 {
536 contract_id: vec![0u8; 32],
537 start_at_group_contract_position: None,
538 count: None,
539 prove: true,
540 })),
541 };
542 let response = GetGroupInfosResponse {
543 version: Some(InfosRespVersion::V0(GetGroupInfosResponseV0 {
544 result: Some(InfosRespResult::Proof(Proof::default())),
545 metadata: None,
546 })),
547 };
548 let err = <Groups as FromProof<_>>::maybe_from_proof(
549 request,
550 response,
551 Network::Testnet,
552 pv(),
553 &UnreachableProvider,
554 )
555 .unwrap_err();
556 assert!(matches!(err, Error::EmptyResponseMetadata), "got: {err:?}");
557 }
558
559 #[test]
562 fn group_actions_empty_version_on_request_missing() {
563 let request = GetGroupActionsRequest { version: None };
564 let response = GetGroupActionsResponse::default();
565 let err = <GroupActions as FromProof<_>>::maybe_from_proof(
566 request,
567 response,
568 Network::Testnet,
569 pv(),
570 &UnreachableProvider,
571 )
572 .unwrap_err();
573 assert!(matches!(err, Error::EmptyVersion), "got: {err:?}");
574 }
575
576 #[test]
577 fn group_actions_request_error_on_bad_contract_id() {
578 let request = GetGroupActionsRequest {
579 version: Some(ActionsReqVersion::V0(GetGroupActionsRequestV0 {
580 contract_id: vec![0u8; 3],
581 group_contract_position: 0,
582 status: 0,
583 start_at_action_id: None,
584 count: None,
585 prove: true,
586 })),
587 };
588 let response = GetGroupActionsResponse::default();
589 let err = <GroupActions as FromProof<_>>::maybe_from_proof(
590 request,
591 response,
592 Network::Testnet,
593 pv(),
594 &UnreachableProvider,
595 )
596 .unwrap_err();
597 match err {
598 Error::RequestError { error } => assert!(error.contains("contract_id"), "got: {error}"),
599 other => panic!("expected RequestError, got: {other:?}"),
600 }
601 }
602
603 #[test]
604 fn group_actions_request_error_on_bad_start_action_id() {
605 let request = GetGroupActionsRequest {
606 version: Some(ActionsReqVersion::V0(GetGroupActionsRequestV0 {
607 contract_id: vec![0u8; 32],
608 group_contract_position: 0,
609 status: 0,
610 start_at_action_id: Some(StartAtActionId {
611 start_action_id: vec![0u8; 9], start_action_id_included: true,
613 }),
614 count: None,
615 prove: true,
616 })),
617 };
618 let response = GetGroupActionsResponse::default();
619 let err = <GroupActions as FromProof<_>>::maybe_from_proof(
620 request,
621 response,
622 Network::Testnet,
623 pv(),
624 &UnreachableProvider,
625 )
626 .unwrap_err();
627 match err {
628 Error::RequestError { error } => {
629 assert!(error.contains("start_action_id"), "got: {error}")
630 }
631 other => panic!("expected RequestError, got: {other:?}"),
632 }
633 }
634
635 #[test]
636 fn group_actions_request_error_on_bad_status() {
637 let request = GetGroupActionsRequest {
638 version: Some(ActionsReqVersion::V0(GetGroupActionsRequestV0 {
639 contract_id: vec![0u8; 32],
640 group_contract_position: 0,
641 status: 999, start_at_action_id: None,
643 count: None,
644 prove: true,
645 })),
646 };
647 let response = GetGroupActionsResponse::default();
648 let err = <GroupActions as FromProof<_>>::maybe_from_proof(
649 request,
650 response,
651 Network::Testnet,
652 pv(),
653 &UnreachableProvider,
654 )
655 .unwrap_err();
656 match err {
657 Error::RequestError { error } => {
658 assert!(error.contains("GroupActionStatus"), "got: {error}")
659 }
660 other => panic!("expected RequestError, got: {other:?}"),
661 }
662 }
663
664 #[test]
665 fn group_actions_empty_response_metadata() {
666 let request = GetGroupActionsRequest {
667 version: Some(ActionsReqVersion::V0(GetGroupActionsRequestV0 {
668 contract_id: vec![0u8; 32],
669 group_contract_position: 0,
670 status: 0,
671 start_at_action_id: None,
672 count: None,
673 prove: true,
674 })),
675 };
676 let response = GetGroupActionsResponse {
677 version: Some(ActionsRespVersion::V0(GetGroupActionsResponseV0 {
678 result: Some(ActionsRespResult::Proof(Proof::default())),
679 metadata: None,
680 })),
681 };
682 let err = <GroupActions as FromProof<_>>::maybe_from_proof(
683 request,
684 response,
685 Network::Testnet,
686 pv(),
687 &UnreachableProvider,
688 )
689 .unwrap_err();
690 assert!(matches!(err, Error::EmptyResponseMetadata), "got: {err:?}");
691 }
692
693 #[test]
696 fn group_action_signers_empty_version_on_request_missing() {
697 let request = GetGroupActionSignersRequest { version: None };
698 let response = GetGroupActionSignersResponse::default();
699 let err = <GroupActionSigners as FromProof<_>>::maybe_from_proof(
700 request,
701 response,
702 Network::Testnet,
703 pv(),
704 &UnreachableProvider,
705 )
706 .unwrap_err();
707 assert!(matches!(err, Error::EmptyVersion), "got: {err:?}");
708 }
709
710 #[test]
711 fn group_action_signers_request_error_on_bad_contract_id() {
712 let request = GetGroupActionSignersRequest {
713 version: Some(SignersReqVersion::V0(GetGroupActionSignersRequestV0 {
714 contract_id: vec![0u8; 9], group_contract_position: 0,
716 status: 0,
717 action_id: vec![1u8; 32],
718 prove: true,
719 })),
720 };
721 let response = GetGroupActionSignersResponse::default();
722 let err = <GroupActionSigners as FromProof<_>>::maybe_from_proof(
723 request,
724 response,
725 Network::Testnet,
726 pv(),
727 &UnreachableProvider,
728 )
729 .unwrap_err();
730 match err {
731 Error::RequestError { error } => assert!(error.contains("contract_id"), "got: {error}"),
732 other => panic!("expected RequestError, got: {other:?}"),
733 }
734 }
735
736 #[test]
737 fn group_action_signers_request_error_on_bad_action_id() {
738 let request = GetGroupActionSignersRequest {
739 version: Some(SignersReqVersion::V0(GetGroupActionSignersRequestV0 {
740 contract_id: vec![0u8; 32],
741 group_contract_position: 0,
742 status: 0,
743 action_id: vec![1u8; 3], prove: true,
745 })),
746 };
747 let response = GetGroupActionSignersResponse::default();
748 let err = <GroupActionSigners as FromProof<_>>::maybe_from_proof(
749 request,
750 response,
751 Network::Testnet,
752 pv(),
753 &UnreachableProvider,
754 )
755 .unwrap_err();
756 match err {
757 Error::RequestError { error } => assert!(error.contains("action_id"), "got: {error}"),
758 other => panic!("expected RequestError, got: {other:?}"),
759 }
760 }
761
762 #[test]
763 fn group_action_signers_request_error_on_bad_status() {
764 let request = GetGroupActionSignersRequest {
765 version: Some(SignersReqVersion::V0(GetGroupActionSignersRequestV0 {
766 contract_id: vec![0u8; 32],
767 group_contract_position: 0,
768 status: 42, action_id: vec![1u8; 32],
770 prove: true,
771 })),
772 };
773 let response = GetGroupActionSignersResponse::default();
774 let err = <GroupActionSigners as FromProof<_>>::maybe_from_proof(
775 request,
776 response,
777 Network::Testnet,
778 pv(),
779 &UnreachableProvider,
780 )
781 .unwrap_err();
782 match err {
783 Error::RequestError { error } => {
784 assert!(error.contains("GroupActionStatus"), "got: {error}")
785 }
786 other => panic!("expected RequestError, got: {other:?}"),
787 }
788 }
789}