Wednesday, 6 June 2018

Segregated Witness (Segwit)

Segwit
Introduction
Segregated Witness (Segwit) [1], proposed in BIP 141 [5], was activated on August 24, 2017. The contributions of Segwit [2]:
1) solve transaction malleability [3]
2) mitigate block size limitation problem
Problem
  1. Transaction malleability:
When transaction is signed, the signature (script_sig) does not cover all the data in a transaction. Specifically, the script_sig is part of the transaction, the signature will not be able to sign script_sig. So the signature does not cover script_sig. The script_sig is added, after the transaction is created and signed.


The script_sig is the tempering point. If script_sig changes, TXID will change. The script_sig can be changed by anyone has access to the corresponding private keys.
2) Block size limitation problem
Originally, Bitcoin does not have limit on block size. This allowed attackers to create large size block data. So a 1MB block size was introduced. The 1MB was a tradeoff, between network propagation times, node capability, and number of transactions that can fit into one block, etc [4].
Proposal
Segwit defines a new structure called witness. Signature and redeem script are moved into this structure, which is not included in the 1MB block size limit.
1)Transaction structure

The conventional transaction structure is used in TXID calculation, and script_sig is empty. Even if script_sig is tempered with, TXID does not change.
2) Lock/Unlock script
For a conventional P2PKH:
scriptPubKey (lock script)
OP_DUP OP_HASH160 <pubkey hash> OP_EQUALVERIFY OP_CHECKSIG
scriptSig (unlock script)
<sig> <pubkey>
For Segwit P2WPKH:
scriptPubKey (lock script)
0 <pubkey hash>
(unlock script)
scriptSig
Witness
Empty
<sig> <pubkey>
In scriptPubKey, there are no opcodes, only 2 data (version and hash) is pushed. When the lock script of this pattern is set, it is evaluated as a conventional P2PKH script.  The signature and public key are obtained from witness instead of scriptSig.
3) Witness extension method
In the extension, Segwit introduces OP_CLTV (OP_NPO2) and OP_CSV (OP_NOP3)
The witness structure
<witness version>
<witness program>
For Segwit, witness version is 0, the witness program is P2WPKH if hash length is 20 bytes and P2WSH if it is 32 bytes.
4) Address format
Segwit uses Bech32 address format. It is based on BCH code instead of previously used Base58 encoding, so that error correction is possible [6]. There is no distinction between uppercase and lowercase letters. QR code is also compact

5) Increase of block size
The increase of block size from Segwit depends on the types of transaction.
  • Before Segwit
block data ≦ 1,000,000 MB
  • After Segwit
block weight = base size × 3 + total size
base size: Size of transaction data not including witness
total size: Size of transaction data including witness
block weight ≦ 4,000,000 MB
  • blocks are non-Segwit transactions, block size is 1MB, same as before
  • all transactions in the block are transactions of P2WPKH with 1 input, 2 output, block size is about 1.6 MB.
  • block has one output and all other transactions are P2WPKH input, it is huge Tx, the block size is about 2.1 MB.
  • block consists of transactions of P2WSH with huge witness (all 15-of-15 multisig etc), the block size is about 3.7 MB.
6) Changes in signature data
The convention message digest items are based on the conventional transaction structure. The message digest items are:
version, txin count, txins, txout count, txouts, locktime, sighash type
For Segwit, the message digest items are:
version
hashPrevouts
Hash of all input outpoint
hashSequence
Hash of all input sequence (TxIns)
outpoint
Previous output (32byte TXID + 4byte index) in TxIns
script code
value
amount of coins held by TxIns
sequence
Sequence of TxIns
hash output
Hash of all outputs (TxOuts)
locktime
sighash type
Segwit changes the calculation of transaction hash for signatures, so that each byte of a transaction is hashed twice, at most [7]. The sighash calculation cost is reduced.
7) Witness commitment in Coinbase transaction
For a conventional transaction, the merkle root calculation is shown as below. The merkle root is calculated using original Tx format. 

Segwit adds the witness commitment. Merkle tree is constructed based on transaction data including signature data of witness. That merkle root is stored in one of coinbase transaction output to make commitment including the witness data.


