Categorygithub.com/cxio/findings
repositorypackage
0.0.0-20241105105557-a08ad04cdbe4
Repository: https://github.com/cxio/findings.git
Documentation: pkg.go.dev

# Packages

No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author
No description provided by the author

# README

节点发现服务(findings)

前言

提供各种类型节点的登记注册,维护一个节点连系信息的暂存池,向请求目标类型节点信息的用户提供连系清单。同时,作为P2P网络的基础设施,为新节点的加入提供 NAT打洞 服务支持,因为findings已经是一个网络,所以可以多机协作而无需双IP配置。

一台服务器维护的节点信息包含同类节点(findings)、数据驿站的 depots/archivesdepots/blockqs 公共服务节点,以及不同区块链的工作节点。

通常,应用节点会启动一个自己的findings服务器,该服务器通过多种方式搜寻其它findings服务节点进行组网。如果存在已知的公共findings节点,组网会很快,否则可能是一个比较耗时的过程。如果需要服务器实时可用,运行一个自己的长期在线的findings是一个不错的选择。

除了对 depots/archives, blockqs 公共服务节点会普遍支持外,对于不同的区块链应用,findings服务器会有自己的选择。服务器通常会先声明自己支持的区块链的名称,并向请求节点信息的区块链应用传递一个相同链的地址,用于接收可能有的奖励。

用法

(略)

性能

(略)

附:网络的连通性

暴力发现

传统的P2P网络有一个致命的缺陷:起始中枢。即新节点刚上线时需要获得其它节点的信息以建立初次连接,这时就需要一个提供这些信息的中心服务器。而一旦这样的服务器被禁用,P2P的逻辑就执行不起来。

目前解决这个问题的办法有几个,比如在App发布时就加入几个主要节点的IP,或者通过专用的渠道获取。但实际上这些都可以被封堵。

findings是一个网络,通过大众化的利益驱动模式运行且成本低廉。如果网络已经启动运行,假以时日,数以万计的服务器规模是可预期的。如果无需顾及时间,用IP遍历的 暴力发现 方式去发现这个已经运行的网络,则不应该是一件太困难的事。findings服务器可以仅仅只是一台旧手机或树莓派,可以长期挂机,因此这个P2P网络就会很大。

用户用一台小设备挂机扫描,暴力发现findings网络,一旦成功,用户就无需担心自己的具体类P2P网络找不到了。

App发布时会默认配置几个IP,这些IP至少曾经有效,用户也可以修改这个配置。作为一种暴力发现优化,这些IP会作为随机IP的衍生起点,新创建的IP会围绕着它。这是基于友好用户可能成群结队的假设。同时,这也可用于IP簇优化:配置一个簇中间的一个IP号,然后随机衍生的IP就会在它附近。

标准端口混入

如果管理者屏蔽了Findings服务的默认端口号,我们可以采用通用服务端口混入的方式,比如 https 的标准端口 443。因为大多数的公网IP都不会提供这种标准服务,所以在这个端口提供 findings 服务也是可行的。

这需要公网IP主机们的共同努力,来应对这种网络封锁。

动态端口(含工作量)

网络阻断可能针对特定的端口,目的是阻断某种特定的应用通讯。对抗这种封锁的一个方法就是采用动态端口的策略。

这里的动态端口是可预测的,预测算法加入了工作量的逻辑,用户需要一定量的计算才能获知目标端口。这种策略迫使攻击者也需要付出同样的代价,如果每一台服务器的动态端口都不一样,那么阻断这种通讯就需要大量的工作量运算。而对于单一节点而言,它们只连接少量的服务器,计算消耗是可以接受的。

算法是通用的,但会加入服务器自身的IP地址作为动态因素。目标端口将在一定的时间段(如1小时)内保持不变,来获得确定性。

算法示意

随机动态端口的算法加入了工作量机制。代码仅为示意,但借助了Go语言的语法高亮友好。

// App配置项:
// 服务器和客户端其值相同,在App发布时设置。
var timeFrom   time.Time = GetConfig("timeFrom")    // 起始计算时间。
var randBase    int64    = GetConfig("randBase")    // 随机数种子。
var diffValue  [32]byte  = GetConfig("diffValue")   // 难度标的值(前置零越多则越难)。

// 参与运算的随机因子
// 这些值在当前App运行期间动态获取。
var IP netip.AddrPort   // 服务器网址(IP+Port)
var hours int64         // 起始时间到当前时间的小时数
var portRnd int64       // 端口随机源

// 随机值确定性
rand.Seed( randBase )
// 随机值边界
const randMax = 1<<63 - 1

// 循环尝试:
// 难度标的值(diffValue)通常会设置在普通单机需运算约1~2秒的程度。
// 工作量在此产生。
for {
    // 以小时为单位
    // 即:1小时内该值不会变,获得一种确定性。
    hours = time.Since(timeFrom) % time.Hour

    // 端口随机源
    portRnd = rand.Intn(randMax)

    // 随机因子串联,计算哈希
    // 注:仅为示意,实际代码需要转换后串联。
    tmp := sha256.Sum256( IP + hours + portRnd )

    // 是否满足目标难度
    if tmp < diffValue {
        break
    }
}
// 目标端口,
// +1024,使不占用系统端口,端口上界 65535
return (portRnd % 64511) + 1024

为避免计算出来的端口与服务器本机上其它端口冲突,可以约定2个备用端口:即目标端口 +1+2。如计算的端口是 1234,则 1235(1234+1)和 1236(1234+2)都为备用端口。