Skip to content

Commit

Permalink
Use TransactionFailureReason as semantic validation error (#2058)
Browse files Browse the repository at this point in the history
* Use TransactionFailureReason as semantic validation error

* Adapt

* nit

* let it error

* python

* nodejs

* Fix doc

* remove if lets

* stealer stealer
  • Loading branch information
thibault-martinez authored Feb 27, 2024
1 parent 137f651 commit 31bcdf4
Show file tree
Hide file tree
Showing 17 changed files with 141 additions and 154 deletions.
2 changes: 1 addition & 1 deletion bindings/core/src/method/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ pub enum UtilsMethod {
output: Output,
},
/// Verifies the semantic of a transaction.
/// Expected response: [`TransactionFailureReason`](crate::Response::TransactionFailureReason)
/// Expected response: [`Ok`](crate::Response::Ok)
#[serde(rename_all = "camelCase")]
VerifyTransactionSemantic {
transaction: TransactionDto,
Expand Down
4 changes: 3 additions & 1 deletion bindings/core/src/method_handler/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,9 @@ pub(crate) fn call_utils_method_internal(method: UtilsMethod) -> Result<Response
protocol_parameters,
);

Response::TransactionFailureReason(context.validate()?)
context.validate().map_err(Error::from)?;

Response::Ok
}
UtilsMethod::ManaWithDecay {
mana,
Expand Down
3 changes: 0 additions & 3 deletions bindings/core/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ use iota_sdk::{
},
payload::{dto::SignedTransactionPayloadDto, signed_transaction::TransactionId},
protocol::ProtocolParameters,
semantic::TransactionFailureReason,
signature::Ed25519Signature,
slot::{SlotCommitment, SlotCommitmentId},
unlock::Unlock,
Expand Down Expand Up @@ -233,8 +232,6 @@ pub enum Response {
CustomJson(serde_json::Value),
/// Response for [`ComputeSlotCommitmentId`](crate::method::UtilsMethod::ComputeSlotCommitmentId)
SlotCommitmentId(SlotCommitmentId),
/// Response for [`VerifyTransactionSemantic`](crate::method::UtilsMethod::VerifyTransactionSemantic).
TransactionFailureReason(Option<TransactionFailureReason>),

// Responses in client and wallet
/// Response for:
Expand Down
7 changes: 3 additions & 4 deletions bindings/nodejs/lib/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -559,16 +559,16 @@ export class Utils {
* @param unlocks The unlocks.
* @param manaRewards The total mana rewards claimed in the transaction.
*
* @returns The conflict reason.
* @returns void.
*/
static verifyTransactionSemantic(
transaction: SignedTransactionPayload,
inputs: InputSigningData[],
protocolParameters: ProtocolParameters,
unlocks?: Unlock[],
manaRewards?: { [outputId: HexEncodedString]: NumericString },
): string {
const conflictReason = callUtilsMethod({
): void {
return callUtilsMethod({
name: 'verifyTransactionSemantic',
data: {
transaction,
Expand All @@ -578,7 +578,6 @@ export class Utils {
manaRewards,
},
});
return conflictReason;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions bindings/python/iota_sdk/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,10 +241,10 @@ def verify_secp256k1_ecdsa_signature(

@staticmethod
def verify_transaction_semantic(
transaction: Transaction, inputs: List[InputSigningData], protocol_parameters: ProtocolParameters, unlocks: Optional[List[Unlock]] = None, mana_rewards: Optional[dict[OutputId, int]] = None) -> str:
transaction: Transaction, inputs: List[InputSigningData], protocol_parameters: ProtocolParameters, unlocks: Optional[List[Unlock]] = None, mana_rewards: Optional[dict[OutputId, int]] = None):
"""Verifies the semantic of a transaction.
"""
return _call_method('verifyTransactionSemantic', {
_call_method('verifyTransactionSemantic', {
'transaction': transaction,
'inputs': inputs,
'unlocks': unlocks,
Expand Down
10 changes: 6 additions & 4 deletions sdk/src/client/api/block_builder/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use alloc::collections::BTreeMap;
use packable::PackableExt;

use crate::{
client::{secret::types::InputSigningData, Error, Result},
client::{secret::types::InputSigningData, Error},
types::block::{
output::{Output, OutputId},
payload::signed_transaction::{SignedTransactionPayload, Transaction},
Expand All @@ -33,7 +33,7 @@ pub fn verify_semantic(
transaction_payload: &SignedTransactionPayload,
mana_rewards: BTreeMap<OutputId, u64>,
protocol_parameters: ProtocolParameters,
) -> crate::client::Result<Option<TransactionFailureReason>> {
) -> Result<(), TransactionFailureReason> {
let inputs = input_signing_data
.iter()
.map(|input| (input.output_id(), &input.output))
Expand All @@ -51,7 +51,9 @@ pub fn verify_semantic(
}

/// Verifies that the signed transaction payload doesn't exceed the block size limit with 8 parents.
pub fn validate_signed_transaction_payload_length(signed_transaction_payload: &SignedTransactionPayload) -> Result<()> {
pub fn validate_signed_transaction_payload_length(
signed_transaction_payload: &SignedTransactionPayload,
) -> crate::client::error::Result<()> {
let signed_transaction_payload_bytes = signed_transaction_payload.pack_to_vec();
if signed_transaction_payload_bytes.len() > MAX_TX_LENGTH_FOR_BLOCK_WITH_8_PARENTS {
return Err(Error::InvalidSignedTransactionPayloadLength {
Expand All @@ -65,7 +67,7 @@ pub fn validate_signed_transaction_payload_length(signed_transaction_payload: &S
/// Verifies that the transaction doesn't exceed the block size limit with 8 parents.
/// Assuming one signature unlock and otherwise reference/account/nft unlocks. `validate_transaction_payload_length()`
/// should later be used to check the length again with the correct unlocks.
pub fn validate_transaction_length(transaction: &Transaction) -> Result<()> {
pub fn validate_transaction_length(transaction: &Transaction) -> crate::client::error::Result<()> {
let transaction_bytes = transaction.pack_to_vec();

// Assuming there is only 1 signature unlock and the rest is reference/account/nft unlocks
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/client/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ pub enum Error {
},
/// The semantic validation of a transaction failed.
#[error("the semantic validation of a transaction failed with conflict reason: {} - {0:?}", *.0 as u8)]
TransactionSemantic(TransactionFailureReason),
TransactionSemantic(#[from] TransactionFailureReason),
/// Unpack error
#[error("{0}")]
Unpack(#[from] packable::error::UnpackError<crate::types::block::Error, UnexpectedEOF>),
Expand Down
11 changes: 5 additions & 6 deletions sdk/src/client/secret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -671,12 +671,11 @@ where

validate_signed_transaction_payload_length(&tx_payload)?;

let conflict = verify_semantic(&inputs_data, &tx_payload, mana_rewards, protocol_parameters.clone())?;

if let Some(conflict) = conflict {
log::debug!("[sign_transaction] conflict: {conflict:?} for {:#?}", tx_payload);
return Err(Error::TransactionSemantic(conflict));
}
verify_semantic(&inputs_data, &tx_payload, mana_rewards, protocol_parameters.clone())
.inspect_err(|e| {
log::debug!("[sign_transaction] conflict: {e:?} for {tx_payload:#?}");
})
.map_err(Error::TransactionSemantic)?;

Ok(tx_payload)
}
Expand Down
11 changes: 11 additions & 0 deletions sdk/src/types/block/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use crate::types::block::{
InputCount, OutputCount,
},
protocol::ProtocolParametersHash,
semantic::TransactionFailureReason,
unlock::{UnlockCount, UnlockIndex, UnlocksCount},
};

Expand Down Expand Up @@ -212,6 +213,7 @@ pub enum Error {
BlockSlotBeforeTransactionCreationSlot,
TransactionCommitmentSlotNotInBlockSlotInterval,
TransactionCommitmentSlotAfterBlockCommitmentSlot,
TransactionFailureReason(TransactionFailureReason),
}

#[cfg(feature = "std")]
Expand Down Expand Up @@ -463,6 +465,9 @@ impl fmt::Display for Error {
Self::TransactionCommitmentSlotAfterBlockCommitmentSlot => {
write!(f, "the transaction commitment slot is after the block commitment slot")
}
Self::TransactionFailureReason(r) => {
write!(f, "transaction failure reason: {r}")
}
}
}
}
Expand All @@ -479,6 +484,12 @@ impl From<CryptoError> for Error {
}
}

impl From<TransactionFailureReason> for Error {
fn from(error: TransactionFailureReason) -> Self {
Self::TransactionFailureReason(error)
}
}

impl From<Infallible> for Error {
fn from(error: Infallible) -> Self {
match error {}
Expand Down
3 changes: 3 additions & 0 deletions sdk/src/types/block/semantic/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,9 @@ impl fmt::Display for TransactionFailureReason {
}
}

#[cfg(feature = "std")]
impl std::error::Error for TransactionFailureReason {}

impl TryFrom<u8> for TransactionFailureReason {
type Error = Error;

Expand Down
Loading

0 comments on commit 31bcdf4

Please sign in to comment.