Effects and Challenges
Segwit changes the consensus, P2P message, address format of Bitcoin protocol. It is amazing Segwit could be realised in soft fork.
Segwit introduces witness extension method. It cancels transaction malleability and increases block size. The actual block size increase depends on the transaction type.
References
  1. https://en.bitcoin.it/wiki/Segregated_Witness
  2. https://en.wikipedia.org/wiki/SegWit
  3. https://en.bitcoin.it/wiki/Transaction_malleability
  4. https://en.bitcoin.it/wiki/Block_size_limit_controversy
  5. https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
  6. https://en.wikipedia.org/wiki/BCH_code
  7. https://bitcoincore.org/en/2016/01/26/segwit-benefits/#linear-scaling-of-sighash-operations

Wednesday, 23 May 2018

Setup and test bitcoind using regtest on two nodes

# On machine A, edit bitcoin.conf in /home/ubuntu/alice
regtest = 1
server = 1
port=18332
rpcport = 18333
rpcuser=bitcoinrpc
rpcpassword=password
rpcallowip=0.0.0.0/0

# execute bitcoind
ubuntu@ip-11-0-0-110:~$ bitcoind -conf=/home/ubuntu/alice/bitcoin.conf -datadir=/home/ubuntu/alice -daemon
Bitcoin server starting

# On machine B, edit bitcoin.conf in /home/ubuntu/alice (machine B connect to machine A)
regtest = 1
server = 1
port=18443
rpcport = 18444
connect = 11.0.0.110:18332
rpcuser=bitcoinrpc
rpcpassword=password

# execute bitcoind
ubuntu@ip-10-0-0-55:~$ bitcoind -conf=/home/ubuntu/alice/bitcoin.conf -datadir=/home/ubuntu/alice -daemon
Bitcoin server starting

# execute the command on either machine A or machine B, they will see the same information
ubuntu@ip-11-0-0-110:~$ bitcoin-cli -conf=/home/ubuntu/alice/bitcoin.conf -datadir=/home/ubuntu/alice/  getinfo
{
  "deprecation-warning": "WARNING: getinfo is deprecated and will be fully removed in 0.16. Projects should transition to using getblockchaininfo, getnetworkinfo, and getwalletinfo before upgrading to 0.16",
  "version": 150100,
  "protocolversion": 70015,
  "walletversion": 139900,
  "balance": 0.00000000,
  "blocks": 101,
  "timeoffset": 0,
  "connections": 1,
  "proxy": "",
  "difficulty": 4.656542373906925e-10,
  "testnet": false,
  "keypoololdest": 1521075913,
  "keypoolsize": 2000,
  "paytxfee": 0.00000000,
  "relayfee": 0.00001000,
  "errors": ""
}

ubuntu@ip-11-0-0-110:~$ bitcoin-cli -conf=/home/ubuntu/alice/bitcoin.conf -datadir=/home/ubuntu/alice/  getconnectioncount
1

Saturday, 24 March 2018

Smart contract with Ethereum ERC20 token

In this tutorial , i will walk through the solidity code to create a smart contract that is ERC20 compliant. ERC20 is an Ethereum Request for Comment technical standard that defines set of interface. An Ethereum token must implement. say, if you want to have an ICO and get your tokens traded on exchange, the tokens must be ERC20 compliant.


Firstly, the smart contract must implement the following interface. 
contract ERC20Interface {
    function totalSupply() public constant returns (uint);
    function balanceOf(address tokenOwner) public constant returns (uint balance);
    function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
    function transfer(address to, uint tokens) public returns (bool success);
    function approve(address spender, uint tokens) public returns (bool success);
    function transferFrom(address from, address to, uint tokens) public returns (bool success);
    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}
