W poprzednim wpisie szczegółowo opisałem budowę wiadomości, które są konieczne, aby zainicjować komunikację z siecią Bitcoin. Aby moć wykorzystać poznaną teorię w praktyce, czas przedstawić przykładową implementację tych wiadomości. W niniejszym wpisie zaczniemy od nagłówka, który jest podstawą każdego pakietu. Zobaczmy, jak zbudowany jest nagłówek wiadomości w protokole Bitcoina.
Przykłady, które tutaj się pojawią będą przedstawione w języku C#, jednak implementując swoją wersję aplikacji nie powinieneś mieć problemów z przepisaniem ich do języka, którego Ty używasz na co dzień. No to lecimy 🙂
Z poprzedniego wpisu wiemy, że nagłówek składa się z czterech elementów: “magic”, “command”, “length” i “checksum”. Według specyfikacji rozmiaru poszczególnych pól, ich reprezentacja mogłaby wyglądać następująco:
public class Header { public uint Magic { get; } public string Command { get; } public uint PayloadLength { get; } public uint Checksum { get; } //... }
Dla pól zajmujących dokładnie 4 bajty idealnie nada się zmienna typu unsigned int, zaś dla pola “command” oznaczającego nazwę wiadomości wykorzystamy zmienną typu string. Pole to powinno zajmować dokładnie 12 bajtów, dlatego w późniejszym procesie jego przetwarzania brakujące bajty zostaną uzupełnione zerami.
Zajmijmy się tworzeniem pustego nagłówka dla wiadomości o podanej nazwie. Do tego celu użyjemy konstruktora, w którym jako parametr podamy nazwę wiadomości:
public class Header { //... public Header(string command) { var payload = new byte[] { }; Magic = 0xD9B4BEF9; Command = command.PadRight(12, '\0'); PayloadLength = 0; Checksum = BitcoinHelper.CalculateChecksum(payload); } //... }
Wiadomość, póki co, nie ma zawartości, dlatego jej treść to pusta tablica bajtów. Komentarza wymaga linijka licząca sumę kontrolną. Wedługo specyfikacji suma kontrolna wiadomości, to pierwsze 8 bajtów podwójnie zastosowanego algorytmu SHA-256. Czynność ta została wydelegowana do metody w statycznej klasie BitcoinHelper:
public static class BitcoinHelper { //... public static uint CalculateChecksum(byte[] bytes) { var sha256Managed = new SHA256Managed(); var hash = sha256Managed.ComputeHash(sha256Managed.ComputeHash(bytes)); return BitConverter.ToUInt32(hash, 0); } //... }
Aby komunikować się z siecią Bitcoin, potrzebujemy metody, która przekształci zawartość nagłówka w ciąg bajtów. Przy każdym wysyłaniu komunikatu, będziemy tworzyć obiekt klasy reprezentującej wiadomość, następnie przekształcać go na ciąg bajtów, aby w kolejnym kroku wysłać do wybranego węzła sieci Bitcoin. Implementacja wspomnianej metody może wyglądać następująco:
public byte[] ToBytes() { var bytes = BitConverter.GetBytes(Magic) .Concat(Encoding.ASCII.GetBytes(Command)) .Concat(BitConverter.GetBytes(PayloadLength)) .Concat(BitConverter.GetBytes(Checksum)) .ToArray(); return bytes; }
Przyda się także metoda, która deserializuje ciąg bajtów otrzymany od sieci. Każda wiadomość zaczyna się nagłówkiem, dlatego każdy ciąg bajtów przybywający od węzła będzie zaczynał swoją przygodę w jego konstruktorze:
public Header(byte[] bytes) { Magic = BitConverter.ToUInt32(bytes, 0); Command = Encoding.ASCII.GetString(bytes, 4, 12); PayloadLength = BitConverter.ToUInt32(bytes, 16); Checksum = BitConverter.ToUInt32(bytes, 20); }
Tym sposobem możemy już budować nagłówek dowolnej wiadomości oraz tworzyć jego obiekt na podstawie przychodzącego od węzła ciągu bajtów. Nie jest to ostateczna wersja klasy nagłówka, w następnych etapach powstawania aplikacji wymagane funkcjonalności będą dodawane na bieżąco. W kolejnym wpisie skupimy się na wykorzystaniu klasy nagłówka do utworzenia pierwszej wiadomości typu “version” i wysłaniu jej do węzła sieci Bitcoin w celu otrzymania pozwolenia na komunikację.
Poniżej znajduje się pełny kod źródłowy omawianych we wpisie klas: