7

Dfinity互联网计算机技术概述

 3 years ago
source link: https://learnblockchain.cn/article/1966
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

解释Dfinity开发平台的基础架构,以及Dfinity软件容器如何让Web服务扩展到数十亿用户

编者注:Dfinity的互联网计算机项目非常宏大,意在改变 DApp 开发范式(甚至是 Web 开发范式),开发过 DApp 的同学应该了解,一个 DAPP 会包含链上部分(即合约)和链下部分(通常是网页前端,和用户交互的部分)。互联网计算机则不再有链上与链下的概念,业务的所有代码会打包为canister(一种轻量级的容器),上传到节点运行,开发者不再需要租用服务器托管应用。同时canister 的数据则和合约一样,可以通过共识持久保存。

互联网计算机(Internet Computer)全面启动之前的最后一个里程碑即将到来, 这是一个由先进的去中心化协议的独立数据中心网络创建的公共软件开发平台。 在9月30日发布Sodium的活动中 , DFINITY基金会将发布“网络神经系统”,这是一个控制互联网计算机的开放算法治理系统。该活动还将展示有关高级加密技术,共识协议和代币经济学的深入技术资料。

在这一重要时刻之前,我们希望向公众提供有关网络运行方式的非常高级的概述。

网络神经系统

互联网计算机是基于称为互联网计算机协议(ICP)的区块链计算协议。网络本身是根据分层结构构建的。底层是托管专用硬件节点(Nodes)的独立数据中心。这些节点机器组合在一起以创建子网(Subnets)。子网主运行着 软件容器(Canisters) ,它们是可互操作的计算单元,由用户上传,并包含代码和状态。

zI32Ujv.jpg!mobile

使ICP独树一帜的要素之一是网络神经系统(NNS),它负责控制,配置和管理网络。

数据中心通过向NNS申请加入网络( NNS负责引入到数据中心)。尽管NNS本身具有开放的治理系统,但它监督参与网络的权限。从某种意义上说,它起着与互联网上的ICANN等效的作用,除其他功能外,它为想要运行BGP路由器的人分配自治系统号。 NNS承担着广泛的网络管理角色,包括监控节点计算机以查找互联网计算机网络上的统计偏差,其可能表明性能较差或行为错误。

NNS在网络代币经济学中也起着关键作用。 NNS生成新的ICP代币(以前称为DFN代币)以奖励节点,这是运行着数据中心和在NNS内投票神经元的节点,他们决定着哪些提案可以提交给它。当NNS发行新的ICP代币来奖励数据中心和神经元时,这部分是通货膨胀的。

yArq2mR.jpg!mobile

最终,数据中心所有者和神经元所有者可以获取代币,并与容器所有者和管理者进行交换。容器所有者和管理者将这些代币转换为cycles(周期),然后使用这些cycles来为其容器充电。例如,当这些容器执行计算或存储内存时,整个循环中,需要消耗cycles,消耗殆尽后,必须为它们补充更多的cycles才能继续运行,这部分是通货紧缩的。

Subnets(子网)

要了解互联网计算机,你必须了解子网的概念,子网是整个网络的基本组成部分。子网负责托管着互联网计算机网络的软件容器的不同子集。在NNS控制下,将从不同数据中心提取的节点机器聚集在一起来创建子网。这些节点通过ICP协作,以对称地复制与它们所托管的软件容器有关的数据和计算。

jmQZbmu.jpg!mobile

NNS在构建子网时会合并来自独立数据中心的节点。通过使用DFINITY开发的拜占庭容错技术和密码技术,ICP协议可以确保子网防篡改且永不停止。尽管子网是整个互联网计算机网络的基本组成部分,但它们对用户和软件是透明的。用户和容器软件仅需要知道容器的身份即可调用其共享的函数。

这种透明性是互联网基本设计原则的延伸。 在(传统)互联网上,如果用户要连接到某些软件,需要知道运行该软件的计算机的IP地址和该软件正在侦听的TCP端口。在互联网计算机上,如果用户希望调用函数,则只需要知道容器的身份和函数签名即可 。与互联网创建无缝连接的方式相同,DFINITY也为软件创建了一个无缝的世界,其中任何获得许可的软件都可以直接调用任何其他软件,而无需了解网络的基础运行情况。

