Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add commitment input syntactic checks #2133

6 changes: 6 additions & 0 deletions sdk/src/types/block/payload/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ pub enum PayloadError {
MissingCreationSlot,
#[display(fmt = "input count and unlock count mismatch: {input_count} != {unlock_count}")]
InputUnlockCountMismatch { input_count: usize, unlock_count: usize },
#[display(fmt = "missing commitment context input for staking feature")]
MissingCommitmentInputForStakingFeature,
#[display(fmt = "missing commitment context input for block issuer feature")]
MissingCommitmentInputForBlockIssuerFeature,
#[display(fmt = "missing commitment context input for delegation output")]
MissingCommitmentInputForDelegationOutput,
#[from]
TransactionSemantic(TransactionFailureReason),
#[from]
Expand Down
33 changes: 31 additions & 2 deletions sdk/src/types/block/payload/signed_transaction/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ impl TransactionBuilder {
verify_outputs(&outputs, protocol_parameters)?;
}

Ok(Transaction {
let transaction = Transaction {
network_id: self.network_id,
creation_slot,
context_inputs: ContextInputs::from_vec(self.context_inputs)?,
Expand All @@ -196,7 +196,11 @@ impl TransactionBuilder {
capabilities: self.capabilities,
payload: self.payload,
outputs,
})
};

verify_transaction(&transaction)?;

Ok(transaction)
}

/// Finishes a [`TransactionBuilder`] into a [`Transaction`] without protocol
Expand All @@ -213,6 +217,7 @@ pub(crate) type OutputCount = BoundedU16<{ *OUTPUT_COUNT_RANGE.start() }, { *OUT
#[derive(Clone, Debug, Eq, PartialEq, Packable)]
#[packable(unpack_error = PayloadError)]
#[packable(unpack_visitor = ProtocolParameters)]
#[packable(verify_with = verify_transaction_packable)]
pub struct Transaction {
/// The unique value denoting whether the block was meant for mainnet, testnet, or a private network.
#[packable(verify_with = verify_network_id)]
Expand Down Expand Up @@ -439,6 +444,30 @@ fn verify_outputs(outputs: &[Output], visitor: &ProtocolParameters) -> Result<()
Ok(())
}

fn verify_transaction_packable(transaction: &Transaction, _: &ProtocolParameters) -> Result<(), PayloadError> {
Thoralf-M marked this conversation as resolved.
Show resolved Hide resolved
verify_transaction(transaction)
}

fn verify_transaction(transaction: &Transaction) -> Result<(), PayloadError> {
let has_commitment_input = transaction.context_inputs().commitment().is_some();

for output in transaction.outputs.iter() {
if output.features().is_some_and(|f| f.staking().is_some()) && !has_commitment_input {
return Err(PayloadError::MissingCommitmentInputForStakingFeature);
}

if output.features().is_some_and(|f| f.block_issuer().is_some()) && !has_commitment_input {
return Err(PayloadError::MissingCommitmentInputForBlockIssuerFeature);
}

if output.is_delegation() && !has_commitment_input {
return Err(PayloadError::MissingCommitmentInputForDelegationOutput);
}
}
Alex6323 marked this conversation as resolved.
Show resolved Hide resolved

Ok(())
}

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[non_exhaustive]
pub enum TransactionCapabilityFlag {
Expand Down
6 changes: 0 additions & 6 deletions sdk/src/types/block/semantic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,6 @@ impl<'a> SemanticValidationContext<'a> {
if output.features().block_issuer().is_some() {
let account_id = output.account_id_non_null(&OutputId::new(self.transaction_id, index as u16));

if self.commitment_context_input.is_none() {
DaughterOfMars marked this conversation as resolved.
Show resolved Hide resolved
return Err(TransactionFailureReason::BlockIssuerCommitmentInputMissing);
}
if !bic_context_inputs.contains(&account_id) {
return Err(TransactionFailureReason::BlockIssuanceCreditInputMissing);
}
Expand All @@ -318,9 +315,6 @@ impl<'a> SemanticValidationContext<'a> {
}
}
if output.features().staking().is_some() {
if self.commitment_context_input.is_none() {
return Err(TransactionFailureReason::StakingCommitmentInputMissing);
}
if output.features().block_issuer().is_none() {
return Err(TransactionFailureReason::StakingBlockIssuerFeatureMissing);
}
Expand Down
Loading