Download a piec#ND2 Problem

I’m stuck on Stage #ND2.

I’m getting the response message with id 7 but the block in the payload seem to be note equal to 16kb (16384 bytes) when i receive the message, it just about 2000 to 4000 bytes one time even though they are not the last piece. Moreover, while getting the message id 7, I also receive some data which are not in the right format from the other peers .

And here’s a snippet of my code:

import process from "node:process";
import * as file from "fs";
import * as http from "http";
import parseTorrent from "parse-torrent";
import bencode from "bencode";
import axios from "axios";
import * as net from "net";

function decode(bencodedValue) {
  return bencode.decode(bencodedValue, "utf8");
}

async function parseTorrentFile(fileName) {
  const fileContent = file.readFileSync(fileName);

  const decodedFileContent = await parseTorrent(fileContent);

  return decodedFileContent;
}

async function discoverPeers(fileName) {
  const fileContent = file.readFileSync(fileName);
  const parsedFile = await parseTorrent(fileContent);

  // console.log(parsedFile);
  let trackerUrl = "";
  if (parsedFile.announce.length > 0) {
    trackerUrl = parsedFile.announce[0];
  }

  function urlEncodeInfoHash(infoHash) {
    let result = "%" + infoHash.match(/.{1,2}/g).join("%");
    return result;
  }

  let infoHash = parsedFile.infoHash;
  infoHash = urlEncodeInfoHash(infoHash);
  const peerId = "019583diuj491n520j21";
  const port = 6881;
  const uploaded = 0;
  const downloaded = 0;
  const left = parsedFile.length;
  const compact = 1;

  const params = new URLSearchParams();
  params.append("peer_id", peerId);
  params.append("port", port);
  params.append("uploaded", uploaded);
  params.append("downloaded", downloaded);
  params.append("left", left);
  params.append("compact", compact);

  const newTrackerUrl = `${trackerUrl}?info_hash=${infoHash}&${params
    .toString()
    .toLowerCase()}`;

  const res = await fetch(newTrackerUrl);
  let response = Buffer.from(await res.arrayBuffer());
  response = bencode.decode(response);
  const peerList = response.peers;

  const result = [];

  for (let i = 0; i < peerList.length; i += 6) {
    const firstParam = peerList[i];
    const secondParam = peerList[i + 1];
    const thirdParam = peerList[i + 2];
    const fourthParam = peerList[i + 3];
    const fifthParam = peerList[i + 4];
    const sixthParam = peerList[i + 5];
    const port = (fifthParam << 8) | sixthParam;

    const address = `${firstParam}.${secondParam}.${thirdParam}.${fourthParam}:${port}`;
    result.push(address);
  }

  return result;
}

async function handshakePeers(fileName, peer) {
  let [peerIP, peerPort] = peer.split(":");
  peerPort = parseInt(peerPort);

  const parsedFile = await parseTorrentFile(fileName);

  let protocolString = Buffer.from("BitTorrent protocol");
  let peerId = Buffer.from("019583diuj491n520j21");
  let protocolLength = 19;
  let reserved = Buffer.alloc(8);
  let infoHash = Buffer.from(parsedFile.infoHashBuffer);
  protocolLength = Buffer.from([protocolLength]);

  const handshake = Buffer.concat([
    protocolLength,
    protocolString,
    reserved,
    infoHash,
    peerId,
  ]);

  return handshake;
}