互联网计算机还以其他方式确保子网的透明性。 NNS可以拆分和合并子网,以平衡整个网络的负载。当然,这对于托管容器也是透明的。

6F3In2j.jpg!mobile

在此示例中,我们有一个虚拟的子网ABC,该子网承载11个容器。 NNS告诉它拆分。子网ABC继续使用容器1–6,并生成了一个新子网,子网XYZ,其继续使用容器7–11 . 所涉及的所有容器都不会遇到服务中断的情况。

在将容器上传到互联网计算机时,必须指定特定的子网类型。实际上,有一个托管NNS的特殊子网,但是你无法将容器上传到该子网。相反,你必须指定子网类型,例如“data(数据)”,“system(系统)”或“fiduciary(受信任)”。

n2I3uye.jpg!mobile

每种子网类型都会为你的容器提供某些属性和功能。例如,如果你的容器托管在数据子网中,则它可以处理调用,但不能调用其他容器。要调用其他容器,你需要一个系统子网。如果希望你的容器能够保持ICP代币的余额或将cycles发送到其他容器,则需要一个受信任的子网。由于这些原因,治理容器只能托管在受信任的子网中。

Ifmi2u7.jpg!mobile

子网的功能部分源自底层的容错能力。这是基础科学领域中令人兴奋的领域,我们希望很快与公众分享,其中包括允许NNS修复损坏的子网的新加密技术。

容器(Canisters)

子网的目的是托管容器。容器在专用的管理程序中运行,并通过公共指定的API与其他的相互容器交互。容器内部是可在WebAssembly虚拟机上运行的WebAssembly字节码及其中运行的内存数据页。通常,可以通过编译诸如Rust或Motoko之类的编程语言来创建该WebAssembly字节码。该字节码将包含一个运行时,使开发人员可以轻松地与API进行交互。

VrUJF3b.jpg!mobile

注意:此处显示的示例代码为伪代码。

在互联网计算机上,调用容器共享的函数有两种方式:更新调用或查询调用。本质区别在于,当你将函数作为更新调用调用时,其对容器内存中的数据所做的任何更改都将保留,而如果将函数作为查询调用进行调用,则其对内存的任何更改在运行后都将被丢弃。

更新调用的更改是持久化的,并且它们是防篡改的,因为ICP区块链计算机协议在子网的每个节点上运行它们。如你所预期的,这些调用在一致的全局调用顺序中,允许在完全确定的执行环境中并发执行。更新调用在两秒内就可完成。

eAN3Qfq.jpg!mobile

在此示例中,用户向容器内托管的金融交易所提交购买订单。

相反,查询调用不会保留更改。它们对内存所做的任何更改在运行后都会被丢弃。它们非常高效且便宜,并且仅需几毫秒即可完成。这是因为它们未在子网中的所有节点上运行,也意味着它们提供了较低的安全级别。

AbIFJre.jpg!mobile

在此示例中,用户正在请求自定义新闻源,几乎立即可获得了新生成的内容。

正交持久性

关于互联网计算机,最有趣的事情之一就是开发人员保留数据的方式。开发人员不必考虑持久性-他们只需编写代码,持久性就会自动发生。这称为正交持久性。这是因为互联网计算机会保留其中运行代码的内存数据页。

你可能想知道这一切如何工作。对于可以使内存数据页面发生变化的更新调用,容器是软件执行者。这意味着在任何给定时间,容器内只能有一个执行线程。

yEfEz2M.jpg!mobile

尽管容器内只有一个执行线程,但默认情况下可以交错进行跨容器更新调用。当更新调用进行跨容器更新调用时会发生这种情况,该调用会阻塞,从而允许将执行线程移至新的更新调用。

Ev2Iv27.jpg!mobile

相比之下,查询调用不会对内存进行永久更改。这样一来,在任何给定时间,容器内可以有任意数量的并发线程来处理查询调用。这些查询调用针对最后确定的状态根中记录的内存快照运行。

最后,容器可以创建新的容器以及容器可以也fork自身。你只需指定WebAssembly字节码即可创建一个新的容器,并且内存数据页开始为空。当容器fork时,将创建一个新生成的副本,该副本与内部的内存页面相同。创建可扩展的互联网服务时,fork功能非常强大。

可扩展性

