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

feat: bitcoin #296

Merged
merged 22 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading