-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
yangyile
committed
Nov 21, 2024
1 parent
e61067b
commit a56ce48
Showing
9 changed files
with
194 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package dusts | ||
|
||
import ( | ||
"github.com/btcsuite/btcd/btcutil" | ||
"github.com/btcsuite/btcd/wire" | ||
) | ||
|
||
type DustLimit struct { | ||
check func(output *wire.TxOut, relayFeePerKb btcutil.Amount) bool | ||
} | ||
|
||
func NewDustLimit(check func(output *wire.TxOut, relayFeePerKb btcutil.Amount) bool) *DustLimit { | ||
return &DustLimit{check: check} | ||
} | ||
|
||
func (D *DustLimit) IsDustOutput(output *wire.TxOut, relayFeePerKb btcutil.Amount) bool { | ||
return D.check(output, relayFeePerKb) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package gobtcsign | ||
|
||
import ( | ||
"github.com/btcsuite/btcwallet/wallet/txrules" | ||
"github.com/yyle88/gobtcsign/internal/dusts" | ||
) | ||
|
||
type DustFee = dusts.DustFee | ||
|
||
func NewDustFee() DustFee { | ||
return dusts.NewDustFee() //比特币没有软灰尘收费,这里配置个空的(因为doge里有,这里为了逻辑相通,而给个空的) | ||
} | ||
|
||
type DustLimit = dusts.DustLimit | ||
|
||
func NewDustLimit() *DustLimit { | ||
return dusts.NewDustLimit(txrules.IsDustOutput) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package gobtcsign | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/btcsuite/btcd/chaincfg" | ||
"github.com/btcsuite/btcd/wire" | ||
"github.com/stretchr/testify/require" | ||
"github.com/yyle88/gobtcsign/dogecoin" | ||
) | ||
|
||
func TestIsDustOutput_BTC(t *testing.T) { | ||
netParams := chaincfg.MainNetParams | ||
|
||
dustLimit := NewDustLimit() | ||
|
||
const address = "1MAgFFbMpgx6hTPvK3HY348Bbnwk6RFHm5" | ||
const feeRate = 1234000 //假设手续费是这个数 | ||
|
||
amount := int64(100000) | ||
for { | ||
output := wire.NewTxOut(amount, MustGetPkScript(MustNewAddress(address, &netParams))) | ||
if !dustLimit.IsDustOutput(output, feeRate) { | ||
break | ||
} | ||
amount++ | ||
} | ||
t.Log("amount:", amount, "IS NOT DUST IN BTC") | ||
t.Log("amount:", amount-1, "IS DUST IN BTC") | ||
} | ||
|
||
func TestIsDustOutput_BTC_2(t *testing.T) { | ||
netParams := chaincfg.MainNetParams | ||
|
||
dustLimit := NewDustLimit() | ||
|
||
const address = "1MAgFFbMpgx6hTPvK3HY348Bbnwk6RFHm5" | ||
const feeRate = 1000 //假设手续费是这个数 | ||
|
||
amount := int64(1) | ||
for { | ||
output := wire.NewTxOut(amount, MustGetPkScript(MustNewAddress(address, &netParams))) | ||
if !dustLimit.IsDustOutput(output, feeRate) { | ||
break | ||
} | ||
amount++ | ||
} | ||
require.Equal(t, int64(546), amount) //这就是有的教程和代码里写 546 的依据 | ||
// 546 是比特币网络中一个常见的 Dust Threshold(灰尘阈值),其来源与比特币的交易手续费和 UTXO(未花费交易输出,Unspent Transaction Output)管理策略相关。 | ||
// 具体来说,546 是在默认设置下,比特币 Core 客户端用于计算 Dust Output(灰尘输出)的默认值。 | ||
// 比特币网络中最早的版本采用的是类似值,后来进行了微调,但很多第三方实现(例如钱包)仍然保留 546 的惯例。 | ||
t.Log("amount:", amount, "IS NOT DUST IN BTC") | ||
t.Log("amount:", amount-1, "IS DUST IN BTC") | ||
} | ||
|
||
func TestIsDustOutput_BTC_3(t *testing.T) { | ||
netParams := chaincfg.TestNet3Params | ||
|
||
dustLimit := NewDustLimit() | ||
|
||
const address = "tb1qy2f7svy0hp57wz3p6hvu0vf5fys750932ct3q5" | ||
const feeRate = 1000 //假设手续费是这个数 | ||
|
||
amount := int64(1) | ||
for { | ||
output := wire.NewTxOut(amount, MustGetPkScript(MustNewAddress(address, &netParams))) | ||
if !dustLimit.IsDustOutput(output, feeRate) { | ||
break | ||
} | ||
amount++ | ||
} | ||
require.Equal(t, int64(294), amount) //当地址类型为 P2WPKH 时由于其 输入大小比 P2PKH 小得多,因此灰尘阈值也更小些 | ||
// 因此有的代码里会这样写 | ||
// DustLimit returns the output dust limit (lowest possible satoshis in a UTXO) for the address type. | ||
// func (a Address) DustLimit() int64 { | ||
// switch a.encodedType { | ||
// case AddressP2TR: | ||
// return 330 | ||
// case AddressP2WPKH: | ||
// return 294 | ||
// default: | ||
// return 546 | ||
// } | ||
// } | ||
// 但实际上大家还都是使用 out >= 546 作为限制 | ||
t.Log("amount:", amount, "IS NOT DUST IN BTC") | ||
t.Log("amount:", amount-1, "IS DUST IN BTC") | ||
} | ||
|
||
func TestIsDustOutput_DOGE(t *testing.T) { | ||
netParams := dogecoin.TestNetParams | ||
|
||
dustLimit := dogecoin.NewDogeDustLimit() | ||
{ | ||
const amount = 500 | ||
const address = "nr2XmwqixAdXwkgVyshx3HPFRMfXugM8Zi" | ||
const feeRate = 0 //这个不重要 | ||
|
||
output := wire.NewTxOut(amount, MustGetPkScript(MustNewAddress(address, &netParams))) | ||
require.True(t, dustLimit.IsDustOutput(output, feeRate)) | ||
t.Log("amount:", amount, "IS DUST IN DOGE") | ||
} | ||
{ | ||
const amount = 100000 | ||
const address = "nqedQEDCgwrXqLd2JrrpCfD9Tcz384rdHA" | ||
const feeRate = 0 //这个不重要 | ||
|
||
output := wire.NewTxOut(amount, MustGetPkScript(MustNewAddress(address, &netParams))) | ||
require.False(t, dustLimit.IsDustOutput(output, feeRate)) | ||
t.Log("amount:", amount, "IS NOT DUST IN DOGE") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,19 +8,15 @@ import ( | |
"github.com/btcsuite/btcwallet/wallet/txrules" | ||
"github.com/btcsuite/btcwallet/wallet/txsizes" | ||
"github.com/pkg/errors" | ||
"github.com/yyle88/gobtcsign/internal/dusts" | ||
) | ||
|
||
type DustFee = dusts.DustFee | ||
|
||
func NewDustFee() DustFee { | ||
return dusts.NewDustFee() //比特币没有软灰尘收费,这里配置个空的(因为doge里有,这里为了逻辑相通,而给个空的) | ||
} | ||
|
||
// EstimateTxFee 通过未签名且未找零的交易,预估出需要的费用 | ||
// 代码基本是仿照这里的 github.com/btcsuite/btcwallet/wallet/[email protected]/author.go 里面 NewUnsignedTransaction 的逻辑 | ||
// 具体参考链接在 | ||
// 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) { | ||
//通过未签名的交易预估出签名后的交易大小,这里预估值会比线上的值略微大些,误差在个位数(具体看vin和out的个数) | ||
maxSignedSize, err := EstimateTxSize(param, netParams, change) | ||
if err != nil { | ||
return 0, errors.WithMessage(err, "wrong estimate-tx-size") | ||
|
@@ -55,6 +51,8 @@ func EstimateTxSize(param *CustomParam, netParams *chaincfg.Params, change *Chan | |
// EstimateSize 计算交易的预估大小(在最坏情况下的预估大小) | ||
// 这个函数还是抄的 github.com/btcsuite/btcwallet/wallet/[email protected]/author.go 里面 NewUnsignedTransaction 的逻辑 | ||
// 详细细节见 https://github.com/btcsuite/btcwallet/blob/master/wallet/txauthor/author.go 这里的逻辑 | ||
// 具体参考链接在 | ||
// https://github.com/btcsuite/btcwallet/blob/b4ff60753aaa3cf885fb09586755f67d41954942/wallet/txauthor/author.go#L93 | ||
// 是否填写找零信息,得依据 outputs 里面是否已经包含找零信息 | ||
func EstimateSize(scripts [][]byte, outputs []*wire.TxOut, change *ChangeTo) (int, error) { | ||
changeScriptSize, err := change.GetChangeScriptSize() | ||
|
@@ -81,21 +79,25 @@ func EstimateSize(scripts [][]byte, outputs []*wire.TxOut, change *ChangeTo) (in | |
} | ||
|
||
// 仿照这个函数 txauthor.NewUnsignedTransaction() 里的预估逻辑 | ||
// 通过未签名的交易信息,估算出要发送上链的交易体的大小,其中每个vin/out的误差至多是个位数的,累计起来误差不大,能够用来预估交易费用 | ||
maxSignedSize := txsizes.EstimateVirtualSize( | ||
p2pkh, p2tr, p2wpkh, nested, outputs, changeScriptSize, | ||
) | ||
return maxSignedSize, nil | ||
} | ||
|
||
// ChangeTo 找零信息,这里为了方便使用,就设置两个属性二选一即可,优先使用公钥哈希,其次使用钱包地址 | ||
type ChangeTo struct { | ||
PkScript []byte //允许为空,当两者皆为空时表示没有找零输出 | ||
AddressX btcutil.Address //允许为空,当两者皆为空时表示没有找零输出 | ||
} | ||
|
||
// NewNoChange 当不需要找零时两个成员都是空 | ||
func NewNoChange() *ChangeTo { | ||
return &ChangeTo{} | ||
} | ||
|
||
// GetChangeScriptSize 计算出找零输出的size | ||
func (T *ChangeTo) GetChangeScriptSize() (int, error) { | ||
if T.PkScript != nil { //优先使用找零脚本进行计算 | ||
return CalculateChangePkScriptSize(T.PkScript) | ||
|
@@ -106,6 +108,7 @@ func (T *ChangeTo) GetChangeScriptSize() (int, error) { | |
return 0, nil //说明不需要找零输出,就返回0 | ||
} | ||
|
||
// CalculateChangeAddressSize 根据钱包地址计算出找零输出的size | ||
func CalculateChangeAddressSize(address btcutil.Address) (int, error) { | ||
pkScript, err := txscript.PayToAddrScript(address) | ||
if err != nil { | ||
|
@@ -114,6 +117,10 @@ func CalculateChangeAddressSize(address btcutil.Address) (int, error) { | |
return CalculateChangePkScriptSize(pkScript) | ||
} | ||
|
||
// CalculateChangePkScriptSize 根据公钥哈希计算出找零输出的size | ||
// 具体参考链接在 | ||
// https://github.com/btcsuite/btcwallet/blob/b4ff60753aaa3cf885fb09586755f67d41954942/wallet/createtx.go#L457 | ||
// 当然这里代码略有差异,但含义是相同的 | ||
func CalculateChangePkScriptSize(pkScript []byte) (int, error) { | ||
var size int | ||
switch { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters