精通IPFS | IPFS 啟動之 start 函式

買賣虛擬貨幣

在系統啟動總共要執行兩個啟動函式,一個是 preStart 函式,另一個就今天我樣研究的 start 函式。這個函式位於 core/components/start.js 檔案中,它的主要作用是真正啟動系統,它的主體是一個 series,老規矩我們直接來分析它的幾個函式。

  1. 執行第一個函式,檢查倉庫是否被關閉,如果是則開啟倉庫,否則,呼叫下一個函式。具體程式碼如下:

    (cb) => {
        self._repo.closed ? self._repo.open(cb) : cb()
    }

  2. 執行第二個函式,根據倉庫的配置檔案,生成 libp2p 物件,並呼叫它的啟動方法,然後設定 IPFS 物件的 libp2p 為生成的 libp2p 物件。具體程式碼如下:

    (cb) => {
        self._repo.config.get((err, config) => {
          if (err) return cb(err)

      const libp2p = createLibp2pBundle(self, config)

      libp2p.start(err => {
        if (err) return cb(err)
        self.libp2p = libp2p
        cb()
      })
    })

    }

    createLibp2pBundle 函式位於當前目錄下的 libp2p.js 檔案中,這個函式的執行流程如下:

    • 檢查是否有配置具體的傳輸方法,比如:TCP。如果沒有指定一個傳輸方法,則丟擲異常。預設情況下,會配置 TCP、WS、wsstar 等3個傳輸方法。

    • 檢查節點的所有 multiaddr 地址,如果沒有指定節點 ID,則地址附加上 /p2p/節點ID。libp2p 物件的 peerInfo 來源於 IPFS 物件的 _peerInfo,後者在 preStart 函式中生成並進行初始化,包含的 multiaddr則來自於配置檔案的 Addresses.Swarm 陣列、libp2p-nodejs.js 生成的 /p2p-websocket-star、前面建構函式生成的 /p2p-circuit/ipfs/節點ID。最後一個地址只有在配置了 modules.streamMuxer 和 relay.enabled 的情況下,即啟用了流複用和電路中繼時候,在建構函式中呼叫 switch.connection 物件的 enableCircuitRelay 方法時生成電路中繼物件時才會生成並加入節點資訊物件的地址中。經過這步處理,最終節點資訊物件的 multiaddrs 變成 "/ip4/0.0.0.0/tcp/4002/ipfs/節點ID/ip4/127.0.0.1/tcp/4003/ws/ipfs/節點ID/p2p-websocket-star/ipfs/節點ID/p2p-circuit/ipfs/節點ID 的形式。

    • 遍歷對等節點指定的所有傳輸方法,如果某個傳輸方法可以處理節點指定的地址,則儲存到 switch.transport 物件中(型別為 TransportManager)。這一步的作用是用節點的地址來過濾傳輸方法,只有能處理某個地址的傳輸方法才會儲存到 switch.transport 物件中。但是,每個傳輸方法都會儲存到 libp2p 物件自身的 _transport 陣列。

    • 序列啟動所有的服務。比如:switch 服務、DHT 服務、節點發現服務等,如果有配置這些服務的話。switch 物件管理所有網路通訊相關的服務,內部也是一個狀態機,通常改變狀態執行不同的方法,當啟動它的服務時,最終會執行 _onStarting 方法,這個方法中會讓所有可以使用(即有地址可以監聽)的傳輸物件開始進行監聽,比如 TCP 傳輸方法監聽在 4002 埠。節點發現服務找到節點後,會觸發事件 peer:discovery,並且會把發現的節點儲存到 peerBook 中,如果當前連線的數量小於規定的數量還會進行連線。

    • 所有服務啟動完成之後,遍歷 peerBook 中儲存的所有地址,觸發事件 peer:discovery,並且如果當前連線的數量小於規定的數量還會進行連線。

    • 首先,設定預設選項。

      const libp2pDefaults = {
          datastore,
          peerInfo,
          peerBook,
          config: {
            peerDiscovery: {
              mdns: {
                enabled: get(options, 'config.Discovery.MDNS.Enabled',
                  get(config, 'Discovery.MDNS.Enabled', true))
              },
              webRTCStar: {
                enabled: get(options, 'config.Discovery.webRTCStar.Enabled',
                  get(config, 'Discovery.webRTCStar.Enabled', true))
              },
              bootstrap: {
                list: get(options, 'config.Bootstrap',
                  get(config, 'Bootstrap', []))
              }
            },
            relay: {
              enabled: get(options, 'relay.enabled',
                get(config, 'relay.enabled', true)),
              hop: {
                enabled: get(options, 'relay.hop.enabled',
                  get(config, 'relay.hop.enabled', false)),
                active: get(options, 'relay.hop.active',
                  get(config, 'relay.hop.active', false))
              }
            },
            dht: {
              kBucketSize: get(options, 'dht.kBucketSize', 20),
              enabled: false,
              randomWalk: {
                enabled: false // disabled waiting for https://github.com/libp2p/js-libp2p-kad-dht/issues/86
              },
              validators: {
                ipns: ipnsUtils.validator
              },
              selectors: {
                ipns: ipnsUtils.selector
              }
            },
            EXPERIMENTAL: {
              pubsub: get(options, 'EXPERIMENTAL.pubsub', false)
            }
          },
          connectionManager: get(options, 'connectionManager',
            {
              maxPeers: get(config, 'Swarm.ConnMgr.HighWater'),
              minPeers: get(config, 'Swarm.ConnMgr.LowWater')
          })
      }

      其中,datastorepeerInfopeerBookoptions 等來自於 IPFS 物件的相關私有屬性,config 來自於最終生成的倉庫的配置檔案和使用者指定的相關配置。

    • 然後,呼叫 mergeOptions 方法,合併預設選項與使用者指定的選項。

      const libp2pOptions = mergeOptions(libp2pDefaults, get(options, 'libp2p', {}))

    • 最後,載入 core/runtime/libp2p-nodejs.js 檔案中定義的 Node 物件(繼承於 libp2p 庫定義的物件),並呼叫其建構函式,生成 libp2p 物件。

      const Node = require('../runtime/libp2p-nodejs')
      return new Node(libp2pOptions)

      libp2p-nodejs.js 檔案中主要定義了建立 libp2p 物件的預設選項,並把前面生成的選項與預設選項進行合併,然後呼叫父類的構造來建立 物件。具體的預設選項為:

      {
        switch: {
          blacklistTTL: 2 * 60 * 1e3, // 2 minute base
          blackListAttempts: 5, // back off 5 times
          maxParallelDials: 150,
          maxColdCalls: 50,
          dialTimeout: 10e3 // Be strict with dial time
        },
        modules: {
          transport: [
            TCP,
            WS,
            wsstar
          ],
          streamMuxer: [
            Multiplex
          ],
          connEncryption: [
            SECIO
          ],
          peerDiscovery: [
            MulticastDNS,
            Bootstrap,
            wsstar.discovery
          ],
          dht: KadDHT
        },
        config: {
          peerDiscovery: {
            autoDial: true,
            mdns: {
              enabled: true
            },
            bootstrap: {
              enabled: true
            },
            websocketStar: {
              enabled: true
            }
          },
          dht: {
            kBucketSize: 20,
            enabled: false,
            randomWalk: {
              enabled: false
            }
          },
          EXPERIMENTAL: {
            pubsub: false
          }
        }
      }

    • 生成配置物件和選項物件,前者透過引數傳遞進來,後者透過 IPFS 物件獲取到。

      const options = self._options || {}
      config = config || {}

    • 確定如何建立 libp2p 物件。如果在選項物件中指定了建立方法,則使用指定的建立方法,否則使用預設的建立方法。預設情況,使用者不會指定建立方法,所以這裡使用預設的建立方法。

      const createBundle = typeof options.libp2p === 'function'
          ? options.libp2p
          : defaultBundle

    • 從 IPFS 物件獲取建立 libp2p 物件所需要的資訊。

      const { datastore } = self._repo
      const peerInfo = self._peerInfo
      const peerBook = self._peerInfoBook

    • 呼叫建立方法建立 libp2p 物件。預設的建立方法執行如下:透過以上程式碼,我們可以發現建立 libp2p 的過程是比較複雜的,libp2p 物件的實際型別為 libp2p 庫中定義的物件。

      因為 libp2p 是一個非常非常重要的元件/庫,即可以使用在 IPFS/Filecoin 中,也可以有獨立使用,或者在其他專案中使用,鑑於它是如此的重要,所以我們以後會專門來講解它,這裡只是簡單涉及。

      libp2p 物件繼承於 EventEmitter 類,所以可以觸發事件,同時本身內部也有一個型別 fsm-event 的狀態變數,所以也可以認為是一個狀態機物件。

    • 呼叫 libp2p 物件的 start 方法,啟動 libp2p 物件。當 libp2p 物件啟動成功後,把它儲存在 IPFS 物件的同名屬性中。具體程式碼如下:

      libp2p.start(err => {
          if (err) return cb(err)
          self.libp2p = libp2p
          cb()
      }

      libp2p 物件的 start 方法,把內部狀態設為 start,從而導致 libp2p 呼叫其 _onStarting 方法,開始啟動處理,具體處理如下:

  3. 執行第三個函式,這個函式的內容也比較多,我們慢慢看。

    • 首先,生成 IPNS 物件,並設定 IPFS 物件的 _ipns 為生成 IPNS 物件。

      const ipnsRouting = routingConfig(self)
      self._ipns = new IPNS(ipnsRouting, self._repo.datastore, self._peerInfo, self._keychain, self._options)

    • 然後,生成 Bitswap 物件,並設定 IPFS 物件的 _bitswap 為生成的 Bitswap 物件,同時呼叫後者的啟動方法;

      self._bitswap = new Bitswap(
        self.libp2p,
        self._repo.blocks,
        { statsEnabled: true }
      )
      self._bitswap.start()

      Bitswap 物件是 IPFS/libp2p 體系中另一個非常的物件,它決定了是從本地倉庫中載入區塊,還是從網路中其他節點請求區塊,還決定了是否相應別的節點請求區塊的請求。它的 start 方法依次啟動了 WantManager 物件(一個定時向別的節點傳送請求訊息的物件)、Network 物件(一個指定 libp2p/switch 物件如何處理 bitswap 協義,同時監聽 libp2p 物件節點連線/斷開連線事件的物件)、DecisionEngine 物件(一個確定是否響應別節點請求的物件)。

    • 接下來,呼叫 blockService 物件的 setExchange 方法,設定前者交換區塊的物件為新生成的 Bitswap 物件。

      self._blockService.setExchange(self._bitswap)

      本方法執行之前,當呼叫區塊服務物件請求區塊時,都是從本地倉庫中載入區塊;當本方法執行之後,區塊服務物件在請求區塊時都要透過 bitswap 物件來確定區塊是從哪裡獲取。

    • 再接下來,呼叫幾個物件的 start 方法,進行啟動。

      self._preload.start()
      self._ipns.republisher.start()
      self._mfsPreload.start(cb)

      預載入物件的 start 方法只是簡單地把內部變數 stopped 設定為假;IPNS 的啟動,以後分析 IPNS 會進行分析,這裡略過。_mfsPreload 的啟動方法也比較簡單,只是呼叫 IPFS 物件的 files 物件的 stat 方法,載入根目錄。根目錄物件在初始化函式中,儲存 init-files/init-docs/ 的過程中被初始化。

當 series 方法的幾個函式執行完成後,系統基本啟動完成,最後一個要執行的動作,就是呼叫 start 函式中定義的 done 函式,把狀態設定為執行中。

當 done 函式執行到完成後,IPFS 系統就算啟動完成了。

 作者介紹:

喬瘋,區塊鏈狂熱愛好者,熟悉比特幣、EOS、以太坊原始碼及合約的開發,有著數年區塊鏈開發經驗,堅信技術是第一生產力,區塊鏈改變整個人類,開設巴位元專欄以來已經獲得 100多萬次的閱讀量。

參與湖南天河國雲 Ulord 公鏈的開發和麵向區塊鏈行業的風險監控平臺,後者在近期成功入選由工信部評選的 101 個網路安全技術應用試點示範專案

在愛健康金融金融有限公司參與組建彗星資訊科技有限公司,並擔任第一任技術部負責人,開發出了彗星播報等深受大家喜愛的區塊鏈產品。

具有良好的協調溝通能力和團隊協作精神!熟悉Scrum、XP、看板等敏捷專案管理,擁有PMP證書!

熟悉JAVA、Python、NodeJS、C/C++、Linux下的開發,熟悉分散式架構設計!熟悉網際網路金融行業,具有豐富的網際網路金融產品開發經驗,對互聯金融有著深入的瞭解。

關於星鑑網

星鑑網自2018年3月成立至今,採訪了眾多業內大佬,包括萊位元礦池創始人江卓爾、礦海會創始人阿牛,知名佈道者董天一、戴嘉樂,共識實驗室合夥人任錚,YottaChain創始人王東臨、ORA甲骨數鏈創始人石柱、Lambda創始人何曉陽、NBS創始人李萬勝等等將近50位區塊鏈、IPFS、分散式儲存大咖。

同時星鑑網也曝光了行業內超過30多個傳銷盤、資金盤、空氣幣、礦機騙局,守護了業內投資者的利益。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;