dash_sdk/platform/transition/
top_up_address.rs1use std::collections::{BTreeMap, BTreeSet};
2
3use super::address_inputs::collect_address_infos_from_proof;
4use super::broadcast::BroadcastStateTransition;
5use super::put_settings::PutSettings;
6use super::validation::ensure_valid_state_transition_structure;
7use crate::{Error, Sdk};
8use dpp::address_funds::{AddressFundsFeeStrategy, PlatformAddress};
9use dpp::dashcore::PrivateKey;
10use dpp::errors::consensus::basic::state_transition::TransitionNoOutputsError;
11use dpp::fee::Credits;
12use dpp::identity::signer::Signer;
13use dpp::prelude::{AddressNonce, AssetLockProof, UserFeeIncrease};
14use dpp::state_transition::address_funding_from_asset_lock_transition::methods::AddressFundingFromAssetLockTransitionMethodsV0;
15use dpp::state_transition::address_funding_from_asset_lock_transition::AddressFundingFromAssetLockTransition;
16use dpp::state_transition::proof_result::StateTransitionProofResult;
17use dpp::state_transition::StateTransition;
18use dpp::ProtocolError;
19use drive_proof_verifier::types::AddressInfos;
20
21#[async_trait::async_trait]
23pub trait TopUpAddress<S: Signer<PlatformAddress>> {
24 async fn top_up(
34 &self,
35 sdk: &Sdk,
36 asset_lock_proof: AssetLockProof,
37 asset_lock_private_key: PrivateKey,
38 fee_strategy: AddressFundsFeeStrategy,
39 signer: &S,
40 settings: Option<PutSettings>,
41 ) -> Result<AddressInfos, Error>;
42
43 #[cfg(feature = "core_key_wallet")]
61 #[allow(clippy::too_many_arguments)]
62 async fn top_up_with_signers<AS>(
63 &self,
64 sdk: &Sdk,
65 asset_lock_proof: AssetLockProof,
66 asset_lock_proof_path: &dpp::key_wallet::bip32::DerivationPath,
67 fee_strategy: AddressFundsFeeStrategy,
68 signer: &S,
69 asset_lock_signer: &AS,
70 settings: Option<PutSettings>,
71 ) -> Result<AddressInfos, Error>
72 where
73 AS: dpp::key_wallet::signer::Signer + Send + Sync;
74}
75
76pub type AddressWithBalance = (PlatformAddress, Option<Credits>);
77pub type AddressesWithBalances = BTreeMap<PlatformAddress, Option<Credits>>;
78
79#[async_trait::async_trait]
80impl<S: Signer<PlatformAddress>> TopUpAddress<S> for AddressWithBalance
81where
82 BTreeMap<PlatformAddress, Option<Credits>>: TopUpAddress<S>,
83{
84 async fn top_up(
85 &self,
86 sdk: &Sdk,
87 asset_lock_proof: AssetLockProof,
88 asset_lock_private_key: PrivateKey,
89 fee_strategy: AddressFundsFeeStrategy,
90 signer: &S,
91 settings: Option<PutSettings>,
92 ) -> Result<AddressInfos, Error> {
93 BTreeMap::from([(self.0, self.1)])
94 .top_up(
95 sdk,
96 asset_lock_proof,
97 asset_lock_private_key,
98 fee_strategy,
99 signer,
100 settings,
101 )
102 .await
103 }
104
105 #[cfg(feature = "core_key_wallet")]
106 #[allow(clippy::too_many_arguments)]
107 async fn top_up_with_signers<AS>(
108 &self,
109 sdk: &Sdk,
110 asset_lock_proof: AssetLockProof,
111 asset_lock_proof_path: &dpp::key_wallet::bip32::DerivationPath,
112 fee_strategy: AddressFundsFeeStrategy,
113 signer: &S,
114 asset_lock_signer: &AS,
115 settings: Option<PutSettings>,
116 ) -> Result<AddressInfos, Error>
117 where
118 AS: dpp::key_wallet::signer::Signer + Send + Sync,
119 {
120 BTreeMap::from([(self.0, self.1)])
121 .top_up_with_signers(
122 sdk,
123 asset_lock_proof,
124 asset_lock_proof_path,
125 fee_strategy,
126 signer,
127 asset_lock_signer,
128 settings,
129 )
130 .await
131 }
132}
133
134#[async_trait::async_trait]
135impl<S: Signer<PlatformAddress>> TopUpAddress<S> for AddressesWithBalances {
136 async fn top_up(
137 &self,
138 sdk: &Sdk,
139 asset_lock_proof: AssetLockProof,
140 asset_lock_private_key: PrivateKey,
141 fee_strategy: AddressFundsFeeStrategy,
142 signer: &S,
143 settings: Option<PutSettings>,
144 ) -> Result<AddressInfos, Error> {
145 if self.is_empty() {
146 return Err(Error::from(TransitionNoOutputsError::new()));
147 }
148
149 let user_fee_increase = settings
150 .as_ref()
151 .and_then(|settings| settings.user_fee_increase)
152 .unwrap_or_default();
153
154 let state_transition = create_address_funding_from_asset_lock_transition(
155 asset_lock_proof,
156 asset_lock_private_key.inner.as_ref(),
157 BTreeMap::new(),
158 self.clone(),
159 fee_strategy,
160 signer,
161 user_fee_increase,
162 sdk,
163 )
164 .await?;
165
166 broadcast_and_collect_address_infos(self, state_transition, sdk, settings).await
167 }
168
169 #[cfg(feature = "core_key_wallet")]
170 #[allow(clippy::too_many_arguments)]
171 async fn top_up_with_signers<AS>(
172 &self,
173 sdk: &Sdk,
174 asset_lock_proof: AssetLockProof,
175 asset_lock_proof_path: &dpp::key_wallet::bip32::DerivationPath,
176 fee_strategy: AddressFundsFeeStrategy,
177 signer: &S,
178 asset_lock_signer: &AS,
179 settings: Option<PutSettings>,
180 ) -> Result<AddressInfos, Error>
181 where
182 AS: dpp::key_wallet::signer::Signer + Send + Sync,
183 {
184 if self.is_empty() {
185 return Err(Error::from(TransitionNoOutputsError::new()));
186 }
187
188 let user_fee_increase = settings
195 .as_ref()
196 .and_then(|settings| settings.user_fee_increase)
197 .unwrap_or_default();
198
199 let state_transition =
200 AddressFundingFromAssetLockTransition::try_from_asset_lock_with_signers::<S, AS>(
201 asset_lock_proof,
202 asset_lock_proof_path,
203 BTreeMap::new(),
204 self.clone(),
205 fee_strategy,
206 signer,
207 asset_lock_signer,
208 user_fee_increase,
209 sdk.version(),
210 )
211 .await?;
212
213 broadcast_and_collect_address_infos(self, state_transition, sdk, settings).await
214 }
215}
216
217async fn broadcast_and_collect_address_infos(
222 expected: &AddressesWithBalances,
223 state_transition: StateTransition,
224 sdk: &Sdk,
225 settings: Option<PutSettings>,
226) -> Result<AddressInfos, Error> {
227 ensure_valid_state_transition_structure(&state_transition, sdk.version())?;
228 let st_result = state_transition
229 .broadcast_and_wait::<StateTransitionProofResult>(sdk, settings)
230 .await?;
231 match st_result {
232 StateTransitionProofResult::VerifiedAddressInfos(address_infos) => {
233 let expected_addresses = expected
234 .keys()
235 .copied()
236 .collect::<BTreeSet<PlatformAddress>>();
237 collect_address_infos_from_proof(address_infos, &expected_addresses)
238 }
239 other => Err(Error::InvalidProvedResponse(format!(
240 "address info proof was expected for {:?}, but received {:?}",
241 state_transition, other
242 ))),
243 }
244}
245
246#[allow(clippy::too_many_arguments)]
247async fn create_address_funding_from_asset_lock_transition<S: Signer<PlatformAddress>>(
248 asset_lock_proof: AssetLockProof,
249 asset_lock_private_key: &[u8],
250 inputs: BTreeMap<PlatformAddress, (AddressNonce, Credits)>,
251 outputs: BTreeMap<PlatformAddress, Option<Credits>>,
252 fee_strategy: AddressFundsFeeStrategy,
253 signer: &S,
254 user_fee_increase: UserFeeIncrease,
255 sdk: &Sdk,
256) -> Result<StateTransition, ProtocolError> {
257 AddressFundingFromAssetLockTransition::try_from_asset_lock_with_signer_and_private_key(
258 asset_lock_proof,
259 asset_lock_private_key,
260 inputs,
261 outputs,
262 fee_strategy,
263 signer,
264 user_fee_increase,
265 sdk.version(),
266 )
267 .await
268}