Skip to content

Commit

Permalink
feat: bitcoin (#296)
Browse files Browse the repository at this point in the history
Signed-off-by: tcar <[email protected]>
Co-authored-by: tcar <[email protected]>
  • Loading branch information
mpetrun5 and tcar121293 authored Jun 11, 2024
1 parent b60bbcd commit ff09579
Show file tree
Hide file tree
Showing 120 changed files with 4,853 additions and 423 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy_stage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,4 @@ jobs:
fields: repo,message,commit,author,action,job,eventName,ref,workflow # selectable (default: repo,message)
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} # required
if: always()
if: always()
11 changes: 6 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,17 @@ test:
./scripts/tests.sh

genmocks:
mockgen -destination=./tss/common/mock/tss.go github.com/binance-chain/tss-lib/tss Message
mockgen -destination=./tss/common/mock/communication.go -source=./tss/common/base.go -package mock_tss
mockgen -destination=./tss/keygen/mock/storer.go -source=./tss/keygen/keygen.go
mockgen -destination=./tss/keygen/mock/storer.go -source=./tss/keygen/keygen.go
mockgen --package mock_tss -destination=./tss/mock/storer.go -source=./tss/resharing/resharing.go
mockgen -destination=./tss/ecdsa/common/mock/tss.go github.com/binance-chain/tss-lib/tss Message
mockgen -destination=./tss/ecdsa/common/mock/communication.go -source=./tss/ecdsa/common/base.go -package mock_tss
mockgen --package mock_tss -destination=./tss/mock/ecdsa.go -source=./tss/ecdsa/keygen/keygen.go
mockgen --package mock_tss -destination=./tss/mock/frost.go -source=./tss/frost/keygen/keygen.go
mockgen -source=./tss/coordinator.go -destination=./tss/mock/coordinator.go
mockgen -source=./comm/communication.go -destination=./comm/mock/communication.go
mockgen -source=./chains/evm/listener/eventHandlers/event-handler.go -destination=./chains/evm/listener/eventHandlers/mock/listener.go
mockgen -source=./chains/evm/calls/events/listener.go -destination=./chains/evm/calls/events/mock/listener.go
mockgen -source=./chains/substrate/listener/event-handlers.go -destination=./chains/substrate/listener/mock/handlers.go
mockgen -source=./chains/btc/listener/event-handlers.go -destination=./chains/btc/listener/mock/handlers.go
mockgen -source=./chains/btc/listener/listener.go -destination=./chains/btc/listener/mock/listener.go
mockgen -source=./topology/topology.go -destination=./topology/mock/topology.go


Expand Down
58 changes: 57 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"time"

