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_with_metadata::<StateTransitionProofResult>(sdk, settings)
242 .await?;
243 match st_result {
244 StateTransitionProofResult::VerifiedAddressInfos(address_infos) => {
245 let expected_addresses = expected
246 .keys()
247 .copied()
248 .collect::<BTreeSet<PlatformAddress>>();
249 collect_address_infos_from_proof(address_infos, &expected_addresses)
250 .map(|infos| (infos, metadata.height))
251 }
252 other => Err(Error::InvalidProvedResponse(format!(
253 "address info proof was expected for {:?}, but received {:?}",
254 state_transition, other
255 ))),
256 }
257}
258
259#[allow(clippy::too_many_arguments)]
260async fn create_address_funding_from_asset_lock_transition<S: Signer<PlatformAddress>>(
261 asset_lock_proof: AssetLockProof,
262 asset_lock_private_key: &[u8],
263 inputs: BTreeMap<PlatformAddress, (AddressNonce, Credits)>,
264 outputs: BTreeMap<PlatformAddress, Option<Credits>>,
265 fee_strategy: AddressFundsFeeStrategy,
266 signer: &S,
267 user_fee_increase: UserFeeIncrease,
268 sdk: &Sdk,
269) -> Result<StateTransition, ProtocolError> {
270 AddressFundingFromAssetLockTransition::try_from_asset_lock_with_signer_and_private_key(
271 asset_lock_proof,
272 asset_lock_private_key,
273 inputs,
274 outputs,
275 fee_strategy,
276 signer,
277 user_fee_increase,
278 sdk.version(),
279 )
280 .await
281}