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

refactor(zetaclient): improve some general structure of the codebase #2032

Merged
merged 16 commits into from
Apr 26, 2024
831 changes: 452 additions & 379 deletions zetaclient/bitcoin/bitcoin_client.go

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions zetaclient/bitcoin/bitcoin_client_db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ func (suite *BitcoinClientDBTestSuite) SetupTest() {
}
}

func (suite *BitcoinClientDBTestSuite) TearDownSuite() {
}
func (suite *BitcoinClientDBTestSuite) TearDownSuite() {}

func (suite *BitcoinClientDBTestSuite) TestSubmittedTx() {
var submittedTransactions []clienttypes.TransactionResultSQLType
Expand Down
2 changes: 1 addition & 1 deletion zetaclient/bitcoin/bitcoin_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ func TestGetBtcEvent(t *testing.T) {
// expected result
memo, err := hex.DecodeString(tx.Vout[1].ScriptPubKey.Hex[4:])
require.NoError(t, err)
eventExpected := &BTCInTxEvnet{
eventExpected := &BTCInTxEvent{
FromAddress: "bc1q68kxnq52ahz5vd6c8czevsawu0ux9nfrzzrh6e",
ToAddress: tssAddress,
Value: tx.Vout[0].Value - depositorFee, // 7008 sataoshis
Expand Down
11 changes: 7 additions & 4 deletions zetaclient/bitcoin/bitcoin_signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
consolidationRank = 10
)

var _ interfaces.ChainSigner = &BTCSigner{}

// BTCSigner deals with signing BTC transactions and implements the ChainSigner interface
type BTCSigner struct {
tssSigner interfaces.TSSSigner
Expand All @@ -48,8 +50,6 @@
coreContext *corecontext.ZetaCoreContext
}

var _ interfaces.ChainSigner = &BTCSigner{}

func NewBTCSigner(
cfg config.BTCConfig,
tssSigner interfaces.TSSSigner,
Expand Down Expand Up @@ -247,11 +247,12 @@
return nil, err
}
}
tss, ok := signer.tssSigner.(*tss.TSS)

tssSigner, ok := signer.tssSigner.(*tss.TSS)

Check warning on line 251 in zetaclient/bitcoin/bitcoin_signer.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/bitcoin/bitcoin_signer.go#L251

Added line #L251 was not covered by tests
if !ok {
return nil, fmt.Errorf("tssSigner is not a TSS")
}
sig65Bs, err := tss.SignBatch(witnessHashes, height, nonce, chain)
sig65Bs, err := tssSigner.SignBatch(witnessHashes, height, nonce, chain)

Check warning on line 255 in zetaclient/bitcoin/bitcoin_signer.go

View check run for this annotation

Codecov / codecov/patch

zetaclient/bitcoin/bitcoin_signer.go#L255

Added line #L255 was not covered by tests
if err != nil {
return nil, fmt.Errorf("SignBatch error: %v", err)
}
Expand All @@ -270,6 +271,7 @@
txWitness := wire.TxWitness{append(sig.Serialize(), byte(hashType)), pkCompressed}
tx.TxIn[ix].Witness = txWitness
}

return tx, nil
}

Expand All @@ -288,6 +290,7 @@
if err != nil {
return err
}

