摘要:NULS,让区块链更简单!
为什么要有网络模块
区块链又被叫做分布式账本系统,是基于分布式网络实现的数据系统。区块链的网络的特点是去中心化,组成网络的所有的节点都是对等的,这种网络又叫做点对点(P2P)网络。将P2P网络协议作为基础协议的区块链网络,就必须遵守P2P协议的规则,完成网络连接和数据交互。
在P2P协议中,每个加入网络的节点,既是服务器(server),可以对外提供服务,也是客户端(client),可以使用其它节点的服务。
想要实现这个目的,遵循P2P协议的网络,通常需要完成如下主要流程:
节点发现:当某个节点启动后,需要从种子节点开始,寻找其它节点;节点连接:根据寻找到的节点信息,与其它节点建立连接;连接管理:维护已经建立连接的节点的连接状态信息;数据交互:建立连接之后,节点之间就可以根据协议规则,进行数据的发送和接收。
网络模块要做的正是维护一个基于P2P协议的区块链网络,所以网络模块需要实现如下主要功能:
节点发现节点连接连接管理数据交互
这里需要特别说明的是:
因为NULS要构建的是一个多链并行的区块链生态网络,将支持跨链,所以在业务上,维护一条区块链内部网络会有一套业务规则,维护跨链网络会有一套业务规则。
在维护跨链网络时,业务相对复杂一些,主要分成4个部分:
1、平行链将参与跨链的节点信息同步给NULS主网的节点,NULS主网会将参与跨链的节点信息同步给平行链的节点。有了参与跨链的节点信息之后,平行链和NULS主网,会将收到的跨链节点信息存储起来;2、NULS主网作为跨链平台,会维护多个跨链网络的节点信息。当NULS主网需要与平行链,进行跨链通信时,NULS主网会基于存储的跨链节点信息,与平行链进行通信;3、某个单独的平行链和NULS主网构成一个跨链网络。当平行链与NULS主网通信时,也是基于存储好的节点信息进行;4、当平行链节点和NULS主网节点收到消息时,会判断消息是发送到哪个端口的,节点通过端口来区分是跨链消息,还是本链的消息。
网络模块功能
网络模块功能体现了该模块的作用,也是开发该模块需要达到的目标。读者阅读本文档的目标是,理解网络模块具有哪些功能,以及这些功能的实现流程。
网络模块功能主要分为三类:
节点管理连接管理数据交互
下面我们将详细讲解以上四类功能的作用及实现流程。
节点管理
节点管理功能主要实现:
节点发现:当某个节点启动后,需要根据已有信息,寻找其它节点;节点连接:根据节点信息,在完成节点验证后,与其它节点建立连接。
节点管理功能实现的主要流程如下:
1、根据本地已有信息,向其它节点发起连接请求;如果是第一次启动节点,已有信息为配置信息;如果是节点重启,已有信息为配置信息和本地已有的节点信息;2、进行节点的握手验证,根据验证结果对节点进行归类;验证通过,归类到已验证集合中,等待进行连接;验证失败,尝试多次验证,达到验证上限,依然失败,则直接移除节点信息,不再验证;3、从已验证集合中获取节点信息进行连接,连接成功将节点归类到已连接集合中;4、节点间相互同步已连接集合中的节点列表信息;当节点A主动连接上节点B时,节点A会主动发起获取节点列表信息的请求;当节点A和节点B建立连接之后,如果B节点有新增的节点信息,节点B会主动将节点信息同步给节点A;
5、根据接收到的节点列表信息,重复以上4个步骤,直到节点连接数量达到上限。
在同步列表信息时,有两点需要注意:
1、节点重复判断:当节点A从节点B获取到节点信息列表时,如果B给出的节点信息中,已经包含了与A节点建立了连接的节点信息,节点A便会将重复的节点信息丢弃;2、局域网节点数量限制:当同一局域网中有多个节点加入网络时,为了避免出现节点中心化的情况出现,会以该局域网的外网IP+端口号为判断条件,将该局域网的节点数量限制为最高10个。
连接管理
连接管理是对节点连接过程的细化,主要分为三个要点:
握手验证连接断开连接状态管理
握手验证
握手验证分为TCP层面的验证和业务层面的验证,我们这里只说明业务层面的验证。
业务层面的验证主要根据网络通讯协议来完成。当节点A与节点B进行握手验证时,双方都会对消息中的魔法参数和协议版本进行验证,如果魔法参数和协议版本其中一个参数不一致,握手验证都会失败。
网络通讯协议中的协议版本(version)消息格式,见后面的网络通讯协议章节。
握手验证的主要流程如下:
1、节点A向节点B发送包含魔法参数、协议版本的握手验证消息;2、节点B收到节点A的握手验证消息,对魔法参数、协议版本等进行验证,验证通过,节点B回传握手验证消息给节点A;3、节点A收到节点B回传的握手验证消息,对魔法参数、协议版本等进行验证,验证通过,回传握手验证响应消息verack。4、节点B接收节点A的握手验证响应消息verack,握手验证成功。
握手验证流程图:
连接断开
在握手验证过程中,会出现连接断开的情况。连接断开有TCP层面的,也有业务层面的。我们这里只针对业务层面的连接断开进行说明。
在进行握手验证时,业务层面会出现连接断开的情况如下:
当节点连接数量达到上限时;当某个局域网内节点数量达到限制上限X=10时;
连接状态管理
连接成功
在握手验证通过之后,代表两个节点的连接已正式建立。之后,节点会进行如下操作:
1、当节点A与节点B连接成功时,节点A和节点B,都会将对方的信息存放到已连接节点集合中;2、当需要向节点B发送消息时,节点A会从已连接节点集合中提取节点B的信息,然后向节点B发送消息。
被存入已连接节点集合的节点代表进入连接成功状态。
连接失败
在之后过程中,如果节点间出现以下情况,连接状态将由连接成功变成连接失败:
1、当节点A和节点B建立连接成功时,如果节点A与节点B进行数据交互,节点B未响应,节点A会与节点B进行重连;2、当节点A重连次数达到设置的上限时,仍未连接成功,节点A会在本地将节点B的信息删除。
这代表着节点A与节点B的连接状态,由连接成功变成了连接失败。
连接断开
在NULS网络中会有多种业务,有些业务情况下,网络模块会根据具体业务要求,进行连接断开。
例如,当普通节点的连接数量达到系统设置的上限,并且仍有剩余可连接节点情况下,为了避免种子节点达到连接上限,其它节点不能连接种子节点,普通节点会随机断开与部分种子节点的连接,最后只保留与一个种子节点的连接。
完成连接断开,只需要调用连接断开的接口即可。接口详细内容请参考接口文档。
因为业务是多样的,并且未来还会增加新的业务功能,在这里对所有需要进行连接断开的业务场景,不做详细说明。
数据交互
在进行数据交互之前,所有模块需要先完成消息注册,只有注册过的消息类型,网络模块才会进行处理,否则,会直接丢弃。
消息注册是所有模块启动后,调用网络模块的消息注册接口,接口详情请参考接口文档。
节点的数据交互主要分为消息接收和消息发送两种情况。
消息接收的主要流程如下:
1、节点网络模块接收到其它节点发来的网络消息;2、根据消息头中的魔法参数和cmd指令,判断消息是否已经注册,如果未注册,直接丢弃;3、如果消息合法,根据消息头中的cmd指令,网络模块将消息发送给对应的模块处理;
消息发送的主要流程如下:
1、如果是广播消息,网络模块会向已连接节点集合中的所有节点发送消息;2、如果是发送给指定节点的消息,网络模块会从已连接节点集合中,获取对应节点的信息,然后进行发送。
其它
网络通讯协议
version
用于建立连接(握手)
Length Fields Type Remark 4 version uint32 节点使用的协议版本标识 20 addr_you byte[20] 对方网络地址【IP+PORT1+PORT2】PORT2为跨链server端口 如:[10.32.12.25 8003 9003] 16byte+2byte+2byte 20 addr_me byte[20] 本节点网络地址【IP+PORT1+PORT2】PORT2为跨链server端口 如:[20.32.12.25 7003 6003] 16byte+2byte+2byte 4 block_height uint32 节点高度 ? block_hash varByte 区块hash ?? extend VarByte 扩展字段,不超过10个字节
verack
用于应答version
Length Fields Type Remark 1 ack_code uint8 返回码,1代表正常
ping
在某条链自己的网络中,因为每隔10s会出块,所以网络中不会存在长时间无消息的收发情况。 但是在主网与平行链连接的跨链网络中,就没有10s出块的区块消息交互,所以需要ping、pong 来维护连接。
Length Fields Type Remark 4 randomCode uint32 随机数
pong
用于回应ping
Length Fields Type Remark 4 randomCode uint32 随机数
getaddr
用于请求网络中可用节点的连接信息。
Length Fields Type Remark 2 chainId uint16 链id 1 isCrossAddress uint8 是否请求跨链地址
addr
用于应答getaddr,或向网络中宣告自身的存在,节点接收到该消息后,判断节点是否已知,如果是未知节点,则保存,待验证通过后向网络中传播该地址。
Length Fields Type Remark ?? addr_list network address 每个节点18字节(16字节IP+2字节port)
getTime
由于节点启动后,会有一个本地时间,但是整个网络会有一个统一的网络时间,整个网络的运行都依照网络时间在进行,这里就是向其它节点获取已有的网络时间,便于本地节点进行网络时间的校正。
Length Fields Type Remark 4 messageId uint32 请求id
responseTime
回复getTime请求,回传网络时间
Length Fields Type Remark 4 messageId uint32 请求id 4 time uint32 时间值
模块配置
“port”: 18001,
“chainId”: 2,
“maxInCount”: 300,
“maxOutCount”: 20,
“packetMagic”: 3136151,
“selfSeedIps”: “192.168.1.57:8888”,
“crossPort”: 18002,
“crossMaxInCount”: 300,
“crossMaxOutCount”: 10,
“moonSeedIps”: “192.168.1.192:8088”,
“timeServers”:”sgp.ntp.org.cn,cn.ntp.org.cn,time1.apple.com,ntp3.aliyun.com,ntp5.aliyun.com,us.ntp.org.cn,kr.ntp.org.cn,de.ntp.org.cn,jp.ntp.org.cn,ntp7.aliyun.com”
网络模块启动时需要依赖的模块
网络模块在启动时,需要依赖以下模块:
1、区块管理模块:因为节点启动时,网络模块需要调用区块管理模块接口,获取最新区块高度和Hash值,区块高度和Hash值,将在进行握手验证时,发送给目标节点。
2、协议升级模块:网络模块动态依赖协议升级模块。动态依赖的意思是:如果有协议升级模块,就依赖,没有协议升级模块,就不依赖。