https://github.com/piotrnar/gocoin
Gocoin是用 Go 语言 (golang) 编写的完整比特币解决方案。
软件架构专注于节点的最大性能和钱包的冷存储安全性。
钱包设计为离线使用。它是确定性和密码播种的。只要您记住密码,就不需要任何备份。钱包可以在没有客户端的情况下使用,但可以使用提供的balio工具。
客户端(p2p 节点)是独立于钱包的应用程序。它将整个 UTXO 设置保存在 RAM 中,提供市场上最好的块处理性能。
块数据库
- 缓存,缓存,新产生的块,添加到老使用的缓存池,当缓存池满后,扔掉最干净的块
- 写缓存,当channel满了之后,flush channel 到db文件中
unspent_db
如介绍中提到的都,所有的 utxo 都存在内存里。
- map, key为txid, v为txid下的所有输出。问题是key的长度为8?txid长度为32,只取前8位?不会存在冲突吗?
- 更新为整体修改,即txid下所有的输出。
- 存入文件,程序启动时从文件读取,必要时存入文件(如程序退出时)
钱包/db.go
账户系统,addr - utxo - balance
var (
AllBalancesP2KH, AllBalancesP2SH, AllBalancesP2WKH 映射[[20]字节]*OneAllAddrBal
AllBalancesP2WSH 映射[[32]字节]*OneAllAddrBal
)
键入 OneAllAddrInp [utxo.UtxoIdxLen + 4]字节
type OneAllAddrBal struct {
Value uint64 // 最高位表示 P2SH
unsp []OneAllAddrInp
unspMap map[OneAllAddrInp]bool
}
看起来主要是几个存于内存中,map的key为addr,OneAllAddrInp为txid的前8位(unspend_db中map的key)+n(第out)
其中,根据中文字判断可以是什么地址的签名。
if out.IsP2KH() {
copy(uidx[:], out.PKScr[3:23])
rec = AllBalancesP2KH[uidx]
if rec == nil {
rec = &OneAllAddrBal{}
AllBalancesP2KH[uidx] = rec
}
} else if out.IsP2SH() {
copy(uidx[:], out.PKScr[2:22])
rec = AllBalancesP2SH[uidx]
if rec == nil {
rec = &OneAllAddrBal{}
AllBalancesP2SH[uidx] = rec
}
} else if out .IsP2WPKH() {
copy(uidx[:], out.PKScr[2:22])
rec = AllBalancesP2WKH[uidx]
if rec == nil {
rec = &OneAllAddrBal{}
AllBalancesP2WKH[uidx] = rec
}
} else if out。 IsP2WSH() {
var uidx [32]字节
复制(uidx[:], out.PKScr[2:34])
rec = AllBalancesP2WSH[uidx]
if rec == nil {
rec = &OneAllAddrBal{}
AllBalancesP2WSH[uidx] = rec
}
} else {
continue
}
func (out *UtxoTxOut) IsP2KH() bool {
return len(out.PKScr) == 25 && out.PKScr[0] == 0x76 && out.PKScr[1] == 0xa9 &&
out.PKScr[2] == 0x14 && out.PKScr[23] == 0x88 && out.PKScr[24] == 0xac
}
func (r *UtxoTxOut) IsP2SH() bool {
return len(r.PKScr) == 23 && r.PKScr[0] == 0xa9 && r.PKScr[1] == 0x14 && r.PKScr[22] == 0x87
}
func (r *UtxoTxOut) IsP2WPKH() bool {
return len(r.PKScr) == 22 && r.PKScr[0] == 0 && r.PKScr[1] == 20
}
func (r *UtxoTxOut) IsP2WSH() bool {
return len(r.PKScr) == 34 && r.PKScr[0] == 0 && r.PKScr[1] == 32
}
分叉处理
首先,同行的部门存下来,只是使用信任区来的表面是主链还是分叉链上的块。
如果这个链子出现分叉了,那么链表的统计链算力(这里会分链的大小算力让我有点费)删除的部分,删除和进行删除的部分。
代码大致位于lib/chain/下,如blockdb.go,chain_accept.go等
分叉算力统计算法
// 如果 b1 的 POW 比 b2 多,则返回 true
func (b1 *BlockTreeNode) MorePOW(b2 *BlockTreeNode) bool {
var b1sum, b2sum float64
for b1.Height > b2.Height {
b1sum += btc.GetDifficulty(b1.Bits( ))
b1 = b1.Parent
}
对于 b2.Height > b1.Height {
b2sum += btc.GetDifficulty(b2.Bits())
b2 = b2.Parent
}
对于 b1 != b2 {
b1sum += btc.GetDifficulty(b1 .Bits())
b2sum += btc.GetDifficulty(b2.Bits())
b1 = b1.Parent
b2 = b2.Parent
}
return b1sum > b2sum
}
关于地址
想通过输入中的脚本直接算出地址,是不做之前的。
普通的P2PKH的输入中签名包含两部分pk+sig,这样才能通过pk解析出的。
但P2SH脚本的签名是不包含pk的,所以是解析不到的。
普遍来说,还是在外面找好