Secondly, the implementation of the interface is shown as below.
    // ------------------------------------------------------------------------
    // Total supply
    // ------------------------------------------------------------------------
    function totalSupply() public constant returns (uint) {
        return _totalSupply  - balances[address(0)];
    }

    // ------------------------------------------------------------------------
    // Get the token balance for account tokenOwner
    // ------------------------------------------------------------------------
    function balanceOf(address tokenOwner) public constant returns (uint balance) {
        return balances[tokenOwner];
    }

    // ------------------------------------------------------------------------
    // Transfer the balance from token owner's account to to account
    // - Owner's account must have sufficient balance to transfer
    // - 0 value transfers are allowed
    // ------------------------------------------------------------------------
    function transfer(address to, uint tokens) public returns (bool success) {
        balances[msg.sender] = safeSub(balances[msg.sender], tokens);
        balances[to] = safeAdd(balances[to], tokens);
        Transfer(msg.sender, to, tokens);
        return true;
    }
    // ------------------------------------------------------------------------
    // Transfer tokens from the from account to the to account
    //
    // The calling account must already have sufficient tokens approve(...)-d
    // for spending from the from account and
    // - From account must have sufficient balance to transfer
    // - Spender must have sufficient allowance to transfer
    // - 0 value transfers are allowed
    // ------------------------------------------------------------------------
    function transferFrom(address from, address to, uint tokens) public returns (bool success) {
        balances[from] = safeSub(balances[from], tokens);
        allowed[manager][from] = safeSub(allowed[manager][from], tokens);
        balances[to] = safeAdd(balances[to], tokens);
        Transfer(from, to, tokens);
        return true;
    }

    // ------------------------------------------------------------------------
    // Token owner can approve for spender to transferFrom(...) tokens
    // from the token owner's account
    //
    // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
    // recommends that there are no checks for the approval double-spend attack
    // as this should be implemented in user interfaces
    // ------------------------------------------------------------------------
    function approve(address spender, uint tokens) public returns (bool success) {
        allowed[manager][spender] = tokens;
        Approval(manager, spender, tokens);
        return true;
    }
    // ------------------------------------------------------------------------
    // Returns the amount of tokens approved by the owner that can be
    // transferred to the spender's account
    // ------------------------------------------------------------------------
    function allowance(address tokenOwner, address spender) public constant returns (uint remaining)     {
        return allowed[tokenOwner][spender];
    }
Thirdly, to test the smart contract, we can go to remix.ethereum.org. In environment, choose Java VM, which will utilize the local network. The details is shown in the figure below.

Fourthly, i will explain the testing sequence according to the figure above. 

1. To add one token to the user with user account address 0x111, call token_create() function, such as token_create(111). The token_create() function is shown below. You can call token_create() continuously.
  function token_create(address userAddr) public {
    transfer(userAddr, 1);  
}
2. To spend the token, must call approve() function first, such as approve(111, 3). It means user account address 0x111 has got three token approved to be spent.

3. To check the allowance to spend , call allowance(), such as allowance(0xca35b7....., 111). The first parameter is the tokenOwner, the second parameter is the spender. The function returns the number of tokens approved to spend.

4. If allowance() return sufficient amount of tokens, to spend the token, call token_redeem(), such as token_redeem(111, 1). It means account address 0x111 wants to spend 1 token.
  function token_redeem(address userAddr, uint amount) public  {
    transferFrom(
userAddr, manager, amount);
  }
Well, the token_redeem() function will transfer tokens from userAddr to the manager address, who owns the smart contract.

Then, if you want to accept crypto currencies in exchange for your token, modify the function below.
    function () public payable {
       ...
    }
to such as:
function () public payable {
  require(now >= startDate && now <= endDate);
  uint tokens;
  balances[msg.sender] = safeAdd(balances[msg.sender], tokens];
  Transfer(address(0), msg.sender, tokens);
}

The end.


Thursday, 1 February 2018

Bitcoin transaction and Open Asset protocol

In Bitcoin transaction, it can have multiple vin and vout. In vout, it contains bitcoin script. The script can use PKSH or P2PKH as recipient. In bitcoin script, the OP_RETURN contains metadata payload.

