Skip to content

Commit

Permalink
Add StorageDepositReturnMismatch check
Browse files Browse the repository at this point in the history
Co-Authored-By: Thoralf-M <[email protected]>
  • Loading branch information
thibault-martinez and Thoralf-M committed Mar 17, 2022
1 parent ae665ca commit 01afb62
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 11 deletions.
33 changes: 33 additions & 0 deletions bee-ledger/src/workers/consensus/white_flag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,17 @@ fn apply_regular_essence<B: StorageBackend>(
}
}

if let Some(storage_deposit_return) = unlock_conditions.storage_deposit_return() {
let amount = context
.storage_deposit_returns
.entry(*storage_deposit_return.return_address())
.or_default();

*amount = amount
.checked_add(storage_deposit_return.amount())
.ok_or(Error::StorageDepositReturnOverflow)?;
}

context.input_amount = context
.input_amount
.checked_add(amount)
Expand Down Expand Up @@ -172,6 +183,28 @@ fn apply_regular_essence<B: StorageBackend>(
}
}

// Validation of storage deposit returns.
for (return_address, return_amount) in context.storage_deposit_returns.iter() {
if context
.essence
.outputs()
.iter()
.find(|output| {
if let Output::Basic(output) = output {
output.address() == return_address
&& output.amount() == *return_amount
&& output.unlock_conditions().len() == 1
&& output.feature_blocks().len() == 0
} else {
false
}
})
.is_none()
{
return Ok(ConflictReason::StorageDepositReturnMismatch);
}
}

// Validation of amounts.
if context.input_amount != context.output_amount {
return Ok(ConflictReason::CreatedConsumedAmountMismatch);
Expand Down
3 changes: 3 additions & 0 deletions bee-ledger/src/workers/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ pub enum Error {
/// Storage backend error.
#[error("storage backend error: {0}")]
Storage(Box<dyn std::error::Error + Send>),
/// Storage deposit return overflow.
#[error("storage deposit return overflow")]
StorageDepositReturnOverflow,
}

impl<E: Into<Error>> From<UnpackError<E, std::io::Error>> for Error {
Expand Down
6 changes: 3 additions & 3 deletions bee-message/src/output/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,9 @@ impl Output {
}

/// Verify if a valid storage deposit was made. Each [`Output`] has to have an amount that covers its associated
/// byte cost, given by [`ByteCostConfig`]. If there is a
/// [`StorageDepositReturnUnlockCondition`](unlock_condition::StorageDepositReturnUnlockCondition), its amount
/// is also checked.
/// byte cost, given by [`ByteCostConfig`].
/// If there is a [`StorageDepositReturnUnlockCondition`](unlock_condition::StorageDepositReturnUnlockCondition),
/// its amount is also checked.
pub fn verify_storage_deposit(&self, config: &ByteCostConfig) -> Result<(), Error> {
let required_output_amount = self.byte_cost(config);

Expand Down
6 changes: 6 additions & 0 deletions bee-message/src/semantic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ pub enum ConflictReason {
IncorrectUnlockMethod = 11,
/// The inputs commitments do not match.
InputsCommitmentsMismatch = 12,
/// Storage deposit return mismatch.
StorageDepositReturnMismatch = 13,
/// The semantic validation failed for a reason not covered by the previous variants.
SemanticValidationFailed = 255,
}
Expand Down Expand Up @@ -85,6 +87,7 @@ impl TryFrom<u8> for ConflictReason {
10 => Self::UnverifiedSender,
11 => Self::IncorrectUnlockMethod,
12 => Self::InputsCommitmentsMismatch,
13 => Self::StorageDepositReturnMismatch,
255 => Self::SemanticValidationFailed,
x => return Err(Self::Error::InvalidConflict(x)),
})
Expand Down Expand Up @@ -119,6 +122,8 @@ pub struct ValidationContext<'a> {
pub output_chains: HashMap<ChainId, &'a Output>,
///
pub unlocked_addresses: HashSet<Address>,
///
pub storage_deposit_returns: HashMap<Address, u64>,
}

impl<'a> ValidationContext<'a> {
Expand Down Expand Up @@ -163,6 +168,7 @@ impl<'a> ValidationContext<'a> {
})
.collect(),
unlocked_addresses: HashSet::new(),
storage_deposit_returns: HashMap::new(),
}
}
}
8 changes: 0 additions & 8 deletions bee-message/tip.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,6 @@ amounts per `Return Addresses` are summed up and the output side must deposit th
- `Return Amount` must be ≥ than `Minimum Storage Deposit` and must not be `0`.
- It must hold true, that `0``Amount` - `Return Amount``Required Storage Deposit of the Output`.

###### Additional semantic transaction validation rule:
- An output that has <i>Storage Deposit Return Unlock Condition</i> specified must only be consumed and unlocked in a
transaction that deposits `Return Amount` IOTA coins to `Return Address` via an output that has no additional spending
constraints. (<i>Basic Output</i> with only an <i>Address Unlock Condition</i>)
- When several outputs with <i>Storage Deposit Return Unlock Condition</i> and the same `Return Address` are consumed,
their return amounts per `Return Addresses` are summed up and the output side of the transaction must deposit this
total sum per `Return Address`.

##### Expiration Unlock Conditions Additional semantic transaction validation rules:
- If `Milestone Index` != `0`, an output that has <i>Expiration Unlock Condition</i> set must only be consumed and
unlocked by the target `Address` (defined in <i>Address Unlock Condition</i>) in a transaction that has a confirming
Expand Down

0 comments on commit 01afb62

Please sign in to comment.