IPFS七层协议栈之交换层
交换层(Exchange)
IPFS中BitSwap协议旨在通过对等节点间交换数据块来分发数据,受到BitTorrent技术的启发,每个对等节点在下载的同时不断向其他对等节点上传已下载的数据。和BitTorrent协议不同的是,BitSwap不局限于一个种子文件中的数据块。BitSwap协议中存在一个数据交换市场,这个市场包括各个节点想要获取的所有块数据,这些块数据可能来自文件系统中完全不相关的文件,同时这个市场是由IPFS网络中所有节点组成的。这样的数据市场很需要创造加密数字货币来实现可信价值交换,也就是得有激励层Filecoin。
BitSwap协议
在IPFS中,数据的分发和交换使用BitSwap协议,BitSwap协议主要负责两件事:向其他节点请求需要的数据块列表(need_list)以及为其他节点提供已有的数据块列表(have_list),源码结构如下:
type BitSwap struct {
ledgers map[NodeId]Ledger //节点账单
active map[NodeId]Peer //当前已经连接的对等点
need_list []Multihash //此节点需要的块数据校验列表
have_list []Multihash //此节点已收到块数据校验列表
}
当节点需要向其他节点请求数据块或者为其他节点提供数据块时,都会发送BitSwap message消息,其中主要包含了两部分内容:想要的数据块列表(want_list)以及对应数据块,整个消息都会使用Protobuf进行编码:
message Message {
message Wantlist {
message Entry {
optional string block = 1;
optional int32 priority = 2; //设置优先级,默认为1
optional bool cancel = 3; //是否会撤销条目
}
repeated Entry entries = 1;
optional bool full = 2;
}
optional Wantlist wantlist = 1;
repeated bytes blocks = 2;
}
在BitSwap系统中,有两个非常重要的模块需求管理器(Want-Manager)和决定引擎(Decision-Engine):前者会在节点请求数据时在本地返回相应的结果或发出合适的请求,而后者决定如何为其他节点分配资源,当节点接收到包含want_list的消息时,消息会被转发到决定引擎,引擎会根据该节点的BitSwap账单决定如何处理请求,整个处理流程如下图:
BitSwap协议流程通过上面的协议流程图,可以看到一次BitSwap数据交换的全过程以及对等连接这块的生命周期,在这个生命周期中,对等节点一般要经历四个状态:
- 状态开放(Open):对等节点间开放待发送BitSwap账单状态,直到建立连接。
- 数据发送(Sending):对等节点间发送want_list和数据块。
- 连接关闭(Close):对等节点发送完数据后断开连接。
- 节点忽略(Ignored):对等节点因为超时、自动以、信用分过低等因素被忽略。
结合对等节点的源码结构,分析一下IPFS节点是如何找到彼此的:
type Peer struct {
nodeid NodeIs
ledger Ledger //节点和此对等节点之间的分类账单
last_seen Timestamp //最后收到消息的时间戳
want_list []Multihash //需要的所有块校验
}
// 协议接口
interface Peer {
open (nodeid:NodeId,ledger:Ledger);
send_want_list(want_list:WantList);
send_block(block:Block) -> (complete:Bool);
close(final:Bool);
}
Peer.open(NodeID,Ledger)
当节点建立连接时,发送方节点会初始化BitSwap账单,可能保存一份对等方的账单,也可能将创建一个新的被清零账单,这取决于节点账单一致性问题。之后,发送方节点将发送一个携带账单的open信息通知接收方节点,接收方节点收到一个open信息后,可以选择是否接受此连接请求。
如接收方根据本地的账单数据,发现发送方是一个不可信的节点,即传输超时,很低信用分,很大的债务率,接收方可能会通过ignore_cooldown忽略这个请求,断开连接,目的是为了防范作弊行为。
如果连接成功,接收方将利用本地账单来初始化一个Peer对象并更新last_seen时间戳,然后,它会将接收到的账单与自己的账单进行比较。如果两个账单完全一样,那么这个连接就被Open,如果账单不完全一致,那么此节点会创建一个新的被清零的账单并发送同步此账单,以此保证之前提到的发送方节点和接收方节点的账单一致性问题。
Peer.send_want_list(WantList)
当连接已经处于open状态,发送方节点将会把want_list广播给所有连接的接收方节点。与此同时,接收方节点在收到一个want_list后,会检查自身是否有接收方想要的数据块,如果有,会使用BitSwap策略来发送传输这些数据块。
Peer.send_block(Block)
发送块的方法逻辑很简单,默认发送方节点只传输数据块,接收到所有数据后,接收方节点计算Multihash以校验它是否与预期的匹配,然后返回确认。在完成块的传输后,接收方节点将数据块信息从need_list移到have_list,并且接收方和发送方都同步更新他们的账单列表。如果传输验证失败,则发送方可能发生故障或者故意攻击接收方的行为,接收方可以拒绝进一步的交易。
Peer.close(Bool)
对等连接应该在两种情况下关闭:
- silent_want超时已过,但未收到来自对方的任何消息,节点发出Peer.close(false)。
- 节点正在退出,BitSwap正在关闭,在这种情况下,节点发出Peer.close(true)。
对于P2P网络,有一个很重要的问题:如何激励大家分享自己的数据,每一个P2P软件都有自己专属的数据分享策略,IPFS也是如此,其中BitSwap的策略体系由信用、策略、账单三部分组成。
BitSwap信用体系
BitSwap协议必须能激励节点去分享数据,IPFS根据节点之间的数据收发建立了一个信用体系:有借有还,再借不难。
- 发送给其他节点数据可以增加信用值。
- 从其他节点接收数据将降低信用值。
如果一个节点只接收数据不上传数据,信用值会降低而被其他节点忽略掉。这能有效防范一些类似女巫攻击,洪泛攻击的网络攻击。
BitSwap策略
有了BitSwap信用体系,还可以采取不同的策略来实现,每一种策略都会对系统的整体性能产生不同的影响,策略的目标是:
- 节点数据交换的整体性能和效率最高。
- 阻止空载节点“吃白食”现象,即不能够只下载不上传数据。
- 有效防止一些攻击行为。
- 对信任节点建立宽松机制。
IPFS在白皮书里提供了一些参考的策略机制,每个节点根据和其他节点的收发数据,计算信用分和负债率(debt ratio,r):
r = bytes_sent / bytes_recv +1
数据发送率(P):
P(send|r) = 1 - (1/(1+exp(6-3r)))
根据上面共识,如果r大于2时,发送率P(send|r)将变得很小,从而其他节点将不会继续发送数据给自己。
BitSwap账单
BitSwap节点记录下来和其他节点通信的账单(数据收发记录),账单数据结构如下:
type Ledger struct {
Owner NodeId
partner NodeId
bytes_sent int
bytes_recv int
timestamp Timestamp
}
这可以让节点追踪历史记录以避免被篡改。当两个节点之间建立连接的时候,BitSwap会相互交换账单信息,如果账单不匹配,则直接清楚并重新记账,恶意节点会有意失去这些账单,从而期望清除自己的债务。其他节点会把这些都记录下来,如果总是发生,伙伴节点可以自由的将其视为不当行为,拒绝交易。
文章借鉴《IPFS与区块链:原理与实战》推荐大家购买正版书籍。
网友评论