现在来谈谈互联网服务可扩展性进行高层次解释。不同类型的容器都有各自的容量上限。例如,由于WebAssembly实现的 限制 ,一个容器只能存储4GB的内存。因此,当我们要创建可扩展到数十亿用户的互联网服务时,我们必须使用多容器架构。

Zrqiia.jpg!mobile

我们可能希望创建一个特殊的容器,创建容器的许多副本,然后将用户内容分片到不同的容器以创建可扩展的互联网服务。但是,由于很多种原因,这个架构过来简单了。

的确,每个额外的容器都会增加整体内存容量,以及增加容器都会增加整体更新和查询调用的吞吐量。但是我们无法扩展针对特定用户内容的查询调用请求。每当我们通过添加更多的容器分片来增加系统容量时,我们都需要重新负载均衡用户内容,这并不是真正的边缘架构。也没有明显的方法从与他们非常接近的副本中向最终用户提供查询调用。我们将同时需要前端容器和后端容器。

jmQjEjy.jpg!mobile

互联网计算机提供了一些有趣的功能,可以将最终用户连接到前端容器。其中之一允许域名通过NNS映射到多个前端容器。当最终用户希望解析这样的域名时,互联网计算机将查看托管前端容器的所有子网中所有副本节点的总数,并返回最接近的副本节点的IP地址。这导致最终用户在附近的副本上执行查询调用,从而减少了固有的网络延迟并改善了用户体验,从而在没有内容分发网络的情况下提供了边缘计算的优势。

ZVN3eie.jpg!mobile

为了充分利用此函数,我们需要一个涉及前端容器和后端数据桶容器的经典体系结构。在此示例中,Web浏览器希望加载个人资料图片。

NjQrQnV.jpg!mobile

首先,Web浏览器将映射到一个前端容器,其上运行在具有附近节点的子网。然后,Web浏览器将提交查询调用请求到附近的节点以检索照片。

r2UreiF.jpg!mobile

然后,前端容器将向保存照片的数据容器发出跨容器查询调用请求。

7BVNRbN.jpg!mobile

如果数据存储容器返回的查询调用响应涉及静态内容(例如照片),则可以将数据存储在缓存中。在此案例中,运行前端容器查询调用的副本节点可以将查询调用响应存储到其查询缓存中。

3AjqYnI.jpg!mobile

当然,查询调用缓存机制对于前端容器代码是完全透明的。一旦用户调用的前端容器收集了所有必要的信息,它就可以通过查询调用响应或HTTP请求返回内容。

FbUjuuV.jpg!mobile

随着时间的流逝,节点的查询缓存会累积静态内容并生成附近用户感兴趣的数据,从而为他们提供更快,更好的用户体验。这样,互联网计算机的原生边缘体系结构提供了内容分发网络的优势,而无需开发人员做任何特殊的事情,也无需争取单独的专有服务的帮助。

yQFR3aA.jpg!mobile

对于更新调用,经典体系结构采用了不同的方法。必须序列化对用户内容和数据的更新,以防止诸如更新丢失之类的问题。通常,会通过hash用户名将用户映射到特定的前端容器来实现的。

vQNNR3f.jpg!mobile

一旦在Web浏览器或智能手机上运行的UX/UI确定了哪个前端容器负责协调对某些内容或数据的更改,便可以通过标准接口提交更新调用来修改内容或数据。

uQJNR3Y.jpg!mobile

然后,此前端容器通常会进行更多的跨容器更新调用,以实现所需的更改。

开放式互联网服务

总结一下,让我们讨论一下设计使用前端容器和后端数据容器的两层体系结构的开放互联网服务。首先,当你编写前端容器代码时,将通过使用称为BigMap的现有库类来简化工作。

BRNVV3z.jpg!mobile

BigMap可以存储EB级(exabytes 十亿字节)数据,写入对象仅使用一行代码就可以完成。通过让前端容器和数据桶容器复制,以在两个容器之间分担责任,让体系结构可以透明,动态地横向扩展。

最后,要创建真正的开放互联网服务,你需要将所有容器的责任分配给一个开放的代币化治理容器。如果你是企业家,则可以通过在早期出售一些治理代币来筹集发展资金。你可能会设计一些方案,通过给他们治理代币提供激励来激发互联网服务的早期参与者,从而获得更好的网络效果,并赢得竞争。

原文: https://medium.com/dfinity/a-technical-overview-of-the-internet-computer-f57c62abc20f

