Skip to content

discv4/packet_factory.cpp

Namespaces

Name
discv4

Source code

// packet_factory.cpp
#include "discv4/packet_factory.hpp"
#include <secp256k1.h>
#include <secp256k1_recovery.h>
#include <boost/outcome/try.hpp>

#include "discv4/discv4_constants.hpp"
#include "discv4/discv4_ping.hpp"
#include "discv4/discv4_pong.hpp"

#include <nil/crypto3/hash/algorithm/hash.hpp>
#include <nil/crypto3/hash/keccak.hpp>

namespace discv4 {

PacketResult PacketFactory::SendPingAndWait(
    asio::io_context& io,
    const std::string& fromIp, uint16_t fUdp, uint16_t fTcp,
    const std::string& toIp, uint16_t tUdp, uint16_t tTcp,
    const std::vector<uint8_t>& privKeyHex,
    SendCallback callback,
    uint16_t* boundPort )
{
    // Create socket
    udp::socket socket( io, udp::v4() );
    socket.set_option( udp::socket::reuse_address( true ) );
    socket.bind( udp::endpoint( boost::asio::ip::address_v4::any(), 0 ) );

    const auto localPort = socket.local_endpoint().port();
    if ( boundPort != nullptr )
    {
        *boundPort = localPort;
    }
    std::cout << "SendPingAndWait bound local UDP port: " << localPort << "\n";

    auto ping = std::make_unique<discv4_ping>( fromIp, fUdp, fTcp, toIp, tUdp, tTcp );

    std::vector<uint8_t> msg;
    auto signResult = SignAndBuildPacket( ping.get(), privKeyHex, msg );
    if ( !signResult )
    {
        return outcome::failure( signResult.error() );
    }

    // Send
    udp::endpoint target( boost::asio::ip::address_v4::from_string( toIp ), tUdp );
    udp::endpoint sender;
    SendPacket( socket, msg, target );


    // Receive async
    std::array<uint8_t, kUdpBufferSize> arrayBuffer;
    boost::system::error_code ec;
    size_t bytesTransferred = socket.receive_from( boost::asio::buffer( arrayBuffer ), sender, 0, ec );

    if ( !ec )
    {
        std::vector<uint8_t> data( arrayBuffer.data(), arrayBuffer.data() + bytesTransferred );
        callback( data, sender );
    }

    // Run io_context (in a loop)
    io.run();

    return outcome::success();
}

PacketResult PacketFactory::SignAndBuildPacket(
    discv4_packet* packet,
    const std::vector<uint8_t>& privKeyHex,
    std::vector<uint8_t>& out )
{

    if ( packet == nullptr )
    {
        return outcome::failure( PacketError::kNullPacket );
    }

    auto payload = packet->RlpPayload();

    // Hash with keccak-256
    std::array<uint8_t, kWireHashSize> hash = discv4_packet::Keccak256( payload );

    // Sign with secp256k1
    auto ctx = secp256k1_context_create( SECP256K1_CONTEXT_SIGN );
    secp256k1_ecdsa_recoverable_signature sig;
    int success = secp256k1_ecdsa_sign_recoverable( ctx, &sig, hash.data(), privKeyHex.data(),
                                                  secp256k1_nonce_function_rfc6979, nullptr );


    if ( !success )
    {
        secp256k1_context_destroy( ctx );
        return outcome::failure( PacketError::kSignFailure );
    }

    std::array<uint8_t, kWireCompactSigSize> serialized{};
    int recid;
    secp256k1_ecdsa_recoverable_signature_serialize_compact( ctx, serialized.data(), &recid, &sig );
    secp256k1_context_destroy( ctx );

    out.reserve( kWireHashSize + kWireSigSize + payload.size() );
    out.insert( out.end(), kWireHashSize, 0 ); // Skip for hash for whole out message
    out.insert( out.end(), serialized.begin(), serialized.end() );
    out.push_back( recid );
    out.insert( out.end(), payload.begin(), payload.end() );

    // Hash for whole out message
    auto payloadHash = nil::crypto3::hash<nil::crypto3::hashes::keccak_1600<256>>( out.begin() + kWireHashSize, out.end() );
    std::array<uint8_t, kWireHashSize> payloadArray = payloadHash;
    std::copy( payloadArray.begin(), payloadArray.end(), out.begin() );

    return outcome::success();
}

void PacketFactory::SendPacket(
    asio::ip::udp::socket& socket,
    const std::vector<uint8_t>& msg,
    const udp::endpoint& target )
{
    socket.send_to( boost::asio::buffer( msg ), target );
}

} // namespace discv4

Updated on 2026-04-13 at 23:22:46 -0700