Skip to content

Commit

Permalink
Merge branch 'develop' into authenticated-call-sc-support
Browse files Browse the repository at this point in the history
  • Loading branch information
skosito committed Sep 24, 2024
2 parents 8c96cea + f6ab039 commit 3c02247
Show file tree
Hide file tree
Showing 23 changed files with 341 additions and 168 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* [2861](https://github.com/zeta-chain/node/pull/2861) - emit events from staking precompile
* [2870](https://github.com/zeta-chain/node/pull/2870) - support for multiple Bitcoin chains in the zetaclient
* [2883](https://github.com/zeta-chain/node/pull/2883) - add chain static information for btc signet testnet
* [2907](https://github.com/zeta-chain/node/pull/2907) - derive Bitcoin tss address by chain id and added more Signet static info
* [2904](https://github.com/zeta-chain/node/pull/2904) - integrate authenticated calls smart contract functionality into protocol

### Refactor
Expand Down
34 changes: 13 additions & 21 deletions cmd/zetaclientd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"time"

"github.com/cometbft/cometbft/crypto/secp256k1"
ethcommon "github.com/ethereum/go-ethereum/common"
maddr "github.com/multiformats/go-multiaddr"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
Expand Down Expand Up @@ -230,21 +229,10 @@ func start(_ *cobra.Command, _ []string) error {
return err
}

btcChains := appContext.FilterChains(zctx.Chain.IsBitcoin)
switch {
case len(btcChains) == 0:
return errors.New("no BTC chains found")
case len(btcChains) > 1:
// In the future we might support multiple UTXO chains;
// right now we only support BTC. Let's make sure there are no surprises.
return errors.New("more than one BTC chain found")
}

tss, err := mc.NewTSS(
ctx,
zetacoreClient,
tssHistoricalList,
btcChains[0].ID(),
hotkeyPass,
server,
)
Expand Down Expand Up @@ -280,16 +268,20 @@ func start(_ *cobra.Command, _ []string) error {
return err
}

// Defensive check: Make sure the tss address is set to the current TSS address and not the newly generated one
// Filter supported BTC chain IDs
btcChains := appContext.FilterChains(zctx.Chain.IsBitcoin)
btcChainIDs := make([]int64, len(btcChains))
for i, chain := range btcChains {
btcChainIDs[i] = chain.ID()
}

// Make sure the TSS EVM/BTC addresses are well formed.
// Zetaclient should not start if TSS addresses cannot be properly derived.
tss.CurrentPubkey = currentTss.TssPubkey
if tss.EVMAddress() == (ethcommon.Address{}) || tss.BTCAddress() == "" {
startLogger.Error().Msg("TSS address is not set in zetacore")
} else {
startLogger.Info().
Str("tss.eth", tss.EVMAddress().String()).
Str("tss.btc", tss.BTCAddress()).
Str("tss.pub_key", tss.CurrentPubkey).
Msg("Current TSS")
err = tss.ValidateAddresses(btcChainIDs)
if err != nil {
startLogger.Error().Err(err).Msg("TSS address validation failed")
return err
}

if len(appContext.ListChainIDs()) == 0 {
Expand Down
2 changes: 1 addition & 1 deletion e2e/e2etests/test_bitcoin_deposit_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestBitcoinDepositAndCall(r *runner.E2ERunner, args []string) {
// deploy an example contract in ZEVM
contractAddr, _, contract, err := testcontract.DeployExample(r.ZEVMAuth, r.ZEVMClient)
require.NoError(r, err)
r.Logger.Print("Bitcoin: Example contract deployed at: %s", contractAddr.String())
r.Logger.Info("Bitcoin: Example contract deployed at: %s", contractAddr.String())

// ACT
// Send BTC to TSS address with a dummy memo
Expand Down
3 changes: 2 additions & 1 deletion e2e/runner/bitcoin.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/btcsuite/btcd/btcjson"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
Expand Down Expand Up @@ -296,7 +297,7 @@ func (r *E2ERunner) GetBitcoinChainID() int64 {

// IsLocalBitcoin returns true if the runner is running on a local bitcoin network
func (r *E2ERunner) IsLocalBitcoin() bool {
return r.BitcoinParams.Name == chains.BitcoinRegnetParams.Name
return r.BitcoinParams.Name == chaincfg.RegressionNetParams.Name
}

// GenerateToAddressIfLocalBitcoin generates blocks to an address if the runner is interacting
Expand Down
42 changes: 21 additions & 21 deletions pkg/chains/bitcoin.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,37 @@ import (
)

var (
BitcoinMainnetParams = &chaincfg.MainNetParams
BitcoinRegnetParams = &chaincfg.RegressionNetParams
BitcoinTestnetParams = &chaincfg.TestNet3Params
// chainIDToNetworkParams maps the Bitcoin chain ID to the network parameters
chainIDToNetworkParams = map[int64]*chaincfg.Params{
BitcoinRegtest.ChainId: &chaincfg.RegressionNetParams,
BitcoinMainnet.ChainId: &chaincfg.MainNetParams,
BitcoinTestnet.ChainId: &chaincfg.TestNet3Params,
BitcoinSignetTestnet.ChainId: &chaincfg.SigNetParams,
}

// networkNameToChainID maps the Bitcoin network name to the chain ID
networkNameToChainID = map[string]int64{
chaincfg.RegressionNetParams.Name: BitcoinRegtest.ChainId,
chaincfg.MainNetParams.Name: BitcoinMainnet.ChainId,
chaincfg.TestNet3Params.Name: BitcoinTestnet.ChainId,
chaincfg.SigNetParams.Name: BitcoinSignetTestnet.ChainId,
}
)

// BitcoinNetParamsFromChainID returns the bitcoin net params to be used from the chain id
func BitcoinNetParamsFromChainID(chainID int64) (*chaincfg.Params, error) {
switch chainID {
case BitcoinRegtest.ChainId:
return BitcoinRegnetParams, nil
case BitcoinMainnet.ChainId:
return BitcoinMainnetParams, nil
case BitcoinTestnet.ChainId:
return BitcoinTestnetParams, nil
default:
return nil, fmt.Errorf("no Bitcoin net params for chain ID: %d", chainID)
if params, found := chainIDToNetworkParams[chainID]; found {
return params, nil
}
return nil, fmt.Errorf("no Bitcoin network params for chain ID: %d", chainID)
}

// BitcoinChainIDFromNetworkName returns the chain id for the given bitcoin network name
func BitcoinChainIDFromNetworkName(name string) (int64, error) {
switch name {
case BitcoinRegnetParams.Name:
return BitcoinRegtest.ChainId, nil
case BitcoinMainnetParams.Name:
return BitcoinMainnet.ChainId, nil
case BitcoinTestnetParams.Name:
return BitcoinTestnet.ChainId, nil
default:
return 0, fmt.Errorf("invalid Bitcoin network name: %s", name)
if chainID, found := networkNameToChainID[name]; found {
return chainID, nil
}
return 0, fmt.Errorf("invalid Bitcoin network name: %s", name)
}

// IsBitcoinRegnet returns true if the chain id is for the regnet
Expand Down
17 changes: 10 additions & 7 deletions pkg/chains/bitcoin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ func TestBitcoinNetParamsFromChainID(t *testing.T) {
expected *chaincfg.Params
wantErr bool
}{
{"Regnet", BitcoinRegtest.ChainId, BitcoinRegnetParams, false},
{"Mainnet", BitcoinMainnet.ChainId, BitcoinMainnetParams, false},
{"Testnet", BitcoinTestnet.ChainId, BitcoinTestnetParams, false},
{"Regnet", BitcoinRegtest.ChainId, &chaincfg.RegressionNetParams, false},
{"Mainnet", BitcoinMainnet.ChainId, &chaincfg.MainNetParams, false},
{"Testnet", BitcoinTestnet.ChainId, &chaincfg.TestNet3Params, false},
{"Signet", BitcoinSignetTestnet.ChainId, &chaincfg.SigNetParams, false},
{"Unknown", -1, nil, true},
}

Expand All @@ -25,9 +26,10 @@ func TestBitcoinNetParamsFromChainID(t *testing.T) {
params, err := BitcoinNetParamsFromChainID(tt.chainID)
if tt.wantErr {
require.Error(t, err)
require.Nil(t, params)
} else {
require.NoError(t, err)
require.Equal(t, tt.expected, params)
require.EqualValues(t, tt.expected, params)
}
})
}
Expand All @@ -40,9 +42,10 @@ func TestBitcoinChainIDFromNetParams(t *testing.T) {
expectedChainID int64
wantErr bool
}{
{"Regnet", BitcoinRegnetParams.Name, BitcoinRegtest.ChainId, false},
{"Mainnet", BitcoinMainnetParams.Name, BitcoinMainnet.ChainId, false},
{"Testnet", BitcoinTestnetParams.Name, BitcoinTestnet.ChainId, false},
{"Regnet", chaincfg.RegressionNetParams.Name, BitcoinRegtest.ChainId, false},
{"Mainnet", chaincfg.MainNetParams.Name, BitcoinMainnet.ChainId, false},
{"Testnet", chaincfg.TestNet3Params.Name, BitcoinTestnet.ChainId, false},
{"Signet", chaincfg.SigNetParams.Name, BitcoinSignetTestnet.ChainId, false},
{"Unknown", "Unknown", 0, true},
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ func TestKeeper_CheckMigration(t *testing.T) {
}

err := k.CheckIfTSSMigrationTransfer(ctx, &msg)
require.ErrorContains(t, err, "no Bitcoin net params for chain ID: 999")
require.ErrorContains(t, err, "no Bitcoin network params for chain ID: 999")
})

t.Run("fails if gateway is not observer ", func(t *testing.T) {
Expand Down
5 changes: 3 additions & 2 deletions x/observer/keeper/grpc_query_tss.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"sort"

"github.com/btcsuite/btcd/chaincfg"
sdk "github.com/cosmos/cosmos-sdk/types"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -54,7 +55,7 @@ func (k Keeper) GetTssAddress(
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
bitcoinParams := chains.BitcoinRegnetParams
bitcoinParams := &chaincfg.RegressionNetParams
if req.BitcoinChainId != 0 {
bitcoinParams, err = chains.BitcoinNetParamsFromChainID(req.BitcoinChainId)
if err != nil {
Expand Down Expand Up @@ -88,7 +89,7 @@ func (k Keeper) GetTssAddressByFinalizedHeight(
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
bitcoinParams := chains.BitcoinRegnetParams
bitcoinParams := &chaincfg.RegressionNetParams
if req.BitcoinChainId != 0 {
bitcoinParams, err = chains.BitcoinNetParamsFromChainID(req.BitcoinChainId)
if err != nil {
Expand Down
22 changes: 17 additions & 5 deletions zetaclient/chains/base/observer.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,22 @@ func (ob *Observer) WithTSS(tss interfaces.TSSSigner) *Observer {
return ob
}

// TSSAddressString returns the TSS address for the chain.
//
// Note: all chains uses TSS EVM address except Bitcoin chain.
func (ob *Observer) TSSAddressString() string {
switch ob.chain.Consensus {
case chains.Consensus_bitcoin:
address, err := ob.tss.BTCAddress(ob.Chain().ChainId)
if err != nil {
return ""
}
return address.EncodeAddress()
default:
return ob.tss.EVMAddress().String()
}
}

// LastBlock get external last block height.
func (ob *Observer) LastBlock() uint64 {
return atomic.LoadUint64(&ob.lastBlock)
Expand Down Expand Up @@ -286,11 +302,7 @@ func (ob *Observer) WithHeaderCache(cache *lru.Cache) *Observer {
// OutboundID returns a unique identifier for the outbound transaction.
// The identifier is now used as the key for maps that store outbound related data (e.g. transaction, receipt, etc).
func (ob *Observer) OutboundID(nonce uint64) string {
// all chains uses EVM address as part of the key except bitcoin
tssAddress := ob.tss.EVMAddress().String()
if ob.chain.Consensus == chains.Consensus_bitcoin {
tssAddress = ob.tss.BTCAddress()
}
tssAddress := ob.TSSAddressString()
return fmt.Sprintf("%d-%s-%d", ob.chain.ChainId, tssAddress, nonce)
}

Expand Down
62 changes: 58 additions & 4 deletions zetaclient/chains/base/observer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import (
"testing"
"time"

sdk "github.com/cosmos/cosmos-sdk/types"
lru "github.com/hashicorp/golang-lru"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/stretchr/testify/require"
"github.com/zeta-chain/node/cmd"
"github.com/zeta-chain/node/pkg/chains"
"github.com/zeta-chain/node/pkg/coin"
"github.com/zeta-chain/node/testutil/sample"
Expand All @@ -21,6 +23,7 @@ import (
zctx "github.com/zeta-chain/node/zetaclient/context"
"github.com/zeta-chain/node/zetaclient/db"
"github.com/zeta-chain/node/zetaclient/metrics"
"github.com/zeta-chain/node/zetaclient/testutils"
"github.com/zeta-chain/node/zetaclient/testutils/mocks"
)

Expand Down Expand Up @@ -271,6 +274,60 @@ func TestObserverGetterAndSetter(t *testing.T) {
})
}

func TestTSSAddressString(t *testing.T) {
testConfig := sdk.GetConfig()
testConfig.SetBech32PrefixForAccount(cmd.Bech32PrefixAccAddr, cmd.Bech32PrefixAccPub)

tests := []struct {
name string
chain chains.Chain
forceError bool
addrExpected string
}{
{
name: "should return TSS BTC address for Bitcoin chain",
chain: chains.BitcoinMainnet,
addrExpected: testutils.TSSAddressBTCMainnet,
},
{
name: "should return TSS EVM address for EVM chain",
chain: chains.Ethereum,
addrExpected: testutils.TSSAddressEVMMainnet,
},
{
name: "should return TSS EVM address for other non-BTC chain",
chain: chains.SolanaDevnet,
addrExpected: testutils.TSSAddressEVMMainnet,
},
{
name: "should return empty address for unknown BTC chain",
chain: chains.BitcoinMainnet,
forceError: true,
addrExpected: "",
},
}

// run tests
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// create observer
ob := createObserver(t, tt.chain, defaultAlertLatency)

// force error if needed
if tt.forceError {
// pause TSS to cause error
tss := mocks.NewTSSMainnet()
tss.Pause()
ob = ob.WithTSS(tss)
}

// get TSS address
addr := ob.TSSAddressString()
require.Equal(t, tt.addrExpected, addr)
})
}
}

func TestIsBlockConfirmed(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -348,10 +405,7 @@ func TestOutboundID(t *testing.T) {
outboundID := ob.OutboundID(tt.nonce)

// expected outbound id
exepctedID := fmt.Sprintf("%d-%s-%d", tt.chain.ChainId, tt.tss.EVMAddress(), tt.nonce)
if tt.chain.Consensus == chains.Consensus_bitcoin {
exepctedID = fmt.Sprintf("%d-%s-%d", tt.chain.ChainId, tt.tss.BTCAddress(), tt.nonce)
}
exepctedID := fmt.Sprintf("%d-%s-%d", tt.chain.ChainId, ob.TSSAddressString(), tt.nonce)
require.Equal(t, exepctedID, outboundID)
})
}
Expand Down
2 changes: 1 addition & 1 deletion zetaclient/chains/bitcoin/observer/inbound.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func (ob *Observer) ObserveInbound(ctx context.Context) error {
// add block header to zetacore
if len(res.Block.Tx) > 1 {
// filter incoming txs to TSS address
tssAddress := ob.TSS().BTCAddress()
tssAddress := ob.TSSAddressString()

// #nosec G115 always positive
events, err := FilterAndParseIncomingTx(
Expand Down
7 changes: 3 additions & 4 deletions zetaclient/chains/bitcoin/observer/observer.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,12 +369,11 @@ func (ob *Observer) FetchUTXOs(ctx context.Context) error {
maxConfirmations := int(bh)

// List all unspent UTXOs (160ms)
tssAddr := ob.TSS().BTCAddress()
address, err := chains.DecodeBtcAddress(tssAddr, ob.Chain().ChainId)
tssAddr, err := ob.TSS().BTCAddress(ob.Chain().ChainId)
if err != nil {
return fmt.Errorf("btc: error decoding wallet address (%s) : %s", tssAddr, err.Error())
return fmt.Errorf("error getting bitcoin tss address")
}
utxos, err := ob.btcClient.ListUnspentMinMaxAddresses(0, maxConfirmations, []btcutil.Address{address})
utxos, err := ob.btcClient.ListUnspentMinMaxAddresses(0, maxConfirmations, []btcutil.Address{tssAddr})
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit 3c02247

Please sign in to comment.