编者注:Dfinity的互联网计算机项目非常宏大,意在改变 DApp 开发范式(甚至是 Web 开发范式),开发过 DApp 的同学应该了解,一个 DAPP 会包含链上部分(即合约)和链下部分(通常是网页前端,和用户交互的部分)。互联网计算机则不再有链上与链下的概念,业务的所有代码会打包为canister(一种轻量级的容器),上传到节点运行,开发者不再需要租用服务器托管应用。同时canister 的数据则和合约一样,可以通过共识持久保存。

互联网计算机(Internet Computer)全面启动之前的最后一个里程碑即将到来, 这是一个由先进的去中心化协议的独立数据中心网络创建的公共软件开发平台。 在9月30日发布Sodium的活动中 , DFINITY基金会将发布“网络神经系统”,这是一个控制互联网计算机的开放算法治理系统。该活动还将展示有关高级加密技术,共识协议和代币经济学的深入技术资料。

在这一重要时刻之前,我们希望向公众提供有关网络运行方式的非常高级的概述。

网络神经系统

互联网计算机是基于称为互联网计算机协议(ICP)的区块链计算协议。网络本身是根据分层结构构建的。底层是托管专用硬件节点(Nodes)的独立数据中心。这些节点机器组合在一起以创建子网(Subnets)。子网主运行着 软件容器(Canisters) ,它们是可互操作的计算单元,由用户上传,并包含代码和状态。

zI32Ujv.jpg!mobile

使ICP独树一帜的要素之一是网络神经系统(NNS),它负责控制,配置和管理网络。

数据中心通过向NNS申请加入网络( NNS负责引入到数据中心)。尽管NNS本身具有开放的治理系统,但它监督参与网络的权限。从某种意义上说,它起着与互联网上的ICANN等效的作用,除其他功能外,它为想要运行BGP路由器的人分配自治系统号。 NNS承担着广泛的网络管理角色,包括监控节点计算机以查找互联网计算机网络上的统计偏差,其可能表明性能较差或行为错误。

NNS在网络代币经济学中也起着关键作用。 NNS生成新的ICP代币(以前称为DFN代币)以奖励节点,这是运行着数据中心和在NNS内投票神经元的节点,他们决定着哪些提案可以提交给它。当NNS发行新的ICP代币来奖励数据中心和神经元时,这部分是通货膨胀的。

yArq2mR.jpg!mobile

最终,数据中心所有者和神经元所有者可以获取代币,并与容器所有者和管理者进行交换。容器所有者和管理者将这些代币转换为cycles(周期),然后使用这些cycles来为其容器充电。例如,当这些容器执行计算或存储内存时,整个循环中,需要消耗cycles,消耗殆尽后,必须为它们补充更多的cycles才能继续运行,这部分是通货紧缩的。

Subnets(子网)

要了解互联网计算机,你必须了解子网的概念,子网是整个网络的基本组成部分。子网负责托管着互联网计算机网络的软件容器的不同子集。在NNS控制下,将从不同数据中心提取的节点机器聚集在一起来创建子网。这些节点通过ICP协作,以对称地复制与它们所托管的软件容器有关的数据和计算。

jmQZbmu.jpg!mobile

NNS在构建子网时会合并来自独立数据中心的节点。通过使用DFINITY开发的拜占庭容错技术和密码技术,ICP协议可以确保子网防篡改且永不停止。尽管子网是整个互联网计算机网络的基本组成部分,但它们对用户和软件是透明的。用户和容器软件仅需要知道容器的身份即可调用其共享的函数。

这种透明性是互联网基本设计原则的延伸。 在(传统)互联网上,如果用户要连接到某些软件,需要知道运行该软件的计算机的IP地址和该软件正在侦听的TCP端口。在互联网计算机上,如果用户希望调用函数,则只需要知道容器的身份和函数签名即可 。与互联网创建无缝连接的方式相同,DFINITY也为软件创建了一个无缝的世界,其中任何获得许可的软件都可以直接调用任何其他软件,而无需了解网络的基础运行情况。

互联网计算机还以其他方式确保子网的透明性。 NNS可以拆分和合并子网,以平衡整个网络的负载。当然,这对于托管容器也是透明的。

6F3In2j.jpg!mobile