"github.com/ChainSafe/sygma-relayer/chains"
"github.com/ChainSafe/sygma-relayer/chains/btc"
"github.com/ChainSafe/sygma-relayer/chains/btc/mempool"
"github.com/ChainSafe/sygma-relayer/chains/evm"
"github.com/ChainSafe/sygma-relayer/chains/evm/calls/contracts/bridge"
"github.com/ChainSafe/sygma-relayer/chains/evm/calls/events"
Expand All @@ -23,6 +25,7 @@ import (
hubEventHandlers "github.com/ChainSafe/sygma-relayer/chains/evm/listener/eventHandlers"
"github.com/ChainSafe/sygma-relayer/chains/substrate"
"github.com/ChainSafe/sygma-relayer/relayer/transfer"
propStore "github.com/ChainSafe/sygma-relayer/store"
"github.com/sygmaprotocol/sygma-core/chains/evm/transactor/gas"
coreSubstrate "github.com/sygmaprotocol/sygma-core/chains/substrate"
"github.com/sygmaprotocol/sygma-core/crypto/secp256k1"
Expand All @@ -32,6 +35,10 @@ import (
"github.com/sygmaprotocol/sygma-core/store"
"github.com/sygmaprotocol/sygma-core/store/lvldb"

btcConfig "github.com/ChainSafe/sygma-relayer/chains/btc/config"
btcConnection "github.com/ChainSafe/sygma-relayer/chains/btc/connection"
btcExecutor "github.com/ChainSafe/sygma-relayer/chains/btc/executor"
btcListener "github.com/ChainSafe/sygma-relayer/chains/btc/listener"
substrateExecutor "github.com/ChainSafe/sygma-relayer/chains/substrate/executor"
substrateListener "github.com/ChainSafe/sygma-relayer/chains/substrate/listener"
substratePallet "github.com/ChainSafe/sygma-relayer/chains/substrate/pallet"
Expand Down Expand Up @@ -116,7 +123,6 @@ func Run() error {
communication := p2p.NewCommunication(host, "p2p/sygma")
electorFactory := elector.NewCoordinatorElectorFactory(host, configuration.RelayerConfig.BullyConfig)
coordinator := tss.NewCoordinator(host, communication, electorFactory)
keyshareStore := keyshare.NewKeyshareStore(configuration.RelayerConfig.MpcConfig.KeysharePath)

// this is temporary solution related to specifics of aws deployment
// effectively it waits until old instance is killed
Expand All @@ -132,6 +138,9 @@ func Run() error {
}
}
blockstore := store.NewBlockStore(db)
keyshareStore := keyshare.NewECDSAKeyshareStore(configuration.RelayerConfig.MpcConfig.KeysharePath)
frostKeyshareStore := keyshare.NewFrostKeyshareStore(configuration.RelayerConfig.MpcConfig.FrostKeysharePath)
propStore := propStore.NewPropStore(db)

// wait until executions are done and then stop further executions before exiting
exitLock := &sync.RWMutex{}
Expand Down Expand Up @@ -170,6 +179,7 @@ func Run() error {
log.Info().Str("domain", config.String()).Msgf("Registering EVM domain")

bridgeAddress := common.HexToAddress(config.Bridge)
frostAddress := common.HexToAddress(config.FrostKeygen)
gasPricer := gas.NewLondonGasPriceClient(client, &gas.GasPricerOpts{
UpperLimitFeePerGas: config.MaxGasPrice,
GasPriceFactor: config.GasMultiplier,
Expand Down Expand Up @@ -213,6 +223,7 @@ func Run() error {
l := log.With().Str("chain", fmt.Sprintf("%v", config.GeneralChainConfig.Name)).Uint8("domainID", *config.GeneralChainConfig.Id)
eventHandlers = append(eventHandlers, hubEventHandlers.NewDepositEventHandler(depositListener, depositHandler, bridgeAddress, *config.GeneralChainConfig.Id, msgChan))
eventHandlers = append(eventHandlers, hubEventHandlers.NewKeygenEventHandler(l, tssListener, coordinator, host, communication, keyshareStore, bridgeAddress, networkTopology.Threshold))
eventHandlers = append(eventHandlers, hubEventHandlers.NewFrostKeygenEventHandler(l, tssListener, coordinator, host, communication, frostKeyshareStore, frostAddress, networkTopology.Threshold))
eventHandlers = append(eventHandlers, hubEventHandlers.NewRefreshEventHandler(l, topologyProvider, topologyStore, tssListener, coordinator, host, communication, connectionGate, keyshareStore, bridgeAddress))
eventHandlers = append(eventHandlers, hubEventHandlers.NewRetryEventHandler(l, tssListener, depositHandler, bridgeAddress, *config.GeneralChainConfig.Id, config.BlockConfirmations, msgChan))
evmListener := listener.NewEVMListener(client, eventHandlers, blockstore, sygmaMetrics, *config.GeneralChainConfig.Id, config.BlockRetryInterval, config.BlockConfirmations, config.BlockInterval)
Expand Down Expand Up @@ -291,6 +302,51 @@ func Run() error {

domains[*config.GeneralChainConfig.Id] = substrateChain
}
case "btc":
{
log.Info().Msgf("Registering btc domain")
config, err := btcConfig.NewBtcConfig(chainConfig)
if err != nil {
panic(err)
}

conn, err := btcConnection.NewBtcConnection(
config.GeneralChainConfig.Endpoint,
config.Username,
config.Password,
false)
if err != nil {
panic(err)
}

l := log.With().Str("chain", fmt.Sprintf("%v", config.GeneralChainConfig.Name)).Uint8("domainID", *config.GeneralChainConfig.Id)
depositHandler := &btcListener.BtcDepositHandler{}
eventHandlers := make([]btcListener.EventHandler, 0)
resources := make(map[[32]byte]btcConfig.Resource)
for _, resource := range config.Resources {
resources[resource.ResourceID] = resource
eventHandlers = append(eventHandlers, btcListener.NewFungibleTransferEventHandler(l, *config.GeneralChainConfig.Id, depositHandler, msgChan, conn, resource))
}
listener := btcListener.NewBtcListener(conn, eventHandlers, config, blockstore)

mempool := mempool.NewMempoolAPI(config.MempoolUrl)
mh := &btcExecutor.BtcMessageHandler{}
executor := btcExecutor.NewExecutor(
propStore,
host,
communication,
coordinator,
frostKeyshareStore,
conn,
mempool,
resources,
config.Network,
exitLock)

btcChain := btc.NewBtcChain(listener, executor, mh, *config.GeneralChainConfig.Id)
domains[*config.GeneralChainConfig.Id] = btcChain

}
default:
panic(fmt.Errorf("type '%s' not recognized", chainConfig["type"]))
}
Expand Down
72 changes: 72 additions & 0 deletions chains/btc/chain.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// The Licensed Work is (c) 2022 Sygma
// SPDX-License-Identifier: LGPL-3.0-only

package btc

import (
"context"
"math/big"

"github.com/ChainSafe/sygma-relayer/chains/btc/executor"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/sygmaprotocol/sygma-core/relayer/message"
"github.com/sygmaprotocol/sygma-core/relayer/proposal"
)

type BatchProposalExecutor interface {
Execute(msgs []*message.Message) error
}
type EventListener interface {
ListenToEvents(ctx context.Context, startBlock *big.Int)
}
type BtcChain struct {
id uint8

listener EventListener
executor *executor.Executor
mh *executor.BtcMessageHandler

startBlock *big.Int
logger zerolog.Logger
}

func NewBtcChain(
listener EventListener,
executor *executor.Executor,
mh *executor.BtcMessageHandler,
id uint8,
) *BtcChain {
return &BtcChain{
listener: listener,
executor: executor,
mh: mh,
id: id,

logger: log.With().Uint8("domainID", id).Logger()}
}

func (c *BtcChain) Write(props []*proposal.Proposal) error {
err := c.executor.Execute(props)
if err != nil {
c.logger.Err(err).Str("messageID", props[0].MessageID).Msgf("error writing proposals %+v on network %d", props, c.DomainID())
return err
}

return nil
}

func (c *BtcChain) ReceiveMessage(m *message.Message) (*proposal.Proposal, error) {
return c.mh.HandleMessage(m)
}

// PollEvents is the goroutine that polls blocks and searches Deposit events in them.
// Events are then sent to eventsChan.
func (c *BtcChain) PollEvents(ctx context.Context) {
c.logger.Info().Str("startBlock", c.startBlock.String()).Msg("Polling Blocks...")
go c.listener.ListenToEvents(ctx, c.startBlock)
}

func (c *BtcChain) DomainID() uint8 {
return c.id
}
158 changes: 158 additions & 0 deletions chains/btc/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// The Licensed Work is (c) 2022 Sygma
// SPDX-License-Identifier: LGPL-3.0-only

package config

import (
"encoding/hex"
"fmt"
"math/big"
"time"

"github.com/ChainSafe/sygma-relayer/config/chain"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/creasty/defaults"
"github.com/mitchellh/mapstructure"
)

type RawResource struct {
Address string
ResourceID string
Tweak string
Script string
}

type Resource struct {
Address btcutil.Address
ResourceID [32]byte
Tweak string
Script []byte
}

type RawBtcConfig struct {
chain.GeneralChainConfig `mapstructure:",squash"`
Resources []RawResource `mapstrcture:"resources"`
StartBlock int64 `mapstructure:"startBlock"`
Username string `mapstructure:"username"`
Password string `mapstructure:"password"`
BlockInterval int64 `mapstructure:"blockInterval" default:"5"`
BlockRetryInterval uint64 `mapstructure:"blockRetryInterval" default:"5"`
BlockConfirmations int64 `mapstructure:"blockConfirmations" default:"10"`
Network string `mapstructure:"network" default:"mainnet"`
MempoolUrl string `mapstructure:"mempoolUrl"`
}

func (c *RawBtcConfig) Validate() error {
if err := c.GeneralChainConfig.Validate(); err != nil {
return err
}

if c.BlockConfirmations != 0 && c.BlockConfirmations < 1 {
return fmt.Errorf("blockConfirmations has to be >=1")
}

if c.Username == "" {
return fmt.Errorf("required field chain.Username empty for chain %v", *c.Id)
}

if c.Password == "" {
return fmt.Errorf("required field chain.Password empty for chain %v", *c.Id)
}
return nil
}

type BtcConfig struct {
GeneralChainConfig chain.GeneralChainConfig
Resources []Resource
Username string
Password string
StartBlock *big.Int
BlockInterval *big.Int
BlockRetryInterval time.Duration
BlockConfirmations *big.Int
Tweak string
Script []byte
MempoolUrl string
Network chaincfg.Params
}

// NewBtcConfig decodes and validates an instance of an BtcConfig from
// raw chain config
func NewBtcConfig(chainConfig map[string]interface{}) (*BtcConfig, error) {
var c RawBtcConfig
err := mapstructure.Decode(chainConfig, &c)
if err != nil {
return nil, err
}

err = defaults.Set(&c)
if err != nil {
return nil, err
}

err = c.Validate()
if err != nil {
return nil, err
}

networkParams, err := networkParams(c.Network)
if err != nil {
return nil, err
}

resources := make([]Resource, len(c.Resources))
for i, r := range c.Resources {
scriptBytes, err := hex.DecodeString(r.Script)
if err != nil {
return nil, err
}

address, err := btcutil.DecodeAddress(r.Address, &networkParams)
if err != nil {
return nil, err
}
resourceBytes, err := hex.DecodeString(r.ResourceID[2:])
if err != nil {
panic(err)
}
var resource32Bytes [32]byte
copy(resource32Bytes[:], resourceBytes)
resources[i] = Resource{
Address: address,
ResourceID: resource32Bytes,
Script: scriptBytes,
Tweak: r.Tweak,
}
}

c.GeneralChainConfig.ParseFlags()
config := &BtcConfig{
GeneralChainConfig: c.GeneralChainConfig,
StartBlock: big.NewInt(c.StartBlock),
BlockConfirmations: big.NewInt(c.BlockConfirmations),
BlockInterval: big.NewInt(c.BlockInterval),
BlockRetryInterval: time.Duration(c.BlockRetryInterval) * time.Second,
Username: c.Username,
Password: c.Password,
Network: networkParams,
MempoolUrl: c.MempoolUrl,
Resources: resources,
}
return config, nil
}

func networkParams(network string) (chaincfg.Params, error) {
switch network {
case "mainnet":
return chaincfg.MainNetParams, nil
case "testnet":
return chaincfg.TestNet3Params, nil
case "regtest":
return chaincfg.RegressionNetParams, nil
case "signet":
return chaincfg.SigNetParams, nil
default:
return chaincfg.Params{}, fmt.Errorf("unknown network %s", network)
}
}
Loading

0 comments on commit ff09579

Please sign in to comment.