精通IPFS:IPFS 啓動之 preStart 函數

上篇說到,在 init 函數初始化系統後,會調用 IPFS 對象的 preStart 和 start 方法,進行系統初始化,這次我們來看第一個方法。首先來看 preStart 方法,這個方法位於 core/components/pre-start.js 文件中,它的主要作用是加載倉庫中的內容到內存中,它的主體是一個 waterfall,老規矩我們直接來分析它的幾個函數。

執行第一個函數,調用倉庫的配置對象的 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 文件中,內容如下:
{

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 方法正是讀取這些配置。

執行第二個函數,處理系統配置。在第一個函數中讀取到系統初始化時設置的所有配置,如果用戶在選項中沒有指定任何配置,那麼直接使用默認的配置;否則,調用 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)

})

}

執行第三個函數,檢查配置中是否有 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)

})

}

執行第四個函數,檢查 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)

}

執行第五個函數,根據配置信息中的節點信息生成節點ID。同樣比較簡單,代碼如下:
(config, cb) => {

const privKey = config.Identity.PrivKey

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

cb(err, config, id)

})

}

執行第六個函數,導入私鑰這個代碼也比較簡單,具體如下:
(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)

})

}

執行第七個函數,填充節點的 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()

}

執行最後一個函數,加載已經 pin 的文件和目錄。代碼如下,後面講到 pin 時再講。
(cb) => self.pin._load(cb)

到此,當 preStart 函數就執行完成了,接下來就開始執行 start 函數,這個函數的內容我們留到下次再來分析。

作者:喬瘋,加密貨幣愛好者,ipfs 愛好者,黑螢科技CTO。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章