I’m stuck on Stage XI4
after send request metadata, can’t receive metadata back
but works locally
(venv) > python -m app.main magnet_info "magnet3.gif.torrent: magnet:?xt=urn:btih:c5fb9894bdaba464811b088d806bdd611ba490af&dn=magnet3.gif&tr=http%3A%2F%2Fbittorrent-test-tracker.codecrafters.io%2Fannounce"
http://bittorrent-test-tracker.codecrafters.io/announce c5fb9894bdaba464811b088d806bdd611ba490af
random peer_id: jczhcpscvlcdyfcwkjwr
{b'peers': b'\xa7G\x8f6\xc8\xe2\xa5\xe8#\x8b\xc9\x03\x8b;\xb8\xff\xc9\\', b'complete': 3, b'incomplete': 0, b'interval': 60, b'min interval': 60}
b'\x13BitTorrent protocol\x00\x00\x00\x00\x00\x10\x00\x04\xc5\xfb\x98\x94\xbd\xab\xa4d\x81\x1b\x08\x8d\x80k\xdda\x1b\xa4\x90\xaf-RN0.0.0-\xe3=\xb7flI\xecPO\xfd\xcb'
reserve bytes: b'\x00\x00\x00\x00\x00\x10\x00\x04'
Peer ID: 2d524e302e302e302de33db7666c49ec504ffdcb
receive length: 2
waiting for bitfield message: PeerMessage(length=2, message_id=5, pay_load=b'\x05\xe0')
bytearray(b'\x00\x00\x00%\x14\x00d1:md11:ut_metadatai1e6:ut_pexi2eee')
receive length: 99
b"d1:md11:ut_metadatai1e6:ut_pexi2ee13:metadata_sizei132e4:reqqi250e1:v10:Rain 0.0.06:yourip4:'\xa8P7e"
receive dict {b'm': {b'ut_metadata': 1, b'ut_pex': 2}, b'metadata_size': 132, b'reqq': 250, b'v': b'Rain 0.0.0', b'yourip': b"'\xa8P7"}
Peer Metadata Extension ID: 1
sending message: PeerMessage(length=27, message_id=20, pay_load=bytearray(b'\x01d8:msg_typei0e5:piecei0ee'))
receive length: 177
b"\x14\x01d8:msg_typei1e5:piecei0e10:total_sizei132eed6:lengthi629944e4:name11:magnet3.gif12:piece le
ngthi262144e6:pieces60:\xca\x80\xfd\x83\xff\xb3Mn\x1b\xbd&\xa8\xefm0X'\xf1\xcd\np\x7f\xd7\xc6W\xf6\xd66\xf0X4f\xc3\xcf\xe14\xdd\xb2\xc0\x8aG\x07m\x10M!L\x00R\x96\x0e\xf7g&&I\xa8\xaf\x0e\xa8e"
Tracker URL: http://bittorrent-test-tracker.codecrafters.io/announce
Length: 629944
Info Hash: c5fb9894bdaba464811b088d806bdd611ba490af
Piece Length: 262144
Piece Hashes:
ca80fd83ffb34d6e1bbd26a8ef6d305827f1cd0a
707fd7c657f6d636f0583466c3cfe134ddb2c08a
47076d104d214c0052960ef767262649a8af0ea8
Here are my logs:
remote: [tester::#XI4] Running tests for Stage #XI4 (Magnet Links - Send extension handshake)
remote: [tester::#XI4] Running ./your_bittorrent.sh magnet_handshake "magnet:?xt=urn:btih:3f994a835e090238873498636b98a3e78d1c34ca&dn=magnet2.gif&tr=http%3A%2F%2F127.0.0.1:45067%2Fannounce"
remote: [tester::#XI4] Peer listening on address: 127.0.0.1:45943
remote: [tester::#XI4] Tracker started on address 127.0.0.1:45067...
remote: [tester::#XI4]
remote: [your_program] http://127.0.0.1:45067/announce 3f994a835e090238873498636b98a3e78d1c34ca
remote: [your_program] random peer_id: lsrpkvmkjljxvxzvzhyq
remote: [your_program] {b'complete': 1, b'incomplete': 0, b'mininterval': 1800, b'peers': b'\x7f\x00\x00\x01\xb3w'}
remote: [tester::#XI4] Waiting to receive handshake message
remote: [tester::#XI4] Received handshake: [infohash: 3f994a835e090238873498636b98a3e78d1c34ca, peer_id: 4aaace2dc04ecf1a2f1b0498ccadd57771fa40fe]
remote: [tester::#XI4]
remote: [tester::#XI4] Sending back handshake with peer_id: ac16c2e4c7138e80b6c405e38321022fa0b3871b
remote: [tester::#XI4] Sending bitfield message
remote: [tester::#XI4] Sending extension handshake
remote: [tester::#XI4] Waiting to receive extension handshake message
remote: [tester::#XI4] Received extension handshake with payload: ing metadata extension id received
remote: [your_program] b'\x13BitTorrent protocol\x00\x00\x00\x00\x00\x10\x00\x00?\x99J\x83^\t\x028\x8
74\x98ck\x98\xa3\xe7\x8d\x1c4\xca\xac\x16\xc2\xe4\xc7\x13\x8e\x80\xb6\xc4\x05\xe3\x83!\x02/\xa0\xb3\x87\x1b'
remote: [your_program] reserve bytes: b'\x00\x00\x00\x00\x00\x10\x00\x00'
remote: [your_program] Peer ID: ac16c2e4c7138e80b6c405e38321022fa0b3871b
remote: [your_program] b'\x00\x00\x00\x02'
remote: [your_program] receive length: 2
remote: [your_program] waiting for bitfield message: PeerMessage(length=2, message_id=5, pay_load=b'\x05\x80')
remote: [your_program] bytearray(b'\x00\x00\x00%\x14\x00d1:md11:ut_metadatai1e6:ut_pexi2eee')
remote: [your_program] b'\x00\x00\x000'
remote: [your_program] receive length: 48
remote: [your_program] b'd1:md11:ut_metadatai156ee13:metadata_sizei91ee'
remote: [your_program] receive dict {b'm': {b'ut_metadata': 156}, b'metadata_size': 91}
remote: [your_program] Peer Metadata Extension ID: 156
remote: [your_program] sending message: PeerMessage(length=27, message_id=20, pay_load=bytearray(b'\x9cd8:msg_typei0e5:piecei0ee'))
remote: [tester::#XI4] timed out, test exceeded 10 seconds
remote: [tester::#XI4] Test failed
And here’s a snippet of my code:
def magnet_handshake_peer(tracker_url, info_hash, peer_ip, peer_port):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((peer_ip, int(peer_port)))
reserved_bytes = (1 << 20).to_bytes(8, "big")
p = PeerProtocol(reserved_bytes=reserved_bytes, info_hash=info_hash)
s.send(p.serialize_to_message())
msg = s.recv(68)
print(msg)
reserved_bytes = int.from_bytes(msg[20:28], "big")
if reserved_bytes >> 20 & 1 == 0:
print(f"peer {peer_ip}:{peer_port} don't support the extension protocol")
return False
print(f"reserve bytes: {msg[20:28]}")
print(f"Peer ID: {msg[48:].hex()}")
while 1:
length, message = receive_message(s)
bitfield_msg = deserialize_peer_message(length, message)
print(f"waiting for bitfield message: {bitfield_msg}")
# The message id for this message type is 5.
if bitfield_msg.message_id == 5:
break
pay_load = bytearray()
pay_load.append(0) # message_id
dic = {"m": {"ut_metadata": 1, "ut_pex": 2}}
# dic = {"m": {"ut_metadata": 18}}
pay_load.extend(bencodepy.encode(dic))
msg = PeerMessage(1 + len(pay_load), message_id=20, pay_load=pay_load)
print(serialize_peer_message(msg))
s.send(serialize_peer_message(msg))
length, msg = receive_message(s)
print(msg[2:])
dic = bencodepy.decode(msg[2:])
metadata_extension_id = dic[b"m"][b"ut_metadata"]
print(f"receive dict {dic}")
print(f"Peer Metadata Extension ID: {metadata_extension_id}")
pay_load = bytearray()
pay_load.append(metadata_extension_id) # extension message id
dic = {"msg_type": 0, "piece": 0}
pay_load.extend(bencodepy.encode(dic))
msg = PeerMessage(1 + len(pay_load), message_id=20, pay_load=pay_load)
print(f"sending message: {msg}")
s.send(serialize_peer_message(msg))
length, msg = receive_message(s)
print(msg)
dic_split_index = msg.find(b"d", 3) # we find the second 'd'
dic = bencodepy.decode(msg[2:dic_split_index])
# print(dic)
msg_type = dic[b"msg_type"]
assert msg_type == 1 # data
piece = dic[b"piece"]
assert piece == 0
total_size = dic[b"total_size"]
info = bencodepy.decode(msg[dic_split_index:]) # meta_info
# print(info)
length = info[b"length"]
_info_hash = sha1(bencodepy.encode(info)).hexdigest()
# print(_info_hash, str(binascii.hexlify(info_hash)))
# assert _info_hash == info_hash
piece_length = info[b"piece length"]
piece_hashes = info[b"pieces"]
print(f"Tracker URL: {tracker_url}")
print(f"Length: {length}")
print(f"Info Hash: {_info_hash}")
print(f"Piece Length: {piece_length}")
print("Piece Hashes: ")
for i in range(0, len(piece_hashes), 20):
print(piece_hashes[i : i + 20].hex())
return True