signer.logger.Info().Msgf("Broadcasting BTC tx , hash %s ", hash)
return nil
}
Expand Down
5 changes: 1 addition & 4 deletions zetaclient/bitcoin/bitcoin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ type BTCSignTestSuite struct {

const (
prevOut = "07a84f4bd45a633e93871be5c98d958afd13a37f3cf5010f40eec0840d19f5fa"
// tb1q7r6lnqjhvdjuw9uf4ehx7fs0euc6cxnqz7jj50
pk = "cQkjdfeMU8vHvE6jErnFVqZYYZnGGYy64jH6zovbSXdfTjte6QgY"
utxoCount = 5
pk = "cQkjdfeMU8vHvE6jErnFVqZYYZnGGYy64jH6zovbSXdfTjte6QgY"
)

func (suite *BTCSignTestSuite) SetupTest() {
Expand Down Expand Up @@ -70,7 +68,6 @@ func (suite *BTCSignTestSuite) TestSign() {
suite.T().Logf("wallet signed tx : %v\n", walletSignedTX)

// sign tx using tss signature

tssSignedTX, err := getTSSTX(suite.testSigner, tx, txSigHashes, idx, amt, subscript, txscript.SigHashAll)
suite.Require().NoError(err)
suite.T().Logf("tss signed tx : %v\n", tssSignedTX)
Expand Down
13 changes: 10 additions & 3 deletions zetaclient/bitcoin/fee.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ const (
)

var (
// The outtx size incurred by the depositor: 68vB
// BtcOutTxBytesDepositor is the outtx size incurred by the depositor: 68vB
BtcOutTxBytesDepositor = OuttxSizeDepositor()

// The outtx size incurred by the withdrawer: 177vB
// BtcOutTxBytesWithdrawer is the outtx size incurred by the withdrawer: 177vB
BtcOutTxBytesWithdrawer = OuttxSizeWithdrawer()

// The default depositor fee is 0.00001360 BTC (20 * 68vB / 100000000)
// DefaultDepositorFee is the default depositor fee is 0.00001360 BTC (20 * 68vB / 100000000)
// default depositor fee calculation is based on a fixed fee rate of 20 sat/byte just for simplicity.
DefaultDepositorFee = DepositorFee(defaultDepositorFeeRate)
)
Expand Down Expand Up @@ -82,8 +82,10 @@ func EstimateOuttxSize(numInputs uint64, payees []btcutil.Address) (uint64, erro
}
bytesToPayees += sizeOutput
}

// calculate the size of the witness
bytesWitness := bytes1stWitness + (numInputs-1)*bytesPerWitness

// https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#transaction-size-calculations
// Calculation for signed SegWit tx: blockchain.GetTransactionWeight(tx) / 4
return bytesWiredTx + bytesInput + bytesOutput + bytesToPayees + bytesWitness/blockchain.WitnessScaleFactor, nil
Expand Down Expand Up @@ -133,6 +135,7 @@ func OuttxSizeWithdrawer() uint64 {
bytesInput := uint64(1) * bytesPerInput // nonce mark
bytesOutput := uint64(2) * bytesPerOutputP2WPKH // 2 P2WPKH outputs: new nonce mark, change
bytesOutput += bytesPerOutputAvg // 1 output to withdrawer's address

return bytesWiredTx + bytesInput + bytesOutput + bytes1stWitness/blockchain.WitnessScaleFactor
}

Expand All @@ -151,6 +154,7 @@ func CalcBlockAvgFeeRate(blockVb *btcjson.GetBlockVerboseTxResult, netParams *ch
if len(blockVb.Tx) == 1 {
return 0, nil // only coinbase tx, it happens
}

txCoinbase := &blockVb.Tx[0]
if blockVb.Weight < blockchain.WitnessScaleFactor {
return 0, fmt.Errorf("block weight %d too small", blockVb.Weight)
Expand Down Expand Up @@ -200,6 +204,7 @@ func CalcBlockAvgFeeRate(blockVb *btcjson.GetBlockVerboseTxResult, netParams *ch

// calculate average fee rate.
vBytes := txsWeight / blockchain.WitnessScaleFactor

return txsFees / int64(vBytes), nil
}

Expand All @@ -220,7 +225,9 @@ func CalcDepositorFee(blockVb *btcjson.GetBlockVerboseTxResult, chainID int64, n
feeRate = defaultDepositorFeeRate // use default fee rate if calculation fails, should not happen
logger.Error().Err(err).Msgf("cannot calculate fee rate for block %d", blockVb.Height)
}

// #nosec G701 always in range
feeRate = int64(float64(feeRate) * clientcommon.BTCOuttxGasPriceMultiplier)

return DepositorFee(feeRate)
}
15 changes: 15 additions & 0 deletions zetaclient/bitcoin/inbound_tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ func (ob *BTCChainClient) WatchIntxTracker() {
}
}

// ObserveTrackerSuggestions checks for inbound tracker suggestions
func (ob *BTCChainClient) ObserveTrackerSuggestions() error {
trackers, err := ob.zetaClient.GetInboundTrackersForChain(ob.chain.ChainId)
if err != nil {
return err
}

for _, tracker := range trackers {
ob.logger.InTx.Info().Msgf("checking tracker with hash :%s and coin-type :%s ", tracker.TxHash, tracker.CoinType)
ballotIdentifier, err := ob.CheckReceiptForBtcTxHash(tracker.TxHash, true)
Expand All @@ -51,49 +53,61 @@ func (ob *BTCChainClient) ObserveTrackerSuggestions() error {
}
ob.logger.InTx.Info().Msgf("Vote submitted for inbound Tracker,Chain : %s,Ballot Identifier : %s, coin-type %s", ob.chain.ChainName, ballotIdentifier, coin.CoinType_Gas.String())
}

return nil
}

// CheckReceiptForBtcTxHash checks the receipt for a btc tx hash
func (ob *BTCChainClient) CheckReceiptForBtcTxHash(txHash string, vote bool) (string, error) {
hash, err := chainhash.NewHashFromStr(txHash)
if err != nil {
return "", err
}

tx, err := ob.rpcClient.GetRawTransactionVerbose(hash)
if err != nil {
return "", err
}

blockHash, err := chainhash.NewHashFromStr(tx.BlockHash)
if err != nil {
return "", err
}

blockVb, err := ob.rpcClient.GetBlockVerboseTx(blockHash)
if err != nil {
return "", err
}

if len(blockVb.Tx) <= 1 {
return "", fmt.Errorf("block %d has no transactions", blockVb.Height)
}

depositorFee := CalcDepositorFee(blockVb, ob.chain.ChainId, ob.netParams, ob.logger.InTx)
tss, err := ob.zetaClient.GetBtcTssAddress(ob.chain.ChainId)
if err != nil {
return "", err
}

// #nosec G701 always positive
event, err := GetBtcEvent(ob.rpcClient, *tx, tss, uint64(blockVb.Height), ob.logger.InTx, ob.netParams, depositorFee)
if err != nil {
return "", err
}

if event == nil {
return "", errors.New("no btc deposit event found")
}

msg := ob.GetInboundVoteMessageFromBtcEvent(event)
if msg == nil {
return "", errors.New("no message built for btc sent to TSS")
}

if !vote {
return msg.Digest(), nil
}

zetaHash, ballot, err := ob.zetaClient.PostVoteInbound(zetabridge.PostVoteInboundGasLimit, zetabridge.PostVoteInboundExecutionGasLimit, msg)
if err != nil {
ob.logger.InTx.Error().Err(err).Msg("error posting to zeta core")
Expand All @@ -102,5 +116,6 @@ func (ob *BTCChainClient) CheckReceiptForBtcTxHash(txHash string, vote bool) (st
ob.logger.InTx.Info().Msgf("BTC deposit detected and reported: PostVoteInbound zeta tx hash: %s inTx %s ballot %s fee %v",
zetaHash, txHash, ballot, depositorFee)
}

return msg.Digest(), nil
}
26 changes: 21 additions & 5 deletions zetaclient/bitcoin/tx_script.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@ import (
)

const (
// Lenth of P2TR script [OP_1 0x20 <32-byte-hash>]
// LengthScriptP2TR is the lenth of P2TR script [OP_1 0x20 <32-byte-hash>]
LengthScriptP2TR = 34

// Length of P2WSH script [OP_0 0x20 <32-byte-hash>]
// LengthScriptP2WSH is the length of P2WSH script [OP_0 0x20 <32-byte-hash>]
LengthScriptP2WSH = 34

// Length of P2WPKH script [OP_0 0x14 <20-byte-hash>]
// LengthScriptP2WPKH is the length of P2WPKH script [OP_0 0x14 <20-byte-hash>]
LengthScriptP2WPKH = 22

// Length of P2SH script [OP_HASH160 0x14 <20-byte-hash> OP_EQUAL]
// LengthScriptP2SH is the length of P2SH script [OP_HASH160 0x14 <20-byte-hash> OP_EQUAL]
LengthScriptP2SH = 23

// Length of P2PKH script [OP_DUP OP_HASH160 0x14 <20-byte-hash> OP_EQUALVERIFY OP_CHECKSIG]
// LengthScriptP2PKH is the length of P2PKH script [OP_DUP OP_HASH160 0x14 <20-byte-hash> OP_EQUALVERIFY OP_CHECKSIG]
LengthScriptP2PKH = 25
)

Expand Down Expand Up @@ -87,11 +87,13 @@ func DecodeScriptP2TR(scriptHex string, net *chaincfg.Params) (string, error) {
if !IsPkScriptP2TR(script) {
return "", fmt.Errorf("invalid P2TR script: %s", scriptHex)
}

witnessProg := script[2:]
receiverAddress, err := chains.NewAddressTaproot(witnessProg, net)
if err != nil { // should never happen
return "", errors.Wrapf(err, "error getting address from script %s", scriptHex)
}

return receiverAddress.EncodeAddress(), nil
}

Expand All @@ -104,11 +106,13 @@ func DecodeScriptP2WSH(scriptHex string, net *chaincfg.Params) (string, error) {
if !IsPkScriptP2WSH(script) {
return "", fmt.Errorf("invalid P2WSH script: %s", scriptHex)
}

witnessProg := script[2:]
receiverAddress, err := btcutil.NewAddressWitnessScriptHash(witnessProg, net)
if err != nil { // should never happen
return "", errors.Wrapf(err, "error getting receiver from script: %s", scriptHex)
}

return receiverAddress.EncodeAddress(), nil
}

Expand All @@ -121,11 +125,13 @@ func DecodeScriptP2WPKH(scriptHex string, net *chaincfg.Params) (string, error)
if !IsPkScriptP2WPKH(script) {
return "", fmt.Errorf("invalid P2WPKH script: %s", scriptHex)
}

witnessProg := script[2:]
receiverAddress, err := btcutil.NewAddressWitnessPubKeyHash(witnessProg, net)
if err != nil { // should never happen
return "", errors.Wrapf(err, "error getting receiver from script: %s", scriptHex)
}

return receiverAddress.EncodeAddress(), nil
}

Expand All @@ -138,7 +144,9 @@ func DecodeScriptP2SH(scriptHex string, net *chaincfg.Params) (string, error) {
if !IsPkScriptP2SH(script) {
return "", fmt.Errorf("invalid P2SH script: %s", scriptHex)
}

scriptHash := script[2:22]

return EncodeAddress(scriptHash, net.ScriptHashAddrID), nil
}

Expand All @@ -151,7 +159,9 @@ func DecodeScriptP2PKH(scriptHex string, net *chaincfg.Params) (string, error) {
if !IsPkScriptP2PKH(script) {
return "", fmt.Errorf("invalid P2PKH script: %s", scriptHex)
}

pubKeyHash := script[3:23]

return EncodeAddress(pubKeyHash, net.PubKeyHashAddrID), nil
}

Expand All @@ -166,6 +176,7 @@ func DecodeOpReturnMemo(scriptHex string, txid string) ([]byte, bool, error) {
if int(memoSize) != (len(scriptHex)-4)/2 {
return nil, false, fmt.Errorf("memo size mismatch: %d != %d", memoSize, (len(scriptHex)-4)/2)
}

memoBytes, err := hex.DecodeString(scriptHex[4:])
if err != nil {
return nil, false, errors.Wrapf(err, "error hex decoding memo: %s", scriptHex)
Expand All @@ -175,6 +186,7 @@ func DecodeOpReturnMemo(scriptHex string, txid string) ([]byte, bool, error) {
}
return memoBytes, true, nil
}

return nil, false, nil
}

Expand All @@ -196,16 +208,19 @@ func DecodeTSSVout(vout btcjson.Vout, receiverExpected string, chain chains.Chai
if err != nil {
return "", 0, errors.Wrap(err, "error getting satoshis")
}

// get btc chain params
chainParams, err := chains.GetBTCChainParams(chain.ChainId)
if err != nil {
return "", 0, errors.Wrapf(err, "error GetBTCChainParams for chain %d", chain.ChainId)
}

// decode cctx receiver address
addr, err := chains.DecodeBtcAddress(receiverExpected, chain.ChainId)
if err != nil {
return "", 0, errors.Wrapf(err, "error decoding receiver %s", receiverExpected)
}

// parse receiver address from vout
var receiverVout string
switch addr.(type) {
Expand All @@ -225,5 +240,6 @@ func DecodeTSSVout(vout btcjson.Vout, receiverExpected string, chain chains.Chai
if err != nil {
return "", 0, errors.Wrap(err, "error decoding TSS vout")
}

return receiverVout, amount, nil
}
2 changes: 2 additions & 0 deletions zetaclient/common/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import (
"github.com/rs/zerolog/log"
)

// ClientLogger is a struct that contains the logger for a chain client
type ClientLogger struct {
Std zerolog.Logger
Compliance zerolog.Logger
}

// DefaultLoggers returns the default loggers for a chain client
func DefaultLoggers() ClientLogger {
return ClientLogger{
Std: log.Logger,
Expand Down
Loading
Loading