mirror of
https://github.com/ayflying/p2p.git
synced 2026-03-04 17:29:22 +00:00
增加dht的去中心化储存
This commit is contained in:
79
build/anjian/main.go
Normal file
79
build/anjian/main.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 定义Windows API所需的结构体和常量(对应SendInput函数参数)
|
||||||
|
const (
|
||||||
|
INPUT_MOUSE = 0x0000 // 输入类型:鼠标
|
||||||
|
MOUSEEVENTF_LEFTDOWN = 0x0002 // 左键按下
|
||||||
|
MOUSEEVENTF_LEFTUP = 0x0004 // 左键释放
|
||||||
|
)
|
||||||
|
|
||||||
|
// INPUT结构体:SendInput的输入参数
|
||||||
|
type INPUT struct {
|
||||||
|
Type uint32
|
||||||
|
Mi MOUSEINPUT
|
||||||
|
}
|
||||||
|
|
||||||
|
// MOUSEINPUT结构体:鼠标输入详情
|
||||||
|
type MOUSEINPUT struct {
|
||||||
|
Dx int32
|
||||||
|
Dy int32
|
||||||
|
MouseData uint32
|
||||||
|
DwFlags uint32
|
||||||
|
Time uint32
|
||||||
|
DwExtraInfo uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// 加载user32.dll并获取SendInput函数地址
|
||||||
|
user32, err := syscall.LoadLibrary("user32.dll")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer syscall.FreeLibrary(user32)
|
||||||
|
|
||||||
|
sendInputProc, err := syscall.GetProcAddress(user32, "SendInput")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义一次完整点击的输入(按下+释放)
|
||||||
|
inputs := []INPUT{
|
||||||
|
{
|
||||||
|
Type: INPUT_MOUSE,
|
||||||
|
Mi: MOUSEINPUT{
|
||||||
|
DwFlags: MOUSEEVENTF_LEFTDOWN, // 左键按下
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: INPUT_MOUSE,
|
||||||
|
Mi: MOUSEINPUT{
|
||||||
|
DwFlags: MOUSEEVENTF_LEFTUP, // 左键释放
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 循环执行1000次点击,间隔10毫秒
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
// 调用SendInput发送鼠标事件(参数:输入数量、输入数组指针、单个输入大小)
|
||||||
|
var args []uintptr
|
||||||
|
args = append(args, uintptr(len(inputs)))
|
||||||
|
args = append(args, uintptr(unsafe.Pointer(&inputs[0])))
|
||||||
|
args = append(args, uintptr(unsafe.Sizeof(INPUT{})))
|
||||||
|
|
||||||
|
ret, _, _ := syscall.SyscallN(sendInputProc, args...)
|
||||||
|
if ret == 0 {
|
||||||
|
panic("发送鼠标事件失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 间隔10毫秒
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
println("已完成1000次鼠标点击")
|
||||||
|
}
|
||||||
50
build/ico/main.go
Normal file
50
build/ico/main.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/icza/icox" // 替换为icox库
|
||||||
|
"github.com/nfnt/resize"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// 1. 读取PNG文件
|
||||||
|
inputPath := "manifest/images/logo.png" // 输入PNG路径
|
||||||
|
outputPath := "manifest/images/favicon.ico" // 输出ICO路径
|
||||||
|
|
||||||
|
pngFile, err := os.Open(inputPath)
|
||||||
|
if err != nil {
|
||||||
|
panic("无法打开PNG文件: " + err.Error())
|
||||||
|
}
|
||||||
|
defer pngFile.Close()
|
||||||
|
|
||||||
|
// 2. 解码PNG为image.Image对象
|
||||||
|
img, _, err := image.Decode(pngFile)
|
||||||
|
if err != nil {
|
||||||
|
panic("PNG解码失败: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 定义ICO需要包含的尺寸(常见尺寸)
|
||||||
|
sizes := []uint{16, 32, 64, 128} // 可根据需求添加更多尺寸
|
||||||
|
var icoImages []image.Image
|
||||||
|
|
||||||
|
// 4. 缩放图像到每个目标尺寸并收集
|
||||||
|
for _, size := range sizes {
|
||||||
|
// 使用Lanczos3算法缩放(高质量)
|
||||||
|
resized := resize.Resize(size, size, img, resize.Lanczos3)
|
||||||
|
icoImages = append(icoImages, resized)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 编码为ICO并写入文件(关键修改:使用icox.Encode)
|
||||||
|
icoFile, err := os.Create(outputPath)
|
||||||
|
if err != nil {
|
||||||
|
panic("无法创建ICO文件: " + err.Error())
|
||||||
|
}
|
||||||
|
defer icoFile.Close()
|
||||||
|
|
||||||
|
// icox.Encode直接支持多尺寸切片[]image.Image
|
||||||
|
if err := icox.Encode(icoFile, icoImages); err != nil {
|
||||||
|
panic("ICO编码失败: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
2
go.mod
2
go.mod
@@ -3,6 +3,7 @@ module github.com/ayflying/p2p
|
|||||||
go 1.24.0
|
go 1.24.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9
|
||||||
github.com/dop251/goja v0.0.0-20250630131328-58d95d85e994
|
github.com/dop251/goja v0.0.0-20250630131328-58d95d85e994
|
||||||
github.com/getlantern/systray v1.2.2
|
github.com/getlantern/systray v1.2.2
|
||||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.3
|
github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.3
|
||||||
@@ -12,6 +13,7 @@ require (
|
|||||||
github.com/libp2p/go-libp2p v0.43.0
|
github.com/libp2p/go-libp2p v0.43.0
|
||||||
github.com/libp2p/go-libp2p-kad-dht v0.35.1
|
github.com/libp2p/go-libp2p-kad-dht v0.35.1
|
||||||
github.com/multiformats/go-multiaddr v0.16.1
|
github.com/multiformats/go-multiaddr v0.16.1
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -10,6 +10,8 @@ git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGy
|
|||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
|
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9 h1:1ltqoej5GtaWF8jaiA49HwsZD459jqm9YFz9ZtMFpQA=
|
||||||
|
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
|
||||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
@@ -258,6 +260,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
|||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
|
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
|
||||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
|
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
|
||||||
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
|
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
err := Main.AddCommand(&Main, &Debug, &P2p)
|
err := Main.AddCommand(&Main, &Debug, &P2p, &DHT)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.Log().Error(gctx.GetInitCtx(), err)
|
g.Log().Error(gctx.GetInitCtx(), err)
|
||||||
return
|
return
|
||||||
@@ -29,6 +29,7 @@ var (
|
|||||||
Usage: "main",
|
Usage: "main",
|
||||||
Brief: "start http server",
|
Brief: "start http server",
|
||||||
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
||||||
|
g.Log().Debug(ctx, "开始执行main")
|
||||||
|
|
||||||
parser, err = gcmd.Parse(g.MapStrBool{
|
parser, err = gcmd.Parse(g.MapStrBool{
|
||||||
"w,ws": true,
|
"w,ws": true,
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ var (
|
|||||||
Usage: "debug",
|
Usage: "debug",
|
||||||
Brief: "调试接口",
|
Brief: "调试接口",
|
||||||
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
||||||
|
g.Log().Debug(ctx, "开始执行debug")
|
||||||
|
|
||||||
g.Log().SetConfigWithMap(g.Map{
|
g.Log().SetConfigWithMap(g.Map{
|
||||||
"level": "all",
|
"level": "all",
|
||||||
"stdout": true,
|
"stdout": true,
|
||||||
|
|||||||
54
internal/cmd/dht.go
Normal file
54
internal/cmd/dht.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ayflying/p2p/internal/service"
|
||||||
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
"github.com/gogf/gf/v2/os/gcmd"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// DHT 命令定义了P2P连接工具的入口命令
|
||||||
|
// 遵循GoFrame的Command对象定义规范,包含名称、用法、简短描述和执行函数
|
||||||
|
DHT = gcmd.Command{
|
||||||
|
// Name 为命令名称
|
||||||
|
Name: "dht",
|
||||||
|
// Usage 描述命令的基本用法
|
||||||
|
Usage: "dht [options]",
|
||||||
|
// Brief 提供命令的简短功能描述
|
||||||
|
Brief: "P2P连接工具,支持网关和客户端模式,实现NAT穿透和点对点通信",
|
||||||
|
// Description 提供命令的详细描述和使用帮助
|
||||||
|
Description: p2pHelpDescription,
|
||||||
|
// Func 为命令的执行函数,接收上下文和参数解析器
|
||||||
|
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
||||||
|
g.Log().Debug(ctx, "开始执行dht")
|
||||||
|
|
||||||
|
parser, err = gcmd.Parse(g.MapStrBool{
|
||||||
|
"p,port": true,
|
||||||
|
})
|
||||||
|
port := parser.GetOpt("port", "0").Int()
|
||||||
|
|
||||||
|
h, _ := service.P2P().CreateLibp2pHost(ctx, port)
|
||||||
|
err = service.P2P().DHTStart(ctx, h, nil)
|
||||||
|
if err != nil {
|
||||||
|
g.Log().Error(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
publicIp, _ := service.P2P().GetIPv4PublicIP()
|
||||||
|
validKey := fmt.Sprintf("%v/ip", h.ID())
|
||||||
|
dataValue := fmt.Sprintf("来自节点 %s 的数据:%v", h.ID().ShortString(), publicIp)
|
||||||
|
if err = service.P2P().StoreToDHT(ctx, validKey, dataValue); err != nil {
|
||||||
|
fmt.Printf("❌ 存储失败: %v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("✅ 存储成功\nKey: %s\nValue: %s\n", validKey, dataValue)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
s.Run()
|
||||||
|
return
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -2,6 +2,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/ayflying/p2p/internal/service"
|
"github.com/ayflying/p2p/internal/service"
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
@@ -40,6 +41,8 @@ var (
|
|||||||
Description: p2pHelpDescription,
|
Description: p2pHelpDescription,
|
||||||
// Func 为命令的执行函数,接收上下文和参数解析器
|
// Func 为命令的执行函数,接收上下文和参数解析器
|
||||||
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
|
||||||
|
g.Log().Debug(ctx, "开始执行p2p")
|
||||||
|
|
||||||
s := g.Server()
|
s := g.Server()
|
||||||
|
|
||||||
// 配置日志输出
|
// 配置日志输出
|
||||||
@@ -76,23 +79,50 @@ var (
|
|||||||
wsStr := "ws://192.168.50.173:51888/ws"
|
wsStr := "ws://192.168.50.173:51888/ws"
|
||||||
err = service.P2P().Start(ctx, wsStr)
|
err = service.P2P().Start(ctx, wsStr)
|
||||||
case "dht":
|
case "dht":
|
||||||
|
g.Log().Debug(ctx, "开始执行dht")
|
||||||
h, _ := service.P2P().CreateLibp2pHost(ctx, 0)
|
h, _ := service.P2P().CreateLibp2pHost(ctx, 0)
|
||||||
err := service.P2P().DHTStart(ctx, h)
|
|
||||||
|
err := service.P2P().DHTStart(ctx, h, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.Log().Error(ctx, err)
|
g.Log().Error(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
publicIp, err := service.P2P().GetIPv4PublicIP()
|
publicIp, err := service.P2P().GetIPv4PublicIP()
|
||||||
err = service.P2P().StoreAddrToDHT(ctx, "ip", publicIp)
|
validKey := "ip"
|
||||||
|
dataValue := fmt.Sprintf("来自节点 %s 的数据:%v", h.ID().ShortString(), publicIp)
|
||||||
|
if err := service.P2P().StoreToDHT(ctx, validKey, dataValue); err != nil {
|
||||||
|
fmt.Printf("❌ 存储失败: %v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("✅ 存储成功\nKey: %s\nValue: %s\n", validKey, dataValue)
|
||||||
|
}
|
||||||
|
|
||||||
case "dht2":
|
case "dht2":
|
||||||
|
g.Log().Debug(ctx, "开始执行dht2")
|
||||||
h, _ := service.P2P().CreateLibp2pHost(ctx, 0)
|
h, _ := service.P2P().CreateLibp2pHost(ctx, 0)
|
||||||
err := service.P2P().DHTStart(ctx, h)
|
|
||||||
|
addr := []string{
|
||||||
|
//"/ip4/192.168.50.173/tcp/23333/p2p/12D3KooWQsb1137nCzqbMMCzwHsyU8aaCZeFnBUBTkWVsfp8gs26",
|
||||||
|
//"/ip4/192.168.50.173/udp/23333/quic-v1/p2p/12D3KooWQsb1137nCzqbMMCzwHsyU8aaCZeFnBUBTkWVsfp8gs26",
|
||||||
|
//"/ip4/114.132.176.115/tcp/23333/p2p/12D3KooWJQMiYyptqSrx4PPsGLY9hjLbaDdxmBXmGtKmSWuiP79D",
|
||||||
|
//"/ip4/114.132.176.115/udp/23333/quic-v1/p2p/12D3KooWJQMiYyptqSrx4PPsGLY9hjLbaDdxmBXmGtKmSWuiP79D",
|
||||||
|
}
|
||||||
|
|
||||||
|
id := gcmd.GetOpt("id").String()
|
||||||
|
err := service.P2P().DHTStart(ctx, h, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.Log().Error(ctx, err)
|
g.Log().Error(ctx, err)
|
||||||
}
|
}
|
||||||
get, _ := service.P2P().FindAddrFromDHT(ctx, "ip")
|
validKey := id
|
||||||
g.Dump(get)
|
go func() {
|
||||||
|
// 5. 查找数据(从网络中的节点获取,不依赖初始 Bootstrap 节点)
|
||||||
|
foundValue, err := service.P2P().FindFromDHT(ctx, validKey)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("❌ 查找失败: %v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("✅ 查找成功\nValue: %s\n", foundValue)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
s.SetPort(0)
|
s.SetPort(0)
|
||||||
default:
|
default:
|
||||||
// 显示帮助信息
|
// 显示帮助信息
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/libp2p/go-libp2p"
|
"github.com/libp2p/go-libp2p"
|
||||||
"github.com/libp2p/go-libp2p/core/crypto"
|
|
||||||
"github.com/libp2p/go-libp2p/core/host"
|
"github.com/libp2p/go-libp2p/core/host"
|
||||||
"github.com/libp2p/go-libp2p/core/network"
|
"github.com/libp2p/go-libp2p/core/network"
|
||||||
"github.com/libp2p/go-libp2p/core/peer"
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
@@ -123,7 +122,8 @@ func (s *sP2P) CreateLibp2pHost(ctx context.Context, port int) (host.Host, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 1. 生成密钥对并初始化节点(确保身份有效)
|
// 1. 生成密钥对并初始化节点(确保身份有效)
|
||||||
s.privKey, _, _ = crypto.GenerateKeyPair(crypto.Ed25519, 0) // 推荐使用Ed25519
|
//s.privKey, _, _ = crypto.GenerateKeyPair(crypto.Ed25519, 0) // 推荐使用Ed25519
|
||||||
|
s.privKey, _ = s.generateFixedKey()
|
||||||
|
|
||||||
// 创建主机
|
// 创建主机
|
||||||
h, err := libp2p.New(
|
h, err := libp2p.New(
|
||||||
|
|||||||
@@ -3,102 +3,58 @@ package p2p
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gogf/gf/v2/crypto/gsha1"
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
|
|
||||||
//"github.com/ipfs/boxo/ipns"
|
//"github.com/ipfs/boxo/ipns"
|
||||||
dht "github.com/libp2p/go-libp2p-kad-dht"
|
dht "github.com/libp2p/go-libp2p-kad-dht"
|
||||||
"github.com/libp2p/go-libp2p/core/host"
|
"github.com/libp2p/go-libp2p/core/host"
|
||||||
"github.com/libp2p/go-libp2p/core/peer"
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
"github.com/libp2p/go-libp2p/core/peerstore"
|
"github.com/libp2p/go-libp2p/core/peerstore"
|
||||||
|
"github.com/multiformats/go-multiaddr"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DHTType struct {
|
type DHTType struct {
|
||||||
KadDHT *dht.IpfsDHT
|
KadDHT *dht.IpfsDHT
|
||||||
|
bootstrapPeers []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
//bootstrapPeers = []string{
|
||||||
|
// "/ip4/192.168.50.173/tcp/53486/p2p/12D3KooWE3v9623SLukT9dKUQLjqAJrPvzoyRjoUh5MAVGDg69Rw",
|
||||||
|
// "/ip4/192.168.50.173/udp/53486/quic-v1/p2p/12D3KooWE3v9623SLukT9dKUQLjqAJrPvzoyRjoUh5MAVGDg69Rw",
|
||||||
|
//}
|
||||||
|
)
|
||||||
|
|
||||||
// 初始化无服务器DHT(作为节点加入DHT网络)
|
// 初始化无服务器DHT(作为节点加入DHT网络)
|
||||||
func (s *sP2P) DHTStart(ctx context.Context, h host.Host) (err error) {
|
func (s *sP2P) DHTStart(ctx context.Context, h host.Host, bootstrapPeers []string) (err error) {
|
||||||
// 创建自定义 DHT 选项,配置验证器
|
//打印节点地址(供其他节点手动加入时使用)
|
||||||
dhtOpts := []dht.Option{
|
s.printNodeAddrs(h)
|
||||||
//设置为“客户端+服务端模式”(既可以查找数据,也可以存储数据)
|
s.dht.bootstrapPeers = bootstrapPeers
|
||||||
dht.Mode(dht.ModeServer),
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建DHT实例,
|
// 2. 通过官方 Bootstrap 节点加入公共 DHT 网络(完全去中心化入口)
|
||||||
s.dht.KadDHT, err = dht.New(
|
s.dht.KadDHT, err = s.joinGlobalDHT(ctx, h)
|
||||||
ctx,
|
|
||||||
h,
|
|
||||||
dhtOpts...,
|
|
||||||
//dht.Mode(dht.ModeServer),
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("初始化DHT失败: %v", err)
|
log.Fatalf("加入 DHT 网络失败: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
fmt.Println("✅ 成功加入完全去中心化 DHT 网络")
|
||||||
|
|
||||||
// 关键:直接替换 DHT 实例的验证器
|
// 3. 定期打印路由表(观察节点自动发现效果)
|
||||||
// v0.35.1 版本中,IpfsDHT 结构体的 Validator 字段是公开可修改的
|
go s.printRoutingTable(s.dht.KadDHT, 60*time.Second)
|
||||||
s.dht.KadDHT.Validator = &NoOpValidator{}
|
|
||||||
|
|
||||||
// 连接到DHT bootstrap节点(种子节点,帮助加入网络)
|
|
||||||
// 这里使用libp2p官方的公共bootstrap节点,生产环境可替换为自己的节点
|
|
||||||
bootstrapPeers := dht.DefaultBootstrapPeers
|
|
||||||
for _, addr := range bootstrapPeers {
|
|
||||||
peerInfo, _ := peer.AddrInfoFromP2pAddr(addr)
|
|
||||||
h.Peerstore().AddAddrs(peerInfo.ID, peerInfo.Addrs, peerstore.PermanentAddrTTL)
|
|
||||||
if err = h.Connect(ctx, *peerInfo); err != nil {
|
|
||||||
fmt.Printf("连接bootstrap节点 %s 失败: %v\n", peerInfo.ID, err)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("已连接bootstrap节点: %s\n", peerInfo.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 启动DHT
|
|
||||||
if err = s.dht.KadDHT.Bootstrap(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 存储数据到DHT(比如存储“目标节点ID-公网地址”的映射)
|
|
||||||
func (s *sP2P) StoreAddrToDHT(ctx context.Context, key string, addr string) (err error) {
|
|
||||||
// Key:目标节点ID(作为哈希键),Value:公网地址(需转成二进制)
|
|
||||||
//key = "/ipns/" + key
|
|
||||||
//key = s.generateStringDHTKey(key)
|
|
||||||
key = fmt.Sprintf("%s/%s", ProtocolID, key)
|
|
||||||
value := []byte(addr)
|
|
||||||
|
|
||||||
// 存储数据(DHT会自动找到负责存储该Key的节点,并同步数据)
|
|
||||||
if err = s.dht.KadDHT.PutValue(ctx, key, value); err != nil {
|
|
||||||
return fmt.Errorf("key=%s,存储地址到DHT失败: %v", key, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
g.Log().Info(ctx, "成功存储地址到DHT,Key=%s, Value=%s", key, addr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从DHT查找数据(比如根据节点ID查找其公网地址)
|
|
||||||
func (s *sP2P) FindAddrFromDHT(ctx context.Context, key string) (string, error) {
|
|
||||||
// 查找数据(DHT会通过路由表层层跳转,找到负责存储该Key的节点并获取数据)
|
|
||||||
//key = s.generateStringDHTKey(key)
|
|
||||||
//key = "/ipns/" + key
|
|
||||||
key = fmt.Sprintf("%s/%s", ProtocolID, key)
|
|
||||||
g.Log().Debugf(ctx, "从DHT查找地址中...,Key=%s", key)
|
|
||||||
value, err := s.dht.KadDHT.GetValue(ctx, key)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("从DHT查找地址失败: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
addr := string(value)
|
|
||||||
fmt.Printf("从DHT找到地址,Key=%s, Value=%s\n", key, addr)
|
|
||||||
return addr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成符合DHT规范的字符串Key
|
// 生成符合DHT规范的字符串Key
|
||||||
func (s *sP2P) generateStringDHTKey(str string) string {
|
func (s *sP2P) generateStringDHTKey(str string) string {
|
||||||
return ""
|
return gsha1.Encrypt(str)
|
||||||
|
//fullKey := fmt.Sprintf("%s/%s", ProtocolID, str)
|
||||||
|
//hash, _ := multihash.Sum([]byte(fullKey), multihash.SHA2_256, -1)
|
||||||
|
//return ipns.key
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自定义验证器:不做任何校验,接受所有数据
|
// 自定义验证器:不做任何校验,接受所有数据
|
||||||
@@ -113,3 +69,192 @@ func (v *NoOpValidator) Validate(key string, value []byte) error {
|
|||||||
func (v *NoOpValidator) Select(key string, values [][]byte) (int, error) {
|
func (v *NoOpValidator) Select(key string, values [][]byte) (int, error) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加入全球公共 DHT 网络(通过官方 Bootstrap 节点,实现完全去中心化)
|
||||||
|
func (s *sP2P) joinGlobalDHT(ctx context.Context, localHost host.Host) (*dht.IpfsDHT, error) {
|
||||||
|
// 创建 DHT 实例(ModeServer:作为完整节点参与存储和路由)
|
||||||
|
kadDHT, err := dht.New(ctx, localHost, dht.Mode(dht.ModeServer))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
kadDHT.Validator = &NoOpValidator{}
|
||||||
|
success := false
|
||||||
|
|
||||||
|
if len(s.dht.bootstrapPeers) > 0 {
|
||||||
|
fmt.Println("正在连接本地种子节点...")
|
||||||
|
seedPeers, _ := s.parseSeedNodes(s.dht.bootstrapPeers)
|
||||||
|
for _, p := range seedPeers {
|
||||||
|
localHost.Peerstore().AddAddrs(p.ID, p.Addrs, peerstore.PermanentAddrTTL)
|
||||||
|
if err = localHost.Connect(ctx, p); err != nil {
|
||||||
|
fmt.Printf("⚠️ 连接本地种子节点 %s 失败: %v\n", p.ID.ShortString(), err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("✅ 连接本地种子节点成功: %s\n", p.ID.ShortString())
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("⚠️ 连接私有节点 %s 失败: %v\n", p.ID.ShortString(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Printf("✅ 连接本地种子节点成功: %s\n", p.ID.ShortString())
|
||||||
|
success = true
|
||||||
|
}
|
||||||
|
if !success {
|
||||||
|
return nil, fmt.Errorf("所有本地种子节点连接失败")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// 连接 libp2p 官方 Bootstrap 节点(仅作为初始入口)
|
||||||
|
officialBootstrapPeers := dht.DefaultBootstrapPeers // 官方节点列表
|
||||||
|
fmt.Println("正在连接官方 Bootstrap 节点(初始入口)...")
|
||||||
|
|
||||||
|
for _, addr := range officialBootstrapPeers {
|
||||||
|
peerInfo, err := peer.AddrInfoFromP2pAddr(addr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("⚠️ 解析官方节点失败: %v\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加节点地址到本地地址簿
|
||||||
|
localHost.Peerstore().AddAddrs(peerInfo.ID, peerInfo.Addrs, peerstore.PermanentAddrTTL)
|
||||||
|
|
||||||
|
// 尝试连接(超时 10 秒)
|
||||||
|
connCtx, connCancel := context.WithTimeout(ctx, 10*time.Second)
|
||||||
|
err = localHost.Connect(connCtx, *peerInfo)
|
||||||
|
connCancel()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("⚠️ 连接官方节点 %s 失败: %v\n", peerInfo.ID.ShortString(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Printf("✅ 连接官方节点成功: %s\n", peerInfo.ID.ShortString())
|
||||||
|
success = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只要连接上至少一个官方节点,即可加入网络(后续会自动发现更多节点)
|
||||||
|
if !success {
|
||||||
|
return nil, fmt.Errorf("无法连接任何官方 Bootstrap 节点,无法加入网络")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动 DHT(自动发现其他节点,构建路由表,脱离对官方节点的依赖)
|
||||||
|
if err = kadDHT.Bootstrap(ctx); err != nil {
|
||||||
|
return nil, fmt.Errorf("DHT 初始化失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待路由表初步填充(新增:给路由表留出初始化时间)
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
return kadDHT, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreToDHT 存储数据到 DHT(自动分布式存储)
|
||||||
|
func (s *sP2P) StoreToDHT(ctx context.Context, key string, value string) (err error) {
|
||||||
|
key = s.generateStringDHTKey(key)
|
||||||
|
key = fmt.Sprintf("%s/%s", ProtocolID, key)
|
||||||
|
g.Log().Debugf(ctx, "StoreToDHT key: %s, value: %s", key, value)
|
||||||
|
|
||||||
|
// 存储到本地
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, 60*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
if err = s.dht.KadDHT.PutValue(ctx, key, []byte(value)); err != nil {
|
||||||
|
return fmt.Errorf("本地存储失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindFromDHT 从 DHT 查找数据(从网络节点获取)
|
||||||
|
func (s *sP2P) FindFromDHT(ctx context.Context, key string) (string, error) {
|
||||||
|
maxRetries := 10 // 最多重试5次
|
||||||
|
retryInterval := 60 * time.Second // 每次重试间隔2秒(本地网络快)
|
||||||
|
|
||||||
|
key = s.generateStringDHTKey(key)
|
||||||
|
key = fmt.Sprintf("%s/%s", ProtocolID, key)
|
||||||
|
g.Log().Debugf(ctx, "FindFromDHT key: %s", key)
|
||||||
|
|
||||||
|
// 1. 先检查本地是否存储了数据(本地节点可能已保存)
|
||||||
|
localValue, err := s.dht.KadDHT.GetValue(ctx, key)
|
||||||
|
if err == nil {
|
||||||
|
g.Log().Debugf(ctx, "✅ 本地查找成功(数据在当前节点)")
|
||||||
|
return string(localValue), nil
|
||||||
|
}
|
||||||
|
g.Log().Debugf(ctx, "⚠️ 本地查找失败: %v,开始重试网络查找...", err)
|
||||||
|
|
||||||
|
// 2. 多次重试网络查找
|
||||||
|
for i := 0; i < maxRetries; i++ {
|
||||||
|
ctx2, cancel := context.WithTimeout(ctx, 120*time.Second) // 本地测试超时短一些
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
g.Log().Debugf(ctx2, "🔍 第%d次查找(共%d次)...", i+1, maxRetries)
|
||||||
|
value, err := s.dht.KadDHT.GetValue(ctx2, key)
|
||||||
|
if err == nil {
|
||||||
|
g.Log().Debugf(ctx2, "✅ 第%d次查找成功", i+1)
|
||||||
|
return string(value), nil
|
||||||
|
}
|
||||||
|
g.Log().Debugf(ctx2, "⚠️ 第%d次查找失败: %v,等待重试...", i+1, err)
|
||||||
|
time.Sleep(retryInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("超过最大重试次数(%d次),未找到数据", maxRetries)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定期打印路由表(观察节点自动发现情况)
|
||||||
|
func (s *sP2P) printRoutingTable(kadDHT *dht.IpfsDHT, interval time.Duration) {
|
||||||
|
ticker := time.NewTicker(interval)
|
||||||
|
defer ticker.Stop()
|
||||||
|
for {
|
||||||
|
<-ticker.C
|
||||||
|
peers := kadDHT.RoutingTable().ListPeers()
|
||||||
|
fmt.Printf("\n📊 当前路由表节点数: %d(完全去中心化网络节点)\n", len(peers))
|
||||||
|
if len(peers) > 0 {
|
||||||
|
fmt.Println("前 5 个节点 ID:")
|
||||||
|
for i, p := range peers[:min(5, len(peers))] {
|
||||||
|
fmt.Printf(" %d. %s\n", i+1, p.ShortString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//// 定期打印节点状态(公网地址+路由表)
|
||||||
|
//func (s *sP2P) printStatus(interval time.Duration) {
|
||||||
|
// ticker := time.NewTicker(interval)
|
||||||
|
// for {
|
||||||
|
// <-ticker.C
|
||||||
|
// //publicIp, err := service.P2P().GetIPv4PublicIP()
|
||||||
|
// //publicAddrs := s.getPublicAddrs()
|
||||||
|
// peers := s.dht.KadDHT.RoutingTable().ListPeers()
|
||||||
|
// fmt.Printf("\n===== 节点状态 =====")
|
||||||
|
// fmt.Printf("\n公网地址数: %d(0表示穿透失败)\n", len(publicAddrs))
|
||||||
|
// fmt.Printf("路由表节点数: %d(自动扩散结果)\n", len(peers))
|
||||||
|
// fmt.Println("====================")
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
// 打印节点地址(供其他节点手动加入时使用)
|
||||||
|
func (s *sP2P) printNodeAddrs(host host.Host) {
|
||||||
|
fmt.Println("节点地址(公网地址将自动同步到DHT):")
|
||||||
|
for _, addr := range host.Addrs() {
|
||||||
|
fullAddr := fmt.Sprintf("%s/p2p/%s", addr, host.ID())
|
||||||
|
ipStr, _ := addr.ValueForProtocol(multiaddr.P_IP4)
|
||||||
|
ip := net.ParseIP(ipStr)
|
||||||
|
if ip.IsPrivate() || ip.IsLoopback() {
|
||||||
|
fmt.Printf(" [内网] %s\n", fullAddr)
|
||||||
|
} else {
|
||||||
|
fmt.Printf(" [公网] %s\n", fullAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sP2P) parseSeedNodes(seedAddrs []string) ([]peer.AddrInfo, error) {
|
||||||
|
peers := make([]peer.AddrInfo, 0, len(seedAddrs))
|
||||||
|
for _, addrStr := range seedAddrs {
|
||||||
|
addr, err := multiaddr.NewMultiaddr(addrStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
peerInfo, err := peer.AddrInfoFromP2pAddr(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
peers = append(peers, *peerInfo)
|
||||||
|
}
|
||||||
|
return peers, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package p2p
|
package p2p
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -11,16 +14,19 @@ import (
|
|||||||
"github.com/ayflying/p2p/internal/service"
|
"github.com/ayflying/p2p/internal/service"
|
||||||
"github.com/gogf/gf/v2/frame/g"
|
"github.com/gogf/gf/v2/frame/g"
|
||||||
"github.com/gogf/gf/v2/os/gctx"
|
"github.com/gogf/gf/v2/os/gctx"
|
||||||
|
"github.com/gogf/gf/v2/os/gfile"
|
||||||
|
"github.com/gogf/gf/v2/util/grand"
|
||||||
"github.com/libp2p/go-libp2p/core/crypto"
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ctx = gctx.New()
|
ctx = gctx.New()
|
||||||
|
ip string
|
||||||
)
|
)
|
||||||
|
|
||||||
// 常量定义
|
// 常量定义
|
||||||
const (
|
const (
|
||||||
ProtocolID string = "/ay/1.0.0"
|
ProtocolID string = "/ay"
|
||||||
DefaultPort = 51888
|
DefaultPort = 51888
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -60,6 +66,7 @@ type sP2P struct {
|
|||||||
|
|
||||||
// New 创建一个新的 P2P 服务实例
|
// New 创建一个新的 P2P 服务实例
|
||||||
func New() *sP2P {
|
func New() *sP2P {
|
||||||
|
|
||||||
return &sP2P{
|
return &sP2P{
|
||||||
Clients: make(map[string]*ClientConn),
|
Clients: make(map[string]*ClientConn),
|
||||||
client: &Client{},
|
client: &Client{},
|
||||||
@@ -69,6 +76,7 @@ func New() *sP2P {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
service.RegisterP2P(New())
|
service.RegisterP2P(New())
|
||||||
|
ip, _ = service.P2P().GetIPv4PublicIP()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取公网IP并判断类型(ipv4/ipv6)
|
// 获取公网IP并判断类型(ipv4/ipv6)
|
||||||
@@ -177,3 +185,49 @@ func (s *sP2P) removeDuplicates(strs []string) []string {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 生成固定密钥(核心:通过固定种子生成相同密钥)
|
||||||
|
func (s *sP2P) generateFixedKey() (crypto.PrivKey, error) {
|
||||||
|
privKeyPath := "runtime/p2p.key"
|
||||||
|
if ok := gfile.Exists(privKeyPath); ok {
|
||||||
|
// 从文件读取密钥
|
||||||
|
keyBytes := gfile.GetBytes(privKeyPath)
|
||||||
|
// 2. 解析PEM块(关键:提取真正的私钥数据)
|
||||||
|
pemBlock, _ := pem.Decode(keyBytes)
|
||||||
|
if pemBlock == nil {
|
||||||
|
return nil, fmt.Errorf("私钥文件格式错误(非PEM格式)")
|
||||||
|
}
|
||||||
|
privKey, err := crypto.UnmarshalPrivateKey(pemBlock.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return privKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 固定种子(修改此种子可生成不同的固定密钥)
|
||||||
|
var fixedSeed = []byte(grand.S(10)) // 自定义固定种子
|
||||||
|
|
||||||
|
// 用固定种子初始化随机数生成器
|
||||||
|
seed := binary.BigEndian.Uint64(fixedSeed[:8]) // 取种子前8字节作为随机数种子
|
||||||
|
r := rand.New(rand.NewSource(int64(seed)))
|
||||||
|
|
||||||
|
// 生成ED25519密钥对(基于固定种子,每次生成结果相同)
|
||||||
|
privKey, _, err := crypto.GenerateEd25519Key(r)
|
||||||
|
keyBytes, err := crypto.MarshalPrivateKey(privKey)
|
||||||
|
// 用PEM格式包装(标准格式,便于存储和解析)
|
||||||
|
pemBlock := &pem.Block{
|
||||||
|
Type: "LIBP2P PRIVATE KEY", // 标识为libp2p私钥
|
||||||
|
Bytes: keyBytes,
|
||||||
|
}
|
||||||
|
err = gfile.PutBytes(privKeyPath, pem.EncodeToMemory(pemBlock))
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("保存私钥失败: %v", err))
|
||||||
|
}
|
||||||
|
//if err := os.WriteFile(privKeyPath, pem.EncodeToMemory(pemBlock), 0600); err != nil {
|
||||||
|
// panic(fmt.Sprintf("保存私钥失败: %v", err))
|
||||||
|
//}
|
||||||
|
|
||||||
|
fmt.Println("私钥生成成功,文件路径:", privKeyPath)
|
||||||
|
|
||||||
|
return privKey, err
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ type (
|
|||||||
// 发现并连接目标节点
|
// 发现并连接目标节点
|
||||||
DiscoverAndConnect(targetID string) error
|
DiscoverAndConnect(targetID string) error
|
||||||
// 初始化无服务器DHT(作为节点加入DHT网络)
|
// 初始化无服务器DHT(作为节点加入DHT网络)
|
||||||
DHTStart(ctx context.Context, h host.Host) (err error)
|
DHTStart(ctx context.Context, h host.Host, bootstrapPeers []string) (err error)
|
||||||
// 存储数据到DHT(比如存储“目标节点ID-公网地址”的映射)
|
// StoreToDHT 存储数据到 DHT(自动分布式存储)
|
||||||
StoreAddrToDHT(ctx context.Context, key string, addr string) (err error)
|
StoreToDHT(ctx context.Context, key string, value string) (err error)
|
||||||
// 从DHT查找数据(比如根据节点ID查找其公网地址)
|
// FindFromDHT 从 DHT 查找数据(从网络节点获取)
|
||||||
FindAddrFromDHT(ctx context.Context, key string) (string, error)
|
FindFromDHT(ctx context.Context, key string) (string, error)
|
||||||
GatewayStart(ctx context.Context, group *ghttp.RouterGroup) (err error)
|
GatewayStart(ctx context.Context, group *ghttp.RouterGroup) (err error)
|
||||||
// 只获取IPv4公网IP(过滤IPv6结果)
|
// 只获取IPv4公网IP(过滤IPv6结果)
|
||||||
GetIPv4PublicIP() (string, error)
|
GetIPv4PublicIP() (string, error)
|
||||||
|
|||||||
5
main.go
5
main.go
@@ -5,6 +5,7 @@ import (
|
|||||||
_ "github.com/ayflying/p2p/internal/logic"
|
_ "github.com/ayflying/p2p/internal/logic"
|
||||||
_ "github.com/ayflying/p2p/internal/packed"
|
_ "github.com/ayflying/p2p/internal/packed"
|
||||||
"github.com/ayflying/p2p/internal/service"
|
"github.com/ayflying/p2p/internal/service"
|
||||||
|
"github.com/gogf/gf/v2/os/gfile"
|
||||||
|
|
||||||
//步骤1:加载驱动
|
//步骤1:加载驱动
|
||||||
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
|
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
|
||||||
@@ -21,5 +22,9 @@ func main() {
|
|||||||
// 启动系统托盘
|
// 启动系统托盘
|
||||||
service.OS().Load(consts.Name, consts.Name+"服务端", "manifest/images/favicon.ico")
|
service.OS().Load(consts.Name, consts.Name+"服务端", "manifest/images/favicon.ico")
|
||||||
|
|
||||||
|
if ok := gfile.Exists("runtime"); !ok {
|
||||||
|
gfile.Mkdir("runtime")
|
||||||
|
}
|
||||||
|
|
||||||
cmd.Main.Run(ctx)
|
cmd.Main.Run(ctx)
|
||||||
}
|
}
|
||||||
|
|||||||
4
runtime/p2p.key
Normal file
4
runtime/p2p.key
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
-----BEGIN LIBP2P PRIVATE KEY-----
|
||||||
|
CAESQMGVYKvJb7HdTGRqhTfTeM74bkNZKu8AjJSAFD8o/BwwkpE5mddDYxGo3MID
|
||||||
|
fCS7r+XqcqegalHv4xZKKk1kdIs=
|
||||||
|
-----END LIBP2P PRIVATE KEY-----
|
||||||
Reference in New Issue
Block a user