From 756d2cd1da51d341f5a8095929bc55be6395cd05 Mon Sep 17 00:00:00 2001 From: charliec Date: Thu, 5 Oct 2023 11:00:22 -0500 Subject: [PATCH 1/5] feed sataoshi/B to zetacore and check size limit --- zetaclient/bitcoin_client.go | 6 +++--- zetaclient/btc_signer.go | 28 ++++++++++++++++++++++------ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/zetaclient/bitcoin_client.go b/zetaclient/bitcoin_client.go index 72bce508cb..a0cd8b19cf 100644 --- a/zetaclient/bitcoin_client.go +++ b/zetaclient/bitcoin_client.go @@ -459,14 +459,14 @@ func (ob *BitcoinChainClient) PostGasPrice() error { if feeResult.Errors != nil || feeResult.FeeRate == nil { return fmt.Errorf("error getting gas price: %s", feeResult.Errors) } - gasPrice := big.NewFloat(0) - gasPriceU64, _ := gasPrice.Mul(big.NewFloat(*feeResult.FeeRate), big.NewFloat(1e8)).Uint64() + feeRate := new(big.Int).SetInt64(int64(*feeResult.FeeRate * 1e8)) + feeRatePerByte := new(big.Int).Div(feeRate, big.NewInt(1000)) bn, err := ob.rpcClient.GetBlockCount() if err != nil { return err } // #nosec G701 always positive - zetaHash, err := ob.zetaClient.PostGasPrice(ob.chain, gasPriceU64, "100", uint64(bn)) + zetaHash, err := ob.zetaClient.PostGasPrice(ob.chain, feeRatePerByte.Uint64(), "100", uint64(bn)) if err != nil { ob.logger.WatchGasPrice.Err(err).Msg("PostGasPrice:") return err diff --git a/zetaclient/btc_signer.go b/zetaclient/btc_signer.go index a31f77bc85..1880089ec4 100644 --- a/zetaclient/btc_signer.go +++ b/zetaclient/btc_signer.go @@ -23,6 +23,8 @@ import ( const ( maxNoOfInputsPerTx = 20 + minFee = 5_000 // 0.00005 BTC + estimateSizeKB = 2 // 2K is a conservative estimate for a 20-input, 3-output tx ) type BTCSigner struct { @@ -59,9 +61,9 @@ func NewBTCSigner(cfg config.BTCConfig, tssSigner TSSSigner, logger zerolog.Logg } // SignWithdrawTx receives utxos sorted by value, amount in BTC, feeRate in BTC per Kb -func (signer *BTCSigner) SignWithdrawTx(to *btcutil.AddressWitnessPubKeyHash, amount float64, gasPrice *big.Int, btcClient *BitcoinChainClient, height uint64, nonce uint64, chain *common.Chain) (*wire.MsgTx, error) { - estimateFee := 0.0001 // 10,000 sats, should be good for testnet - minFee := 0.00005 +func (signer *BTCSigner) SignWithdrawTx(to *btcutil.AddressWitnessPubKeyHash, amount float64, gasPrice *big.Int, sizeLimit uint64, + btcClient *BitcoinChainClient, height uint64, nonce uint64, chain *common.Chain) (*wire.MsgTx, error) { + estimateFee := float64(gasPrice.Uint64()) * estimateSizeKB / 1e8 nonceMark := NonceMarkAmount(nonce) // refresh unspent UTXOs and continue with keysign regardless of error @@ -93,8 +95,15 @@ func (signer *BTCSigner) SignWithdrawTx(to *btcutil.AddressWitnessPubKeyHash, am return nil, err } + // size checking + txSize := uint64(tx.SerializeSize()) + // if txSize > sizeLimit { + // fmt.Printf("tx size %d is greater than sizeLimit %d; use sizeLimit", txSize, sizeLimit) + // txSize = sizeLimit + // } + // fee checking - fees := new(big.Int).Mul(big.NewInt(int64(tx.SerializeSize())), gasPrice) + fees := new(big.Int).Mul(big.NewInt(int64(txSize)), gasPrice) fees.Div(fees, big.NewInt(1000)) //FIXME: feeRate KB is 1000B or 1024B? // #nosec G701 always in range if fees.Int64() < int64(minFee*1e8) { @@ -102,6 +111,12 @@ func (signer *BTCSigner) SignWithdrawTx(to *btcutil.AddressWitnessPubKeyHash, am // #nosec G701 always in range fees = big.NewInt(int64(minFee * 1e8)) } + // #nosec G701 always in range + if fees.Int64() > int64(estimateFee*1e8) { + fmt.Printf("fees %d is greater than estimateFee %f; use estimateFee", fees, estimateFee*1e8) + // #nosec G701 always in range + fees = big.NewInt(int64(estimateFee * 1e8)) + } // calculate remaining btc to TSS self tssAddrWPKH := signer.tssSigner.BTCAddressWitnessPubkeyHash() @@ -253,8 +268,9 @@ func (signer *BTCSigner) TryProcessOutTx(send *types.CrossChainTx, outTxMan *Out return } + sizelimit := params.OutboundTxGasLimit gasprice, ok := new(big.Int).SetString(params.OutboundTxGasPrice, 10) - if !ok { + if !ok || gasprice.Cmp(big.NewInt(0)) < 0 { logger.Error().Msgf("cannot convert gas price %s ", params.OutboundTxGasPrice) return } @@ -273,7 +289,7 @@ func (signer *BTCSigner) TryProcessOutTx(send *types.CrossChainTx, outTxMan *Out logger.Info().Msgf("SignWithdrawTx: to %s, value %d sats", addr.EncodeAddress(), params.Amount.Uint64()) logger.Info().Msgf("using utxos: %v", btcClient.utxos) - tx, err := signer.SignWithdrawTx(to, float64(params.Amount.Uint64())/1e8, gasprice, btcClient, height, + tx, err := signer.SignWithdrawTx(to, float64(params.Amount.Uint64())/1e8, gasprice, sizelimit, btcClient, height, outboundTxTssNonce, &btcClient.chain) if err != nil { logger.Warn().Err(err).Msgf("SignOutboundTx error: nonce %d chain %d", outboundTxTssNonce, params.ReceiverChainId) From ba9b730551de27785f61947615658e68acb81ab0 Mon Sep 17 00:00:00 2001 From: charliec Date: Thu, 5 Oct 2023 11:15:11 -0500 Subject: [PATCH 2/5] removed fee cap --- zetaclient/btc_signer.go | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/zetaclient/btc_signer.go b/zetaclient/btc_signer.go index 1880089ec4..4a137da382 100644 --- a/zetaclient/btc_signer.go +++ b/zetaclient/btc_signer.go @@ -97,10 +97,10 @@ func (signer *BTCSigner) SignWithdrawTx(to *btcutil.AddressWitnessPubKeyHash, am // size checking txSize := uint64(tx.SerializeSize()) - // if txSize > sizeLimit { - // fmt.Printf("tx size %d is greater than sizeLimit %d; use sizeLimit", txSize, sizeLimit) - // txSize = sizeLimit - // } + if txSize > sizeLimit { + fmt.Printf("tx size %d is greater than sizeLimit %d; use sizeLimit", txSize, sizeLimit) + txSize = sizeLimit + } // fee checking fees := new(big.Int).Mul(big.NewInt(int64(txSize)), gasPrice) @@ -111,12 +111,6 @@ func (signer *BTCSigner) SignWithdrawTx(to *btcutil.AddressWitnessPubKeyHash, am // #nosec G701 always in range fees = big.NewInt(int64(minFee * 1e8)) } - // #nosec G701 always in range - if fees.Int64() > int64(estimateFee*1e8) { - fmt.Printf("fees %d is greater than estimateFee %f; use estimateFee", fees, estimateFee*1e8) - // #nosec G701 always in range - fees = big.NewInt(int64(estimateFee * 1e8)) - } // calculate remaining btc to TSS self tssAddrWPKH := signer.tssSigner.BTCAddressWitnessPubkeyHash() From f279fa87f6c7b73e1ffa34658c781a601e01ce34 Mon Sep 17 00:00:00 2001 From: charliec Date: Fri, 6 Oct 2023 17:01:29 -0500 Subject: [PATCH 3/5] replaced magic number 1000 with constant bytesPerKB --- zetaclient/bitcoin_client.go | 3 ++- zetaclient/btc_signer.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/zetaclient/bitcoin_client.go b/zetaclient/bitcoin_client.go index a0cd8b19cf..943ae8710b 100644 --- a/zetaclient/bitcoin_client.go +++ b/zetaclient/bitcoin_client.go @@ -73,6 +73,7 @@ const ( minConfirmations = 0 maxHeightDiff = 10000 dustOffset = 2000 + bytesPerKB = 1000 ) func (ob *BitcoinChainClient) SetCoreParams(params observertypes.CoreParams) { @@ -460,7 +461,7 @@ func (ob *BitcoinChainClient) PostGasPrice() error { return fmt.Errorf("error getting gas price: %s", feeResult.Errors) } feeRate := new(big.Int).SetInt64(int64(*feeResult.FeeRate * 1e8)) - feeRatePerByte := new(big.Int).Div(feeRate, big.NewInt(1000)) + feeRatePerByte := new(big.Int).Div(feeRate, big.NewInt(bytesPerKB)) bn, err := ob.rpcClient.GetBlockCount() if err != nil { return err diff --git a/zetaclient/btc_signer.go b/zetaclient/btc_signer.go index 4a137da382..889460819e 100644 --- a/zetaclient/btc_signer.go +++ b/zetaclient/btc_signer.go @@ -104,7 +104,7 @@ func (signer *BTCSigner) SignWithdrawTx(to *btcutil.AddressWitnessPubKeyHash, am // fee checking fees := new(big.Int).Mul(big.NewInt(int64(txSize)), gasPrice) - fees.Div(fees, big.NewInt(1000)) //FIXME: feeRate KB is 1000B or 1024B? + fees.Div(fees, big.NewInt(bytesPerKB)) // #nosec G701 always in range if fees.Int64() < int64(minFee*1e8) { fmt.Printf("fees %d is less than minFee %f; use minFee", fees, minFee*1e8) From a2c2e81ffbbd0b7326e5394f5b3155f3cff09f7b Mon Sep 17 00:00:00 2001 From: charliec Date: Sun, 8 Oct 2023 16:56:39 -0500 Subject: [PATCH 4/5] put lowerbound, upperbound limit on sizeLimit --- zetaclient/btc_signer.go | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/zetaclient/btc_signer.go b/zetaclient/btc_signer.go index 889460819e..ab9e00411c 100644 --- a/zetaclient/btc_signer.go +++ b/zetaclient/btc_signer.go @@ -23,8 +23,13 @@ import ( const ( maxNoOfInputsPerTx = 20 - minFee = 5_000 // 0.00005 BTC - estimateSizeKB = 2 // 2K is a conservative estimate for a 20-input, 3-output tx + outTxBytesMin = 400 // 500B is a conservative estimate for a 2-input, 3-output SegWit tx + outTxBytesMax = 4_000 // 4KB is a conservative estimate for a 21-input, 3-output SegWit tx + outTxBytesCap = 10_000 // in case of accident + + // for ZRC20 configuration + bytesPerInput = 150 // each input is about 150 bytes + ZRC20GasLimit = outTxBytesMin + bytesPerInput*8 // 1600B a suggested ZRC20 GAS_LIMIT for a 10-input, 3-output SegWit tx ) type BTCSigner struct { @@ -63,7 +68,7 @@ func NewBTCSigner(cfg config.BTCConfig, tssSigner TSSSigner, logger zerolog.Logg // SignWithdrawTx receives utxos sorted by value, amount in BTC, feeRate in BTC per Kb func (signer *BTCSigner) SignWithdrawTx(to *btcutil.AddressWitnessPubKeyHash, amount float64, gasPrice *big.Int, sizeLimit uint64, btcClient *BitcoinChainClient, height uint64, nonce uint64, chain *common.Chain) (*wire.MsgTx, error) { - estimateFee := float64(gasPrice.Uint64()) * estimateSizeKB / 1e8 + estimateFee := float64(gasPrice.Uint64()) * outTxBytesMax / 1e8 nonceMark := NonceMarkAmount(nonce) // refresh unspent UTXOs and continue with keysign regardless of error @@ -97,20 +102,22 @@ func (signer *BTCSigner) SignWithdrawTx(to *btcutil.AddressWitnessPubKeyHash, am // size checking txSize := uint64(tx.SerializeSize()) - if txSize > sizeLimit { - fmt.Printf("tx size %d is greater than sizeLimit %d; use sizeLimit", txSize, sizeLimit) - txSize = sizeLimit + if txSize > sizeLimit { // ZRC20 'withdraw' charged less fee from end user + signer.logger.Info().Msgf("sizeLimit %d is less than txSize %d for nonce %d", sizeLimit, txSize, nonce) + } + if sizeLimit < outTxBytesMin { // outbound shouldn't be blocked a low sizeLimit + signer.logger.Warn().Msgf("sizeLimit %d is less than outTxBytesMin %d; use outTxBytesMin", sizeLimit, outTxBytesMin) + sizeLimit = outTxBytesMin + } + if sizeLimit > outTxBytesCap { // in case of accident + signer.logger.Warn().Msgf("sizeLimit %d is greater than outTxBytesCap %d; use outTxBytesCap", sizeLimit, outTxBytesCap) + sizeLimit = outTxBytesCap } - // fee checking - fees := new(big.Int).Mul(big.NewInt(int64(txSize)), gasPrice) + // fee calculation + fees := new(big.Int).Mul(big.NewInt(int64(sizeLimit)), gasPrice) fees.Div(fees, big.NewInt(bytesPerKB)) - // #nosec G701 always in range - if fees.Int64() < int64(minFee*1e8) { - fmt.Printf("fees %d is less than minFee %f; use minFee", fees, minFee*1e8) - // #nosec G701 always in range - fees = big.NewInt(int64(minFee * 1e8)) - } + signer.logger.Info().Msgf("bitcoin outTx nonce %d gasPrice %s size %d fees %s", nonce, gasPrice.String(), txSize, fees.String()) // calculate remaining btc to TSS self tssAddrWPKH := signer.tssSigner.BTCAddressWitnessPubkeyHash() From d9dffa2a98aeb18196a14dcd512856a770245fa0 Mon Sep 17 00:00:00 2001 From: charliec Date: Sun, 8 Oct 2023 17:23:51 -0500 Subject: [PATCH 5/5] use actual txSize for fee calculation --- zetaclient/btc_signer.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/zetaclient/btc_signer.go b/zetaclient/btc_signer.go index ab9e00411c..a2b39f473f 100644 --- a/zetaclient/btc_signer.go +++ b/zetaclient/btc_signer.go @@ -105,17 +105,17 @@ func (signer *BTCSigner) SignWithdrawTx(to *btcutil.AddressWitnessPubKeyHash, am if txSize > sizeLimit { // ZRC20 'withdraw' charged less fee from end user signer.logger.Info().Msgf("sizeLimit %d is less than txSize %d for nonce %d", sizeLimit, txSize, nonce) } - if sizeLimit < outTxBytesMin { // outbound shouldn't be blocked a low sizeLimit + if txSize < outTxBytesMin { // outbound shouldn't be blocked a low sizeLimit signer.logger.Warn().Msgf("sizeLimit %d is less than outTxBytesMin %d; use outTxBytesMin", sizeLimit, outTxBytesMin) - sizeLimit = outTxBytesMin + txSize = outTxBytesMin } - if sizeLimit > outTxBytesCap { // in case of accident + if txSize > outTxBytesCap { // in case of accident signer.logger.Warn().Msgf("sizeLimit %d is greater than outTxBytesCap %d; use outTxBytesCap", sizeLimit, outTxBytesCap) - sizeLimit = outTxBytesCap + txSize = outTxBytesCap } // fee calculation - fees := new(big.Int).Mul(big.NewInt(int64(sizeLimit)), gasPrice) + fees := new(big.Int).Mul(big.NewInt(int64(txSize)), gasPrice) fees.Div(fees, big.NewInt(bytesPerKB)) signer.logger.Info().Msgf("bitcoin outTx nonce %d gasPrice %s size %d fees %s", nonce, gasPrice.String(), txSize, fees.String())