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: