# Osiris **Repository Path**: hilex/osiris ## Basic Information - **Project Name**: Osiris - **Description**: blockchain node backend developed by go - **Primary Language**: Go - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-09-07 - **Last Updated**: 2023-12-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: P2P, Blockchain ## README ## 概述 #### 仓库地址 https://gitee.com/hilex/osiris #### 环境准备 - windows 或 Linux均可 - VSCode 或 goLand均可 - go 1.21.0 - MYSQL(root:123456@tcp(127.0.0.1:3306)) - 安装扩展(VSCode):Code Runner、GOComment、GO Doc、Todo Tree(可选)、VS Code Counter(可选) #### 代码文档 文档是基于GOComment根据注释生成的,注释遵循Go Doc规范;文档没有固定的地址 查看文档: - 确保你的go的bin目录在环境变量里(一般是$GOROOT$/bin) - 安装godoc命令: ``` go get -u golang.org/x/tools/cmd/godoc go install golang.org/x/tools/cmd/godoc ``` - 进入项目根目录,执行命令 ``` godoc -http=:6060 ``` - 打开浏览器,输入http://localhost:6060/pkg/osiris/查看文档(可能要等几秒刷新) #### 和ETH设计上的区别 - 所有模型的通信都没加密,只有hash验证和签名验证 - 广播目前只支持flooding模式(通过DTO Hash缓存校验进行广播刹车),后续会增加gossip模式(适合小范围共识,比如DPOS委员会内部的共识) - 没有基于内存映射的存储优化 - 同一个节点同一条链上只有本地和远端两个并发的交易池,ETH有多个子交易池同时并发 - 暂时没有智能合约 - GAS机制受制于智能合约,因此还不够完善 - 暂时没有收据树、bloomfilter等查询优化 - POS:增加币龄机制 - POS:通过引入节点状态树代替智能合约进行代币质押,在每一轮共识时通过节点状态树选出权重最大的节点作为下一轮共识的记账节点 - 可以出现分叉,交易、区块验证都有完善的回滚机制,但没有考虑分叉后主动合入的情况(既在单个节点的视角里始终只有一条链) ## 运行&测试 #### 节点启动 在项目根目录执行命令: ``` go run main.go -p [httpServerPort] -l [p2pListenPort] -k [privKeyStr] -bs ``` **可选参数**: - -p: http服务端口,默认8888 - -l: P2P节点监听端口,默认10000 - -k: 用于指代p2p节点身份的ed25519私钥,默认从读privKey.txt读,文件不存在会随机生成一个然后写进privKey.txt - -bs: 是否作为引导节点启动,默认false,若为true,其它节点可以从此节点发起bootstrap请求来获取此节点已知的所有节点的peer.ID和其对应的[ ]ma.Multiaddr 注意:同一台机子上终端多开时注意端口不能重复,http服务端口和 P2P节点监听端口不能一样 节点启动后你需注意的命令行信息: - **Node Full Multiaddr**:节点完整的通信地址 长这样:/ip4/127.0.0.1/tcp/10000/p2p/12D3KooWC3CoFkgusyw2PsH1mr5NZvdsRQmqcUwAV1naKYhDuwrT 每个路径代表的意思: 1. ipv4 / ipv6 2. IP 3. tcp / udp 4. P2P通信的监听端口(由-l参数指定) 5. p2p 6. peer.ID (节点的ID,由ed25519私钥转换而来,私钥由-k参数指定) - **Node Code Name**: 节点的代号 长这样:fuvgingxsgimgcuzxt(一串长度不定的小写字母,对私钥做短hash取前32个字节做base64编码) 用于: 1. 该节点对应的MYSQL DB的名字 2. 该节点对应的日志文件夹的名字 #### 二级命令 ###### ping(握手) ``` ping [Full Multiaddr] ``` 执行命令后会发生: - 与Full Multiaddr对应的节点交换ed25519公钥 - 交换完整的通信地址([ ]ma.Multiaddr) - 交换完成后节点会将对方的公钥、地址存入MYSQL,下次启动时会根据MYSQL加载之前ping过的节点的公钥和地址 例子: ``` ping /ip4/127.0.0.1/tcp/10000/p2p/12D3KooWC3CoFkgusyw2PsH1mr5NZvdsRQmqcUwAV1naKYhDuwrT ``` ###### bootstrap(节点引导) ``` bootstrap [peer.ID] ``` 执行命令后会发生: - peer.ID对应的节点向本节点发送所有已知节点的公钥和通信地址 - 本节点筛选出其中所有未知的节点,并与之发起握手协议(ping) 注意: - peer.ID必须是一个已知的节点(之前ping过) - peer.ID必须是一个引导节点(启动时带-bs参数) ###### pos(共识) ``` pos start ``` 执行命令后会发生: - 本节点变为创世节点,在一个时隙(17s,可改)后开始产生第一个区块并广播 ###### autotx(自动交易) ``` autotx start ``` 执行命令后会发生: - 每隔7s随机生成一个交易提交至交易池,并广播该交易 一些特性: - 随机的来源地址(只会在本地账户地址中随机)、随机的目标地址(全账户地址) - 随机的交易类型(按一定的概率分布,通常pledge > deposit > transform > register) - 随机的金额(在账户余额范围内均匀分布) - 没有本地账户地址会优先执行register交易 - 若随机选到的账户地址没有任何余额,会优先执行deposit交易 ``` auotx stop ``` 执行命令后会发生: - 停止自动交易 ###### rm(状态清除) ``` rm state ``` 执行命令后会发生: - 清空账户状态树、节点状态树 - 清空所有交易池 - 清空MYSQL中的:区块链、账户状态、交易记录 建议在自动交易关闭以及共识没有开始的情况下使用,要不然可能清不干净 ###### peers(节点信息) ``` peers ``` 执行命令后会发生: - 显示所有已知的节点的peer.ID ``` peers addr ``` 执行命令后会发生: - 显示所有已知节点的通信地址(Full Multiaddr) ``` peers pubkey ``` 执行命令后会发生: - 显示所有已知节点的ed25519公钥 ###### txpool(交易池信息) ``` txpool local/remote queued/pending ``` 执行命令后会发生: - 显示 本地 or 远端 交易池的queud or pending中包含的交易信息 #### RESTful API 不重要,可以不管,科研时可以不用 基于GIN实现,用于客户端交互,也可用于测试 如需使用以下内容,建议装个Postman,自己有能力写一个客户端也行 对应的请求端口是节点启动-p参数指定的 状态码对应的意思在代码中的dto包里有 ###### GET: /account/get/:chainID/:addr 用于:获取账户状态 chainID: 链ID(目前只能是0,多通道/多链涉及到创新点,还没有一个明确的开发方案) addr: 账户地址 请求例子: ``` GET http://localhost:8888/account/get/0/0xEb0E1491C5bE1C53deDf35401d9db03fd00d7109 ``` ###### GET: /peer/get/:chainID/:peerID 用于:获节点状态 chainID: 链ID(目前只能是0,多通道/多链涉及到创新点,还没有一个明确的开发方案) addr: 账户地址,或节点的peer.ID peerID:节点的peer.ID 请求例子: ``` GET http://localhost:8888/account/get/0/12D3KooWQ9tv1hnW4JSSPFyMwceEE78vQE7JzqdZCw9DS9LGRT7j ``` ###### GET: /account/register 用于:注册一个本地的账户地址 ###### POST: /test/tx/sign 用于:对一个交易签名(打时间戳、算hash、对hash签名) dto json (TxData)例子: ``` { "timestamp": 1700404985, "chainID": 0, "txType": 4, "fromAddr": "0xEb0E1491C5bE1C53deDf35401d9db03fd00d7109", "toAddr": "12D3KooWC3CoFkgusyw2PsH1mr5NZvdsRQmqcUwAV1naKYhDuwrT", "amount": 6, "coinSince": 1700404656, "nonce": 896121, "txHash": "0xc6a978bb950076313782b20897381d42a861f206a4edd490c4e3bc1fe5c44e50", "accountSignature": "0x09056eeaaa66e9e5d9deeeb718169c602b1f1e899227664624cec4973c5b8688207dd74a7cd562b91b4335f5d8441f1ac9d94ec8c091e5047b01bc5d8a9f9a1400", "txDataMap":{ "privKey": "8353dff70c318c14b49da3c980bcfda18548d21b134a1028038a70cad135a8c3" } } ``` 注意: - json中需包含除timestamp,txHash,accountSignature中的所有信息 - 返回的json是一个密码学合法的TxData(覆盖了请求txData中的timestamp,txHash,accountSignature) ###### POST /account/tx 用于:提交一个交易至本地交易池 注意:必须是个密码学合法(能过hash验证、签名验证)、状态合法(能过nonce验证、余额验证、地址合法验证)的交易 #### 查看日志 查看运行状态和定位错误的重要手段 所有节点的日志都在runtime/log下,按节点的code name、日期、等级(error, warn, info, debug)划分 关于error日志: - 理想状态下,不应有error日志出现,若出现请务必排查 - 目前有个出现概率较小的error日志,来源基本为交易广播:No connection could be made because the target machine actively refused it。目前暂不清楚错误产生的原因,但这个错误影响不大,先不管了,因为:假设A发了个交易广播,和B通信时出现该错误导致B没收到A传递的交易,但C成功收到了A的交易广播,并把广播转发给 了B,相当于B还是收到了交易信息 关于warn日志: - 多数为安全验证未通过 - 有warn日志是正常的,因为在分布式账本中每个节点的状态在某一时刻不统一是很正常的,状态落后的节点就容易产生状态不合法的交易 #### 查看数据库 步骤: 1. cd到你的MYSQL的bin目录下 2. 登录MYSQL ``` mysql -u root -p ``` 3. 切换到节点对应的数据库(数据库的名字是节点的code name) ``` use [NODE CODE NAME] ``` 有哪些表(model): 1. accounts:存储整个链的账户状态 2. headers:存储区块链的头 3. txs: 存储已上链确认的交易 4. locals:存储本地账户地址(在本节点下注册的账户地址) 5. addrs:存储已知节点的Full Multiaddr 6. pubkeys: 存储已知节点的ed25519公钥 #### 一个完整的测试例子 目标:让4个节点通过POS共识跑起来(四个节点全部为全节点,其中一个节点兼职创世节点,一个兼职引导节点) 4个节点的peer.ID和ed25519私钥在根目录的privkey.txt里 ###### 启动节点 开4个终端,切换到项目根目录: 启动A作为引导节点 ``` go run main.go -k 0801124018cefd3a19ac7f6d12f4d21641a0460a016706ef2cc8b49a05d2daa02e7317442101544061278888f248094337d6567d92d9dfc0dd7941b976c191b56398e06c -bs ``` 启动B: ``` go run main.go -p 8889 -l 10001 -k 08011240b9f632db76bb9e42010d431b0aaefb965115fe3c839ebc33e0b14bb69a9e54fdd5046abe266b774f88b05169c019985a71a7b303fdd1a13394b00596c6386e3e ``` 启动C: ``` go run main.go -p 8890 -l 10002 -k 080112402c3094866c6b0565917694aff4787a26197414b910438a8df1f85345236f62b07c1bed992006b04298d24ccebf38ca24745cc6de2818b640e043a101d49967ab ``` 启动D: ``` go run main.go -p 8891 -l 10003 -k 08011240cb6cec126720f4851c46fb1fb1f0c9905fdf2d643b38f640dcc4e010dc3ce0df91b044d69c861362287f3312f696d18e24d99ea0b03361dc724d84b1c08012fc ``` ###### P2P网络搭建 B、C、D分别ping A: ``` ping /ip4/127.0.0.1/tcp/10000/p2p/12D3KooWC3CoFkgusyw2PsH1mr5NZvdsRQmqcUwAV1naKYhDuwrT ``` B、C、D三个节点任选两个向A发起bootstrap请求 ``` bootstrap 12D3KooWC3CoFkgusyw2PsH1mr5NZvdsRQmqcUwAV1naKYhDuwrT ``` ###### 启动共识 A,B, C, D均启动自动交易: ``` autotx start ``` 选任意一个节点作为创世节点开启共识: ``` pos start ``` ###### 运行监测 通过查看日志、二级命令、数据库监测运行状态 正常状况下不会有区块被拒绝的情况,每次共识所有密码学验证和状态验证均能通过 ###### 准备下一次测试 对每个节点挨个执行: 1. 按control+C关闭节点 2. 重新开启节点,执行rm state命令,清空所有状态信息 3. 再次按control+C关闭节点 4. 删除节点的日志文件夹(你也可以不删,删的目的是为了更容易和上一次测试的日志区分开来) ## 用到的第三方框架或库 #### GIN https://gin-gonic.com/zh-cn/docs/ #### go-libp2p getting-start: https://docs.libp2p.io/guides/getting-started/go/ doc: https://pkg.go.dev/github.com/libp2p/go-libp2p#section-documentation gossip: https://zhuanlan.zhihu.com/p/41228196 #### gorm https://gorm.io/zh_CN/docs/index.html #### ETH 开发文档:https://ethereum.org/zh/developers/docs/ 源码地址:https://github.com/ethereum/go-ethereum #### Redis https://redis.uptrace.dev/zh/guide/