Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: feed sataoshi/B to zetacore and check actual outTx size #1243

Merged
merged 9 commits into from
Oct 11, 2023
7 changes: 4 additions & 3 deletions zetaclient/bitcoin_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const (
minConfirmations = 0
maxHeightDiff = 10000
dustOffset = 2000
bytesPerKB = 1000
)

func (ob *BitcoinChainClient) SetCoreParams(params observertypes.CoreParams) {
Expand Down Expand Up @@ -459,14 +460,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(bytesPerKB))
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
Expand Down
43 changes: 30 additions & 13 deletions zetaclient/btc_signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ import (

const (
maxNoOfInputsPerTx = 20
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 {
Expand Down Expand Up @@ -59,9 +66,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()) * outTxBytesMax / 1e8
nonceMark := NonceMarkAmount(nonce)

// refresh unspent UTXOs and continue with keysign regardless of error
Expand Down Expand Up @@ -93,16 +100,25 @@ func (signer *BTCSigner) SignWithdrawTx(to *btcutil.AddressWitnessPubKeyHash, am
return nil, err
}

// fee checking
fees := new(big.Int).Mul(big.NewInt(int64(tx.SerializeSize())), 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) {
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))
// size checking
txSize := uint64(tx.SerializeSize())
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 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)
txSize = outTxBytesMin
}
if txSize > outTxBytesCap { // in case of accident
signer.logger.Warn().Msgf("sizeLimit %d is greater than outTxBytesCap %d; use outTxBytesCap", sizeLimit, outTxBytesCap)
txSize = outTxBytesCap
}

// fee calculation
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())

// calculate remaining btc to TSS self
tssAddrWPKH := signer.tssSigner.BTCAddressWitnessPubkeyHash()
payToSelf, err := payToWitnessPubKeyHashScript(tssAddrWPKH.WitnessProgram())
Expand Down Expand Up @@ -253,8 +269,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
}
Expand All @@ -273,7 +290,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)
Expand Down
Loading