Messages part three – Ping Pong and VerAck

Messages part three – Ping Pong and VerAck

VerAck

When establishing connection, we first need to send a version message to the node we wish to connect to. But keeping our connection alive will require the use of 3 more messages: ping, pong and VerAck.

The VerAck message is the most simple type of message, it’s basically an empty message, it has no payload, only a header. Alexis and I have decided that any type of message will have its own file to construct and parse its payload, even if that message doesn’t have any payload. It helps us to maintain the structure of our project.

Bitpy/Packets/control_messages/verack.py:

class EncodeVerack:
    def __init__(self):
        self.command_name = "verack"

    # Verack messages have an empty payload
    def forge(self):
        return ""


# No need this class because, like GetAddr, there is no payload
class DecodeVerack:
    def __init__(self, payload=""):
        pass

As you can see, the function forge will return an empty string and the class DecodeVerAck won’t do anything.

Pay attention that even though the VerAck payload is empty, this message still needs to have a header. The header will contain 4 bytes for the Magic number (or starting string), 12 bytes for the command name (VerAck), 4 bytes for the size of the payload (which in this case will be zero) and another 4 bytes for the checksum (which in this case will always be the same).

 

 

Ping and Pong

One node can always send a ping request to the other. That is how peers on the network can check if the connection is still alive. Ping request is a nothing more than a message, just like any other Bitcoin message, that contains one field:

Size (Bytes) Name Data type Description
8 nonce uint_64 A random number

Alice sends Bob a ping message. This ping message will contain 8 bytes of random number. Bob will receive this ping message from Alice, and will acknowledge her request by sending her a pong message, this pong message will contain the same random number that Alice sent to Bob.

Let’s have a look at the code implementation

Bitpy/Packets/control_messages/ping.py:

import random
from Utils.dataTypes import *


class EncodePing:
    def __init__(self):
        self.command_name = "ping"
        self.nonce = to_uint64(random.getrandbits(64))

    def forge(self):
        return self.nonce


class DecodePing:
    def __init__(self, payload):
        self.nonce = payload.read(8)

When we want to construct a ping message we’ll use the class EncodePing which will set the field nonce to be 8 bytes long random number (64 bit = 8 bytes). When we’ll receive a ping message, we’ll use the class DecodePing with the payload of the incoming message to find out the nonce of that ping message (We’re only reading the nonce in the ping message, we haven’t used it yet). We’ll use this nonce when we’ll construct our returning (pong) message.

 

Bitpy/Packets/control_messages/pong.py:

from Utils.dataTypes import *

class EncodePong:
    def __init__(self, ping_received):
        self.command_name = "pong"

        self.nonce = ping_received

    def forge(self):
        return self.nonce

class DecodedPong:
    def __init__(self, pong_received):
        self.command_name = "pong"
        self.nonce = read_uint64(pong_received.read(8))

    def get_decoded_info(self):
        return "\npong   :\t\t %s" % self.nonce

The class EncodePong will take the payload of the incoming ping message and will assign it to be the nonce of the pong message (the message that we’ll send back). The DecodedPong class will receive the payload of the pong and will assign it to the self variable nonce. We can use the get_decoded_info function to display this nonce number. Because the pong is just a returning message, there’s nothing else we need to do with it.

Leave a Reply

Your email address will not be published. Required fields are marked *