比特币钱包gocoin学习笔记

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的,所以是解析不到的。

普遍来说,还是在外面找好