在此示例中,我们有一个虚拟的子网ABC,该子网承载11个容器。 NNS告诉它拆分。子网ABC继续使用容器1–6,并生成了一个新子网,子网XYZ,其继续使用容器7–11 . 所涉及的所有容器都不会遇到服务中断的情况。

在将容器上传到互联网计算机时,必须指定特定的子网类型。实际上,有一个托管NNS的特殊子网,但是你无法将容器上传到该子网。相反,你必须指定子网类型,例如“data(数据)”,“system(系统)”或“fiduciary(受信任)”。

n2I3uye.jpg!mobile

每种子网类型都会为你的容器提供某些属性和功能。例如,如果你的容器托管在数据子网中,则它可以处理调用,但不能调用其他容器。要调用其他容器,你需要一个系统子网。如果希望你的容器能够保持ICP代币的余额或将cycles发送到其他容器,则需要一个受信任的子网。由于这些原因,治理容器只能托管在受信任的子网中。

Ifmi2u7.jpg!mobile

子网的功能部分源自底层的容错能力。这是基础科学领域中令人兴奋的领域,我们希望很快与公众分享,其中包括允许NNS修复损坏的子网的新加密技术。

容器(Canisters)

子网的目的是托管容器。容器在专用的管理程序中运行,并通过公共指定的API与其他的相互容器交互。容器内部是可在WebAssembly虚拟机上运行的WebAssembly字节码及其中运行的内存数据页。通常,可以通过编译诸如Rust或Motoko之类的编程语言来创建该WebAssembly字节码。该字节码将包含一个运行时,使开发人员可以轻松地与API进行交互。

VrUJF3b.jpg!mobile

注意:此处显示的示例代码为伪代码。

在互联网计算机上,调用容器共享的函数有两种方式:更新调用或查询调用。本质区别在于,当你将函数作为更新调用调用时,其对容器内存中的数据所做的任何更改都将保留,而如果将函数作为查询调用进行调用,则其对内存的任何更改在运行后都将被丢弃。

更新调用的更改是持久化的,并且它们是防篡改的,因为ICP区块链计算机协议在子网的每个节点上运行它们。如你所预期的,这些调用在一致的全局调用顺序中,允许在完全确定的执行环境中并发执行。更新调用在两秒内就可完成。

eAN3Qfq.jpg!mobile

在此示例中,用户向容器内托管的金融交易所提交购买订单。

相反,查询调用不会保留更改。它们对内存所做的任何更改在运行后都会被丢弃。它们非常高效且便宜,并且仅需几毫秒即可完成。这是因为它们未在子网中的所有节点上运行,也意味着它们提供了较低的安全级别。

AbIFJre.jpg!mobile

在此示例中,用户正在请求自定义新闻源,几乎立即可获得了新生成的内容。

正交持久性

关于互联网计算机,最有趣的事情之一就是开发人员保留数据的方式。开发人员不必考虑持久性-他们只需编写代码,持久性就会自动发生。这称为正交持久性。这是因为互联网计算机会保留其中运行代码的内存数据页。

你可能想知道这一切如何工作。对于可以使内存数据页面发生变化的更新调用,容器是软件执行者。这意味着在任何给定时间,容器内只能有一个执行线程。

yEfEz2M.jpg!mobile

尽管容器内只有一个执行线程,但默认情况下可以交错进行跨容器更新调用。当更新调用进行跨容器更新调用时会发生这种情况,该调用会阻塞,从而允许将执行线程移至新的更新调用。

Ev2Iv27.jpg!mobile

相比之下,查询调用不会对内存进行永久更改。这样一来,在任何给定时间,容器内可以有任意数量的并发线程来处理查询调用。这些查询调用针对最后确定的状态根中记录的内存快照运行。

最后,容器可以创建新的容器以及容器可以也fork自身。你只需指定WebAssembly字节码即可创建一个新的容器,并且内存数据页开始为空。当容器fork时,将创建一个新生成的副本,该副本与内部的内存页面相同。创建可扩展的互联网服务时,fork功能非常强大。

可扩展性

现在来谈谈互联网服务可扩展性进行高层次解释。不同类型的容器都有各自的容量上限。例如,由于WebAssembly实现的 限制 ,一个容器只能存储4GB的内存。因此,当我们要创建可扩展到数十亿用户的互联网服务时,我们必须使用多容器架构。

