dash_sdk/platform/transition/
waitable.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use std::collections::BTreeMap;

use super::broadcast::BroadcastStateTransition;
use super::put_settings::PutSettings;
use crate::platform::Fetch;
use crate::Error;
use crate::Sdk;
use dpp::document::Document;
use dpp::prelude::{DataContract, Identifier, Identity};
use dpp::state_transition::identity_create_transition::accessors::IdentityCreateTransitionAccessorsV0;
use dpp::state_transition::StateTransition;
use dpp::state_transition::StateTransitionLike;
use dpp::voting::votes::Vote;
use dpp::ProtocolError;

/// Waitable trait provides a wait to wait for a response of a state transition after it has been broadcast and
/// receive altered objects.
///
/// This is simple conveniance trait wrapping the [`BroadcastStateTransition::wait_for_response`] method.
#[async_trait::async_trait]
pub trait Waitable: Sized {
    async fn wait_for_response(
        sdk: &Sdk,
        state_transition: StateTransition,
        settings: Option<PutSettings>,
    ) -> Result<Self, Error>;
}
#[async_trait::async_trait]
impl Waitable for DataContract {
    async fn wait_for_response(
        sdk: &Sdk,
        state_transition: StateTransition,
        settings: Option<PutSettings>,
    ) -> Result<DataContract, Error> {
        state_transition.wait_for_response(sdk, settings).await
    }
}

#[async_trait::async_trait]
impl Waitable for Document {
    async fn wait_for_response(
        sdk: &Sdk,
        state_transition: StateTransition,
        settings: Option<PutSettings>,
    ) -> Result<Self, Error> {
        let doc_id = if let StateTransition::DocumentsBatch(transition) = &state_transition {
            let ids = transition.modified_data_ids();
            if ids.len() != 1 {
                return Err(Error::Protocol(
                    dpp::ProtocolError::InvalidStateTransitionType(format!(
                        "expected state transition with exactly one document, got {}",
                        ids.into_iter()
                            .map(|id| id
                                .to_string(dpp::platform_value::string_encoding::Encoding::Base58))
                            .collect::<Vec<_>>()
                            .join(", ")
                    )),
                ));
            }
            ids[0]
        } else {
            return Err(Error::Protocol(ProtocolError::InvalidStateTransitionType(
                format!(
                    "expected state transition to be a DocumentsBatchTransition, got {}",
                    state_transition.name()
                ),
            )));
        };

        let mut documents: BTreeMap<Identifier, Option<Document>> =
            state_transition.wait_for_response(sdk, settings).await?;

        let document: Document = documents
            .remove(&doc_id)
            .ok_or(Error::InvalidProvedResponse(
                "did not prove the sent document".to_string(),
            ))?
            .ok_or(Error::InvalidProvedResponse(
                "expected there to actually be a document".to_string(),
            ))?;

        Ok(document)
    }
}

#[async_trait::async_trait]
impl Waitable for Identity {
    async fn wait_for_response(
        sdk: &Sdk,
        state_transition: StateTransition,
        settings: Option<PutSettings>,
    ) -> Result<Self, Error> {
        let result: Result<Self, Error> = state_transition.wait_for_response(sdk, settings).await;

        match result {
            Ok(identity) => Ok(identity),
            // TODO: We need to refactor sdk Error to be able to retrieve gRPC error code and identify conflicts
            Err(Error::AlreadyExists(_)) => {
                let identity_id = if let StateTransition::IdentityCreate(st) = state_transition {
                    st.identity_id()
                } else {
                    return Err(Error::Generic(format!(
                        "expected identity create state transition, got {:?}",
                        state_transition.name()
                    )));
                };

                tracing::debug!(
                    ?identity_id,
                    "attempt to create identity that already exists"
                );
                let identity = Identity::fetch(sdk, identity_id).await?;
                identity.ok_or(Error::DapiClientError(
                    "identity was proved to not exist but was said to exist".to_string(),
                ))
            }
            Err(e) => Err(e),
        }
    }
}

#[async_trait::async_trait]
impl Waitable for Vote {
    async fn wait_for_response(
        sdk: &Sdk,
        state_transition: StateTransition,
        settings: Option<PutSettings>,
    ) -> Result<Self, Error> {
        state_transition.wait_for_response(sdk, settings).await
    }
}