diff --git a/create_wallet.go b/create_wallet.go index 53784a0..18709e4 100644 --- a/create_wallet.go +++ b/create_wallet.go @@ -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 } diff --git a/go.mod b/go.mod index 0d58b1a..6591e7e 100644 --- a/go.mod +++ b/go.mod @@ -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 ) diff --git a/go.sum b/go.sum index 90b7bc5..eb86cfa 100644 --- a/go.sum +++ b/go.sum @@ -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= @@ -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= diff --git a/internal/demos/signbtc/main/main.go b/internal/demos/signbtc/main/main.go index 5773002..ce26333 100644 --- a/internal/demos/signbtc/main/main.go +++ b/internal/demos/signbtc/main/main.go @@ -14,7 +14,7 @@ func main() { netParams := chaincfg.TestNet3Params - param := gobtcsign.CustomParam{ + param := gobtcsign.BitcoinTxParams{ VinList: []gobtcsign.VinType{ { OutPoint: *gobtcsign.MustNewOutPoint("e1f05d4ef10d6d4245839364c637cc37f429784883761668978645c67e723919", 2), @@ -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)) diff --git a/internal/demos/signdoge/main/main.go b/internal/demos/signdoge/main/main.go index efcbb1d..6edbfee 100644 --- a/internal/demos/signdoge/main/main.go +++ b/internal/demos/signdoge/main/main.go @@ -14,7 +14,7 @@ func main() { netParams := dogecoin.TestNetParams - param := gobtcsign.CustomParam{ + param := gobtcsign.BitcoinTxParams{ VinList: []gobtcsign.VinType{ { OutPoint: *gobtcsign.MustNewOutPoint( @@ -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) //签名 diff --git a/param.go b/param.go index 4f65bf8..ed65107 100644 --- a/param.go +++ b/param.go @@ -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以免交易长期被卡的 @@ -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) //这是发送者和发送数量的列表,很明显,这是需要签名的关键信息,现在只把待签名信息收集起来 @@ -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) @@ -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) @@ -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 @@ -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{ @@ -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 @@ -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 这里提供简易的逻辑把交易的原始参数再拼回来 // 以校验参数和校验签名等信息 // 因此该函数的主要作用是校验 @@ -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 @@ -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), }) } @@ -249,7 +175,7 @@ func NewCustomParamFromMsgTx(msgTx *wire.MsgTx, preImp GetUtxoFromInterface) (*C }) } - param := &CustomParam{ + param := &BitcoinTxParams{ VinList: vinList, OutList: outList, RBFInfo: *NewRBFNotUse(), //这里是不需要的,因为各个输入里将会有RBF的全部信息 @@ -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") @@ -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)), diff --git a/param_test.go b/param_test.go index 3b0fc58..0661672 100644 --- a/param_test.go +++ b/param_test.go @@ -21,7 +21,7 @@ func TestCustomParam_GetSignParam(t *testing.T) { //which address own the utxo. convert to pk-script bytes pkScript := caseGetAddressPkScript(t, senderAddress, &netParams) - customParam := &CustomParam{ + customParam := &BitcoinTxParams{ VinList: []VinType{ { OutPoint: *MustNewOutPoint( @@ -54,7 +54,7 @@ func TestCustomParam_GetSignParam(t *testing.T) { require.Equal(t, int64(340500), int64(customParam.GetFee())) - res, err := customParam.GetSignParam(&netParams) + res, err := customParam.CreateTxSignParams(&netParams) require.NoError(t, err) require.Equal(t, res.NetParams.Net, netParams.Net) @@ -88,8 +88,8 @@ func TestCustomParam_VerifyMsgTxSign(t *testing.T) { netParams := chaincfg.TestNet3Params - preMap := NewUtxoFromOutMap(map[wire.OutPoint]*UtxoSenderAmountTuple{ - *MustNewOutPoint("a06b4450eb63c8a245e799800e7554ef7a25d415874a957720bee79be8fc15e2", 1): NewUtxoSenderAmountTuple(NewAddressTuple("tb1q92kpf4hlj5khmdalshlz6602lvs8vcakxz8hzq"), 49560582), + preMap := NewOutPointUtxoSenderAmountMap(map[wire.OutPoint]*UtxoSenderAmount{ + *MustNewOutPoint("a06b4450eb63c8a245e799800e7554ef7a25d415874a957720bee79be8fc15e2", 1): NewUtxoSenderAmount(NewAddressTuple("tb1q92kpf4hlj5khmdalshlz6602lvs8vcakxz8hzq"), 49560582), }) param, err := NewCustomParamFromMsgTx(msgTx, preMap) @@ -112,9 +112,9 @@ func TestCustomParam_CheckMsgTxParam(t *testing.T) { netParams := dogecoin.MainNetParams - preMap := NewUtxoFromOutMap(map[wire.OutPoint]*UtxoSenderAmountTuple{ - *MustNewOutPoint("cb15601f24db291d1ca679e769de5c10891fcd1f2b1257680813986bedda81b8", 0): NewUtxoSenderAmountTuple(NewAddressTuple("D9taZdfvonxSn8USudmhqhwvE7wt3aPW79"), 9230995), - *MustNewOutPoint("90676039a3c6fefb32dfadcaed501d6c20c1d0dcbf4071487e35f22b8097b099", 2): NewUtxoSenderAmountTuple(NewAddressTuple("D9taZdfvonxSn8USudmhqhwvE7wt3aPW79"), 437264864), + preMap := NewOutPointUtxoSenderAmountMap(map[wire.OutPoint]*UtxoSenderAmount{ + *MustNewOutPoint("cb15601f24db291d1ca679e769de5c10891fcd1f2b1257680813986bedda81b8", 0): NewUtxoSenderAmount(NewAddressTuple("D9taZdfvonxSn8USudmhqhwvE7wt3aPW79"), 9230995), + *MustNewOutPoint("90676039a3c6fefb32dfadcaed501d6c20c1d0dcbf4071487e35f22b8097b099", 2): NewUtxoSenderAmount(NewAddressTuple("D9taZdfvonxSn8USudmhqhwvE7wt3aPW79"), 437264864), }) param, err := NewCustomParamFromMsgTx(msgTx, preMap) @@ -137,10 +137,10 @@ func TestCustomParam_CheckMsgTxParam_BTC(t *testing.T) { netParams := chaincfg.MainNetParams - preMap := NewUtxoFromOutMap(map[wire.OutPoint]*UtxoSenderAmountTuple{ - *MustNewOutPoint("d19f5a52f98c4bbc25421913ba450c48b3ccd56d042518c92bb6e9656b65e648", 1): NewUtxoSenderAmountTuple(NewAddressTuple("bc1qvuhjmgfr4kxye8eh63qvkv3yst950u8mye9fxh"), 700623171), - *MustNewOutPoint("d8824a43740567d0f1f7e214462ceb0494142194d1b8a55b66b1276edfaa88c0", 0): NewUtxoSenderAmountTuple(NewAddressTuple("bc1q963tmm9tv9884k60puxc3syyld0xzte3duy9uc"), 17232), - *MustNewOutPoint("f9999fb7df8edf4d6aa95189bb6eccabde67d99657ee5e1dc93e3f1f815841cb", 0): NewUtxoSenderAmountTuple(NewAddressTuple("bc1q963tmm9tv9884k60puxc3syyld0xzte3duy9uc"), 17191), + preMap := NewOutPointUtxoSenderAmountMap(map[wire.OutPoint]*UtxoSenderAmount{ + *MustNewOutPoint("d19f5a52f98c4bbc25421913ba450c48b3ccd56d042518c92bb6e9656b65e648", 1): NewUtxoSenderAmount(NewAddressTuple("bc1qvuhjmgfr4kxye8eh63qvkv3yst950u8mye9fxh"), 700623171), + *MustNewOutPoint("d8824a43740567d0f1f7e214462ceb0494142194d1b8a55b66b1276edfaa88c0", 0): NewUtxoSenderAmount(NewAddressTuple("bc1q963tmm9tv9884k60puxc3syyld0xzte3duy9uc"), 17232), + *MustNewOutPoint("f9999fb7df8edf4d6aa95189bb6eccabde67d99657ee5e1dc93e3f1f815841cb", 0): NewUtxoSenderAmount(NewAddressTuple("bc1q963tmm9tv9884k60puxc3syyld0xzte3duy9uc"), 17191), }) param, err := NewCustomParamFromMsgTx(msgTx, preMap) @@ -163,10 +163,10 @@ func TestCustomParam_CheckMsgTxParam_BTC_TXN(t *testing.T) { netParams := chaincfg.MainNetParams - preMap := NewUtxoFromOutMap(map[wire.OutPoint]*UtxoSenderAmountTuple{ - *MustNewOutPoint("56ee8ace223a6fb8585458866ba2a5c5d620ccf968eca3060c28e1033f198c89", 6): NewUtxoSenderAmountTuple(NewAddressTuple("bc1qrhut5t4g2wa2lf9h48fcth869h48khe0avxlq60vs5m0y2s8memq6fjt7r"), 31759346), - *MustNewOutPoint("840bdc8d28b5919ea2a8f19e9993109ebd297f5279a6e00e70278684ec187d8d", 3): NewUtxoSenderAmountTuple(NewAddressTuple("bc1qlhqvxmpcqqw64r82h7sm89hn5n8g9p7w6y2m7cxkvtytavgthups57rnws"), 16616320), - *MustNewOutPoint("c57c06e4f2207420663fdf3d8a92a5a5755da469591b9677a03bc8247b36f590", 2): NewUtxoSenderAmountTuple(NewAddressTuple("bc1q673p5npwsz78j4vdzqltsa4fvtvpteynudu446n56xrpy38tgrkqjmzwpk"), 15201401), + preMap := NewOutPointUtxoSenderAmountMap(map[wire.OutPoint]*UtxoSenderAmount{ + *MustNewOutPoint("56ee8ace223a6fb8585458866ba2a5c5d620ccf968eca3060c28e1033f198c89", 6): NewUtxoSenderAmount(NewAddressTuple("bc1qrhut5t4g2wa2lf9h48fcth869h48khe0avxlq60vs5m0y2s8memq6fjt7r"), 31759346), + *MustNewOutPoint("840bdc8d28b5919ea2a8f19e9993109ebd297f5279a6e00e70278684ec187d8d", 3): NewUtxoSenderAmount(NewAddressTuple("bc1qlhqvxmpcqqw64r82h7sm89hn5n8g9p7w6y2m7cxkvtytavgthups57rnws"), 16616320), + *MustNewOutPoint("c57c06e4f2207420663fdf3d8a92a5a5755da469591b9677a03bc8247b36f590", 2): NewUtxoSenderAmount(NewAddressTuple("bc1q673p5npwsz78j4vdzqltsa4fvtvpteynudu446n56xrpy38tgrkqjmzwpk"), 15201401), }) param, err := NewCustomParamFromMsgTx(msgTx, preMap) diff --git a/rbfconfig.go b/rbfconfig.go new file mode 100644 index 0000000..41f194c --- /dev/null +++ b/rbfconfig.go @@ -0,0 +1,30 @@ +package gobtcsign + +import "github.com/btcsuite/btcd/wire" + +type RBFConfig struct { + AllowRBF bool //当需要RBF时需要设置,推荐启用RBF发交易,否则,当手续费过低时交易会卡在节点的内存池里 + Sequence uint32 //这是后来出的功能,RBF,使用更高手续费重发交易用的,当BTC交易发出到系统以后假如没人打包(手续费过低时),就可以增加手续费覆盖旧的交易 +} + +func NewRBFConfig(sequence uint32) *RBFConfig { + return &RBFConfig{ + AllowRBF: sequence != wire.MaxTxInSequenceNum, //避免设置为0被误认为是不使用RBF的 + Sequence: sequence, + } +} + +func NewRBFActive() *RBFConfig { + return NewRBFConfig(wire.MaxTxInSequenceNum - 2) // recommended sequence BTC推荐的默认启用RBF的就是这个数 // 选择 wire.MaxTxInSequenceNum - 2 而不是 wire.MaxTxInSequenceNum - 1 是出于一种谨慎性和规范性的考虑。虽然在技术上 wire.MaxTxInSequenceNum - 1 也可以支持 RBF,但 -2 更为常用 +} + +func NewRBFNotUse() *RBFConfig { + return NewRBFConfig(wire.MaxTxInSequenceNum) //当两个元素都为零值时表示不启用RBF机制,当然这里设置为 wire.MaxTxInSequenceNum 也行,逻辑已经做过判定 +} + +func (cfg *RBFConfig) GetSequence() uint32 { + if cfg.AllowRBF || cfg.Sequence > 0 { //启用RBF机制,精确的RBF逻辑 + return cfg.Sequence + } + return wire.MaxTxInSequenceNum //当两个元素都为零值时表示不启用RBF机制-因此这里使用默认的最大值表示不启用 +} diff --git a/rbfconfig_test.go b/rbfconfig_test.go new file mode 100644 index 0000000..5dd7aff --- /dev/null +++ b/rbfconfig_test.go @@ -0,0 +1,79 @@ +package gobtcsign + +import ( + "testing" + + "github.com/btcsuite/btcd/wire" + "github.com/stretchr/testify/require" +) + +func TestRBFConfig_NewRBFConfig_AllowRBFTrue(t *testing.T) { + cfg := NewRBFConfig(wire.MaxTxInSequenceNum - 1) + require.Equal(t, true, cfg.AllowRBF) + require.Equal(t, wire.MaxTxInSequenceNum-1, cfg.Sequence) +} + +func TestRBFConfig_NewRBFConfig_AllowRBFFalse(t *testing.T) { + cfg := NewRBFConfig(wire.MaxTxInSequenceNum) + require.Equal(t, false, cfg.AllowRBF) + require.Equal(t, wire.MaxTxInSequenceNum, cfg.Sequence) +} + +func TestRBFConfig_NewRBFActive(t *testing.T) { + cfg := NewRBFActive() + require.Equal(t, true, cfg.AllowRBF) + require.Equal(t, wire.MaxTxInSequenceNum-2, cfg.Sequence) +} + +func TestRBFConfig_NewRBFNotUse(t *testing.T) { + cfg := NewRBFNotUse() + require.Equal(t, false, cfg.AllowRBF) + require.Equal(t, wire.MaxTxInSequenceNum, cfg.Sequence) +} + +func TestRBFConfig_GetSequence_AllowRBFTrue(t *testing.T) { + cfg := &RBFConfig{AllowRBF: true, Sequence: wire.MaxTxInSequenceNum - 1} + require.Equal(t, wire.MaxTxInSequenceNum-1, cfg.GetSequence()) +} + +func TestRBFConfig_GetSequence_AllowRBFFalseWithCustomSequence(t *testing.T) { + cfg := &RBFConfig{AllowRBF: false, Sequence: wire.MaxTxInSequenceNum - 2} + require.Equal(t, wire.MaxTxInSequenceNum-2, cfg.GetSequence()) +} + +func TestRBFConfig_GetSequence_AllowRBFFalseWithDefaultSequence(t *testing.T) { + cfg := &RBFConfig{AllowRBF: false, Sequence: 0} + require.Equal(t, wire.MaxTxInSequenceNum, cfg.GetSequence()) +} + +func TestRBFConfig_NewRBFConfig_AllowRBFTrueWithZeroSequence(t *testing.T) { + // 测试当 AllowRBF 为 true 且 Sequence 为 0 时的行为 + cfg := NewRBFConfig(0) + require.Equal(t, true, cfg.AllowRBF) // AllowRBF 应该是 true + require.Equal(t, uint32(0), cfg.Sequence) // Sequence 应该是 0 +} + +func TestRBFConfig_NewRBFConfig_WithLargeSequence(t *testing.T) { + // 测试 Sequence 设置为一个较大的值 + cfg := NewRBFConfig(999999999) + require.Equal(t, true, cfg.AllowRBF) // AllowRBF 应该是 true + require.Equal(t, uint32(999999999), cfg.Sequence) // Sequence 应该是设置的值 +} + +func TestRBFConfig_GetSequence_AllowRBFTrueWithZeroSequence(t *testing.T) { + // 测试当 AllowRBF 为 true 且 Sequence 为 0 时,GetSequence 的返回值 + cfg := &RBFConfig{AllowRBF: true, Sequence: 0} + require.Equal(t, uint32(0), cfg.GetSequence()) // 如果 AllowRBF 为 true,应该返回 Sequence 本身的值,即 0 +} + +func TestRBFConfig_GetSequence_AllowRBFTrueWithLargeSequence(t *testing.T) { + // 测试当 AllowRBF 为 true 且 Sequence 设置为较大值时,GetSequence 的返回值 + cfg := &RBFConfig{AllowRBF: true, Sequence: 999999999} + require.Equal(t, uint32(999999999), cfg.GetSequence()) // 应该返回设置的值 +} + +func TestRBFConfig_GetSequence_AllowRBFFalseWithSequenceEqualToMaxTxInSequenceNum(t *testing.T) { + // 测试当 Sequence 设置为 wire.MaxTxInSequenceNum 且 AllowRBF 为 false 时 + cfg := &RBFConfig{AllowRBF: false, Sequence: wire.MaxTxInSequenceNum} + require.Equal(t, wire.MaxTxInSequenceNum, cfg.GetSequence()) // 由于 Sequence 为 MaxTxInSequenceNum,应该返回 MaxTxInSequenceNum +} diff --git a/signbtc.go b/signbtc.go index 4e4d479..1991d71 100644 --- a/signbtc.go +++ b/signbtc.go @@ -154,8 +154,8 @@ func SignP2PKH(signParam *SignParam, privKey *btcec.PrivateKey, compress bool) e return VerifySign(msgTx, signParam.InputOuts, prevOutFetcher, sigHashes) } -// CheckMsgTxSameWithParam 避免签名逻辑修改数量和目标位置 -func CheckMsgTxSameWithParam(msgTx *wire.MsgTx, param CustomParam, netParams *chaincfg.Params) error { +// CheckMsgTxParam 当签完名以后最好是再用这个函数检查检查,避免签名逻辑在有BUG时修改输入或输出的内容 +func (param *BitcoinTxParams) CheckMsgTxParam(msgTx *wire.MsgTx, netParams *chaincfg.Params) error { // 验证输入的长度是否匹配 if len(msgTx.TxIn) != len(param.VinList) { return errors.Errorf("input count mismatch: got %d, expected %d", len(msgTx.TxIn), len(param.VinList)) @@ -172,7 +172,7 @@ func CheckMsgTxSameWithParam(msgTx *wire.MsgTx, param CustomParam, netParams *ch return errors.Errorf("input %d outpoint-index mismatch: got %v, expected %v", idx, txVin.PreviousOutPoint.Index, input.OutPoint.Index) } // 检查 vin 的 RBF 序号是否完全匹配 - if seqNo := param.GetTxInSequenceNum(input); seqNo != txVin.Sequence { + if seqNo := param.GetTxInputSequence(input); seqNo != txVin.Sequence { return errors.Errorf("input %d tx-in-sequence mismatch: got %v, expected %v", idx, txVin.Sequence, seqNo) } } diff --git a/signbtc_test.go b/signbtc_test.go index b8d86ac..659d25e 100644 --- a/signbtc_test.go +++ b/signbtc_test.go @@ -15,7 +15,7 @@ func TestSignBTC(t *testing.T) { netParams := chaincfg.TestNet3Params - param := &gobtcsign.CustomParam{ + param := &gobtcsign.BitcoinTxParams{ VinList: []gobtcsign.VinType{ { OutPoint: *gobtcsign.MustNewOutPoint("fb87cc4010bd4a34cb4be86f37182fada63c9923ae8eae5d2f793cb5f50c6328", 0), @@ -69,7 +69,7 @@ func TestSignBTC(t *testing.T) { require.Equal(t, int64(23456), int64(param.GetFee())) //得到待签名的交易 - signParam, err := param.GetSignParam(&netParams) + signParam, err := param.CreateTxSignParams(&netParams) require.NoError(t, err) t.Log(len(signParam.InputOuts)) @@ -83,7 +83,7 @@ func TestSignBTC(t *testing.T) { //验证签名 require.NoError(t, gobtcsign.VerifySignV2(msgTx, param.GetInputList(), &netParams)) //比较信息 - require.NoError(t, gobtcsign.CheckMsgTxSameWithParam(msgTx, *param, &netParams)) + require.NoError(t, param.CheckMsgTxParam(msgTx, &netParams)) //获得交易哈希 txHash := gobtcsign.GetTxHash(msgTx) @@ -108,7 +108,7 @@ func TestSignDOGE(t *testing.T) { netParams := dogecoin.TestNetParams - param := gobtcsign.CustomParam{ + param := gobtcsign.BitcoinTxParams{ VinList: []gobtcsign.VinType{ { OutPoint: *gobtcsign.MustNewOutPoint("57a3514865d3f4c5cbd49270204aaf4928c4c10651430dcd0cb79b80cda5ef0b", 0), @@ -148,7 +148,7 @@ func TestSignDOGE(t *testing.T) { require.Equal(t, int64(345678), int64(param.GetFee())) //得到待签名的交易 - signParam, err := param.GetSignParam(&netParams) + signParam, err := param.CreateTxSignParams(&netParams) require.NoError(t, err) //签名 @@ -160,7 +160,7 @@ func TestSignDOGE(t *testing.T) { //验证签名 require.NoError(t, gobtcsign.VerifySignV2(msgTx, param.GetInputList(), &netParams)) //比较信息 - require.NoError(t, gobtcsign.CheckMsgTxSameWithParam(msgTx, param, &netParams)) + require.NoError(t, param.CheckMsgTxParam(msgTx, &netParams)) //获得交易哈希 txHash := gobtcsign.GetTxHash(msgTx) diff --git a/target.go b/target.go new file mode 100644 index 0000000..1cb970e --- /dev/null +++ b/target.go @@ -0,0 +1,55 @@ +package gobtcsign + +import ( + "bytes" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/pkg/errors" +) + +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 && len(one.Address) > 0 { + // 这里的目的不是缓存而是两个参数都可以填,但当两个参数都填的时候就得保证匹配,避免出问题 + pkScript, err := GetAddressPkScript(one.Address, netParams) + if err != nil { + return nil, errors.WithMessage(err, "wrong-address") + } + if bytes.Compare(one.PkScript, pkScript) != 0 { + return nil, errors.New("address-pk-script-mismatch") + } + return pkScript, nil + } + if len(one.PkScript) > 0 { + return one.PkScript, nil //假如有就直接返回,否则就根据地址计算 + } + if one.Address != "" { + return GetAddressPkScript(one.Address, netParams) //这里不用做缓存避免增加复杂度 + } + return nil, errors.New("no-pk-script-no-address") +} + +func (one *AddressTuple) VerifyMatch(netParams *chaincfg.Params) error { + if one.Address != "" && len(one.PkScript) > 0 { + pkScript, err := GetAddressPkScript(one.Address, netParams) + if err != nil { + return errors.WithMessage(err, "wrong-address") + } + if bytes.Compare(one.PkScript, pkScript) != 0 { + return errors.New("address-pk-script-mismatch") + } + } + return nil +} diff --git a/target_test.go b/target_test.go new file mode 100644 index 0000000..29243ce --- /dev/null +++ b/target_test.go @@ -0,0 +1,64 @@ +package gobtcsign + +import ( + "testing" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/stretchr/testify/require" +) + +func TestNewAddressTuple_ValidAddress(t *testing.T) { + // 测试有效地址 + address := "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" + + res := NewAddressTuple(address) + t.Log("address:", res.Address) + + require.Equal(t, address, res.Address) + require.NoError(t, res.VerifyMatch(&chaincfg.MainNetParams)) + + pkScript, err := res.GetPkScript(&chaincfg.MainNetParams) + require.NoError(t, err) + t.Log("pk-script:", pkScript) + + expected := []byte{118, 169, 20, 98, 233, 7, 177, 92, 191, 39, 213, 66, 83, 153, 235, 246, 240, 251, 80, 235, 184, 143, 24, 136, 172} + require.Equal(t, expected, pkScript) +} + +func TestNewAddressTuple_AddressPredefined_PkScriptPredefined(t *testing.T) { + // 测试预设 PkScript + address := "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" + + res := &AddressTuple{ + Address: address, + PkScript: []byte{118, 169, 20, 98, 233, 7, 177, 92, 191, 39, 213, 66, 83, 153, 235, 246, 240, 251, 80, 235, 184, 143, 24, 136, 172}, + } + t.Log("address:", res.Address) + t.Log("predefined pk-script:", res.PkScript) + + require.Equal(t, address, res.Address) + require.NoError(t, res.VerifyMatch(&chaincfg.MainNetParams)) + + pkScript, err := res.GetPkScript(&chaincfg.MainNetParams) + require.NoError(t, err) + + expected := []byte{118, 169, 20, 98, 233, 7, 177, 92, 191, 39, 213, 66, 83, 153, 235, 246, 240, 251, 80, 235, 184, 143, 24, 136, 172} + require.Equal(t, expected, pkScript) +} + +func TestNewAddressTuple_PkScriptPredefined(t *testing.T) { + // 测试预设 PkScript + res := &AddressTuple{ + Address: "", + PkScript: []byte{118, 169, 20, 98, 233, 7, 177, 92, 191, 39, 213, 66, 83, 153, 235, 246, 240, 251, 80, 235, 184, 143, 24, 136, 172}, + } + t.Log("predefined pk-script:", res.PkScript) + + require.NoError(t, res.VerifyMatch(&chaincfg.MainNetParams)) + + pkScript, err := res.GetPkScript(&chaincfg.MainNetParams) + require.NoError(t, err) + + expected := []byte{118, 169, 20, 98, 233, 7, 177, 92, 191, 39, 213, 66, 83, 153, 235, 246, 240, 251, 80, 235, 184, 143, 24, 136, 172} + require.Equal(t, expected, pkScript) +} diff --git a/txfee.go b/txfee.go index e040291..e28bd5d 100644 --- a/txfee.go +++ b/txfee.go @@ -15,7 +15,7 @@ import ( // 具体参考链接在 // https://github.com/btcsuite/btcwallet/blob/b4ff60753aaa3cf885fb09586755f67d41954942/wallet/txauthor/author.go#L132 // 由于是计算手续费的,因为这个交易里不应该包含找零的 output 信息,否则结果是无意义的 -func EstimateTxFee(param *CustomParam, netParams *chaincfg.Params, change *ChangeTo, feeRatePerKb btcutil.Amount, dustFee DustFee) (btcutil.Amount, error) { +func EstimateTxFee(param *BitcoinTxParams, netParams *chaincfg.Params, change *ChangeTo, feeRatePerKb btcutil.Amount, dustFee DustFee) (btcutil.Amount, error) { //通过未签名的交易预估出签名后的交易大小,这里预估值会比线上的值略微大些,误差在个位数(具体看vin和out的个数) maxSignedSize, err := EstimateTxSize(param, netParams, change) if err != nil { @@ -32,7 +32,7 @@ func EstimateTxFee(param *CustomParam, netParams *chaincfg.Params, change *Chang } // EstimateTxSize 通过未签名的交易,预估出签名后交易体的大小,结果是 v-size 的,而且略微>=实际值 -func EstimateTxSize(param *CustomParam, netParams *chaincfg.Params, change *ChangeTo) (int, error) { +func EstimateTxSize(param *BitcoinTxParams, netParams *chaincfg.Params, change *ChangeTo) (int, error) { var scripts = make([][]byte, 0, len(param.VinList)) for _, txIn := range param.VinList { pkScript, err := txIn.Sender.GetPkScript(netParams) diff --git a/txfee_test.go b/txfee_test.go index 82e6413..67f58e0 100644 --- a/txfee_test.go +++ b/txfee_test.go @@ -27,7 +27,7 @@ func TestEstimateTxSize(t *testing.T) { netParams := chaincfg.MainNetParams - param := &CustomParam{ + param := &BitcoinTxParams{ VinList: []VinType{ { OutPoint: *MustNewOutPoint("6e8ca976bcd473565c1d6d96483c0ce2225b91c87661f03bdf6e5afc6511b17f", 1), @@ -78,7 +78,7 @@ func TestEstimateTxSize_VIN_1_P2PKH(t *testing.T) { netParams := chaincfg.MainNetParams - param := &CustomParam{ + param := &BitcoinTxParams{ VinList: []VinType{ { OutPoint: *MustNewOutPoint("c9a45420a135a5424c7b75a70f860a0dc7d9716496a782e55e7a1520971cc9a4", 0), @@ -114,7 +114,7 @@ func TestEstimateTxFee(t *testing.T) { netParams := dogecoin.TestNetParams - param := &CustomParam{ + param := &BitcoinTxParams{ VinList: []VinType{ { OutPoint: *MustNewOutPoint("5ae74f2d6c4a0513e3c75484a726820c2b0653c2b26352afe97f4bf813dcf859", 0), diff --git a/utils.go b/utils.go index 55a09c3..1de9932 100644 --- a/utils.go +++ b/utils.go @@ -78,6 +78,18 @@ func MustGetPkScript(address btcutil.Address) []byte { return pkScript } +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 的输出位置,比如一个交易中有多个输出,这里要选择输出的位置 + ) +} + // NewInputOuts 因为 SignParam 的成员里有 []*wire.TxOut 类型的前置输出字段 // 但教程常用的是 pkScripts [][]byte 和 amounts []int64 两个属性 // 因此这里写个转换逻辑 diff --git a/utxo_from.go b/utxo_from.go index b5ae2a3..0993b93 100644 --- a/utxo_from.go +++ b/utxo_from.go @@ -8,7 +8,7 @@ import ( ) type GetUtxoFromInterface interface { - GetUtxoFrom(utxo wire.OutPoint) (*UtxoSenderAmountTuple, error) + GetUtxoFrom(utxo wire.OutPoint) (*UtxoSenderAmount, error) } type UtxoFromClient struct { @@ -19,7 +19,7 @@ func NewUtxoFromClient(client *rpcclient.Client) *UtxoFromClient { return &UtxoFromClient{client: client} } -func (uc *UtxoFromClient) GetUtxoFrom(utxo wire.OutPoint) (*UtxoSenderAmountTuple, error) { +func (uc *UtxoFromClient) GetUtxoFrom(utxo wire.OutPoint) (*UtxoSenderAmount, error) { preTxn, err := GetRawTransaction(uc.client, utxo.Hash.String()) if err != nil { return nil, errors.WithMessage(err, "get-raw-txn") @@ -31,34 +31,34 @@ func (uc *UtxoFromClient) GetUtxoFrom(utxo wire.OutPoint) (*UtxoSenderAmountTupl return nil, errors.WithMessage(err, "get-pre-amt") } - utxoFrom := NewUtxoSenderAmountTuple( + utxoFrom := NewUtxoSenderAmount( NewAddressTuple(preOut.ScriptPubKey.Address), int64(preAmt), ) return utxoFrom, nil } -type UtxoSenderAmountTuple struct { +type UtxoSenderAmount struct { sender *AddressTuple amount int64 } -func NewUtxoSenderAmountTuple(sender *AddressTuple, amount int64) *UtxoSenderAmountTuple { - return &UtxoSenderAmountTuple{ +func NewUtxoSenderAmount(sender *AddressTuple, amount int64) *UtxoSenderAmount { + return &UtxoSenderAmount{ sender: sender, amount: amount, } } -type UtxoFromOutMap struct { - mxp map[wire.OutPoint]*UtxoSenderAmountTuple +type OutPointUtxoSenderAmountMap struct { + mxp map[wire.OutPoint]*UtxoSenderAmount } -func NewUtxoFromOutMap(mxp map[wire.OutPoint]*UtxoSenderAmountTuple) *UtxoFromOutMap { - return &UtxoFromOutMap{mxp: mxp} +func NewOutPointUtxoSenderAmountMap(mxp map[wire.OutPoint]*UtxoSenderAmount) *OutPointUtxoSenderAmountMap { + return &OutPointUtxoSenderAmountMap{mxp: mxp} } -func (uc UtxoFromOutMap) GetUtxoFrom(utxo wire.OutPoint) (*UtxoSenderAmountTuple, error) { +func (uc OutPointUtxoSenderAmountMap) GetUtxoFrom(utxo wire.OutPoint) (*UtxoSenderAmount, error) { utxoFrom, ok := uc.mxp[utxo] if !ok { return nil, errors.Errorf("not-exist-utxo[%s:%d]", utxo.Hash.String(), utxo.Index) diff --git a/utxo_from_test.go b/utxo_from_test.go index 00fa510..0d152b1 100644 --- a/utxo_from_test.go +++ b/utxo_from_test.go @@ -9,5 +9,5 @@ func TestUtxoFromClient_GetUtxoFrom(t *testing.T) { } func TestUtxoFromOutMap_GetUtxoFrom(t *testing.T) { - var _ GetUtxoFromInterface = &UtxoFromOutMap{} + var _ GetUtxoFromInterface = &OutPointUtxoSenderAmountMap{} }