<commit_before><?php

namespace BitWasp\Bitcoin\Serializer\Network;

use BitWasp\Bitcoin\Bitcoin;
use BitWasp\Bitcoin\Crypto\Hash;
use BitWasp\Bitcoin\Network\Messages\GetAddr;
use BitWasp\Bitcoin\Network\Messages\MemPool;
use BitWasp\Bitcoin\Network\Messages\VerAck;
use BitWasp\Bitcoin\Network\NetworkMessage;
use BitWasp\Bitcoin\Serializer\Block\HexBlockHeaderSerializer;
use BitWasp\Bitcoin\Serializer\Block\HexBlockSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\AddrSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\GetDataSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\HeadersSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\InvSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\NotFoundSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\PingSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\PongSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\RejectSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\VersionSerializer;
use BitWasp\Bitcoin\Serializer\Network\Structure\InventoryVectorSerializer;
use BitWasp\Bitcoin\Serializer\Network\Structure\NetworkAddressSerializer;
use BitWasp\Bitcoin\Serializer\Network\Structure\NetworkAddressTimestampSerializer;
use BitWasp\Bitcoin\Serializer\Transaction\TransactionSerializer;
use BitWasp\Buffertools\Buffertools;
use BitWasp\Buffertools\Parser;
use BitWasp\Buffertools\Buffer;
use BitWasp\Bitcoin\Network\NetworkInterface;
use BitWasp\Buffertools\TemplateFactory;

class NetworkMessageSerializer
{
    /**
     * @var NetworkInterface
     */
    protected $network;

    /**
     * @param NetworkInterface $network
     */
    public function __construct(NetworkInterface $network)
    {
        $this->network = $network;
    }

    /**
     * @return \BitWasp\Buffertools\Template
     */
    private function getHeaderTemplate()
    {
        return (new TemplateFactory())
            ->bytestringle(4)
            ->bytestring(12)
            ->uint32le()
            ->bytestring(4)
            ->getTemplate();
    }

    /**
     * @param Parser $parser
     * @return NetworkMessage
     * @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange
     * @throws \Exception
     */
    public function fromParser(Parser & $parser)
    {
        $math = Bitcoin::getMath();

        $parsed = $this->getHeaderTemplate()->parse($parser);
        ;
        /** @var Buffer $netBytes */
        $netBytes = $parsed[0];
        /** @var Buffer $command */
        $command = $parsed[1];
        /** @var int|string $payloadSize */
        $payloadSize = $parsed[2];
        /** @var Buffer $checksum */
        $checksum = $parsed[3];

        if ($netBytes->getHex() !== $this->network->getNetMagicBytes()) {
            throw new \RuntimeException('Invalid magic bytes for network');
        }

        if ($payloadSize > 0) {
            $buffer = $parser->readBytes($payloadSize);
        }

        $cmd = trim($command->getBinary());
        switch($cmd) {
            case 'version':
                $serializer = new VersionSerializer(new NetworkAddressSerializer());
                $payload = $serializer->parse($buffer);
                break;
            case 'verack':
                $payload = new VerAck();
                break;
            case 'addr':
                $serializer = new AddrSerializer(new NetworkAddressTimestampSerializer());
                $payload = $serializer->parse($buffer);
                break;<commit_msg>Should have an empty Buffer even if the messages don't have a payload also validate checksums<commit_after><?php

namespace BitWasp\Bitcoin\Serializer\Network;

use BitWasp\Bitcoin\Bitcoin;
use BitWasp\Bitcoin\Crypto\Hash;
use BitWasp\Bitcoin\Network\Messages\GetAddr;
use BitWasp\Bitcoin\Network\Messages\MemPool;
use BitWasp\Bitcoin\Network\Messages\VerAck;
use BitWasp\Bitcoin\Network\NetworkMessage;
use BitWasp\Bitcoin\Serializer\Block\HexBlockHeaderSerializer;
use BitWasp\Bitcoin\Serializer\Block\HexBlockSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\AddrSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\GetDataSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\HeadersSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\InvSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\NotFoundSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\PingSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\PongSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\RejectSerializer;
use BitWasp\Bitcoin\Serializer\Network\Message\VersionSerializer;
use BitWasp\Bitcoin\Serializer\Network\Structure\InventoryVectorSerializer;
use BitWasp\Bitcoin\Serializer\Network\Structure\NetworkAddressSerializer;
use BitWasp\Bitcoin\Serializer\Network\Structure\NetworkAddressTimestampSerializer;
use BitWasp\Bitcoin\Serializer\Transaction\TransactionSerializer;
use BitWasp\Buffertools\Buffertools;
use BitWasp\Buffertools\Parser;
use BitWasp\Buffertools\Buffer;
use BitWasp\Bitcoin\Network\NetworkInterface;
use BitWasp\Buffertools\TemplateFactory;

class NetworkMessageSerializer
{
    /**
     * @var NetworkInterface
     */
    private $network;

    /**
     * @param NetworkInterface $network
     */
    public function __construct(NetworkInterface $network)
    {
        $this->network = $network;
    }

    /**
     * @return \BitWasp\Buffertools\Template
     */
    private function getHeaderTemplate()
    {
        return (new TemplateFactory())
            ->bytestringle(4)
            ->bytestring(12)
            ->uint32le()
            ->bytestring(4)
            ->getTemplate();
    }

    /**
     * @param Parser $parser
     * @return NetworkMessage
     * @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange
     * @throws \Exception
     */
    public function fromParser(Parser & $parser)
    {
        $math = Bitcoin::getMath();

        $parsed = $this->getHeaderTemplate()->parse($parser);

        /** @var Buffer $netBytes */
        $netBytes = $parsed[0];
        /** @var Buffer $command */
        $command = $parsed[1];
        /** @var int|string $payloadSize */
        $payloadSize = $parsed[2];
        /** @var Buffer $checksum */
        $checksum = $parsed[3];

        if ($netBytes->getHex() !== $this->network->getNetMagicBytes()) {
            throw new \RuntimeException('Invalid magic bytes for network');
        }

        $buffer = $payloadSize > 0
            ? $parser->readBytes($payloadSize)
            : new Buffer();

        // Compare payload checksum against header value
        if (Hash::sha256d($buffer)->slice(0, 4)->getBinary() !== $checksum->getBinary()) {
            throw new \RuntimeException('Invalid packet checksum');
        }

        $cmd = trim($command->getBinary());
        switch($cmd) {
            case 'version':
                $serializer = new VersionSerializer(new NetworkAddressSerializer());
                $payload = $serializer->parse($buffer);
                break;
            case 'verack':
                $payload = new VerAck();
                break;
            case 'addr':
                $serializer = new AddrSerializer(new NetworkAddressTimestampSerializer());
                $payload = $serializer->parse($buffer);
                break;
{'fraction_non_alphanumeric': 0.1162280701754386, 'fraction_numerical': 0.003700657894736842, 'mean_word_length': 2.9939792008757524, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 38, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3743930', 'n_tokens_mistral': 2044, 'n_tokens_neox': 2022, 'n_words': 354}