Zrqiia.jpg!mobile

我们可能希望创建一个特殊的容器,创建容器的许多副本,然后将用户内容分片到不同的容器以创建可扩展的互联网服务。但是,由于很多种原因,这个架构过来简单了。

的确,每个额外的容器都会增加整体内存容量,以及增加容器都会增加整体更新和查询调用的吞吐量。但是我们无法扩展针对特定用户内容的查询调用请求。每当我们通过添加更多的容器分片来增加系统容量时,我们都需要重新负载均衡用户内容,这并不是真正的边缘架构。也没有明显的方法从与他们非常接近的副本中向最终用户提供查询调用。我们将同时需要前端容器和后端容器。

jmQjEjy.jpg!mobile

互联网计算机提供了一些有趣的功能,可以将最终用户连接到前端容器。其中之一允许域名通过NNS映射到多个前端容器。当最终用户希望解析这样的域名时,互联网计算机将查看托管前端容器的所有子网中所有副本节点的总数,并返回最接近的副本节点的IP地址。这导致最终用户在附近的副本上执行查询调用,从而减少了固有的网络延迟并改善了用户体验,从而在没有内容分发网络的情况下提供了边缘计算的优势。

ZVN3eie.jpg!mobile

为了充分利用此函数,我们需要一个涉及前端容器和后端数据桶容器的经典体系结构。在此示例中,Web浏览器希望加载个人资料图片。

NjQrQnV.jpg!mobile

首先,Web浏览器将映射到一个前端容器,其上运行在具有附近节点的子网。然后,Web浏览器将提交查询调用请求到附近的节点以检索照片。

r2UreiF.jpg!mobile

然后,前端容器将向保存照片的数据容器发出跨容器查询调用请求。

7BVNRbN.jpg!mobile

如果数据存储容器返回的查询调用响应涉及静态内容(例如照片),则可以将数据存储在缓存中。在此案例中,运行前端容器查询调用的副本节点可以将查询调用响应存储到其查询缓存中。

3AjqYnI.jpg!mobile

当然,查询调用缓存机制对于前端容器代码是完全透明的。一旦用户调用的前端容器收集了所有必要的信息,它就可以通过查询调用响应或HTTP请求返回内容。

FbUjuuV.jpg!mobile

随着时间的流逝,节点的查询缓存会累积静态内容并生成附近用户感兴趣的数据,从而为他们提供更快,更好的用户体验。这样,互联网计算机的原生边缘体系结构提供了内容分发网络的优势,而无需开发人员做任何特殊的事情,也无需争取单独的专有服务的帮助。

yQFR3aA.jpg!mobile

对于更新调用,经典体系结构采用了不同的方法。必须序列化对用户内容和数据的更新,以防止诸如更新丢失之类的问题。通常,会通过hash用户名将用户映射到特定的前端容器来实现的。

vQNNR3f.jpg!mobile

一旦在Web浏览器或智能手机上运行的UX/UI确定了哪个前端容器负责协调对某些内容或数据的更改,便可以通过标准接口提交更新调用来修改内容或数据。

uQJNR3Y.jpg!mobile

然后,此前端容器通常会进行更多的跨容器更新调用,以实现所需的更改。

开放式互联网服务

总结一下,让我们讨论一下设计使用前端容器和后端数据容器的两层体系结构的开放互联网服务。首先,当你编写前端容器代码时,将通过使用称为BigMap的现有库类来简化工作。

BRNVV3z.jpg!mobile

BigMap可以存储EB级(exabytes 十亿字节)数据,写入对象仅使用一行代码就可以完成。通过让前端容器和数据桶容器复制,以在两个容器之间分担责任,让体系结构可以透明,动态地横向扩展。

最后,要创建真正的开放互联网服务,你需要将所有容器的责任分配给一个开放的代币化治理容器。如果你是企业家,则可以通过在早期出售一些治理代币来筹集发展资金。你可能会设计一些方案,通过给他们治理代币提供激励来激发互联网服务的早期参与者,从而获得更好的网络效果,并赢得竞争。

原文: https://medium.com/dfinity/a-technical-overview-of-the-internet-computer-f57c62abc20f

本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

  • 发表于 24分钟前
  • 阅读 ( 10 )
  • 学分 ( 0 )
  • 分类:其他链

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK