Skip to content

Commit

Permalink
Merge branch '2.0' into balance-delegation-implicit-accounts
Browse files Browse the repository at this point in the history
  • Loading branch information
Thoralf-M committed Mar 20, 2024
2 parents 208e26c + de4b7f9 commit bde0f66
Show file tree
Hide file tree
Showing 14 changed files with 188 additions and 214 deletions.
41 changes: 19 additions & 22 deletions cli/src/wallet_cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -601,25 +601,24 @@ pub async fn claimable_outputs_command(wallet: &Wallet) -> Result<(), Error> {
println_log_info!(" + {} {}", native_token.amount(), native_token.token_id());
}

if let Some(unlock_conditions) = output.unlock_conditions() {
let deposit_return = unlock_conditions
.storage_deposit_return()
.map(|deposit_return| deposit_return.amount())
.unwrap_or(0);
let amount = output.amount() - deposit_return;
println_log_info!(" - base coin amount: {}", amount);

if let Some(expiration) = unlock_conditions.expiration() {
let slot_index = wallet.client().get_slot_index().await?;

if *expiration.slot_index() > *slot_index {
println_log_info!(" - expires in {} slot indices", *expiration.slot_index() - *slot_index);
} else {
println_log_info!(
" - expired {} slot indices ago",
*slot_index - *expiration.slot_index()
);
}
let deposit_return = output
.unlock_conditions()
.storage_deposit_return()
.map(|deposit_return| deposit_return.amount())
.unwrap_or(0);
let amount = output.amount() - deposit_return;
println_log_info!(" - base coin amount: {}", amount);

if let Some(expiration) = output.unlock_conditions().expiration() {
let slot_index = wallet.client().get_slot_index().await?;

if *expiration.slot_index() > *slot_index {
println_log_info!(" - expires in {} slot indices", *expiration.slot_index() - *slot_index);
} else {
println_log_info!(
" - expired {} slot indices ago",
*slot_index - *expiration.slot_index()
);
}
}
}
Expand Down Expand Up @@ -1342,11 +1341,9 @@ async fn print_wallet_address(wallet: &Wallet) -> Result<(), Error> {
Output::Delegation(delegation) => delegations.push(delegation.delegation_id_non_null(&output_id)),
Output::Anchor(anchor) => anchors.push(anchor.anchor_id_non_null(&output_id)),
}
let unlock_conditions = output_data
let sdr_amount = output_data
.output
.unlock_conditions()
.expect("output must have unlock conditions");
let sdr_amount = unlock_conditions
.storage_deposit_return()
.map(|sdr| sdr.amount())
.unwrap_or(0);
Expand Down
17 changes: 7 additions & 10 deletions sdk/examples/wallet/17_check_unlock_conditions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.add_unlock_condition(AddressUnlockCondition::new(wallet_address.clone()))
.finish_output()?;

let controlled_by_account = if let [UnlockCondition::Address(address_unlock_condition)] = output
.unlock_conditions()
.expect("output needs to have unlock conditions")
.as_ref()
{
// Check that the address in the unlock condition belongs to the wallet
wallet_address.inner() == address_unlock_condition.address()
} else {
false
};
let controlled_by_account =
if let [UnlockCondition::Address(address_unlock_condition)] = output.unlock_conditions().as_ref() {
// Check that the address in the unlock condition belongs to the wallet
wallet_address.inner() == address_unlock_condition.address()
} else {
false
};

println!(
"The output has only an address unlock condition and the address is from the account: {controlled_by_account:?}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ impl TransactionBuilder {
if input
.output
.unlock_conditions()
.map_or(false, |u| u.iter().any(|u| u.is_timelock() || u.is_expiration()))
.iter()
.any(|u| u.is_timelock() || u.is_expiration())
{
log::debug!("Adding commitment context input for timelocked or expiring output");
needs_commitment_context = true;
Expand Down
5 changes: 1 addition & 4 deletions sdk/src/client/api/block_builder/transaction_builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -640,10 +640,7 @@ impl TransactionBuilder {
return false;
}

// PANIC: safe to unwrap as non basic/account/foundry/nft outputs are already filtered out.
let unlock_conditions = input.output.unlock_conditions().unwrap();

if unlock_conditions.is_timelocked(
if input.output.unlock_conditions().is_timelocked(
self.latest_slot_commitment_id.slot_index(),
self.protocol_parameters.min_committable_age(),
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ impl TransactionBuilder {
// Find the first value that matches the remainder address
self.non_remainder_outputs().any(|o| {
(o.is_basic() || o.is_account() || o.is_anchor() || o.is_nft())
&& o.unlock_conditions()
.map_or(true, |uc| uc.expiration().is_none() && uc.timelock().is_none())
&& o.unlock_conditions().expiration().is_none()
&& o.unlock_conditions().timelock().is_none()
&& matches!(o.required_address(
self.latest_slot_commitment_id.slot_index(),
self.protocol_parameters.committable_age_range(),
Expand All @@ -156,10 +156,7 @@ impl TransactionBuilder {
.provided_outputs
.iter_mut()
.chain(&mut self.added_outputs)
.filter(|o| {
o.unlock_conditions()
.map_or(true, |uc| uc.expiration().is_none() && uc.timelock().is_none())
})
.filter(|o| o.unlock_conditions().expiration().is_none() && o.unlock_conditions().timelock().is_none())
.filter_map(|o| sort_order.get(&o.kind()).map(|order| (*order, o)))
.collect::<BTreeMap<_, _>>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,11 @@ pub(crate) fn sdruc_not_expired(
output: &Output,
slot_index: SlotIndex,
) -> Option<&StorageDepositReturnUnlockCondition> {
// PANIC: safe to unwrap as outputs without unlock conditions have been filtered out already.
let unlock_conditions = output.unlock_conditions().unwrap();

unlock_conditions.storage_deposit_return().and_then(|sdr| {
let expired = unlock_conditions
output.unlock_conditions().storage_deposit_return().filter(|_| {
output
.unlock_conditions()
.expiration()
.map_or(false, |expiration| slot_index >= expiration.slot_index());

// We only have to send the storage deposit return back if the output is not expired
(!expired).then_some(sdr)
.map_or(true, |expiration| slot_index < expiration.slot_index())
})
}

Expand Down
19 changes: 8 additions & 11 deletions sdk/src/types/block/output/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,14 +220,14 @@ impl Output {
}

/// Returns the unlock conditions of an [`Output`], if any.
pub fn unlock_conditions(&self) -> Option<&UnlockConditions> {
pub fn unlock_conditions(&self) -> &UnlockConditions {
match self {
Self::Basic(output) => Some(output.unlock_conditions()),
Self::Account(output) => Some(output.unlock_conditions()),
Self::Anchor(output) => Some(output.unlock_conditions()),
Self::Foundry(output) => Some(output.unlock_conditions()),
Self::Nft(output) => Some(output.unlock_conditions()),
Self::Delegation(output) => Some(output.unlock_conditions()),
Self::Basic(output) => output.unlock_conditions(),
Self::Account(output) => output.unlock_conditions(),
Self::Anchor(output) => output.unlock_conditions(),
Self::Foundry(output) => output.unlock_conditions(),
Self::Nft(output) => output.unlock_conditions(),
Self::Delegation(output) => output.unlock_conditions(),
}
}

Expand Down Expand Up @@ -340,10 +340,7 @@ impl Output {
});
}

if let Some(return_condition) = self
.unlock_conditions()
.and_then(UnlockConditions::storage_deposit_return)
{
if let Some(return_condition) = self.unlock_conditions().storage_deposit_return() {
// We can't return more tokens than were originally contained in the output.
// `Return Amount` ≤ `Amount`.
if return_condition.amount() > self.amount() {
Expand Down
38 changes: 20 additions & 18 deletions sdk/src/types/block/semantic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,25 +355,27 @@ impl<'a> SemanticValidationContext<'a> {
}
}

if let Some(unlock_conditions) = created_output.unlock_conditions() {
if let (Some(address), Some(timelock)) = (unlock_conditions.address(), unlock_conditions.timelock()) {
if let Address::Account(account_address) = address.address() {
if let Some(entry) = self.block_issuer_mana.get_mut(account_address.account_id()) {
if let Some(commitment_context_input) = self.commitment_context_input {
let past_bounded_slot =
self.protocol_parameters.past_bounded_slot(commitment_context_input);

if timelock.slot_index()
>= past_bounded_slot + self.protocol_parameters.max_committable_age()
{
entry.1 = entry
.1
.checked_add(created_output.mana())
.ok_or(TransactionFailureReason::SemanticValidationFailed)?;
}
} else {
return Err(TransactionFailureReason::BlockIssuerCommitmentInputMissing);
if let Some((address, timelock)) = created_output
.unlock_conditions()
.address()
.zip(created_output.unlock_conditions().timelock())
{
if let Address::Account(account_address) = address.address() {
if let Some(entry) = self.block_issuer_mana.get_mut(account_address.account_id()) {
if let Some(commitment_context_input) = self.commitment_context_input {
let past_bounded_slot =
self.protocol_parameters.past_bounded_slot(commitment_context_input);

if timelock.slot_index()
>= past_bounded_slot + self.protocol_parameters.max_committable_age()
{
entry.1 = entry
.1
.checked_add(created_output.mana())
.ok_or(TransactionFailureReason::SemanticValidationFailed)?;
}
} else {
return Err(TransactionFailureReason::BlockIssuerCommitmentInputMissing);
}
}
}
Expand Down
39 changes: 14 additions & 25 deletions sdk/src/wallet/operations/balance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,7 @@ impl<S: 'static + SecretManage> Wallet<S> {
_ => {
// If there is only an [AddressUnlockCondition], then we can spend the output at any time
// without restrictions
if let [UnlockCondition::Address(address_unlock_cond)] = output
.unlock_conditions()
.expect("output needs to have unlock conditions")
.as_ref()
{
if let [UnlockCondition::Address(address_unlock_cond)] = output.unlock_conditions().as_ref() {
// add nft_id for nft outputs
if let Output::Nft(nft) = &output {
let nft_id = nft.nft_id_non_null(output_id);
Expand Down Expand Up @@ -193,21 +189,18 @@ impl<S: 'static + SecretManage> Wallet<S> {
// If output has a StorageDepositReturnUnlockCondition, the amount of it should
// be subtracted, because this part
// needs to be sent back
let amount = output
.unlock_conditions()
.and_then(|u| u.storage_deposit_return())
.map_or_else(
|| output.amount(),
|sdr| {
if wallet_address.inner() == sdr.return_address() {
// sending to ourself, we get the full amount
output.amount()
} else {
// Sending to someone else
output.amount() - sdr.amount()
}
},
);
let amount = output.unlock_conditions().storage_deposit_return().map_or_else(
|| output.amount(),
|sdr| {
if wallet_address.inner() == sdr.return_address() {
// sending to ourself, we get the full amount
output.amount()
} else {
// Sending to someone else
output.amount() - sdr.amount()
}
},
);

// add nft_id for nft outputs
if let Output::Nft(output) = &output {
Expand Down Expand Up @@ -251,11 +244,7 @@ impl<S: 'static + SecretManage> Wallet<S> {
}
} else {
// Don't add expired outputs that can't ever be unlocked by us
if let Some(expiration) = output
.unlock_conditions()
.expect("output needs to have unlock conditions")
.expiration()
{
if let Some(expiration) = output.unlock_conditions().expiration() {
// Not expired, could get unlockable when it's expired, so we insert it
if slot_index < expiration.slot_index() {
balance.potentially_locked_outputs.insert(*output_id, false);
Expand Down
51 changes: 26 additions & 25 deletions sdk/src/wallet/operations/helpers/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ pub(crate) fn can_output_be_unlocked_now(
commitment_slot_index: impl Into<SlotIndex> + Copy,
committable_age_range: CommittableAgeRange,
) -> Result<bool, WalletError> {
if let Some(unlock_conditions) = output_data.output.unlock_conditions() {
if unlock_conditions.is_timelocked(commitment_slot_index, committable_age_range.min) {
return Ok(false);
}
if output_data
.output
.unlock_conditions()
.is_timelocked(commitment_slot_index, committable_age_range.min)
{
return Ok(false);
}

let required_address = output_data
Expand All @@ -40,30 +42,29 @@ pub(crate) fn can_output_be_unlocked_from_now_on(
slot_index: impl Into<SlotIndex> + Copy,
committable_age_range: CommittableAgeRange,
) -> bool {
if let Some(unlock_conditions) = output.unlock_conditions() {
if unlock_conditions.is_timelocked(slot_index, committable_age_range.min) {
return false;
}
if output
.unlock_conditions()
.is_timelocked(slot_index, committable_age_range.min)
{
return false;
}

// If there is an expiration unlock condition, we can only unlock it forever from now on, if it's expired and
// the return address belongs to the wallet
if let Some(expiration) = unlock_conditions.expiration() {
if let Some(address) = expiration.return_address_expired(
// Safe to unwrap, if there is an expiration, then there also needs to be an address unlock condition
unlock_conditions.address().unwrap().address(),
slot_index,
committable_age_range,
) {
if address != expiration.return_address() || !controlled_addresses.contains(address) {
return false;
}
} else {
// If there is an expiration unlock condition, we can only unlock it forever from now on, if it's expired and
// the return address belongs to the wallet
if let Some(expiration) = output.unlock_conditions().expiration() {
if let Some(address) = expiration.return_address_expired(
// Safe to unwrap, if there is an expiration, then there also needs to be an address unlock condition
output.unlock_conditions().address().unwrap().address(),
slot_index,
committable_age_range,
) {
if address != expiration.return_address() || !controlled_addresses.contains(address) {
return false;
}
} else {
return false;
}

true
} else {
false
}

true
}
Loading

0 comments on commit bde0f66

Please sign in to comment.