Skip to content

Commit

Permalink
Fix regex of several MQTT topics (#1594)
Browse files Browse the repository at this point in the history
* Fix regex of several MQTT topics

* Add invalid_tags tests

* Merge spent

* Fmt

* Add $ back
  • Loading branch information
thibault-martinez authored Feb 21, 2023
1 parent e8422a8 commit 7c7b9b7
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 16 deletions.
2 changes: 2 additions & 0 deletions client/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `StrongholdDatabaseProvider` renamed to `StrongholdStorageProvider`;
- `high_level` module moved to `api`;
- Added `#[serde(rename_all = "camelCase")]` to `NodeAuth`;
- `Topic::try_new` parameter from `String` to `impl Into<String>`;

### Removed

Expand All @@ -44,6 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Automatic input selection with sender and issuer features;
- Automatic alias governance transition does not change output amount in input selection;
- ISA does not select aliases it can't unlock for amount;
- Regex of several MQTT topics;

## 2.0.1-rc.6 - 2023-02-08

Expand Down
31 changes: 15 additions & 16 deletions client/src/node_api/mqtt/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,35 +163,34 @@ impl<'de> Deserialize<'de> for Topic {

impl Topic {
/// Creates a new topic and checks if it's valid.
pub fn try_new(topic: String) -> Result<Self> {
pub fn try_new(topic: impl Into<String>) -> Result<Self> {
let valid_topics = lazy_static!(
RegexSet::new([
// Milestone topics
// Milestone topics.
r"^milestone-info/latest$",
r"^milestone-info/confirmed$",
r"^milestones$",
// Block topics
// Block topics.
r"^blocks$",
r"^blocks/transaction$",
r"^blocks/transaction/tagged-data$",
r"^blocks/transaction/tagged-data/0x([a-f0-9]{128})$",
r"^blocks/transaction/tagged-data/0x((?:[a-f0-9]{2}){1,64})$",
r"^blocks/tagged-data$",
r"^blocks/tagged-data/0x([a-f0-9]{64})$",
r"^block-metadata/0x([a-f0-9]{64})",
r"^blocks/tagged-data/0x((?:[a-f0-9]{2}){1,64})$",
r"^block-metadata/0x([a-f0-9]{64})$",
r"^block-metadata/referenced$",
// Transaction topics
// Transaction topics.
r"^transactions/0x([a-f0-9]{64})/included-block$",
// Output topics
r"^outputs/([a-f0-9]{64})(\d{4})$",
r"^outputs/alias/0x([a-f0-9]{40})$",
r"^outputs/nft/0x([a-f0-9]{40})$",
r"^outputs/foundry/0x([a-f0-9]{52})$",
// BIP-173 compliant bech32 address
r"^outputs/unlock/(\+|address|storage-return|expiration|state-controller|governor|immutable-alias)/[\x21-\x7E]{1,30}1[A-Za-z0-9]+$",
// BIP-173 compliant bech32 address
r"^outputs/unlock/(\+|address|storage-return|expiration|state-controller|governor|immutable-alias)/[\x21-\x7E]{1,30}1[A-Za-z0-9]+/spent$",
// Output topics.
r"^outputs/0x([a-f0-9]{64})(\d{4})$",
r"^outputs/alias/0x([a-f0-9]{64})$",
r"^outputs/nft/0x([a-f0-9]{64})$",
r"^outputs/foundry/0x([a-f0-9]{76})$",
r"^outputs/unlock/(\+|address|storage-return|expiration|state-controller|governor|immutable-alias)/[\x21-\x7E]{1,30}1[A-Za-z0-9]+(?:/spent)?$",
// Receipt topics.
r"^receipts$",
]).expect("cannot build regex set") => RegexSet);
let topic = topic.into();

if valid_topics.is_match(&topic) {
Ok(Self(topic))
Expand Down
1 change: 1 addition & 0 deletions client/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

mod input_selection;
mod mqtt;
mod signing;

use std::{collections::HashMap, hash::Hash, str::FromStr};
Expand Down
4 changes: 4 additions & 0 deletions client/tests/mqtt/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright 2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

mod topic;
89 changes: 89 additions & 0 deletions client/tests/mqtt/topic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use iota_client::{node_api::mqtt::Topic, Error};

#[test]
fn valid_topics() {
assert!(Topic::try_new("milestone-info/latest").is_ok());
assert!(Topic::try_new("milestone-info/confirmed").is_ok());
assert!(Topic::try_new("milestones").is_ok());
assert!(Topic::try_new("blocks").is_ok());
assert!(Topic::try_new("blocks/transaction").is_ok());
assert!(Topic::try_new("blocks/transaction/tagged-data").is_ok());
assert!(Topic::try_new("blocks/transaction/tagged-data/0x0123456789abcdef").is_ok());
assert!(Topic::try_new("blocks/tagged-data").is_ok());
assert!(Topic::try_new("blocks/tagged-data/0x0123456789abcdef").is_ok());
assert!(
Topic::try_new("block-metadata/0x36845227a59864ac12d3d2389fcb4ea0bdd1a5d1d4ed464bde3154216c3246c4").is_ok()
);
assert!(Topic::try_new("block-metadata/referenced").is_ok());
assert!(
Topic::try_new(
"transactions/0x36845227a59864ac12d3d2389fcb4ea0bdd1a5d1d4ed464bde3154216c3246c4/included-block"
)
.is_ok()
);
assert!(Topic::try_new("outputs/0x36845227a59864ac12d3d2389fcb4ea0bdd1a5d1d4ed464bde3154216c3246c40000").is_ok());
assert!(Topic::try_new("outputs/alias/0xb21517992e96865d5fd90b403fe05fe25c6d4acfb6cdd6e7c9bbfb4266d05151").is_ok());
assert!(Topic::try_new("outputs/nft/0x38500750eb788bfb89b4589634a82b0cee9c6a9724bafde505ffa1bb875ab0b5").is_ok());
assert!(
Topic::try_new(
"outputs/foundry/0x08e10a5c7bcfdce48ff500156040f7548ca511d79a6e253a22759116c2ae8c818d0100000000"
)
.is_ok()
);
assert!(
Topic::try_new("outputs/unlock/address/iota1qrwfnskm4f7utdrxqnkfntfqxehtpj8s0kf68zkcwm0yrhuemzjp5sjfw5v")
.is_ok()
);
assert!(
Topic::try_new("outputs/unlock/address/iota1qrwfnskm4f7utdrxqnkfntfqxehtpj8s0kf68zkcwm0yrhuemzjp5sjfw5v/spent")
.is_ok()
);
assert!(Topic::try_new("receipts").is_ok());
}

#[test]
fn invalid_tags() {
// Empty.
assert!(matches!(
Topic::try_new("blocks/transaction/tagged-data/0x"),
Err(Error::InvalidMqttTopic(_))
));
assert!(matches!(
Topic::try_new("blocks/tagged-data/0x"),
Err(Error::InvalidMqttTopic(_))
));
// Uneven.
assert!(matches!(
Topic::try_new("blocks/transaction/tagged-data/0x0123456789abcde"),
Err(Error::InvalidMqttTopic(_))
));
assert!(matches!(
Topic::try_new("blocks/tagged-data/0x0123456789abcde"),
Err(Error::InvalidMqttTopic(_))
));
// Too large.
assert!(matches!(
Topic::try_new(
"blocks/transaction/tagged-data/0xb21517992e96865d5fd90b403fe05fe25c6d4acfb6cdd6e7c9bbfb4266d05151b21517992e96865d5fd90b403fe05fe25c6d4acfb6cdd6e7c9bbfb4266d05151ff"
),
Err(Error::InvalidMqttTopic(_))
));
assert!(matches!(
Topic::try_new(
"blocks/tagged-data/0xb21517992e96865d5fd90b403fe05fe25c6d4acfb6cdd6e7c9bbfb4266d05151b21517992e96865d5fd90b403fe05fe25c6d4acfb6cdd6e7c9bbfb4266d05151ff"
),
Err(Error::InvalidMqttTopic(_))
));
// Invalid chars.
assert!(matches!(
Topic::try_new("blocks/transaction/tagged-data/0x012345@789abcde"),
Err(Error::InvalidMqttTopic(_))
));
assert!(matches!(
Topic::try_new("blocks/tagged-data/0x012345@789abcde"),
Err(Error::InvalidMqttTopic(_))
));
}

0 comments on commit 7c7b9b7

Please sign in to comment.