I’m stuck on Stage #RB2
I can’t get the expected hash value when hashing encoded info. Could anyone help me with this?
This is the result I get
Tracker URL: http://bittorrent-test-tracker.codecrafters.io/announce
Length: 92063
Info Hash: 7a4224da30feac31c30284c8d5fa8688c55c3dec
Here are my files
Program.cs
using System.Text.Json;
using BitTorrent.Helpers;
using BitTorrent.Models;
var (command, param) = args.Length switch
{
0 => throw new InvalidOperationException("Usage: your_bittorrent.sh <command> <param>"),
1 => throw new InvalidOperationException("Usage: your_bittorrent.sh <command> <param>"),
_ => (args[0], args[1])
};
// Parse command and act accordingly
if (command == "decode")
{
var bencodeDecoder = new BencodeDecoder(param);
Console.WriteLine(bencodeDecoder.Parse());
}
else if (command == "info")
{
using var stream = File.Open(param, FileMode.Open);
var bencodeDecoder = new BencodeDecoder(stream);
var parsedString = bencodeDecoder.Parse();
Torrent torrentFile = JsonSerializer.Deserialize<Torrent>(parsedString)!;
var encodedInfo = BencodeEncoder.EncodeDictionary(torrentFile.Info);
var hash = Sha1Hash.ComputeHash(encodedInfo);
Console.WriteLine("Tracker URL: {0}", torrentFile.Announce);
Console.WriteLine("Length: {0}", torrentFile.Info.Length);
Console.WriteLine("Info Hash: {0}", hash);
}
else
{
throw new InvalidOperationException($"Invalid command: {command}");
}
BencodeDecoder.cs
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
namespace BitTorrent.Helpers
{
public class BencodeDecoder
{
private readonly BinaryReader _reader;
public BencodeDecoder(Stream stream)
{
_reader = new BinaryReader(stream, Encoding.UTF8);
}
public BencodeDecoder(string data)
{
byte[] bytes = Encoding.UTF8.GetBytes(data);
_reader = new BinaryReader(new MemoryStream(bytes), Encoding.UTF8);
}
public string Parse()
{
return JsonSerializer.Serialize(ParseValue());
}
private object ParseValue()
{
char type = (char)_reader.PeekChar();
if (type == 'i')
{
return ParseInt();
}
else if (type == 'l')
{
return ParseList();
}
else if (type == 'd')
{
return ParseDictionary();
}
else if (char.IsDigit(type))
{
return ParseString();
}
else
{
throw new InvalidOperationException("Unhandled encoded value");
}
}
private long ParseInt()
{
_reader.ReadChar(); // 'i'
StringBuilder parsedInt = new();
while (_reader.PeekChar() != 'e')
{
parsedInt.Append(_reader.ReadChar());
}
_reader.ReadChar(); // 'e'
return long.Parse(parsedInt.ToString());
}
private List<object> ParseList()
{
_reader.ReadChar(); // 'l'
List<object> parsedList = [];
while (_reader.PeekChar() != 'e')
{
parsedList.Add(ParseValue());
}
_reader.ReadChar(); // 'e';
return parsedList;
}
private string ParseString()
{
StringBuilder stringLength = new();
while (_reader.PeekChar() != ':')
{
stringLength.Append(_reader.ReadChar());
}
int length = int.Parse(stringLength.ToString());
_reader.ReadChar(); // ':'
return new string(_reader.ReadChars(length));
}
private Dictionary<object, object> ParseDictionary()
{
Dictionary<object, object> parsedDictionary = [];
_reader.ReadChar(); // 'd';
while (_reader.PeekChar() != 'e')
{
object key = ParseValue();
object value = ParseValue();
parsedDictionary.Add(key, value);
}
_reader.ReadChar(); // 'e';
return parsedDictionary;
}
}
}
BencodeEncoder.cs
using System.Reflection;
using System.Text;
using BitTorrent.Extensions;
namespace BitTorrent.Helpers
{
public static class BencodeEncoder
{
public static string EncodeDictionary<T>(T obj)
{
StringBuilder encodedDictionary = new();
foreach (PropertyInfo prop in obj!.GetType().GetProperties())
{
var type = prop.PropertyType;
encodedDictionary.Append(EncodeString(prop.Name.ToLowerCaseWithSpaces()));
switch (Type.GetTypeCode(type))
{
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
var intValue = Convert.ToInt64(prop.GetValue(obj, null));
encodedDictionary.Append(EncodeInteger(intValue));
break;
case TypeCode.String:
var stringValue = Convert.ToString(prop.GetValue(obj, null));
encodedDictionary.Append(EncodeString(stringValue!));
break;
default:
throw new InvalidOperationException(
string.Format("Type: {0} is not supported for bencode encoding", type.Name));
}
}
return string.Format("d{0}e", encodedDictionary.ToString());
}
public static string EncodeInteger(long value)
{
return string.Format("i{0}e", value);
}
public static string EncodeString(string value)
{
return string.Format("{0}:{1}", value.Length, value);
}
}
}