-
Notifications
You must be signed in to change notification settings - Fork 110
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
initiate Bitcoin mempool watcher and RBF keysign logic
- Loading branch information
1 parent
3af0e09
commit 1ad6628
Showing
21 changed files
with
1,758 additions
and
937 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
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,65 @@ | ||
package observer | ||
|
||
import ( | ||
"github.com/pkg/errors" | ||
|
||
"github.com/zeta-chain/node/pkg/chains" | ||
clienttypes "github.com/zeta-chain/node/zetaclient/types" | ||
) | ||
|
||
// SaveBroadcastedTx saves successfully broadcasted transaction | ||
func (ob *Observer) SaveBroadcastedTx(txHash string, nonce uint64) { | ||
outboundID := ob.OutboundID(nonce) | ||
ob.Mu().Lock() | ||
ob.broadcastedTx[outboundID] = txHash | ||
ob.Mu().Unlock() | ||
|
||
broadcastEntry := clienttypes.ToOutboundHashSQLType(txHash, outboundID) | ||
if err := ob.DB().Client().Save(&broadcastEntry).Error; err != nil { | ||
ob.logger.Outbound.Error(). | ||
Err(err). | ||
Msgf("SaveBroadcastedTx: error saving broadcasted txHash %s for outbound %s", txHash, outboundID) | ||
} | ||
ob.logger.Outbound.Info().Msgf("SaveBroadcastedTx: saved broadcasted txHash %s for outbound %s", txHash, outboundID) | ||
} | ||
|
||
// LoadLastBlockScanned loads the last scanned block from the database | ||
func (ob *Observer) LoadLastBlockScanned() error { | ||
err := ob.Observer.LoadLastBlockScanned(ob.Logger().Chain) | ||
if err != nil { | ||
return errors.Wrapf(err, "error LoadLastBlockScanned for chain %d", ob.Chain().ChainId) | ||
} | ||
|
||
// observer will scan from the last block when 'lastBlockScanned == 0', this happens when: | ||
// 1. environment variable is set explicitly to "latest" | ||
// 2. environment variable is empty and last scanned block is not found in DB | ||
if ob.LastBlockScanned() == 0 { | ||
blockNumber, err := ob.btcClient.GetBlockCount() | ||
if err != nil { | ||
return errors.Wrapf(err, "error GetBlockCount for chain %d", ob.Chain().ChainId) | ||
} | ||
// #nosec G115 always positive | ||
ob.WithLastBlockScanned(uint64(blockNumber)) | ||
} | ||
|
||
// bitcoin regtest starts from hardcoded block 100 | ||
if chains.IsBitcoinRegnet(ob.Chain().ChainId) { | ||
ob.WithLastBlockScanned(RegnetStartBlock) | ||
} | ||
ob.Logger().Chain.Info().Msgf("chain %d starts scanning from block %d", ob.Chain().ChainId, ob.LastBlockScanned()) | ||
|
||
return nil | ||
} | ||
|
||
// LoadBroadcastedTxMap loads broadcasted transactions from the database | ||
func (ob *Observer) LoadBroadcastedTxMap() error { | ||
var broadcastedTransactions []clienttypes.OutboundHashSQLType | ||
if err := ob.DB().Client().Find(&broadcastedTransactions).Error; err != nil { | ||
ob.logger.Chain.Error().Err(err).Msgf("error iterating over db for chain %d", ob.Chain().ChainId) | ||
return err | ||
} | ||
for _, entry := range broadcastedTransactions { | ||
ob.broadcastedTx[entry.Key] = entry.Hash | ||
} | ||
return nil | ||
} |
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,106 @@ | ||
package observer | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/pkg/errors" | ||
|
||
"github.com/zeta-chain/node/pkg/chains" | ||
"github.com/zeta-chain/node/zetaclient/chains/bitcoin" | ||
"github.com/zeta-chain/node/zetaclient/chains/bitcoin/rpc" | ||
clienttypes "github.com/zeta-chain/node/zetaclient/types" | ||
) | ||
|
||
// WatchGasPrice watches Bitcoin chain for gas rate and post to zetacore | ||
func (ob *Observer) WatchGasPrice(ctx context.Context) error { | ||
// report gas price right away as the ticker takes time to kick in | ||
err := ob.PostGasPrice(ctx) | ||
if err != nil { | ||
ob.logger.GasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.Chain().ChainId) | ||
} | ||
|
||
// start gas price ticker | ||
ticker, err := clienttypes.NewDynamicTicker("Bitcoin_WatchGasPrice", ob.ChainParams().GasPriceTicker) | ||
if err != nil { | ||
return errors.Wrapf(err, "NewDynamicTicker error") | ||
} | ||
ob.logger.GasPrice.Info().Msgf("WatchGasPrice started for chain %d with interval %d", | ||
ob.Chain().ChainId, ob.ChainParams().GasPriceTicker) | ||
|
||
defer ticker.Stop() | ||
for { | ||
select { | ||
case <-ticker.C(): | ||
if !ob.ChainParams().IsSupported { | ||
continue | ||
} | ||
err := ob.PostGasPrice(ctx) | ||
if err != nil { | ||
ob.logger.GasPrice.Error().Err(err).Msgf("PostGasPrice error for chain %d", ob.Chain().ChainId) | ||
} | ||
ticker.UpdateInterval(ob.ChainParams().GasPriceTicker, ob.logger.GasPrice) | ||
case <-ob.StopChannel(): | ||
ob.logger.GasPrice.Info().Msgf("WatchGasPrice stopped for chain %d", ob.Chain().ChainId) | ||
return nil | ||
} | ||
} | ||
} | ||
|
||
// PostGasPrice posts gas price to zetacore | ||
func (ob *Observer) PostGasPrice(ctx context.Context) error { | ||
var ( | ||
err error | ||
feeRateEstimated int64 | ||
) | ||
|
||
// special handle regnet and testnet gas rate | ||
// regnet: RPC 'EstimateSmartFee' is not available | ||
// testnet: RPC 'EstimateSmartFee' returns unreasonable high gas rate | ||
if ob.Chain().NetworkType != chains.NetworkType_mainnet { | ||
feeRateEstimated, err = ob.specialHandleFeeRate() | ||
if err != nil { | ||
return errors.Wrap(err, "unable to execute specialHandleFeeRate") | ||
} | ||
} else { | ||
feeRateEstimated, err = rpc.GetEstimatedFeeRate(ob.btcClient, 1) | ||
if err != nil { | ||
return errors.Wrap(err, "unable to get estimated fee rate") | ||
} | ||
} | ||
|
||
// query the current block number | ||
blockNumber, err := ob.btcClient.GetBlockCount() | ||
if err != nil { | ||
return errors.Wrap(err, "GetBlockCount error") | ||
} | ||
|
||
// Bitcoin has no concept of priority fee (like eth) | ||
const priorityFee = 0 | ||
|
||
// #nosec G115 always positive | ||
_, err = ob.ZetacoreClient(). | ||
PostVoteGasPrice(ctx, ob.Chain(), uint64(feeRateEstimated), priorityFee, uint64(blockNumber)) | ||
if err != nil { | ||
return errors.Wrap(err, "PostVoteGasPrice error") | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// specialHandleFeeRate handles the fee rate for regnet and testnet | ||
func (ob *Observer) specialHandleFeeRate() (int64, error) { | ||
switch ob.Chain().NetworkType { | ||
case chains.NetworkType_privnet: | ||
// hardcode gas price for regnet | ||
return 1, nil | ||
case chains.NetworkType_testnet: | ||
feeRateEstimated, err := bitcoin.GetRecentFeeRate(ob.btcClient, ob.netParams) | ||
if err != nil { | ||
return 0, errors.Wrapf(err, "error GetRecentFeeRate") | ||
} | ||
return feeRateEstimated, nil | ||
default: | ||
return 0, fmt.Errorf(" unsupported bitcoin network type %d", ob.Chain().NetworkType) | ||
} | ||
} |
Oops, something went wrong.