Skip to content

Commit

Permalink
简单优化代码
Browse files Browse the repository at this point in the history
  • Loading branch information
yangyile committed Dec 12, 2024
1 parent d69ca70 commit 12ddc3c
Show file tree
Hide file tree
Showing 18 changed files with 347 additions and 169 deletions.
66 changes: 39 additions & 27 deletions create_wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,51 +9,63 @@ import (
"github.com/pkg/errors"
)

// CreateWalletP2PKH 随机创建个比特币钱包
// 你需要知道的是目前比特币的地址格式有5种,而这5种分别代表5个版本的,他们的签名逻辑各不相同
// 随着系统的升级以后可能还会有更多的版本出现
// 这里只是选择 P2PKH 这一种
// 其次是比特币分为正式网络和测试网络,他们的地址也不是互通的
// 需要在概念上区分清楚
func CreateWalletP2PKH(netParams *chaincfg.Params) (address string, private string, err error) {
// 随机生成一个新的比特币私钥
// CreateWalletP2PKH generates a Bitcoin wallet using the P2PKH format.
// This function returns the wallet address and private key hex-string.
// CreateWalletP2PKH 使用 P2PKH 格式生成比特币钱包。
// 该函数返回钱包地址和私钥的十六进制格式。
func CreateWalletP2PKH(netParams *chaincfg.Params) (addressString string, privateKeyHex string, err error) {
// Generate a new Bitcoin private key // 创建新的比特币私钥
privateKey, err := btcec.NewPrivateKey()
if err != nil {
return "", "", errors.WithMessage(err, "随机私钥出错")
return "", "", errors.WithMessage(err, "wrong to generate random private key")
}
// 通过私钥生成比特币地址的公钥

// Generate the public key hash (SHA256 -> RIPEMD160) from the private key // 从私钥生成公钥哈希(SHA256 -> RIPEMD160)
pubKeyHash := btcutil.Hash160(privateKey.PubKey().SerializeCompressed())
// 通过公钥得到地址

// Create a Bitcoin address using the public key hash // 使用公钥哈希生成比特币地址
addressPubKeyHash, err := btcutil.NewAddressPubKeyHash(pubKeyHash, netParams)
if err != nil {
return "", "", errors.WithMessage(err, "创建地址出错")
return "", "", errors.WithMessage(err, "wrong to create address from public key hash")
}
address = addressPubKeyHash.EncodeAddress() // 转换为浏览器里常用的字符串的结果
private = hex.EncodeToString(privateKey.Serialize())
return address, private, nil

// Return the generated address and private key (hex-encoded) // 返回生成的地址和私钥(十六进制编码)
addressString = addressPubKeyHash.EncodeAddress()
privateKeyHex = hex.EncodeToString(privateKey.Serialize())
return addressString, privateKeyHex, nil
}

func CreateWalletP2WPKH(netParams *chaincfg.Params) (address string, private string, err error) {
// 创建一个新的随机私钥
// CreateWalletP2WPKH generates a Bitcoin wallet using the P2WPKH format.
// This function returns the wallet address and private key hex-string.
// CreateWalletP2WPKH 使用 P2WPKH 格式生成比特币钱包。
// 该函数返回钱包地址和私钥的十六进制格式。
func CreateWalletP2WPKH(netParams *chaincfg.Params) (addressString string, privateKeyHex string, err error) {
// Generate a new Bitcoin private key // 创建新的比特币私钥
privateKey, err := btcec.NewPrivateKey()
if err != nil {
return "", "", errors.WithMessage(err, "随机私钥出错")
return "", "", errors.WithMessage(err, "wrong to generate random private key")
}
// WIF(Wallet Import Format)私钥编码格式的类型

// Encode the private key using Wallet Import Format (WIF) // 使用 WIF 格式编码私钥
privateWif, err := btcutil.NewWIF(privateKey, netParams, true)
if err != nil {
return "", "", errors.WithMessage(err, "创建钱包引用格式出错")
return "", "", errors.WithMessage(err, "wrong to create Wallet Import Format (WIF) for private key")
}
// 直接从私钥生成公钥

// Get the public key // 获取公钥
pubKey := privateWif.PrivKey.PubKey()
// 计算公钥哈希(P2WPKH使用的公钥哈希是公钥的SHA256和RIPEMD160哈希值)

// Compute the public key hash (SHA256 -> RIPEMD160) // 计算公钥哈希(SHA256 -> RIPEMD160)
pubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed())
// 创建P2WPKH地址

// Create a P2WPKH address using the public key hash // 使用公钥哈希生成 P2WPKH 地址
witnessPubKeyHash, err := btcutil.NewAddressWitnessPubKeyHash(pubKeyHash, netParams)
if err != nil {
return "", "", errors.WithMessage(err, "创建P2WPKH地址出错")
return "", "", errors.WithMessage(err, "wrong to create P2WPKH address")
}
address = witnessPubKeyHash.EncodeAddress()
private = hex.EncodeToString(privateKey.Serialize())
return address, private, nil

// Return the generated address and private key (hex-encoded) // 返回生成的地址和私钥(十六进制编码)
addressString = witnessPubKeyHash.EncodeAddress()
privateKeyHex = hex.EncodeToString(privateKey.Serialize())
return addressString, privateKeyHex, nil
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ require (
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/stretchr/objx v0.5.2 // indirect
golang.org/x/crypto v0.29.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/sys v0.28.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
Expand All @@ -131,8 +131,8 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
Expand Down
4 changes: 2 additions & 2 deletions internal/demos/signbtc/main/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func main() {

netParams := chaincfg.TestNet3Params

param := gobtcsign.CustomParam{
param := gobtcsign.BitcoinTxParams{
VinList: []gobtcsign.VinType{
{
OutPoint: *gobtcsign.MustNewOutPoint("e1f05d4ef10d6d4245839364c637cc37f429784883761668978645c67e723919", 2),
Expand Down Expand Up @@ -44,7 +44,7 @@ func main() {
fmt.Println("estimate-tx-size:", size) //这是预估值 略微 >= 实际值

//得到待签名的交易
signParam, err := param.GetSignParam(&netParams)
signParam, err := param.CreateTxSignParams(&netParams)
utils.MustDone(err)

fmt.Println("utxo inputs:", len(signParam.InputOuts))
Expand Down
4 changes: 2 additions & 2 deletions internal/demos/signdoge/main/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func main() {

netParams := dogecoin.TestNetParams

param := gobtcsign.CustomParam{
param := gobtcsign.BitcoinTxParams{
VinList: []gobtcsign.VinType{
{
OutPoint: *gobtcsign.MustNewOutPoint(
Expand Down Expand Up @@ -47,7 +47,7 @@ func main() {
fmt.Println("estimate-tx-size:", size) //这是预估值 略微 >= 实际值

//得到待签名的交易
signParam, err := param.GetSignParam(&netParams)
signParam, err := param.CreateTxSignParams(&netParams)
utils.MustDone(err)

//签名
Expand Down
108 changes: 17 additions & 91 deletions param.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@ package gobtcsign
import (
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/pkg/errors"
)

// CustomParam 这是客户自定义的参数类型,表示要转入和转出的信息
type CustomParam struct {
// BitcoinTxParams 这是客户自定义的参数类型,表示要转入和转出的信息
type BitcoinTxParams struct {
VinList []VinType //要转入进BTC节点的
OutList []OutType //要从BTC节点转出的-这里面通常包含1个目标(转账)和1个自己(找零)
RBFInfo RBFConfig //详见RBF机制,通常是需要启用RBF以免交易长期被卡的
Expand All @@ -22,81 +21,13 @@ type VinType struct {
RBFInfo RBFConfig //还是RBF机制,前面的是控制整个交易的,这里控制单个UTXO的
}

func MustNewOutPoint(srcTxHash string, utxoIndex uint32) *wire.OutPoint {
//which tx the utxo from.
utxoHash, err := chainhash.NewHashFromStr(srcTxHash)
if err != nil {
panic(errors.WithMessagef(err, "wrong param utxo-from-tx-hash=%s", srcTxHash))
}
return wire.NewOutPoint(
utxoHash, //这个是收到 utxo 的交易哈希,即 utxo 是从哪里来的,配合位置索引序号构成唯一索引,就能确定是花的哪个utxo
utxoIndex, //这个是收到 utxo 的输出位置,比如一个交易中有多个输出,这里要选择输出的位置
)
}

type OutType struct {
Target AddressTuple //接收者信息,钱包地址和公钥文本,二选一填写即可
Amount int64 //聪的数量
}

type AddressTuple struct {
Address string //钱包地址 和 公钥脚本 二选一填写即可
PkScript []byte //公钥脚本 和 钱包地址 二选一填写即可 PkScript(Public Key Script)在拼装交易和签名时使用
}

func NewAddressTuple(address string) *AddressTuple {
return &AddressTuple{
Address: address,
PkScript: nil, //这里 address 和 pk-script 是二选一的,因此不设,在后续的逻辑里会根据地址获得 pk-script 信息
}
}

// GetPkScript 获得公钥文本,当公钥文本存在时就用已有的,否则就根据地址计算
func (one *AddressTuple) GetPkScript(netParams *chaincfg.Params) ([]byte, error) {
if len(one.PkScript) > 0 {
return one.PkScript, nil //假如有就直接返回,否则就根据地址计算
}
if one.Address == "" {
return nil, errors.New("no-pk-script-no-address")
}
return GetAddressPkScript(one.Address, netParams) //这里不用做缓存避免增加复杂度
}

type RBFConfig struct {
AllowRBF bool //当需要RBF时需要设置,推荐启用RBF发交易,否则,当手续费过低时交易会卡在节点的内存池里
Sequence uint32 //这是后来出的功能,RBF,使用更高手续费重发交易用的,当BTC交易发出到系统以后假如没人打包(手续费过低时),就可以增加手续费覆盖旧的交易
}

func NewRBFActive() *RBFConfig {
return &RBFConfig{
AllowRBF: true,
Sequence: wire.MaxTxInSequenceNum - 2, // recommended sequence BTC推荐的默认启用RBF的就是这个数 // 选择 wire.MaxTxInSequenceNum - 2 而不是 wire.MaxTxInSequenceNum - 1 是出于一种谨慎性和规范性的考虑。虽然在技术上 wire.MaxTxInSequenceNum - 1 也可以支持 RBF,但 -2 更为常用
}
}

func NewRBFNotUse() *RBFConfig {
return &RBFConfig{
AllowRBF: false, //当两个元素都为零值时表示不启用RBF机制
Sequence: wire.MaxTxInSequenceNum, //当两个元素都为零值时表示不启用RBF机制,当然这里设置为 wire.MaxTxInSequenceNum 也行,逻辑已经做过判定
}
}

func NewRBFSeqNum(sequence uint32) *RBFConfig {
return &RBFConfig{
AllowRBF: sequence != wire.MaxTxInSequenceNum, //避免设置为0被误认为是不使用RBF的
Sequence: sequence,
}
}

func (cfg *RBFConfig) GetSequence() uint32 {
if cfg.AllowRBF || cfg.Sequence > 0 { //启用RBF机制,精确的RBF逻辑
return cfg.Sequence
}
return wire.MaxTxInSequenceNum //当两个元素都为零值时表示不启用RBF机制-因此这里使用默认的最大值表示不启用
}

// GetSignParam 根据用户的输入信息拼接交易
func (param *CustomParam) GetSignParam(netParams *chaincfg.Params) (*SignParam, error) {
// CreateTxSignParams 根据用户的输入信息拼接交易
func (param *BitcoinTxParams) CreateTxSignParams(netParams *chaincfg.Params) (*SignParam, error) {
var msgTx = wire.NewMsgTx(wire.TxVersion)

//这是发送者和发送数量的列表,很明显,这是需要签名的关键信息,现在只把待签名信息收集起来
Expand All @@ -117,7 +48,7 @@ func (param *CustomParam) GetSignParam(netParams *chaincfg.Params) (*SignParam,
return nil, errors.Errorf("wrong tx_in.sequence default value: %v", txIn.Sequence)
}
// 查看是否需要启用 RBF 机制
if seqNo := param.GetTxInSequenceNum(input); seqNo != wire.MaxTxInSequenceNum {
if seqNo := param.GetTxInputSequence(input); seqNo != wire.MaxTxInSequenceNum {
txIn.Sequence = seqNo
}
msgTx.AddTxIn(txIn)
Expand All @@ -138,7 +69,7 @@ func (param *CustomParam) GetSignParam(netParams *chaincfg.Params) (*SignParam,
}, nil
}

func (param *CustomParam) GetOutputs(netParams *chaincfg.Params) ([]*wire.TxOut, error) {
func (param *BitcoinTxParams) GetOutputs(netParams *chaincfg.Params) ([]*wire.TxOut, error) {
outputs := make([]*wire.TxOut, 0, len(param.OutList))
for _, output := range param.OutList {
pkScript, err := output.Target.GetPkScript(netParams)
Expand All @@ -150,7 +81,7 @@ func (param *CustomParam) GetOutputs(netParams *chaincfg.Params) ([]*wire.TxOut,
return outputs, nil
}

func (param *CustomParam) GetTxInSequenceNum(input VinType) uint32 {
func (param *BitcoinTxParams) GetTxInputSequence(input VinType) uint32 {
// 当你确实是需要对每个交易单独设置RBF时,就可以在这里设置,单独设置到这个 vin 里面
if seqNo := input.RBFInfo.GetSequence(); seqNo != wire.MaxTxInSequenceNum { //启用RBF机制,精确的RBF逻辑
return seqNo
Expand All @@ -171,7 +102,7 @@ func (param *CustomParam) GetTxInSequenceNum(input VinType) uint32 {
}

// GetInputList 把拼交易的参数转换为验签的参数
func (param *CustomParam) GetInputList() []*VerifyTxInputParam {
func (param *BitcoinTxParams) GetInputList() []*VerifyTxInputParam {
var inputList = make([]*VerifyTxInputParam, 0, len(param.VinList))
for _, x := range param.VinList {
inputList = append(inputList, &VerifyTxInputParam{
Expand All @@ -186,7 +117,7 @@ func (param *CustomParam) GetInputList() []*VerifyTxInputParam {
}

// GetFee 全部输入和全部输出的差额,即交易的费用
func (param *CustomParam) GetFee() btcutil.Amount {
func (param *BitcoinTxParams) GetFee() btcutil.Amount {
var sum int64
for _, v := range param.VinList {
sum += v.Amount
Expand All @@ -198,23 +129,18 @@ func (param *CustomParam) GetFee() btcutil.Amount {
}

// GetChangeAmountWithFee 根据交易费用计算出找零数量
func (param *CustomParam) GetChangeAmountWithFee(fee btcutil.Amount) btcutil.Amount {
func (param *BitcoinTxParams) GetChangeAmountWithFee(fee btcutil.Amount) btcutil.Amount {
return param.GetFee() - fee
}

func (param *CustomParam) EstimateTxSize(netParams *chaincfg.Params, change *ChangeTo) (int, error) {
func (param *BitcoinTxParams) EstimateTxSize(netParams *chaincfg.Params, change *ChangeTo) (int, error) {
return EstimateTxSize(param, netParams, change)
}

func (param *CustomParam) EstimateTxFee(netParams *chaincfg.Params, change *ChangeTo, feeRatePerKb btcutil.Amount, dustFee DustFee) (btcutil.Amount, error) {
func (param *BitcoinTxParams) EstimateTxFee(netParams *chaincfg.Params, change *ChangeTo, feeRatePerKb btcutil.Amount, dustFee DustFee) (btcutil.Amount, error) {
return EstimateTxFee(param, netParams, change, feeRatePerKb, dustFee)
}

// CheckMsgTxParam 当签完名以后最好是再用这个函数检查检查,避免签名逻辑在有BUG时修改输入或输出的内容
func (param *CustomParam) CheckMsgTxParam(msgTx *wire.MsgTx, netParams *chaincfg.Params) error {
return CheckMsgTxSameWithParam(msgTx, *param, netParams)
}

// NewCustomParamFromMsgTx 这里提供简易的逻辑把交易的原始参数再拼回来
// 以校验参数和校验签名等信息
// 因此该函数的主要作用是校验
Expand All @@ -223,7 +149,7 @@ func (param *CustomParam) CheckMsgTxParam(msgTx *wire.MsgTx, netParams *chaincfg
// 第二个参数是设置如何获取前置输出的
// 通常是使用 客户端 请求获取前置输出,但也可以使用map把前置输出存起来,因此使用 interface 获取前置输出,提供两种实现方案
// 在项目中推荐使用 rpc 获取,这样就很方便,而在单元测试中则只需要通过 map 预先配置就行,避免网络请求也避免暴露节点配置
func NewCustomParamFromMsgTx(msgTx *wire.MsgTx, preImp GetUtxoFromInterface) (*CustomParam, error) {
func NewCustomParamFromMsgTx(msgTx *wire.MsgTx, preImp GetUtxoFromInterface) (*BitcoinTxParams, error) {
var vinList = make([]VinType, 0, len(msgTx.TxIn))
for _, vin := range msgTx.TxIn {
costUtxo := vin.PreviousOutPoint
Expand All @@ -237,7 +163,7 @@ func NewCustomParamFromMsgTx(msgTx *wire.MsgTx, preImp GetUtxoFromInterface) (*C
OutPoint: *wire.NewOutPoint(&costUtxo.Hash, costUtxo.Index),
Sender: *utxoFrom.sender,
Amount: utxoFrom.amount,
RBFInfo: *NewRBFSeqNum(vin.Sequence),
RBFInfo: *NewRBFConfig(vin.Sequence),
})
}

Expand All @@ -249,7 +175,7 @@ func NewCustomParamFromMsgTx(msgTx *wire.MsgTx, preImp GetUtxoFromInterface) (*C
})
}

param := &CustomParam{
param := &BitcoinTxParams{
VinList: vinList,
OutList: outList,
RBFInfo: *NewRBFNotUse(), //这里是不需要的,因为各个输入里将会有RBF的全部信息
Expand All @@ -258,7 +184,7 @@ func NewCustomParamFromMsgTx(msgTx *wire.MsgTx, preImp GetUtxoFromInterface) (*C
}

// VerifyMsgTxSign 使用这个检查签名是否正确
func (param *CustomParam) VerifyMsgTxSign(msgTx *wire.MsgTx, netParams *chaincfg.Params) error {
func (param *BitcoinTxParams) VerifyMsgTxSign(msgTx *wire.MsgTx, netParams *chaincfg.Params) error {
inputsItem, err := param.GetVerifyTxInputsItem(netParams)
if err != nil {
return errors.WithMessage(err, "wrong get-inputs")
Expand All @@ -269,7 +195,7 @@ func (param *CustomParam) VerifyMsgTxSign(msgTx *wire.MsgTx, netParams *chaincfg
return nil
}

func (param *CustomParam) GetVerifyTxInputsItem(netParams *chaincfg.Params) (*VerifyTxInputsType, error) {
func (param *BitcoinTxParams) GetVerifyTxInputsItem(netParams *chaincfg.Params) (*VerifyTxInputsType, error) {
var res = &VerifyTxInputsType{
PkScripts: make([][]byte, 0, len(param.VinList)),
InAmounts: make([]btcutil.Amount, 0, len(param.VinList)),
Expand Down
Loading

0 comments on commit 12ddc3c

Please sign in to comment.