function handlePeerMessages(client, data, parsedFile, pieceIndex, chunks) {
  const messagePrefixLength = data.readUInt32BE(0);
  const messageID = data.readUInt8(4);
  let payload = data.subarray(5, messagePrefixLength + 5);

  // console.log(payload);

  const pieceLength = parsedFile.pieceLength;
  const lastPieceLength = parsedFile.lastPieceLength;

  const sendInterestedMessage = () => {
    const interestedMessage = Buffer.from([0, 0, 0, 1, 2]);
    return interestedMessage;
  };

  const sendRequestMessage = (messageID) => {
    console.log("Sending request message");

    let blockSize = 2 ** 14;

    let byteOffset;

    //First time sending Request
    if (messageID === 1) {
      byteOffset = 0;
    } else {
      // console.log(">>> check payload length", payload.subarray(8).length);
      byteOffset = payload.readUInt32BE(4) + payload.subarray(8).length;
      // console.log(byteOffset);
    }

    if (byteOffset + blockSize >= pieceLength) {
      blockSize = pieceLength - byteOffset;
    }

    const requestMessage = Buffer.alloc(17);
    requestMessage.writeUInt32BE(13, 0);
    requestMessage.writeUInt8(6, 4);
    requestMessage.writeUInt32BE(pieceIndex, 5);
    requestMessage.writeUInt32BE(byteOffset, 9);
    requestMessage.writeUInt32BE(blockSize, 13);

    // console.log(message);

    return requestMessage;
  };

  if (messageID === 5) {
    client.write(sendInterestedMessage());
  } else if (messageID === 1) {
    client.write(sendRequestMessage(1));
  } else if (messageID === 7) {
    const payloadBlockSize = payload.subarray(8).length;
    console.log(">>>check payloadBlockSize", payloadBlockSize);
    console.log("messageID 7 come here bro");
    if (payloadBlockSize === 0) {
      client.end();
    } else {
      const block = payload.subarray(8);
      chunks.push(block);
      client.write(sendRequestMessage(7));
    }
  }
}

async function makeConnection(fileName, peer, pieceIndex) {
  const client = new net.Socket();

  const chunks = [];

  const [peerIP, peerPort] = peer.split(":");
  const handshake = await handshakePeers(fileName, peer);
  const parsedFile = await parseTorrentFile(fileName);

  client.connect(peerPort, peerIP, () => {
    console.log("Connected");
    client.write(handshake);
  });

  client.on("data", (data) => {
    //HandshakeMessage
    console.log(data.length);
    if (data.length === 68) {
      console.log("Peer ID:", data.subarray(data.length - 20).toString("hex"));
    } else {
      console.log(data);
      handlePeerMessages(client, data, parsedFile, pieceIndex, chunks);
    }
  });

  client.on("close", () => {
    console.log({chunks})
    console.log("Connection closed");
  });

  client.on("error", (err) => {
    console.error("Client error:", err);
  });
}

async function main() {
  // test = "d3:foo3:bar5:helloi52ee"
  // console.log(decode(test));
  const command = process.argv[2];

  // You can use print statements as follows for debugging, they'll be visible when running tests.
  // console.log("Logs from your program will appear here!");

  // Uncomment this block to pass the first stage
  if (command === "decode") {
    const bencodedValue = process.argv[3];
    console.log(JSON.stringify(decode(bencodedValue)));
  } else if (command === "info") {
    const fileName = process.argv[3];
    const parsedTorrentFile = await parseTorrentFile(fileName);
    let trackerUrl = "";
    trackerUrl = parsedTorrentFile.announce[0];
    const fileLength = parsedTorrentFile.length;
    const infoHash = parsedTorrentFile.infoHash;
    const pieceLength = parsedTorrentFile.pieceLength;
    const pieces = parsedTorrentFile.pieces.join("\n");

    const result = `Tracker URL: ${trackerUrl}\nLength: ${fileLength}\nInfo Hash: ${infoHash}\nPiece Length: ${pieceLength}\nPieceHashes:\n${pieces}`;

    console.log(result);
  } else if (command === "peers") {
    const fileName = process.argv[3];
    console.log((await discoverPeers(fileName)).join("\n"));
  } else if (command === "handshake") {
    const fileName = process.argv[3];
    const peer = process.argv[4];
    await makeConnection(fileName, peer);
  } else if (command === "download_piece") {
    const flag = process.argv[3];
    const outputPath = process.argv[4];
    const fileName = process.argv[5];
    const pieceIndex = parseInt(process.argv[6]);

    const peers = await discoverPeers(fileName);
    //Took 1 peer
    const peerAddress = peers[0];
    await makeConnection(fileName, peerAddress, pieceIndex);
  } else {
    throw new Error(`Unknown command ${command}`);
  }
}

main();

@tuanemtramtinh Looks like you’ve got past this stage — do you happen to remember what was wrong? Would love to see if we can improve the tester / instructions.

Closing this thread due to inactivity. If you still need assistance, feel free to reopen or start a new discussion!

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.