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(
40 &self,
41 sdk: &Sdk,
42 asset_lock_proof: AssetLockProof,
43 asset_lock_private_key: PrivateKey,
44 fee_strategy: AddressFundsFeeStrategy,
45 signer: &S,
46 settings: Option<PutSettings>,
47 ) -> Result<(AddressInfos, u64), Error>;
48
49 #[cfg(feature = "core_key_wallet")]
67 #[allow(clippy::too_many_arguments)]
68 async fn top_up_with_signers<AS>(
69 &self,
70 sdk: &Sdk,
71 asset_lock_proof: AssetLockProof,
72 asset_lock_proof_path: &dpp::key_wallet::bip32::DerivationPath,
73 fee_strategy: AddressFundsFeeStrategy,
74 signer: &S,
75 asset_lock_signer: &AS,
76 settings: Option<PutSettings>,
77 ) -> Result<(AddressInfos, u64), Error>
78 where
79 AS: dpp::key_wallet::signer::Signer + Send + Sync;
80}
81
82pub type AddressWithBalance = (PlatformAddress, Option<Credits>);
83pub type AddressesWithBalances = BTreeMap<PlatformAddress, Option<Credits>>;
84
85#[async_trait::async_trait]
86impl<S: Signer<PlatformAddress>> TopUpAddress<S> for AddressWithBalance
87where
88 BTreeMap<PlatformAddress, Option<Credits>>: TopUpAddress<S>,
89{
90 async fn top_up(
91 &self,
92 sdk: &Sdk,
93 asset_lock_proof: AssetLockProof,
94 asset_lock_private_key: PrivateKey,
95 fee_strategy: AddressFundsFeeStrategy,
96 signer: &S,
97 settings: Option<PutSettings>,
98 ) -> Result<(AddressInfos, u64), Error> {
99 BTreeMap::from([(self.0, self.1)])
100 .top_up(
101 sdk,
102 asset_lock_proof,
103 asset_lock_private_key,
104 fee_strategy,
105 signer,
106 settings,
107 )
108 .await
109 }
110
111 #[cfg(feature = "core_key_wallet")]
112 #[allow(clippy::too_many_arguments)]
113 async fn top_up_with_signers<AS>(
114 &self,
115 sdk: &Sdk,
116 asset_lock_proof: AssetLockProof,
117 asset_lock_proof_path: &dpp::key_wallet::bip32::DerivationPath,
118 fee_strategy: AddressFundsFeeStrategy,
119 signer: &S,
120 asset_lock_signer: &AS,
121 settings: Option<PutSettings>,
122 ) -> Result<(AddressInfos, u64), Error>
123 where
124 AS: dpp::key_wallet::signer::Signer + Send + Sync,
125 {
126 BTreeMap::from([(self.0, self.1)])
127 .top_up_with_signers(
128 sdk,
129 asset_lock_proof,
130 asset_lock_proof_path,
131 fee_strategy,
132 signer,
133 asset_lock_signer,
134 settings,
135 )
136 .await
137 }
138}
139
140#[async_trait::async_trait]
141impl<S: Signer<PlatformAddress>> TopUpAddress<S> for AddressesWithBalances {
142 async fn top_up(
143 &self,
144 sdk: &Sdk,
145 asset_lock_proof: AssetLockProof,
146 asset_lock_private_key: PrivateKey,
147 fee_strategy: AddressFundsFeeStrategy,
148 signer: &S,
149 settings: Option<PutSettings>,
150 ) -> Result<(AddressInfos, u64), Error> {
151 if self.is_empty() {
152 return Err(Error::from(TransitionNoOutputsError::new()));
153 }
154
155 let user_fee_increase = settings
156 .as_ref()
157 .and_then(|settings| settings.user_fee_increase)
158 .unwrap_or_default();
159
160 let state_transition = create_address_funding_from_asset_lock_transition(
161 asset_lock_proof,
162 asset_lock_private_key.inner.as_ref(),
163 BTreeMap::new(),
164 self.clone(),
165 fee_strategy,
166 signer,
167 user_fee_increase,
168 sdk,
169 )
170 .await?;
171
172 broadcast_and_collect_address_infos(self, state_transition, sdk, settings).await
173 }
174
175 #[cfg(feature = "core_key_wallet")]
176 #[allow(clippy::too_many_arguments)]
177 async fn top_up_with_signers<AS>(
178 &self,
179 sdk: &Sdk,
180 asset_lock_proof: AssetLockProof,
181 asset_lock_proof_path: &dpp::key_wallet::bip32::DerivationPath,
182 fee_strategy: AddressFundsFeeStrategy,
183 signer: &S,
184 asset_lock_signer: &AS,
185 settings: Option<PutSettings>,
186 ) -> Result<(AddressInfos, u64), Error>
187 where
188 AS: dpp::key_wallet::signer::Signer + Send + Sync,
189 {
190 if self.is_empty() {
191 return Err(Error::from(TransitionNoOutputsError::new()));
192 }
193
194 let user_fee_increase = settings
201 .as_ref()
202 .and_then(|settings| settings.user_fee_increase)
203 .unwrap_or_default();
204
205 let state_transition =
206 AddressFundingFromAssetLockTransition::try_from_asset_lock_with_signers::<S, AS>(
207 asset_lock_proof,
208 asset_lock_proof_path,
209 BTreeMap::new(),
210 self.clone(),
211 fee_strategy,
212 signer,
213 asset_lock_signer,
214 user_fee_increase,
215 sdk.version(),
216 )
217 .await?;
218
219 broadcast_and_collect_address_infos(self, state_transition, sdk, settings).await
220 }
221}
222
223async fn broadcast_and_collect_address_infos(
234 expected: &AddressesWithBalances,
235 state_transition: StateTransition,
236 sdk: &Sdk,
237 settings: Option<PutSettings>,
238) -> Result<(AddressInfos, u64), Error> {
239 ensure_valid_state_transition_structure(&state_transition, sdk.version())?;
240 let (st_result, metadata) = state_transition
241 .broadcast_and_wait_for_affected_state_with_metadata::<StateTransitionProofResult>(
242 sdk, settings,
243 )
244 .await?;
245 match st_result {
246 StateTransitionProofResult::VerifiedAddressInfos(address_infos) => {
247 let expected_addresses = expected
248 .keys()
249 .copied()
250 .collect::<BTreeSet<PlatformAddress>>();
251 collect_address_infos_from_proof(address_infos, &expected_addresses)
252 .map(|infos| (infos, metadata.height))
253 }
254 other => Err(Error::InvalidProvedResponse(format!(
255 "address info proof was expected for {:?}, but received {:?}",
256 state_transition, other
257 ))),
258 }
259}
260
261#[allow(clippy::too_many_arguments)]
262async fn create_address_funding_from_asset_lock_transition<S: Signer<PlatformAddress>>(
263 asset_lock_proof: AssetLockProof,
264 asset_lock_private_key: &[u8],
265 inputs: BTreeMap<PlatformAddress, (AddressNonce, Credits)>,
266 outputs: BTreeMap<PlatformAddress, Option<Credits>>,
267 fee_strategy: AddressFundsFeeStrategy,
268 signer: &S,
269 user_fee_increase: UserFeeIncrease,
270 sdk: &Sdk,
271) -> Result<StateTransition, ProtocolError> {
272 AddressFundingFromAssetLockTransition::try_from_asset_lock_with_signer_and_private_key(
273 asset_lock_proof,
274 asset_lock_private_key,
275 inputs,
276 outputs,
277 fee_strategy,
278 signer,
279 user_fee_increase,
280 sdk.version(),
281 )
282 .await
283}