Skip to content

Commit 110e159

Browse files
committed
test: [#261]: do not allow uploading two torrents with the same canonical infohash
If you upload a torrent, the infohash migth change if the `info` dictionary contains custom fields. The Index removes non-standard custom fields, and that generates a new infohash for the torrent. If you upload a second torrent which is different from a previous one only in the custom fields, the same canonical infohash will be generated, so the torrent will be rejected as duplicated. The new original infohash will be stored in the database.
1 parent 3b7a762 commit 110e159

File tree

13 files changed

+226
-24
lines changed

13 files changed

+226
-24
lines changed

.github/workflows/coverage.yaml

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,55 @@ name: Coverage
22

33
on:
44
push:
5-
pull_request:
5+
branches:
6+
- develop
7+
pull_request_target:
8+
branches:
9+
- develop
610

711
env:
812
CARGO_TERM_COLOR: always
913

1014
jobs:
15+
secrets:
16+
name: Secrets
17+
environment: coverage
18+
runs-on: ubuntu-latest
19+
20+
outputs:
21+
continue: ${{ steps.check.outputs.continue }}
22+
23+
steps:
24+
- id: check
25+
name: Check
26+
env:
27+
CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}"
28+
if: "${{ env.CODECOV_TOKEN != '' }}"
29+
run: echo "continue=true" >> $GITHUB_OUTPUT
30+
1131
report:
1232
name: Report
33+
environment: coverage
34+
needs: secrets
35+
if: needs.secrets.outputs.continue == 'true'
1336
runs-on: ubuntu-latest
1437
env:
1538
CARGO_INCREMENTAL: "0"
1639
RUSTFLAGS: "-Z profile -C codegen-units=1 -C inline-threshold=0 -C link-dead-code -C overflow-checks=off -C panic=abort -Z panic_abort_tests"
1740
RUSTDOCFLAGS: "-Z profile -C codegen-units=1 -C inline-threshold=0 -C link-dead-code -C overflow-checks=off -C panic=abort -Z panic_abort_tests"
1841

1942
steps:
20-
- id: checkout
21-
name: Checkout Repository
22-
uses: actions/checkout@v3
43+
- id: checkout_push
44+
if: github.event_name == 'push'
45+
name: Checkout Repository (Push)
46+
uses: actions/checkout@v4
47+
48+
- id: checkout_pull_request_target
49+
if: github.event_name == 'pull_request_target'
50+
name: Checkout Repository (Pull Request Target)
51+
uses: actions/checkout@v4
52+
with:
53+
ref: "refs/pull/${{ github.event.pull_request.number }}/head"
2354

2455
- id: setup
2556
name: Setup Toolchain
@@ -61,4 +92,4 @@ jobs:
6192
token: ${{ secrets.CODECOV_TOKEN }}
6293
files: ${{ steps.coverage.outputs.report }}
6394
verbose: true
64-
fail_ci_if_error: true
95+
fail_ci_if_error: true

.github/workflows/testing.yaml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,14 @@ jobs:
6565
name: Run Lint Checks
6666
run: cargo clippy --tests --benches --examples --workspace --all-targets --all-features -- -D clippy::correctness -D clippy::suspicious -D clippy::complexity -D clippy::perf -D clippy::style -D clippy::pedantic
6767

68-
- id: doc
69-
name: Run Documentation Checks
68+
- id: testdoc
69+
name: Run Documentation Tests
7070
run: cargo test --doc
7171

72+
- id: builddoc
73+
name: Build Documentation
74+
run: cargo doc --no-deps --bins --examples --workspace --all-features
75+
7276
unit:
7377
name: Units
7478
runs-on: ubuntu-latest

src/databases/database.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,14 +231,17 @@ pub trait Database: Sync + Send {
231231
))
232232
}
233233

