星鉴网>>精通IPFS:IPFS 启动之 preStart 函数

精通IPFS:IPFS 启动之 preStart 函数

2019/7/4 10:34:14 25499人阅读

【导读】 继续上一篇的干货。


上篇说到,在 init 函数初始化系统后,会调用 IPFS 对象的 preStart 和 start 方法,进行系统初始化。


这次我们来看第一个方法。


首先来看 preStart 方法,这个方法位于 core/components/pre-start.js 文件中,它的主要作用是加载仓库中的内容到内存中,它的主体是一个 waterfall,老规矩我们直接来分析它的几个函数。


  1. 执行第一个函数,调用仓库的配置对象的 get 方法,获取配置系统配置。具体代码如下:



    (cb) => self._repo.config.get(cb)

    我们知道,仓库的配置对象是在仓库的构造函数中生成,在初始化方法 init 中设置的。


    现在我们来详细看下配置对象,这个对象以仓库对象的 root 为参数创建的,它的所有操作最终都是调用这个对象来完成的,也即最终都是保存保存在文件系统的配置文件中,那么配置文件内容是在哪里被写入的呢?答案就是仓库的 init 方法。


    在这个方法中会调用配置对象的同名方法来完成保存所有配置。这里还有一个问题就是具体的配置是在哪里定义的,这个问题也比较简单。


    在前面系统初始化时,即 IPFS 的 init 方法中曾经调用 mergeOptions 方法来合并默认配置和用户通过 config 指定的配置。


    从这个方法可以发现所有默认配置定义在 core/runtime/config-node.js 文件中,从这个文件可以发现默认配置如下:


    {
      Addresses: {
        Swarm: [
          '/ip4/0.0.0.0/tcp/4002',
          '/ip4/127.0.0.1/tcp/4003/ws'
        ],
        API: '/ip4/127.0.0.1/tcp/5002',
        Gateway: '/ip4/127.0.0.1/tcp/9090'
      },
      Discovery: {
        MDNS: {
          Enabled: true,
          Interval: 10
        },
        webRTCStar: {
          Enabled: true
        }
      },
      Bootstrap: [
        '/ip4/104.236.176.52/tcp/4001/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z',
        '/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ',
        '/ip4/104.236.179.241/tcp/4001/ipfs/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
        '/ip4/162.243.248.213/tcp/4001/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
        '/ip4/128.199.219.111/tcp/4001/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
        '/ip4/104.236.76.40/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64',
        '/ip4/178.62.158.247/tcp/4001/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
        '/ip4/178.62.61.185/tcp/4001/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
        '/ip4/104.236.151.122/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx',
        '/ip6/2604:a880:1:20::1f9:9001/tcp/4001/ipfs/QmSoLnSGccFuZQJzRadHn95W2CrSFmZuTdDWP8HXaHca9z',
        '/ip6/2604:a880:1:20::203:d001/tcp/4001/ipfs/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM',
        '/ip6/2604:a880:0:1010::23:d001/tcp/4001/ipfs/QmSoLueR4xBeUbY9WZ9xGUUxunbKWcrNFTDAadQJmocnWm',
        '/ip6/2400:6180:0:d0::151:6001/tcp/4001/ipfs/QmSoLSafTMBsPKadTEgaXctDQVcqN88CNLHXMkTNwMKPnu',
        '/ip6/2604:a880:800:10::4a:5001/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64',
        '/ip6/2a03:b0c0:0:1010::23:1001/tcp/4001/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd',
        '/ip6/2a03:b0c0:1:d0::e7:1/tcp/4001/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3',
        '/ip6/2604:a880:1:20::1d9:6001/tcp/4001/ipfs/QmSoLju6m7xTh3DuokvT3886QRYqxAzb1kShaanJgW36yx',
        '/dns4/node0.preload.ipfs.io/tcp/443/wss/ipfs/QmZMxNdpMkewiVZLMRxaNxUeZpDUb34pWjZ1kZvsd16Zic',
        '/dns4/node1.preload.ipfs.io/tcp/443/wss/ipfs/Qmbut9Ywz9YEDrz8ySBSgWyJk41Uvm2QJPhwDJzJyGFsD6'
      ],
      Swarm: {
        ConnMgr: {
          LowWater: 200,
          HighWater: 500
        }
      }
    }

    除了系统定义的默认配置和用户指定的配置之外,在创建节点时把节点的相关信息,比如节点ID、节点私钥等也保存在配置对象的 Identity 属性上。


    除了上面这些配置之外,在仓库的初始化方法中又通过 buildConfig 方法把用户指定的 datastore 和系统默认的数据存储配置进行了合并。


    统默认的数据存储配置在 ipfs-repo 项目的 default-datastore.js 文件中,内容如下:

  2. {
      Spec: {
        type: 'mount',
        mounts: [
          {
            mountpoint: '/blocks',
            type: 'measure',
            prefix: 'flatfs.datastore',
            child: {
              type: 'flatfs',
              path: 'blocks',
              sync: true,
              shardFunc: '/repo/flatfs/shard/v1/next-to-last/2'
            }
          },
          {
            mountpoint: '/',
            type: 'measure',
            prefix: 'leveldb.datastore',
            child: {
              type: 'levelds',
              path: 'datastore',
              compression: 'none'
            }
          }
        ]
      }
    }

    综上所述,系统的配置来源于前面的几个地方,这些配置最终在仓库的 init 方法调用配置对象的 set 方法保存到配置文件中。那么在这里调用配置对象的 get 方法正是读取这些配置。


    4,执行第二个函数,处理系统配置。


    在第一个函数中读取到系统初始化时设置的所有配置,如果用户在选项中没有指定任何配置,那么直接使用默认的配置;


    否则,调用 mergeOptions 方法合并默认的配置和用户指定的配置,然后调用 IPFS 对象的 config 组件的 replace 方法,把合并后的配置保存到仓库对象的配置对象中。上述逻辑的代码如下:

  (config, cb) => {
    if (!self._options.config) {
      return cb(null, config)
    }
    config = mergeOptions(config, self._options.config)
    self.config.replace(config, (err) => {
      if (err) {
        return cb(err)
      }
      cb(null, config)
    })
  }

  1. 执行第三个函数,检查配置中是否有 Keychain 。


    如果配置中已有 Keychain,则执行第四个函数,否则,生成它,并保存在配置对象中。上述逻辑的代码如下:

      (config, cb) => {
        if (config.Keychain) {
          return cb(null, config)
        }
        config.Keychain = Keychain.generateOptions()
        self.config.set('Keychain', config.Keychain, (err) => {
          self.log('using default keychain options')
          cb(err, config)
        })
      }

  2. 执行第四个函数,检查 IPFS 对象是否有 Keychain。具体代码比较简单,代码如下:

      (config, cb) => {
        if (self._keychain) {
        } else if (pass) {
          const keychainOptions = Object.assign({ passPhrase: pass }, config.Keychain)
          self._keychain = new Keychain(self._repo.keys, keychainOptions)
        } else {
          self._keychain = new NoKeychain()
        }
        cb(null, config)
      }

  3. 执行第五个函数,根据配置信息中的节点信息生成节点ID。同样比较简单,代码如下:

      (config, cb) => {
        const privKey = config.Identity.PrivKey

    peerId.createFromPrivKey(privKey, (err, id) => {
      cb(err, config, id)
    })

      }

  4. 执行第六个函数,导入私钥这个代码也比较简单,具体如下:

      (config, id, cb) => {
        if (!pass) {
          return cb(null, config, id)
        }
        self._keychain.findKeyByName('self', (err) => {
          if (err) {
            return self._keychain.importPeer('self', id, (err) => cb(err, config, id))
          }
          cb(null, config, id)
        })
      }

  5. 执行第七个函数,填充节点的 multiaddr 信息。代码如下,一看就明白,不细讲:

      (config, id, cb) => {
        self.log('peer created')
        self._peerInfo = new PeerInfo(id)
        if (config.Addresses && config.Addresses.Swarm) {
          config.Addresses.Swarm.forEach((addr) => {
            let ma = multiaddr(addr)
            if (ma.getPeerId()) {
              ma = ma.encapsulate('/ipfs/'   self._peerInfo.id.toB58String())
            }
            self._peerInfo.multiaddrs.add(ma)
          })
        }
        cb()
      }

  6. 执行最后一个函数,加载已经 pin 的文件和目录。代码如下,后面讲到 pin 时再讲。

    (cb) => self.pin._load(cb)


到此,当 preStart 函数就执行完成了,接下来就开始执行 start 函数,这个函数的内容我们留到下次再来分析。



 作者介绍:  


乔疯区块链狂热爱好者,熟悉比特币、EOS、以太坊源码及合约的开发,有着数年区块链开发经验,坚信技术是第一生产力,区块链改变整个人类,开设巴比特专栏以来已经获得 100多万次的阅读量。


参与湖南天河国云 Ulord 公链的开发和面向区块链行业的风险监控平台,后者在近期成功入选由工信部评选的 101 个网络安全技术应用试点示范项目


在爱健康金融金融有限公司参与组建彗星信息科技有限公司,并担任第一任技术部负责人,开发出了彗星播报等深受大家喜爱的区块链产品。


具有良好的协调沟通能力和团队协作精神!熟悉Scrum、XP、看板等敏捷项目管理,拥有PMP证书!


熟悉JAVA、Python、NodeJS、C/C 、Linux下的开发,熟悉分布式架构设计!熟悉互联网金融行业,具有丰富的互联网金融产品开发经验,对互联金融有着深入的了解。





114

参与讨论

登录后参加评论......

全部评论 0

作者

返回顶部