美文网首页我爱编程程序员
区块链源码学习(2)-Bytecoin节点程序及其数据库初探

区块链源码学习(2)-Bytecoin节点程序及其数据库初探

作者: 偏执的工匠 | 来源:发表于2018-07-02 22:29 被阅读4次

    上一节区块链源码学习(1)-Bytecoin源码编译,生成3个可执行文件,分别是bytecoind,walletd和tests。bytecoind是核心节点程序,与其他节点进行点对点通信,记录区块信息。walletd顾名思义,用于钱包管理的程序,tests为测试程序。本节主要介绍bytecoind程序。

    bytecoind默认会在当前用户的根目录下创建一个隐藏的目录.bytecoin,用于区块数据、日志数据、缓存数据等的存储。其中blockchain目录是使用lmdb数据库存储的区块数据,bytecoin在源码编译时,也可以指定使用SQLite来存储区块数据,默认情况下使用lmdb数据库,关于lmdb库的使用可参考链接(lmdb简介及示例代码)。logs目录用于存储日志文件,peer_db目录存储p2p通信节点信息,后面会详细讲述peer_db存储数据的格式。

    ➜  .bytecoin ls -ahl
    总用量 20K
    drwxr-xr-x 5 root root 4.0K 6月  12 21:48 .
    drwxr-xr-x 3 root root 4.0K 6月  12 21:46 ..
    drwxr-xr-x 2 root root 4.0K 6月  12 21:48 blockchain
    -rw------- 1 root root    0 6月  12 21:48 bytecoind.lock
    drwxr-xr-x 2 root root 4.0K 6月  12 21:49 logs
    drwxr-xr-x 2 root root 4.0K 6月  12 21:48 peer_db
    

    运行bytecoind,看到输出信息如下。

    ➜  bin git:(master) ✗ ./bytecoind
    Starting multicore ring checker using 2/2 cpus
    BlockChain::db_commit started... tip_height=0 header_cache.size=1
    BlockChain::db_commit finished...
    21:48:16.138649 I BlockChainState BlockChainState::BlockChainState height=0 cumulative_difficulty=1 bid=a742885cb01d11b7b36fb8bf14616d42cd3d8c1429a224df41afa81b86b8a3a8
    21:48:16.246808 I P2P Connecting to=45.76.29.96:8080
    21:48:16.247016 I P2P No peers to connect to, will try again after 10 seconds
    Starting multicore POW checker using 2/2 cpus
    bytecoind started seconds=0.218
    21:48:26.247250 I P2P No peers to connect to, will try again after 10 seconds
    21:48:36.247641 I P2P No peers to connect to, will try again after 10 seconds
    21:48:46.247706 I P2P Connecting to=207.246.127.160:8080
    21:48:46.248063 I P2P Connecting to=108.61.174.232:8080
    21:48:46.248284 I P2P No peers to connect to, will try again after 10 seconds
    P2p COMMAND_HANDSHAKE response version=1 unique_number=18227590769910343221 current_height=0 local_peerlist.size=250
    21:48:49.969575 I P2P Connecting to=72.228.0.160:8080
    21:48:49.969923 I P2P Connecting to=94.232.78.204:8080
    21:48:49.970208 I P2P Connecting to=196.217.81.244:8080
    21:48:49.970483 I P2P Connecting to=74.192.143.134:8080
    21:48:49.970758 I P2P Connecting to=104.199.238.247:8080
    21:48:49.971036 I P2P Connecting to=83.181.211.60:8080
    21:48:49.971813 I P2P Connecting to=45.32.156.183:8080
    P2p COMMAND_HANDSHAKE response version=1 unique_number=9744417281224826320 current_height=1549945 local_peerlist.size=250
    21:48:50.088996 I Node DownloaderV11::advance_chain Requesting chain from 104.199.238.247:8080 remote height=1549945 our height=0
    Received chain from 104.199.238.247:8080 start_height=0 length=10000
    Requesting block 1 from 104.199.238.247:8080
    Received block with height=1 (queue=399) from 104.199.238.247:8080
    redo_block {0} height=1 bid=13a537627c969dfda1bf6a2688694aa139f66d643073d49afc5cdd75d3c1e400
    Requesting block 564 from 104.199.238.247:8080
    redo_block {1} height=181 bid=40f90f4272773369fabbf795e9030fb331c2d8d7d78cda32c47aab2d86d9f4ba
    
    

    其中P2P连接信息,有一些ip地址,如45.76.29.96:8080、207.246.127.160:8080等,在运行bytecoind没有指定任何配置信息,那么猜测这些ip地址应该是写死在代码中的。搜索代码源文件,果然在 CryptoNoteConfig.hpp 文件中看这些ip:

    //CryptoNoteConfig.hpp
    const char *const SEED_NODES[] = {
        "207.246.127.160:8080", "108.61.174.232:8080", "45.32.156.183:8080", "45.76.29.96:8080"};
    

    也就是说bytecoind节点程序在p2p点对点通信时,需要一些初始的节点ip,通过这些节点,能够在短时间内迅速地扩散到全网。

    下面来看一下bytecoind的参数,执行./bytecoind --help

    ➜  bin git:(master) ✗ ./bytecoind --help
    bytecoind 3.1.1.
    
    Usage:
      bytecoind [options]
      bytecoind --help | -h
      bytecoind --version | -v
    
    Options:
      --allow-local-ip                     Allow local ip add to peer list, mostly in debug purposes.
      --p2p-bind-address=<ip:port>         Interface and port for P2P network protocol [default: 0.0.0.0:8080].
      --p2p-external-port=<port>           External port for P2P network protocol, if port forwarding used with NAT [default: 8080].
      --bytecoind-bind-address=<ip:port>   Interface and port for bytecoind RPC [default: 127.0.0.1:8081].
      --seed-node-address=<ip:port>        Specify list (one or more) of nodes to start connecting to.
      --priority-node-address=<ip:port>    Specify list (one or more) of nodes to connect to and attempt to keep the connection open.
      --exclusive-node-address=<ip:port>   Specify list (one or more) of nodes to connect to only. All other nodes including seed nodes will be ignored.
      --export-blocks=<folder>             Perform hot export of blockchain into specified folder as blocks.bin and blockindexes.bin, then exit. This overwrites existing files.
      --backup-blockchain=<folder>         Perform hot backup of blockchain into specified backup data folder, then exit.
      --data-folder=<full-path>            Folder for blockchain, logs and peer DB [default: ~/.bytecoin].
      --bytecoind-authorization=<usr:pass> HTTP authorization for RPC.
      --ssl-certificate-pem-file=<file>    Full path to file containing both server SSL certificate and private key in PEM format.
      --ssl-certificate-password=<pass>    DEPRECATED. Will read password from stdin if not specified.
    

    参数信息中有默认值的基本都不需要修改,其中--data-folder指定数据目录,如果不想将数据保存在默认的.bytecoin目录,可以指定该参数。--seed-node-address指定初始的p2p连接节点ip列表,如果源码中写死的节点ip都无法使用时,可以手动指定该参数,设置可用的p2p初始节点ip。其他参数通过其名称和参数说明,大概可以猜测其作用,这里不再赘述。

    正常情况下,bytecoind程序运行起来后,会一直通过周围的节点,以点对点通信的方式下载区块历史信息,直到同步到最新的区块。

    bytecoin源码中有两个地方使用到了数据库,一个是PeerDB类,用于存储点对点通信时节点的信息, 主要包括ip、端口等。另一个是BlockChain,用来存储区块信息。下面是这两个类的定义。

    // src/p2p/PeerDB.hpp
    class PeerDB {
    public:
          typedef platform::DB DB;
    private:
          DB db;
    };
    
    // src/Core/BlockChain.hpp
    class BlockChain {
    public:
          typedef platform::DB DB;
          ...
          DB m_db;
          ...
    };
    

    platform::DB 这个类,根据是否定义platform_USE_SQLITE宏,来决定使用sqlite还是lmdb,默认情况下使用lmdb。使用cmake编译时,指定-DUSE_SQLITE=1,则使用sqlite数据库存储数据。

    // src/platform/DB.hpp
    
    #pragma once
    #if platform_USE_SQLITE
    #include "platform/DBsqlite3.hpp"
    namespace platform {
    typedef DBsqlite DB;
    }
    #else
    #include "platform/DBlmdb.hpp"
    namespace platform {
    typedef DBlmdb DB;
    }
    #endif
    

    lmdb可以看成是一个key-value数据库。分析PeerDB类的实现代码,每一条记录都是一个key-value数据。
    key的值为prefix + 节点ip + 端口, prefix 一般为 "graylist/" 或者 "whitelist/"
    value的值为一个Entry对象的二进制序列化值
    看下源码:

    // src/p2p/PeerDB.cpp
    
    static const std::string GRAY_LIST("graylist/");
    static const std::string WHITE_LIST("whitelist/");
    ...
    void PeerDB::update_db(const std::string &prefix, const Entry &entry) {
          auto key = prefix + common::to_string(entry.adr.ip) + ":" +  common::to_string(entry.adr.port);
          db.put(key, seria::to_binary(entry), false);
    }
    ...
    update_db(GRAY_LIST, new_entry);
    

    再看下Entry结构的定义,主要就是一个存储ip地址和端口信息的结构体:

    // src/p2p/P2pProtocolTypes.hpp
    struct NetworkAddress {
        uint32_t ip   = 0;
        uint32_t port = 0;
    };
    struct PeerlistEntry {
         NetworkAddress adr;
         PeerIdType id      = 0;
         uint32_t last_seen = 0;  // coincides with Timestamp
         uint32_t reserved  = 0;  // High part of former 64-bit last_seen
    };
    
    // src/p2p/PeerDB.hpp
    struct Entry : public PeerlistEntry {
                Entry()
                    : PeerlistEntry{}  // Initialize all fields
                {}
                Timestamp ban_until               = 0;
                Timestamp next_connection_attempt = 0;
                uint64_t shuffle_random = 0;  // We assign random number to  each record, for deterministic order of equal items
                std::string error;            // last ban reason
          };
    

    使用lmdb库,编写了一个简单的C语言程序,读取peer_db数据库中的内容,将读取到的数据转成可读的文本打印输出如下,这里只打印了前5行:

    key:/whitelist/1289242423:8080, value:Entry(adr.ip:10000,adr.port:8080,id:0,last_seen:0,reserved:0,ban_until:0,next_connection_attempt:0,shuffle_random:0,error:)
    key:graylist/1006109508:8080, value:Entry(adr.ip:1006109508,adr.port:8080,id:964940239481739396,last_seen:0,reserved:0,ban_until:0,next_connection_attempt:0,shuffle_random:12844025959447800046,error:)
    key:graylist/1006990104:8080, value:Entry(adr.ip:1006990104,adr.port:8080,id:10284378849939737697,last_seen:0,reserved:0,ban_until:0,next_connection_attempt:0,shuffle_random:6511915129648608432,error:)
    key:graylist/1020507475:8080, value:Entry(adr.ip:1020507475,adr.port:8080,id:8440730593804793034,last_seen:0,reserved:0,ban_until:0,next_connection_attempt:0,shuffle_random:10276282581105767265,error:)
    key:graylist/103674199:8080, value:Entry(adr.ip:103674199,adr.port:8080,id:16166886514328276571,last_seen:0,reserved:0,ban_until:0,next_connection_attempt:0,shuffle_random:6207942281420898257,error:)
    

    相关文章

      网友评论

        本文标题:区块链源码学习(2)-Bytecoin节点程序及其数据库初探

        本文链接:https://www.haomeiwen.com/subject/ritdeftx.html