In Open Asset Protocol, the OP_RETURN metadata is used to store Open Asset asset id and quantity (see https://en.bitcoin.it/wiki/Colored_Coins#Open_Assets). The encoding is based on LEB128. LEB128 is a compression algorithm to store a large number of small integers.

Now, the OP_RETURN accepts up to 83 bytes: https://github.com/bitcoin/bitcoin/blob/master/src/script/standard.h#L34.

The OA metadata format is shown as below:

OAP marker: 2 bytes,
version number: 2 bytes,
Asset quantity count (varint): minimum 1 byte.
Asset quantity (LED encoded): varies
Arbitrary metadata length
Arbitrary metadata

The official spec is here:
https://github.com/OpenAssets/open-assets-protocol

Thursday, 16 November 2017

Bitcoin core tutorial & code walk through (Part 9) - P2PKH/P2SH

In part 9 of the tutorial, the Bitcoin transaction will be analysed. Specifically, we will look at two types of transaction.
  • Pay to Public Key Hash (P2PKH)
  • Pay to Script Hash (P2SH)
As an example, a typical Bitcoin transaction is shown below:
Input:
Previous tx: f5d8ee39a430901c91a5917b9f2dc19d6d1a0e9cea205b009ca73dd04470b9a6
Index: 0
scriptSig: 304502206e21798a42fae0e854281abd38bacd1aeed3ee3738d9e1446618c4571d10
90db022100e2ac980643b0b82c0e88ffdfec6b64e3e6ba35e7ba5fdd7d5d6cc8d25c6b241501

Output:
Value: 100000000
scriptPubKey: OP_DUP OP_HASH160 404371705fa9bd789a2fcd52d2c580b65d35549d
OP_EQUALVERIFY OP_CHECKSIG
In input field, previous tx is the hash of the previous transaction. Index chooses the specific output in the transaction. ScriptSig contains the signature to satisfy the transaction conditions, for the recipient who is spending the bitcoin. This input uses the previous transaction of f5d8... and chooses output 0 of that transaction (as seen in Index: 0). 
In output field, scriptPubKey defines the conditions to spend the bitcoins. Value is the satoshi to be sent. One BTC is 100,000,000 satoshi. This output sends 1 BTC, it is sent to Bitcoin address 4043... 

P2PKH

scriptPubKey: OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
scriptSig: <sig> <pubKey>
As seen above, the scriptSig contains sender public key and signature of the sender. The scriptPubKey contains the hash of the receiver's public key. The recipient of P2PKH Bitcoin transaction, checks the signature and the public key hash. The public key must generate the hash that matches the pubKeyHash. The sender signature can be verified using the sender's public key.

The scriptSig part is what the receiver uses to spend the money that they got from P2PKH. Because they are spending the money, at that point they would be the new sender. The <pubKey> for this new transaction hashes to the receiver's <pubKeyHash> from the old transaction when they first got the money.
The checking process is as below:
StackScriptDescription
Empty.<sig> <pubKey> OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIGscriptSig and scriptPubKey are combined.
<sig> <pubKey>OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIGConstants are added to the stack.
<sig> <pubKey> <pubKey>OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIGTop stack item is duplicated.
<sig> <pubKey> <pubHashA><pubKeyHash> OP_EQUALVERIFY OP_CHECKSIGTop stack item is hashed.
<sig> <pubKey> <pubHashA> <pubKeyHash>OP_EQUALVERIFY OP_CHECKSIGConstant added.
<sig> <pubKey>OP_CHECKSIGEquality is checked between the top two stack items.
trueEmpty.Signature is checked for top two stack items.
For the source code, in the bitcoin-tx.cpp, there is a separate main() function that calls CommandLineRawTx(), which in turn calls MutateTx(). MutateTx() handles adding key and value to CMutableTransaction. The key and value are declare as std::string key, valueThe bitcoin-tx.cpp is compiled into bitcoin-tx binary.

In MutateTx(), depending on the command line option, MutateTxAddOutPubKey() adds value and scriptPubKey to the output, using public key from command line input. MutateTxAddOutAddr() adds value and scriptPubKey to the output, using address from command line input.

For bitcoind binary, in rest.cpp, the rest_getutxos(HTTPRequest *, std::string &) finds the txid of the UTXO, and calls ScriptPubKeyToUniv() to include the scriptPubKey in output.

In rest.cpp, the rest_tx(HTTPRequest*, std::string &) prepares the transaction with CTransactionRef class, then for JSON format, calls TxToUniv() to write the HTTP request.

In core_write.cpp, the TxToUniv(CTransaction&, uint256&, UniValue&) pushes scriptSig to vin and scriptPubKey to vout. For vout, it calls ScriptPubKeyToUniv() to include the scriptPubKey in vout.

The rest_tx() and rest_getutxos() are in uri_prefixes structure. This structure is registered in HTTP handler in StartREST().  StartREST() is called by AppInitServers() in init.cpp.
P2SH
P2SH lets the sender funds a transaction using 20 byte hash. The script supplied to redeem must hash to the scriptHash. 
Without P2SH, the scripts are shown below:
locking script: 2 <pubKey1> <pubKey2> <pubKey3> 3 OP_CHECKMULTISIG
unlocking script: <sig1> <sig2>
With P2SH, the scripts become:

redeem script: 2 <pubKey1> <pubKey2> <pubKey3> 3 OP_CHECKMULTISIG 
locking script: OP_HASH160 <redeem script Hash> OP_EQUAL
unlocking script: <sig1> <sig2>

So with P2SH, the locking script is simplified. The actual scripts in transactions are as below:

scriptPubKey: OP_HASH160 <scriptHash> OP_EQUAL 
scriptSig: <sig1> <sig2> OP_m <pubKey1> ... OP_n OP_CHECKMULTISIG
The locking script is OP_HASH160 <scriptHash> OP_EQUAL. The locking script is a simplified form of multisig script. From multisig script: 2 <pubKey1> <pubKey2> <pubKey3> 3 OP_CHECKMULTISIG, the mutisig script hashes to 20 byte value of 8ac1d7a2fa204a16dc984fa81cfdf86a2a4e1731. Therefore, the locking script becomes OP_HASH160 8ac1d7a2fa204a16dc984fa81cfdf86a2a4e1731 OP_EQUAL.
The scriptSig <sig1> <sig2> OP_2 <pubKey1> <pubKey2> <pubKey3> OP_3 OP_CHECKMULTISIG, is presented when recipient wants to spend the bitcoins.

The checking process:
StackScriptDescription
Empty.OP_2 <pubKey1> <pubKey2> <pubKey3> OP_3 OP_CHECKMULTISIG OP_HASH160 <scriptHash> OP_EQUALredeem script checked with locking script, to make sure scriptHash matches
true<sig1> <sig2> OP_2 <pubKey1> <pubKey2> <pubKey3> OP_3 OP_CHECKMULTISIGunlocking script executed to unlock redeem script
trueEmpty.Signatures validated in the order of the keys in the script.
For the source code, to be continued...

Friday, 10 November 2017

Bitcoin core tutorial & code walk through (Part 8) - blockchain

In part 8, we will look at code that handles blockchain.

In validation.cpp,
ProcessNewBlockHeaders(std::vector<CBlockHeader>&, CValidationState&, CChainParams&, CBlockIndex**) loops through the CBlockHeaders, for each header, it calls AcceptBlockHeader().

**ProcessNewBlockHeaders() is called in ProcessMessage() in net_processing.cpp.

ProcessNewBlock(CChainParams&, std::shared_ptr<const CBlock>, bool, bool) calls CheckBlock(), if that is successful, it calls AcceptBlock(). Then, the best chain is activated using ActivateBestChain().

**ProcessNewBlock() is called in ProcessMessage() in net_processing.cpp.

AcceptBlock(std::shared_ptr<const CBlock>&, CValidationState&, CChainParams&, CBlockIndex**, bool, CDiskBlockPos*, bool) calls AcceptBlockHeader(). It checks if it already has the block and the block has more work to advance blockchain tip. It also checks for block height if the block is too too ahead for blockchain pruning. If the above conditions are fulfilled, it calls CheckBlock(). If header is valid and there is sufficient work, merkle tree and segwit merkle tree are good, calls NewPowValidBlock() to announce new block to peer nodes. Then, it calls WriteBlockToDisk(). Finally, the ReceivedBlockTransactions() is called to connect the new block to the chain.

AcceptBlockHeader(CBlockHeader&, CValidationState&, CChainParams&, CBlockIndex**) firstly checks for genesis block. If block header is already known, the block is not accepted. It checks for previous block and bad block. It calls CheckBlockIndex().

CheckBlockIndex(Consensus::Params&) iterates over the entire blockchain, and checks for consistency using CBlockIndex *pindex. For example, it checks for block height, the chainwork length. tree validity.

CheckBlock(CBlock&, CValidationState&, Consensus::Params&, bool, bool) calls CheckBlockHeader() for checking header validity. Next, it check merkle root. After that, it checks for size limits, duplicate coinbase. The transactions is checked using CheckTransaction().

In consensus/tx_verify.cpp.
CheckTransaction(CTransaction&, CValidationState&, bool) firstly check for empty vin and vout. Then, it checks for negative or too large output value, duplicate inputs.

Monday, 6 November 2017

Bitcoin core tutorial & code walk through (Part 7) - networking

In part 7, the tutorial will analyse the networking code of Bitcoin core.

The net.cpp and net_processing.cpp contain the bulk of the socket handling and network message processing.

In net.cpp:
In the CConnman::Start(CScheduler&, Options) , this function initialises the connection options, such as maximum connections, maximum buffer size, and starts threads. CConnman::Start() is called in AppInitMain() as "connman.Start(scheduler, connOptions)".

The threads are listed below.

  1. The ThreadSocketHandler reads from socket and puts the messages into vRecvMsg. The select() is used to listen to file descriptor sets, the accept() is used to accept from fdSetRecv, and recv() is used to read from fdSetRecv into the buffer. If received bytes is > 0, the CNode->ReceiveMsgBytes() is called to store buffer into vRecvMsg**.
  2. The ThreadMessageHandler reads the messages from vRecvMsg, processes and sends out the messages. The handler is a loop.  It loops through std::vector<CNode>. For each node, it calls ProcessMessages() to read messages and SendMessages() to send out messages.
  3. The ThreadDNSAddressSeed finds addresses from DNS seeds. It loops through std::vector<CDNSSeedData>. From the DNS host, it look up the IP address. If IP address is found, it stores the ip address and port number in std::vector<CAddress>.
  4. The ThreadOpenAddedConnections opens network connections to added nodes.  The handler loops through std::vector<AddedNodeInfo>. If not connected,  it calls function OpenNetworkConnection().
  5. The ThreadOpenConnections is a loop. It prepares feeler connection setup. Feeler connection is short lived connections, used to test if address is online or offline The purpose of feeler connections is to increase the number of online addresses. It opens network connection from "CAddrMan addrman" variable in CConnman. It uses the addrman to setup "CAddrInfo addr". If addr is valid and feeler flag is setup, It calls function OpenNetworkConnection().
**The vRecvMsg is declared as std::list<CNetMessage> in CNode.


In net_processing.cpp:
In ProcessMessages(CNode*, CConnman&, std::atomic<bool>&), if std::dequeue<CInv> vRecvGetData is not empty, it calls ProcessGetData() to get from CNode->vRecvGetData. Then, it declares std::list<CNetMessage> msgs, and uses splice() to get from CNode->vProcessMsg to msgs. vProcessMsg is also of type std::list<CNetMessage>. After that, it checks the header for validity, set message size, initialise CDataStream& vRecv, compare checksum. Subsequently, it calls ProcessMessage() and pass vRecv to it.

In ProcessMessage(CNode*, std::string&, CDataStream&, int64_t, CChainParams&, CConnman&, td::atomic<bool>&) , if the command type is VERSION, it deserialise the vRecv to nVersion, nServiceInt, nTime. It checks the services offered, if peers' services not matched, it pushes reject message to the peers and returns. If version is less than minimum required. it pushes reject message to the peers, and returns. The CConnman.PushMessage() is used to push messages. Otherwise, it push version ack message to the peers. Then, if fInbound is false, the code uses CNode->PushAddress() to advertise its own address.

If the command type is VERACK, it pushes send headers message to peers if version is greater than  sendheaders version. It pushes send compact block message to peers if version is greater than ids block version.

If the command type is ADDR, it reads from vRecv to std::vector<CAddress> vAddr. Then, it calls RelayAddress(). If addr is reacheable, it stores them by calling CConnman.AddNewAddress().

If the command type is SENDHEADERS, it sets the CNodeState fPreferHeaders to true.

If the command type is SENDCMPCT, it reads from vRecv to fAnnounceUsingCMPCTBLOCK and nCMPCTBLOCKVersion. Then it sets the CNodeState flags.

If the command type is INV, it reads from vRecv to std:vector<CInv> vInv. If size is too big, it calls Misbehaving(). Then, it loops through vInv, if the inventory msg type is MSG_BLOCK, it pushes get headers message.

If the command type is GETDATA, it also reads from vRecv to vInv, checks for size. After that, it calls ProcessGetData().

If the command type is GETBLOCKS, it reads from vRecv into locator and hashStop. It activates the best chain from most_recent_block by calling ActivateBestChain(). It uses locator and chainActive**, loop thru the chainActive, and push newly created CInv by calling PushInventory().

**chainActive is the blockchain, starts from genesis block and ends with tip, of class CChain, declared in validation.cpp. ActivateBestChain() is called in init.cpp to initialise blockchain.

If the command type is GETBLOCKTXN, it reads from vRecv to BlockTransactionsRequest req.  If older block is requested, it calls ProcessGetData() to send block response and returns. Otherwise, it read blocks from disk and calls SendBlockTransactions().

If the command type is GETHEADERS, it reads from vRecv into locator and hashStop. It pushes the headers message after using std::vector<CBlock> vHeaders to store CBlockIndex * pindex. The pindex is from locator value.

If the command type is TX, it reads from vRecv to CTransactionRef ptx. Then, it creates double ended queue of COutPoint (vWorkQueue) and vector of uint256 (vEraseQueue), creates CInv of MSG_TX. If inv is not available, it stores inv hash to vWorkQueue. It loops through if work queue is empty, calls RelayTransaction() and stores orphan hash to work queue and erase queue. If missing input is true, it sets that orphan parents are rejected. Else, it calls AddToCompactExtraTransactions() and lastly it checks for nDos flag.

If the command type is CMPCTBLOCK, it read from vRecv to CBlockHeaderAndShortTxIDs cmpctblock. It calls ProcessNewBlockHeaders(). If fAlreadyInFlight is set, push get data message out. It checks the chainActive height, check block transaction request from compact block tx count.

If the command type is BLOCKTXN, it reads from vRecv to BlockTransactions resp. It opens a shared_ptr to CBlock pblock, checks Read status from pblock and resp. If status is invalid, it calls Misbehaving() and returns. If status is failed,  it push get data message. Else it calls MarkBlockAsReceived().

If the command type is HEADERS, it reads from vRecv to CBlockHeader vector. If it is block announcement and headers is at the end, push get headers message. Then, calls UpdateBlockAvailability(). If header msg is at max size, peer may have more headers, push the get headers message again. If headers are valid, ends in block that is greater than block in the tip, download as much as possible by calling MarkBlockAsInFlight().

If the command type is BLOCK, it reads from vRecv to pblock, shared_ptr to CBlock. Then calls ProcessNewBlock().

If the command type is GETADDR, it loops thru addr from CConnman and push addr using CNode->PushAddress().

If the command type is MEMPOOL, it checks for bloom filter and bandwidth limit.

If the command type is PING, it push pong message with nonce. Nonce is read from vRecv.

If the command type is PONG, and vRecv.in_avail() is bigger than size of nonce, read vRecv to nonce. It checks nonce to find matching ping,  process pong msg only if there is matching ping.

The ProcessGetData(CNode*, Consensus::Params&, CConnman&std::atomic<bool>&), it loops thru  CNode->vRecvGetData. If inv type is MSG_BLOCK , MSG_FILTERED_BLOCK, MSG_CMPCT_BLOCK, or MSG_WITNESS_BLOCK, it calls ActivateBestChain() if not yet validated. It checks blocks for data, pushes message of BLOCK if MSG_WITNESS_BLOCK. If MSG_FILTERED_BLOCK , it needs to send merkle block, push block message  and serialise_transaction message. If MSG_CMPCT_BLOCK, it pushes message of block or compact block.


Code to Bitcoin protocol mapping

Referring to Bitcoin protocol, Bitcoin core nodes work on p2p network. The new nodes download blocks from sync nodes, using block-first or header-first method.

For block-first download method:
Messagegetblocksinvgetdatablock
From→ToIBD→SyncSync→IBDIBD→SyncSync→IBD
PayloadOne or more header hashesUp to 500 block inventories (unique identifiers, hash of block's header)One or more blockinventoriesOne serialized block
**IBD : initial block download, refers to new node which is just trying to download blocks

and for header-first download method:
Messagegetheadersheadersgetdatablock
From→ToIBD→SyncSync→IBDIBDManyManyIBD
PayloadOne or more header hashesUp to 2,000 block headersOne or more block inventories derived from header hashesOne serialized block

The ProcessMessage() code indeed processes the Bitcoin protocol messages.