generated from codecrafters-io/course-template
-
Notifications
You must be signed in to change notification settings - Fork 13
/
course-definition.yml
1079 lines (832 loc) · 48.3 KB
/
course-definition.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Used in your course's URL: https://app.codecrafters.io/courses/<slug>
# Example: "redis"
slug: "bittorrent"
# The name of your course. This will be displayed in the course catalog, and on other course pages.
# Example: "Build your own Redis"
name: "Build your own BitTorrent"
# A short name for your course, this'll be used in copy like emails.
# Example: "Redis"
short_name: "BitTorrent"
# The release status for your course.
#
# - alpha: Only visible to yourself and CodeCrafters staff.
# - beta: Visible to all CodeCrafters users, but with a "beta" label.
# - live: Visible to all CodeCrafters users, no label.
#
# Allowed values: "alpha", "beta", "live"
release_status: "live"
# This is shown on the course overview page. Markdown supported, recommended length ~40 words.
#
# Recommended format:
#
# > ABC is <whatever>. In this challenge, you'll build your own ABC that's capable of D, E, F and G.
# >
# > Along the way, we'll learn about X, Y, Z and more.
#
# Example:
#
# > Redis is an in-memory data structure store often used as a database, cache, message broken and streaming engine. In this challenge
# > you'll build your own Redis server that is capable of serving basic commands, reading RDB files and more.
# >
# > Along the way, you'll learn about TCP servers, the Redis Protocol and more.
description_md: |-
BitTorrent is a peer-to-peer file sharing protocol used for distributing large amounts of data. In this challenge, you'll
build a BitTorrent client that's capable of downloading a publicly available file using the BitTorrent protocol.
Along the way, you'll learn about the BitTorrent protocol, .torrent files more.
# This is shown on the catalog. Plaintext only, recommended length ~10 words.
#
# Recommended format:
#
# > Learn about X, Y, Z and more
#
# Example:
#
# > Learn about TCP servers, the Redis protocol and more
#
# **TODO**: Remove _md suffix since markdown isn't supported
short_description_md: |-
Learn about .torrent files, the BitTorrent Peer Protocol and more
# The percentage of users who complete your course. We'll calculate this automatically in the future, safe to ignore for now.
completion_percentage: 15
# The languages that your course supports.
languages:
- slug: "c"
release_status: "beta"
- slug: "cpp"
release_status: "beta"
- slug: "csharp"
release_status: "beta"
- slug: "elixir"
release_status: "beta"
- slug: "go"
- slug: "haskell"
release_status: "beta"
- slug: "java"
release_status: "beta"
- slug: "javascript"
- slug: "kotlin"
release_status: "beta"
- slug: "python"
- slug: "ruby"
- slug: "rust"
- slug: "zig"
release_status: "beta"
- slug: "typescript"
release_status: "beta"
marketing:
# Shown in the catalog.
#
# Recommended guidelines:
#
# - "easy": < 2h of work for an experienced developer
# - "medium": > 6h of work for an experienced developer
# - "hard": > 6h of work for an experienced developer
#
# Allowed values: "easy", "medium", "hard"
difficulty: medium
# This is shown as an example when users suggest extensions to your course.
# Example: "Persistence" (from the Redis challenge)
sample_extension_idea_title: "Multiple Peers"
# This is shown as an example when users suggest extensions to your course.
# Example: "A Redis server that can read and write .rdb files" (from the Redis challenge)
sample_extension_idea_description: "A bittorrent client that can download a file by combining pieces from multiple peers"
# These are some default testimonials that you can use. Feel free to switch these out with your own.
testimonials:
- author_name: "Ananthalakshmi Sankar"
author_description: "Automation Engineer at Apple"
author_avatar: "https://codecrafters.io/images/external/testimonials/oxta.jpeg"
link: "https://github.com/anu294"
text: "There are few sites I like as much that have a step by step guide. The real-time feedback is so good, it's creepy!"
- author_name: "Patrick Burris"
author_description: "Senior Software Developer, CenturyLink"
author_avatar: "https://codecrafters.io/images/external/testimonials/patrick-burris.jpeg"
link: "https://github.com/Jumballaya"
text: |-
I think the instant feedback right there in the git push is really cool.
Didn't even know that was possible!
extensions:
- slug: "magnet-links"
name: "Magnet Links"
description_markdown: |
This extension covers magnet links. Magnet links allow downloading files without the need of downloading a .torrent file first.
Along the way, you'll learn about the [magnet URI format](https://en.wikipedia.org/wiki/Magnet_URI_scheme), the [extension protocol](https://www.bittorrent.org/beps/bep_0010.html) and the [metadata extension](https://www.bittorrent.org/beps/bep_0009.html).
stages:
- slug: "ns2" # A identifier for this stage, needs to be unique within a course.
# The name of the stage. This is shown in the course catalog, and on other course pages.
name: "Decode bencoded strings"
# The difficulty of this stage.
#
# Recommended guidelines, based on how long the stage will take an experienced developer to complete:
#
# - Very Easy (< 5 minutes)
# - Easy (5-10 minutes)
# - Medium (30m-1h)
# - Hard (> 1h)
#
# Allowed values: "very_easy", "easy", "medium", "hard"
difficulty: very_easy
# The instructions for your stage. Markdown supported. Shown on the course page.
description_md: |-
[Bencode](https://en.wikipedia.org/wiki/Bencode) (pronounced *Bee-encode*) is a serialization format used in [the BitTorrent protocol](https://www.bittorrent.org/beps/bep_0003.html). It is used in torrent files and in communication between trackers and peers.
Bencode supports four data types:
- strings
- integers
- arrays
- dictionaries
In this stage, we'll focus on decoding strings.
Strings are encoded as `<length>:<contents>`. For example, the string `"hello"` is encoded as `"5:hello"`.
You'll implement a `decode` command which takes a bencoded value as input and prints the decoded value as JSON.
Here’s how the tester will execute your program:
```
$ ./your_bittorrent.sh decode 5:hello
"hello"
```
# A description of this stage that is used on the course overview page and other marketing material. Markdown supported.
marketing_md: |-
[Bencode](https://en.wikipedia.org/wiki/Bencode) is a binary serialization format used in BitTorrent protocol. In this stage, you’ll decode a bencoded string.
- slug: "eb4"
name: "Decode bencoded integers"
difficulty: easy
description_md: |-
In this stage, you'll extend the `decode` command to support bencoded integers.
Integers are encoded as `i<number>e`. For example, `52` is encoded as `i52e` and `-52` is encoded as `i-52e`.
Here's how the tester will execute your program:
```
$ ./your_bittorrent.sh decode i52e
52
```
{{#lang_is_go}}
If you'd prefer to use a library for this stage, [bencode-go](https://github.com/jackpal/bencode-go) is available for you to use.
{{/lang_is_go}}
{{#lang_is_python}}
If you'd prefer to use a library for this stage, [bencode.py](https://pypi.org/project/bencode.py/) is available for you to use.
{{/lang_is_python}}
{{#lang_is_rust}}
If you'd prefer to use a library crate for this stage, [serde-bencode](https://github.com/toby/serde-bencode/) is available for you to use.
{{/lang_is_rust}}
{{#lang_is_java}}
If you'd prefer to use a library for this stage, [bencode](https://github.com/dampcake/bencode) parser is available for you to use.
{{/lang_is_java}}
{{#lang_is_kotlin}}
If you'd prefer to use a library for this stage, [bencode](https://github.com/dampcake/bencode) parser is available for you to use.
{{/lang_is_kotlin}}
marketing_md: |-
In this stage, you’ll decode a bencoded integer.
- slug: "ah1"
name: "Decode bencoded lists"
difficulty: easy
description_md: |-
In this stage, you'll extend the `decode` command to support bencoded lists.
Lists are encoded as `l<bencoded_elements>e`.
For example, `["hello", 52]` would be encoded as `l5:helloi52ee`. Note that there are no separators between the elements.
Here’s how the tester will execute your program:
```
$ ./your_bittorrent.sh decode l5:helloi52ee
[“hello”,52]
```
{{#lang_is_go}}
If you'd prefer to use a library for this stage, [bencode-go](https://github.com/jackpal/bencode-go) is available for you to use.
{{/lang_is_go}}
{{#lang_is_python}}
If you'd prefer to use a library for this stage, [bencode.py](https://pypi.org/project/bencode.py/) is available for you to use.
{{/lang_is_python}}
{{#lang_is_rust}}
If you'd prefer to use a library crate for this stage, [serde-bencode](https://github.com/toby/serde-bencode/) is available for you to use.
{{/lang_is_rust}}
{{#lang_is_java}}
If you'd prefer to use a library for this stage, [bencode](https://github.com/dampcake/bencode) parser is available for you to use.
{{/lang_is_java}}
{{#lang_is_kotlin}}
If you'd prefer to use a library for this stage, [bencode](https://github.com/dampcake/bencode) parser is available for you to use.
{{/lang_is_kotlin}}
marketing_md: |-
In this stage, you’ll decode a bencoded list.
- slug: "mn6"
name: "Decode bencoded dictionaries"
difficulty: easy
description_md: |-
In this stage, you'll extend the `decode` command to support bencoded dictionaries.
A dictionary is encoded as `d<key1><value1>...<keyN><valueN>e`. `<key1>`, `<value1>` etc. correspond to the bencoded keys & values. The keys are sorted in lexicographical order and must be strings.
For example, `{"hello": 52, "foo":"bar"}` would be encoded as: `d3:foo3:bar5:helloi52ee` (note that the keys were reordered).
Here’s how the tester will execute your program:
```
$ ./your_bittorrent.sh decode d3:foo3:bar5:helloi52ee
{"foo":"bar","hello":52}
```
{{#lang_is_go}}
If you'd prefer to use a library for this stage, [bencode-go](https://github.com/jackpal/bencode-go) is available for you to use.
{{/lang_is_go}}
{{#lang_is_python}}
If you'd prefer to use a library for this stage, [bencode.py](https://pypi.org/project/bencode.py/) is available for you to use.
{{/lang_is_python}}
{{#lang_is_rust}}
If you'd prefer to use a library crate for this stage, [serde-bencode](https://github.com/toby/serde-bencode/) is available for you to use.
{{/lang_is_rust}}
{{#lang_is_java}}
If you'd prefer to use a library for this stage, [bencode](https://github.com/dampcake/bencode) parser is available for you to use.
{{/lang_is_java}}
{{#lang_is_kotlin}}
If you'd prefer to use a library for this stage, [bencode](https://github.com/dampcake/bencode) parser is available for you to use.
{{/lang_is_kotlin}}
marketing_md: |-
In this stage, you’ll decode a bencoded dictionary.
- slug: "ow9"
name: "Parse torrent file"
difficulty: easy
description_md: |-
In this stage, you'll parse a torrent file and print information about the torrent.
A torrent file (also known as a [metainfo file](https://www.bittorrent.org/beps/bep_0003.html#metainfo-files)) contains a bencoded dictionary with the following keys and values:
- `announce`:
- URL to a "tracker", which is a central server that keeps track of peers participating in the sharing of a torrent.
- `info`:
- A dictionary with keys:
- `length`: size of the file in bytes, for single-file torrents
- `name`: suggested name to save the file / directory as
- `piece length`: number of bytes in each piece
- `pieces`: concatenated SHA-1 hashes of each piece
{{#lang_is_java}}
**Note**: .torrent files contain bytes that aren’t valid UTF-16 characters. You’ll run into problems if you try to read the contents of this file as a `String`. Use `byte[]` instead.
{{/lang_is_java}}
{{#lang_is_kotlin}}
**Note**: .torrent files contain bytes that aren’t valid UTF-16 characters. You’ll run into problems if you try to read the contents of this file as a `String`. Use `byte[]` instead.
{{/lang_is_kotlin}}
{{#lang_is_rust}}
**Note:** .torrent files contain bytes that aren’t valid UTF-8 characters. You'll run into problems if you try to read the contents of this file as a `String`. Use `&[u8]` or `Vec<u8>` instead.
{{/lang_is_rust}}
{{#lang_is_elixir}}
**Note:** .torrent files contain bytes that aren’t valid UTF-8 characters. You'll run into problems if you try to read the contents of this file as a `String`. Use `binary` instead, see `IO.iodata_to_binary()`.
{{/lang_is_elixir}}
{{^lang_is_java}}
{{^lang_is_kotlin}}
{{^lang_is_rust}}
{{^lang_is_elixir}}
**Note:** .torrent files contain bytes that aren’t valid UTF-8 characters. If the language you're using treats strings as a sequence of unicode characters (like Python's [str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)), you'll need to use a byte sequence (like Python's [bytes](https://docs.python.org/3/library/stdtypes.html#bytes-objects)) instead.
{{/lang_is_elixir}}
{{/lang_is_rust}}
{{/lang_is_kotlin}}
{{/lang_is_java}}
**Note**: The `info` dictionary looks slightly different for multi-file torrents. For this challenge, we'll only implement support for single-file torrents.
In this stage, we'll focus on extracting the tracker URL and the length of the file (in bytes).
Here’s how the tester will execute your program:
```
$ ./your_bittorrent.sh info sample.torrent
```
and here’s the output it expects:
```
Tracker URL: http://bittorrent-test-tracker.codecrafters.io/announce
Length: 92063
```
marketing_md: |-
In this stage, you’ll parse a .torrent file and extract information about the torrent.
- slug: "rb2"
name: "Calculate info hash"
difficulty: medium
description_md: |-
Info hash is a unique identifier for a torrent file. It's used when talking to trackers or peers.
In this stage, you'll calculate the info hash for a torrent file and print it in hexadecimal format.
To calculate the info hash, you'll need to:
- Extract the `info` dictionary from the torrent file after parsing
- Bencode the contents of the `info` dictionary
- Calculate the SHA-1 hash of this bencoded dictionary
Here’s how the tester will execute your program:
```
$ ./your_bittorrent.sh info sample.torrent
```
and here’s the output it expects:
```
Tracker URL: http://bittorrent-test-tracker.codecrafters.io/announce
Length: 92063
Info Hash: d69f91e6b2ae4c542468d1073a71d4ea13879a7f
```
marketing_md: |-
In this stage, you'll calculate a unique identifier for a torrent, known as info hash, used in communication with trackers and peers.
- slug: "bf7"
name: "Piece hashes"
difficulty: easy
description_md: |-
In a torrent, a file is split into equally-sized parts called **pieces**. A piece is usually 256 KB or 1 MB in size.
Each piece is assigned a SHA-1 hash value. On public networks, there may be malicious peers that send fake data. These hash values allow us to verify the integrity of each piece that we'll download.
Piece length and piece hashes are specified in the `info` dictionary of the torrent file under the following keys:
- `piece length`: number of bytes in each piece, an integer
- `pieces`: concatenated SHA-1 hashes of each piece (20 bytes each), a string
The [BitTorrent Protocol Specification](https://www.bittorrent.org/beps/bep_0003.html#info-dictionary) has more information about these keys.
In this stage, the tester will expect your program to print piece length and a list of piece hashes in hexadecimal format.
Here's how the tester will execute your program:
```
$ ./your_bittorrent.sh info sample.torrent
```
and here's the output it expects:
```
Tracker URL: http://bittorrent-test-tracker.codecrafters.io/announce
Length: 92063
Info Hash: d69f91e6b2ae4c542468d1073a71d4ea13879a7f
Piece Length: 32768
Piece Hashes:
e876f67a2a8886e8f36b136726c30fa29703022d
6e2275e604a0766656736e81ff10b55204ad8d35
f00d937a0213df1982bc8d097227ad9e909acc17
```
marketing_md: |-
In this stage, you'll extract hash values for each piece of the file. On public networks, there may be malicious peers sending fake data. Piece hashes will help us ensure the integrity of downloaded pieces.
- slug: "fi9"
name: "Discover peers"
difficulty: medium
description_md: |-
Trackers are central servers that maintain information about peers participating in the sharing and downloading of a torrent.
In this stage, you'll make a GET request to a HTTP tracker to discover peers to download the file from.
### Tracker GET request
You'll need to make a request to the tracker URL you extracted in the previous stage, and include these query params:
- `info_hash`: the info hash of the torrent
- 20 bytes long, will need to be URL encoded
- **Note**: this is **NOT** the hexadecimal representation, which is 40 bytes long
- `peer_id`: a unique identifier for your client
- A string of length 20 that you get to pick.
- `port`: the port your client is listening on
- You can set this to `6881`, you will not have to support this functionality during this challenge.
- `uploaded`: the total amount uploaded so far
- Since your client hasn't uploaded anything yet, you can set this to `0`.
- `downloaded`: the total amount downloaded so far
- Since your client hasn't downloaded anything yet, you can set this to `0`.
- `left`: the number of bytes left to download
- Since you client hasn't downloaded anything yet, this'll be the total length of the file (you've extracted this value from the torrent file in previous stages)
- `compact`: whether the peer list should use the [compact representation](https://www.bittorrent.org/beps/bep_0023.html)
- For the purposes of this challenge, set this to `1`.
- The compact representation is more commonly used in the wild, the non-compact representation is mostly supported for backward-compatibility.
Read [the BitTorrent Protocol Specification](https://www.bittorrent.org/beps/bep_0003.html#trackers) for more information about these query parameters.
### Tracker response
The tracker's response will be a bencoded dictionary with two keys:
- `interval`:
- An integer, indicating how often your client should make a request to the tracker.
- You can ignore this value for the purposes of this challenge.
- `peers`.
- A string, which contains list of peers that your client can connect to.
- Each peer is represented using 6 bytes. The first 4 bytes are the peer's IP address and the last 2 bytes are the peer's port number.
---
Here’s how the tester will execute your program:
```
$ ./your_bittorrent.sh peers sample.torrent
```
and here’s the output it expects:
```
165.232.41.73:51556
165.232.38.164:51532
165.232.35.114:51437
```
marketing_md: |-
In this stage, you’ll interact with a tracker, a central server that keeps track of peers participating in the sharing of a torrent. You'll make a GET request to a HTTP tracker to discover peers from whom you can download the file.
- slug: "ca4"
name: "Peer handshake"
difficulty: medium
description_md: |-
In this stage, you’ll establish a TCP connection with a peer and complete a handshake.
The handshake is a message consisting of the following parts as described in the [peer protocol](https://www.bittorrent.org/beps/bep_0003.html#peer-protocol):
1. length of the protocol string (BitTorrent protocol) which is `19` (1 byte)
2. the string `BitTorrent protocol` (19 bytes)
3. eight reserved bytes, which are all set to zero (8 bytes)
4. sha1 infohash (20 bytes) (**NOT** the hexadecimal representation, which is 40 bytes long)
5. peer id (20 bytes) (generate 20 random byte values)
After we send a handshake to our peer, we should receive a handshake back in the same format.
Your program should print the hexadecimal representation of the peer id you've received during the handshake.
Here’s how the tester will execute your program:
```
$ ./your_bittorrent.sh handshake sample.torrent <peer_ip>:<peer_port>
```
and here’s the output it expects:
```
Peer ID: 0102030405060708090a0b0c0d0e0f1011121314
```
(Exact value will be different as it is randomly generated.)
**Note**: To get a peer IP & port to test this locally, run `./your_bittorrent.sh peers sample.torrent` and pick any peer from the list.
marketing_md: |-
In this stage, you’ll establish a TCP connection with a peer and complete a handshake according to [BitTorrent Peer Protocol](https://www.bittorrent.org/beps/bep_0003.html#peer-protocol)
- slug: "nd2"
name: "Download a piece"
difficulty: hard
description_md: |-
In this stage, you'll download one piece and save it to disk. In the next stage we'll combine these pieces into a file.
To download a piece, your program will need to send [peer messages](https://www.bittorrent.org/beps/bep_0003.html#peer-messages) to a peer. The overall flow looks like this:
- Read the torrent file to get the tracker URL
- you've done this in previous stages
- Perform the tracker GET request to get a list of peers
- you've done this in previous stages
- Establish a TCP connection with a peer, and perform a handshake
- you've done this in previous stages
- Exchange multiple [peer messages](https://www.bittorrent.org/beps/bep_0003.html#peer-messages) to download the file
- **This is the part you'll implement in this stage**
### Peer Messages
Peer messages consist of a message length prefix (4 bytes), message id (1 byte) and a payload (variable size).
Here are the peer messages you'll need to exchange once the handshake is complete:
- Wait for a `bitfield` message from the peer indicating which pieces it has
- The message id for this message type is `5`.
- You can read and ignore the payload for now, the tracker we use for this challenge ensures that all peers have all pieces available.
- Send an `interested` message
- The message id for `interested` is `2`.
- The payload for this message is empty.
- Wait until you receive an `unchoke` message back
- The message id for `unchoke` is `1`.
- The payload for this message is empty.
- Break the piece into blocks of 16 kiB (16 * 1024 bytes) and send a `request` message for each block
- The message id for `request` is `6`.
- The payload for this message consists of:
- `index`: the zero-based piece index
- `begin`: the zero-based byte offset within the piece
- This'll be `0` for the first block, `2^14` for the second block, 2*2^14 for the third block etc.
- `length`: the length of the block in bytes
- This'll be `2^14` (16 * 1024) for all blocks except the last one.
- The last block will contain `2^14` bytes or less, you'll need calculate this value using the piece length.
- Wait for a `piece` message for each block you've requested
- The message id for `piece` is `7`.
- The payload for this message consists of:
- `index`: the zero-based piece index
- `begin`: the zero-based byte offset within the piece
- `block`: the data for the piece, usually `2^14` bytes long
After receiving blocks and combining them into pieces, you'll want to check the integrity of each piece by comparing its hash
with the piece hash value found in the torrent file.
Here’s how the tester will execute your program:
```
$ ./your_bittorrent.sh download_piece -o /tmp/test-piece sample.torrent <piece_index>
```
The tester will validate that the piece was downloaded correctly.
**Optional:** To improve download speeds, you can consider pipelining your requests. [BitTorrent Economics Paper](http://bittorrent.org/bittorrentecon.pdf) recommends having 5 requests pending at once, to avoid a delay between blocks being sent.
marketing_md: |-
In this stage, you'll connect to a peer and download a piece of the file. You'll download the piece in blocks, which you'll later combine and verify using SHA-1, a cryptographic hash value.
- slug: "jv8"
name: "Download the whole file"
difficulty: hard
description_md: |-
In this stage, you’ll download the entire file and save it to disk.
You can start with using a single peer to download all the pieces. You’ll need to download all the pieces, verify their integrity using piece hashes, and combine them to assemble the file.
### Tests
Here’s how the tester will execute your program:
```
$ ./your_bittorrent.sh download -o /tmp/test.txt sample.torrent
```
The tester will validate that the file was downloaded correctly.
**Optional:** To improve download speeds, you can download from multiple peers at once. You could have a work queue consisting of each piece that needs to be downloaded. Your worker (connection with a peer) could pick a piece from the work queue, attempt to download it, check the integrity, and write the downloaded piece into a buffer. Any failure (network issue, hashes not matching, peer not having the piece etc.) would put the piece back into the work queue to be tried again.
marketing_md: |-
In this stage, you'll download the entire file. You'll download all the pieces, verify them using SHA-1 and save them to disk.
- slug: "hw0"
primary_extension_slug: "magnet-links"
name: "Parse magnet link"
difficulty: easy
description_md: |-
Welcome to the BitTorrent Magnet Links extension! In this extension, you'll add support for downloading files using magnet links.
In this stage, you will parse a magnet link and print out the info hash and tracker URL.
### Magnet links
[Magnet links](https://www.bittorrent.org/beps/bep_0009.html) allow users to download files from peers without needing a torrent file.
For example, here's a magnet link:
`magnet:?xt=urn:btih:ad42ce8109f54c99613ce38f9b4d87e70f24a165&dn=magnet1.gif&tr=http%3A%2F%2Fbittorrent-test-tracker.codecrafters.io%2Fannounce`
Unlike .torrent files, magnet links don't contain information like file length, piece length and piece hashes. They only include the bare minimum
information necessary to discover peers. A client can then request the rest of the information from peers using the [metadata exchange protocol](https://www.bittorrent.org/beps/bep_0009.html#metadata-exchange). You'll implement this in later stages.
These are the query parameters in a magnet link:
- `xt`: `urn:btih:` followed by the 40-char hex-encoded info hash (example: `urn:btih:ad42ce8109f54c99613ce38f9b4d87e70f24a165`)
- `dn`: The name of the file to be downloaded (example: `magnet1.gif`)
- `tr`: The tracker URL (example: `http://bittorrent-test-tracker.codecrafters.io/announce`)
[This Wikipedia article](https://en.wikipedia.org/wiki/Magnet_URI_scheme) has more information about the magnet link format.
### Magnet link example
Here's how the tester will execute your program:
```bash
$ ./your_bittorrent.sh magnet_parse <magnet-link>
```
and here's the output it expects:
```bash
Tracker URL: http://bittorrent-test-tracker.codecrafters.io/announce
Info Hash: d69f91e6b2ae4c542468d1073a71d4ea13879a7f
```
### Notes
- We'll be using v1 of [magnet URI format](https://www.bittorrent.org/beps/bep_0009.html#magnet-uri-format). v2 is not widely used yet.
- `xt` (info hash) is the only required parameter, all others are optional.
- A magnet link can contain multiple tracker URLs, but for the purposes of this challenge it'll only contain one.
marketing_md: |-
In this stage, you'll extract information from a magnet link.
- slug: "pk2"
primary_extension_slug: "magnet-links"
name: "Announce extension support"
difficulty: easy
description_md: |-
In this stage, you'll modify the handshake message to indicate that your client supports extensions.
### Extension handshake
Exchanging torrent metadata between peers wasn't originally part of the standard BitTorrent protocol, it was introduced as
an [extension](https://www.bittorrent.org/beps/bep_0009.html). This extension uses BitTorrent's [extension protocol](https://www.bittorrent.org/beps/bep_0010.html) that is designed to add
functionality without breaking backward-compatibility.
During the "Peer handshake" stage, the handshake message includes eight reserved bytes (64 bits), all set to zero. To signal
support for extensions, a client must set the 20th bit from the right (counting starts at 0) in the reserved bytes to 1.
In Hex, here's how the reserved bytes will look like after setting the 20th bit from the right to 1:
```bash
00 00 00 00 00 10 00 00
```
(`10` in hex is `16` in decimal, which is `00010000` in binary)
When looking at individual bits, here's how it'll look like (left side truncated):
```bash
.... 00010000 00000000 00000000
^ 20th bit from the right, counting starts at 0
```
All the other reserved bits will stay zero.
### The `magnet_handshake` command
To test your program, we'll introduce a new command in this stage: `magnet_handshake`. Here's how it works:
```bash
$ ./your_bittorrent.sh magnet_handshake <magnet-link>
Peer ID: 0102030405060708090a0b0c0d0e0f1011121314
```
Your program will need to:
- Parse the magnet link to get the tracker URL
- Perform the tracker GET request to get a list of peers
- Establish a TCP connection with a peer, and perform a handshake
- In this handshake, your program will need to set the correct reserved bit to indicate support for extensions
- Print the peer ID received during the handshake
### Tests
Here's how the tester will execute your program:
```
$ ./your_bittorrent.sh magnet_handshake <magnet-link>
```
and here's the output it expects:
```
Peer ID: 0102030405060708090a0b0c0d0e0f1011121314
```
Tester will also assert that correct bit is set in the reserved bytes of the handshake message.
### Notes
- You can use [these magnet links](https://github.com/codecrafters-io/bittorrent-test-seeder/blob/main/torrent_files/magnet_links.txt) to test your program locally. You may need to surround links with double quotes to escape special characters in terminal.
marketing_md: |-
In this stage, you'll announce to other clients that you support extensions.
- slug: "xi4"
primary_extension_slug: "magnet-links"
name: "Send extension handshake"
difficulty: easy
description_md: |-
In this stage, you'll send back a list of extensions that your client supports.
### Extension IDs & handshake
Each peer maintains a mapping of extension names to IDs. The same extension name might map to different IDs on different peers (but it'll always remain stable for a given peer).
To communicate with other peers using the [metadata extension](https://www.bittorrent.org/beps/bep_0009.html#ut_metadata), your peer will
need to know the ID that the other peer uses for this extension. The name for the metadata extension is `ut_metadata`. To get the ID,
your peer will need to send an [extension handshake](https://www.bittorrent.org/beps/bep_0010.html#handshake-message) message and
wait for the corresponding handshake message back from the peer.
The extension handshake message contains a dictionary like this:
```json
{
"m": {
"ut_metadata": 16,
... (other extension names and IDs)
}
}
```
In the example above, `16` is the ID that the peer uses for the metadata extension. This is just an example, you can use any value you want for this ID.
Your program will need to send the extension handshake message soon after receiving the bitfield message. Overall, the flow will look like this:
- Establish a TCP connection with a peer
- Send the base handshake message
- Receive the base handshake message
- Send the bitfield message (safe to ignore in this challenge)
- Receive the bitfield message
- If the peer supports extensions (based on the reserved bit in the base handshake):
- Send the extension handshake message
- Receive the extension handshake message
Note that the extension handshake message is only sent if the other peer supports extensions (indicated by the reserved bit in the base handshake). This
is how backward compatibility is maintained with peers that don't support extensions.
We'll implement sending extension handshake messages in this stage. Receiving extension handshake messages will be implemented in the next stage.
### Extension handshake format
Extension messages follow the standard BitTorrent message format:
- message length prefix (4 bytes)
- message id (1 byte)
- This will be 20 for all messages implemented by extensions
- payload (variable size)
The payload will be structured as follows:
- extension message id (1 byte)
- This will be 0 for the extension handshake
- bencoded dictionary (variable size)
- This will contain a key "m" with another dictionary as its value.
- The inner dictionary maps supported extension names to their corresponding message IDs.
- For example, the inner dictionary contents might be `{"ut_metadata": 1, "ut_pex": 2}`, indicating that your peer supports the "ut_metadata" and "ut_pex" extensions with IDs 1 and 2 respectively.
The extension name we're interested in is "ut_metadata", which corresponds to the [metadata extension](https://www.bittorrent.org/beps/bep_0009.html#ut_metadata).
### Tests
Here's how the tester will execute your program:
```
$ ./your_bittorrent.sh magnet_handshake <magnet-link>
```
and here's the output it expects:
```
Peer ID: 0102030405060708090a0b0c0d0e0f1011121314
```
The expected output is same as last stage. In this stage, the tester will additionaly verify that:
- An extension handshake message is received.
- The extension handshake message contains a dictionary that looks like this: `{"m": {"ut_metadata": <PICK YOUR ID>}}`.
- The ID corresponding to `ut_metadata` is between 1 and 255 (it needs to be represented as an 8-bit unsigned integer, that isn't 0)
### Notes
- Your program must only send an extension handshake **if** the peer supports extensions
- To know whether a peer supports the extension protocol, you'll need to look at the reserved bytes in the base handshake message
- The extension handshake message is defined in [handshake section](https://www.bittorrent.org/beps/bep_0010.html#handshake-message) of extension protocol doc.
marketing_md: |-
In this stage, you'll send the list of extensions your client supports.
- slug: "jk6"
primary_extension_slug: "magnet-links"
name: "Receive extension handshake"
difficulty: easy
description_md: |-
In this stage, you'll add support for receiving the extension handshake message back.
### Extension handshake (part 2)
In the previous stage, your client sent an extension handshake message to the other peer. In this stage, you'll
add support for receiving the peer's extension handshake message back.
The message will follow the same structure as the one you sent in the previous stage. The payload dictionary
will contain the following keys: `{"m": {"ut_metadata": <PEER'S ID>}}`.
Your program will need to store the ID for the `ut_metadata` extension and print it out. This'll be used in later stages to send extension messages specific to the `ut_metadata` extension.
To recap, the overall flow will look like this:
- Establish a TCP connection with a peer
- Send the base handshake message
- Receive the base handshake message
- Send the bitfield message (safe to ignore in this challenge)
- Receive the bitfield message
- If the peer supports extensions (based on the reserved bit in the base handshake):
- Send the extension handshake message (previous stage)
- Receive the extension handshake message (**this stage**)
### Tests
Here's how the tester will execute your program:
```bash
$ ./your_bittorrent.sh magnet_handshake <magnet-link>
```
and here's the output it expects:
```
Peer ID: 0102030405060708090a0b0c0d0e0f1011121314
Peer Metadata Extension ID: 123
```
The `Peer Metadata Extension ID` will be randomly generated, you'll need to fetch it from the dictionary in the extension handshake message.
### Notes
- You can use [these magnet links](https://github.com/codecrafters-io/bittorrent-test-seeder/blob/main/torrent_files/magnet_links.txt) to test your program locally. You may need to surround links with double quotes to escape special characters in terminal.
- Extension IDs need to be stored for every peer, as different peers may use different IDs for the same extension.
marketing_md: |-
In this stage, you'll receive the list of extensions your peer supports.
- slug: "ns5"
primary_extension_slug: "magnet-links"
name: "Request metadata"
difficulty: easy
description_md: |-
In this stage, you'll request torrent metadata from a peer using the metadata extension.
### Metadata extension messages
As mentioned earlier, a magnet link doesn't contain all the information found in a .torrent file, such as file length, piece length and piece hashes.
To fetch these additional details, your client will need to use messages defined by the [metadata extension](https://www.bittorrent.org/beps/bep_0009.html#extension-message).
The metadata extension supports the following message types:
- `request` (msg_type: 0)
- Requests a piece of metadata from the peer
- `data` (msg_type: 1)
- Sends a piece of metadata to the peer
- `reject` (msg_type: 2)
- Signals that the peer doesn't have the piece of metadata that was requested
`msg_type` is an identifier for message types within the metadata extension.
Metadata is stored in "pieces", which are 16kiB chunks. For the purposes of this challenge, you can assume that the entire metadata for a torrent fits in a single piece.
You'll implement sending the `request` message (msg_type: 0) in this stage. We'll look at other `msg_type` values in later stages.
### The metadata `request` message
As seen above, the `request` message is part of the metadata extension.
Note that this `request` message is different from the request message (ID: 6) in the base [BitTorrent protocol](https://www.bittorrent.org/beps/bep_0003.html).
This message follows the standard BitTorrent extension message format:
- message length prefix (4 bytes)
- message id (1 byte)
- This will be 20, since this is a message implemented by an extension
- payload (variable size)
- extension message id (1 byte)
- This will be the peer's metadata extension ID, which you received during the extension handshake
- bencoded dictionary (variable size)
- This dictionary will look like this: `{'msg_type': 0, 'piece': 0}` (encoded as a bencoded dictionary)
- `msg_type` will be 0 since this is a `request` message
- `piece` is the zero-based piece index of the metadata being requested
- Since we're only requesting one piece in this challenge, this will always be 0
### The `magnet_info` command
To test your program, we'll introduce a new command in this stage: `magnet_info`. Here's how it works:
```bash
$ ./your_bittorrent.sh magnet_info <magnet-link>
Tracker URL: http://bittorrent-test-tracker.codecrafters.io/announce
Length: 92063
Info Hash: d69f91e6b2ae4c542468d1073a71d4ea13879a7f
Piece Length: 32768
Piece Hashes:
6e2275e604a0766656736e81ff10b55204ad8d35
e876f67a2a8886e8f36b136726c30fa29703022d
f00d937a0213df1982bc8d097227ad9e909acc17
```
To fetch this info, your program will need to:
- Parse the magnet link to get the tracker URL
- Perform the tracker GET request to get a list of peers
- Establish a TCP connection with a peer, and perform a handshake
- Perform the base handshake
- Send the bitfield message (can be ignored in this challenge)
- Receive the bitfield message
- Perform the extension handshake
- Send the metadata request message (**This stage**)
- Receive the metadata message (later stages)
- Print out the data received, as per the format above.
### Tests
Here's how the tester will execute your program:
```bash
$ ./your_bittorrent.sh magnet_info <magnet-link>
```
There's no expected output at this stage, the tester will just assert if it received a correct metadata request. We'll
verify the output in later stages.
### Notes
- If metadata is larger than 16kb, you would need to request multiple pieces, but for the purposes of this challenge there will only be one piece.
marketing_md: |-
In this stage, you'll request torrent metadata from a peer.
- slug: "zh1"
primary_extension_slug: "magnet-links"
name: "Receive metadata"
difficulty: easy
description_md: |-
In this stage, you'll receive torrent metadata from a peer.
### The metadata `data` message
The `data` message is the response to the `request` message that your peer sent in the previous stage.
It contains the actual metadata, which you'll need to download the file.
This message follows the standard BitTorrent extension message format:
- message length prefix (4 bytes)
- message id (1 byte)
- This will be 20, since this is a message implemented by an extension
- payload (variable size)
- extension message id (1 byte)
- This will be your peer's metadata extension ID, which it sent as part of the extension handshake
- bencoded dictionary (variable size)
- This dictionary will look like this: `{'msg_type': 1, 'piece': 0, 'total_size': XXXX}`
- `msg_type` will be 1, since this is a `data` message
- `piece` will be 0, since we're only requesting one piece in this challenge
- `total_size` will be the length of the metadata piece
- metadata piece contents (variable size)
The "metadata piece contents" is what you're interested in. When these pieces are combined, they'll form a bencoded dictionary that
looks something like this (presented as JSON for readability):
```
{
"piece length": 262144,
"pieces": "<hash1><hash2>...",
"name": "debian-12.3.0-amd64-netinst.iso",
"length": 67108864
}
```
This is same as the [info dictionary](https://www.bittorrent.org/beps/bep_0003.html#info-dictionary) in a .torrent file, which you've seen in previous stages.
You should be able to compute the hash of this and validate it against the info hash present in the magnet link.
### Tests
Here's how the tester will execute your program:
```
$ ./your_bittorrent.sh magnet_info <magnet-link>
```
and here's the output it expects:
```
Tracker URL: http://bittorrent-test-tracker.codecrafters.io/announce
Length: 92063
Info Hash: d69f91e6b2ae4c542468d1073a71d4ea13879a7f
Piece Length: 32768
Piece Hashes:
6e2275e604a0766656736e81ff10b55204ad8d35
e876f67a2a8886e8f36b136726c30fa29703022d
f00d937a0213df1982bc8d097227ad9e909acc17
```
### Notes
- For this challenge, all torrents we use will have a single metadata piece (i.e. it'll fit in one 16kiB chunk).
- You can use [these magnet links](https://github.com/codecrafters-io/bittorrent-test-seeder/blob/main/torrent_files/magnet_links.txt) to test your program locally.