234-
/// Returns the list of original infohashes ofr a canonical infohash.
234+
/// Returns the list of all infohashes producing the same canonical infohash.
235235
///
236236
/// When you upload a torrent the infohash migth change because the Index
237237
/// remove the non-standard fields in the `info` dictionary. That makes the
238238
/// infohash change. The canonical infohash is the resulting infohash.
239239
/// This function returns the original infohashes of a canonical infohash.
240+
///
241+
/// If the original infohash was unknown, it returns the canonical infohash.
242+
///
240243
/// The relationship is 1 canonical infohash -> N original infohashes.
241-
async fn get_torrent_original_info_hashes(&self, canonical: &InfoHash) -> Result<OriginalInfoHashes, Error>;
244+
async fn get_torrent_canonical_info_hash_group(&self, canonical: &InfoHash) -> Result<OriginalInfoHashes, Error>;
242245

243246
async fn insert_torrent_info_hash(&self, original: &InfoHash, canonical: &InfoHash) -> Result<(), Error>;
244247

src/databases/mysql.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ impl Database for Mysql {
592592
}
593593
}
594594

595-
async fn get_torrent_original_info_hashes(&self, canonical: &InfoHash) -> Result<OriginalInfoHashes, database::Error> {
595+
async fn get_torrent_canonical_info_hash_group(&self, canonical: &InfoHash) -> Result<OriginalInfoHashes, database::Error> {
596596
let db_info_hashes = query_as::<_, DbTorrentInfoHash>(
597597
"SELECT info_hash, canonical_info_hash, original_is_known FROM torrust_torrent_info_hashes WHERE canonical_info_hash = ?",
598598
)

src/databases/sqlite.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ impl Database for Sqlite {
582582
}
583583
}
584584

585-
async fn get_torrent_original_info_hashes(&self, canonical: &InfoHash) -> Result<OriginalInfoHashes, database::Error> {
585+
async fn get_torrent_canonical_info_hash_group(&self, canonical: &InfoHash) -> Result<OriginalInfoHashes, database::Error> {
586586
let db_info_hashes = query_as::<_, DbTorrentInfoHash>(
587587
"SELECT info_hash, canonical_info_hash, original_is_known FROM torrust_torrent_info_hashes WHERE canonical_info_hash = ?",
588588
)

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@
202202
//! torrent_info_update_interval = 3600
203203
//! ```
204204
//!
205-
//! For more information about configuration you can visit the documentation for the [`config`](crate::config) module.
205+
//! For more information about configuration you can visit the documentation for the [`config`]) module.
206206
//!
207207
//! Alternatively to the `config.toml` file you can use one environment variable `TORRUST_IDX_BACK_CONFIG` to pass the configuration to the tracker:
208208
//!

src/services/torrent.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ impl Index {
170170

171171
let original_info_hashes = self
172172
.torrent_info_hash_repository
173-
.get_torrent_original_info_hashes(&canonical_info_hash)
173+
.get_canonical_info_hash_group(&canonical_info_hash)
174174
.await?;
175175

176176
if !original_info_hashes.is_empty() {
@@ -582,13 +582,13 @@ impl DbTorrentInfoHashRepository {
582582
Self { database }
583583
}
584584

585-
/// It returns all the original infohashes associated to the canonical one.
585+
/// It returns all the infohashes associated to the canonical one.
586586
///
587587
/// # Errors
588588
///
589589
/// This function will return an error there is a database error.
590-
pub async fn get_torrent_original_info_hashes(&self, info_hash: &InfoHash) -> Result<OriginalInfoHashes, Error> {
591-
self.database.get_torrent_original_info_hashes(info_hash).await
590+
pub async fn get_canonical_info_hash_group(&self, info_hash: &InfoHash) -> Result<OriginalInfoHashes, Error> {
591+
self.database.get_torrent_canonical_info_hash_group(info_hash).await
592592
}
593593

594594
/// Inserts a new infohash for the torrent. Torrents can be associated to

src/web/api/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//!
33
//! Currently, the API has only one version: `v1`.
44
//!
5-
//! Refer to the [`v1`](crate::web::api::v1) module for more information.
5+
//! Refer to the [`v1`]) module for more information.
66
pub mod server;
77
pub mod v1;
88

src/web/api/v1/contexts/torrent/mod.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,32 @@
22
//!
33
//! This API context is responsible for handling all torrent related requests.
44
//!
5+
//! # Original and canonical infohashes
6+
//!
7+
//! Uploaded torrents can contain non-standard fields in the `info` dictionary.
8+
//!
9+
//! For example, this is a torrent file in JSON format with a "custom" field.
10+
//!
11+
//! ```json
12+
//! {
13+
//! "info": {
14+
//! "length": 602515,
15+
//! "name": "mandelbrot_set_01",
16+
//! "piece length": 32768,
17+
//! "pieces": "<hex>8A 88 32 BE ED 05 5F AA C4 AF 4A 90 4B 9A BF 0D EC 83 42 1C 73 39 05 B8 D6 20 2C 1B D1 8A 53 28 1F B5 D4 23 0A 23 C8 DB AC C4 E6 6B 16 12 08 C7 A4 AD 64 45 70 ED 91 0D F1 38 E7 DF 0C 1A D0 C9 23 27 7C D1 F9 D4 E5 A1 5F F5 E5 A0 E4 9E FB B1 43 F5 4B AD 0E D4 9D CB 49 F7 E6 7B BA 30 5F AF F9 88 56 FB 45 9A B4 95 92 3E 2C 7F DA A6 D3 82 E7 63 A3 BB 4B 28 F3 57 C7 CB 7D 8C 06 E3 46 AB D7 E8 8E 8A 8C 9F C7 E6 C5 C5 64 82 ED 47 BB 2A F1 B7 3F A5 3C 5B 9C AF 43 EC 2A E1 08 68 9A 49 C8 BF 1B 07 AD BE E9 2D 7E BE 9C 18 7F 4C A1 97 0E 54 3A 18 94 0E 60 8D 5C 69 0E 41 46 0D 3C 9A 37 F6 81 62 4F 95 C0 73 92 CA 9A D5 A9 89 AC 8B 85 12 53 0B FB E2 96 26 3E 26 A6 5B 70 53 48 65 F3 6C 27 0F 6B BD 1C EE EB 1A 9D 5F 77 A8 D8 AF D8 14 82 4A E0 B4 62 BC F1 A5 F5 F2 C7 60 F8 38 C8 5B 0B A9 07 DD 86 FA C0 7B F0 26 D7 D1 9A 42 C3 1F 9F B9 59 83 10 62 41 E9 06 3C 6D A1 19 75 01 57 25 9E B7 FE DF 91 04 D4 51 4B 6D 44 02 8D 31 8E 84 26 95 0F 30 31 F0 2C 16 39 BD 53 1D CF D3 5E 3E 41 A9 1E 14 3F 73 24 AC 5E 9E FC 4D C5 70 45 0F 45 8B 9B 52 E6 D0 26 47 8F 43 08 9E 2A 7C C5 92 D5 86 36 FE 48 E9 B8 86 84 92 23 49 5B EE C4 31 B2 1D 10 75 8E 4C 07 84 8F</hex>",
18+
//! "custom": "custom03"
19+
//! }
20+
//! }
21+
//! ```
22+
//!
23+
//! When you upload a torrent file with non-standards fields in the `info`
24+
//! dictionary, the Index removes those non-standard fields. That generates a
25+
//! new info-hash because all fields in the `info` key are used to calculate it.
26+
//!
27+
//! The Index stores the original info-hash. The resulting info-hash after
28+
//! removing the non-standard fields is called "canonical" infohash. The Index
29+
//! stores the relationship between the original info-hash and the canonical one.
30+
//!
531
//! # Endpoints
632
//!
733
//! - [Upload new torrent](#upload-new-torrent)

src/web/api/v1/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//!
33
//! The API is organized in contexts.
44
//!
5-
//! Refer to the [`contexts`](crate::web::api::v1::contexts) module for more
5+
//! Refer to the [`contexts`] module for more
66
//! information.
77
pub mod auth;
88
pub mod contexts;

0 commit comments

Comments
 (0)