diff --git a/changelog.md b/changelog.md index 4562c21d66..8cc99d1d24 100644 --- a/changelog.md +++ b/changelog.md @@ -29,6 +29,7 @@ * fix docker build issues with version: golang:1.20-alpine3.18 * [1525](https://github.com/zeta-chain/node/pull/1525) - relax EVM chain block header length check 1024->4096 * [1522](https://github.com/zeta-chain/node/pull/1522/files) - block `distribution` module account from receiving zeta +* [1528](https://github.com/zeta-chain/node/pull/1528) - fix panic caused on decoding malformed BTC addresses ### Refactoring diff --git a/common/address.go b/common/address.go index 032a0b88b0..77b4234fed 100644 --- a/common/address.go +++ b/common/address.go @@ -1,8 +1,11 @@ package common import ( + "errors" + "fmt" "strings" + "github.com/btcsuite/btcutil" eth "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/zetacore/common/cosmos" ) @@ -41,3 +44,37 @@ func (addr Address) IsEmpty() bool { func (addr Address) String() string { return string(addr) } + +func ConvertRecoverToError(r interface{}) error { + switch x := r.(type) { + case string: + return errors.New(x) + case error: + return x + default: + return fmt.Errorf("%v", x) + } +} + +func DecodeBtcAddress(inputAddress string, chainID int64) (address btcutil.Address, err error) { + defer func() { + if r := recover(); r != nil { + err = ConvertRecoverToError(r) + err = fmt.Errorf("input address:%s, chainId:%d, err:%s", inputAddress, chainID, err.Error()) + return + } + }() + chainParams, err := GetBTCChainParams(chainID) + if err != nil { + return nil, err + } + if chainParams == nil { + return nil, fmt.Errorf("chain params not found") + } + address, err = btcutil.DecodeAddress(inputAddress, chainParams) + ok := address.IsForNet(chainParams) + if !ok { + return nil, fmt.Errorf("address is not for network %s", chainParams.Name) + } + return +} diff --git a/common/address_test.go b/common/address_test.go index 4487c48e28..af386c3bdc 100644 --- a/common/address_test.go +++ b/common/address_test.go @@ -20,3 +20,23 @@ func TestAddress(t *testing.T) { addr = NewAddress("0x90f2b1ae50e6018230e90a33f98c7844a0ab635a") require.EqualValuesf(t, "0x90f2b1ae50e6018230e90a33f98c7844a0ab635a", addr.String(), "address string should be equal") } + +func TestDecodeBtcAddress(t *testing.T) { + t.Run("invalid string", func(t *testing.T) { + _, err := DecodeBtcAddress("�U�ڷ���i߭����꿚�l", 18332) + require.ErrorContains(t, err, "runtime error: index out of range") + }) + t.Run("invalid chain", func(t *testing.T) { + _, err := DecodeBtcAddress("14CEjTd5ci3228J45GdnGeUKLSSeCWUQxK", 0) + require.ErrorContains(t, err, "is not a Bitcoin chain") + }) + t.Run("nil pointer dereference", func(t *testing.T) { + _, err := DecodeBtcAddress("tb1qy9pqmk2pd9sv63g27jt8r657wy0d9uee4x2dt2", 18332) + require.ErrorContains(t, err, "runtime error: invalid memory address or nil pointer dereference") + }) + t.Run("valid address", func(t *testing.T) { + _, err := DecodeBtcAddress("bcrt1qy9pqmk2pd9sv63g27jt8r657wy0d9uee4x2dt2", 18444) + require.NoError(t, err) + }) + +} diff --git a/common/chain.go b/common/chain.go index ee2b26d855..d6762c1a91 100644 --- a/common/chain.go +++ b/common/chain.go @@ -52,7 +52,7 @@ func (chain Chain) EncodeAddress(b []byte) (string, error) { if err != nil { return "", err } - addr, err := btcutil.DecodeAddress(addrStr, chainParams) + addr, err := DecodeBtcAddress(addrStr, chain.ChainId) if err != nil { return "", err } diff --git a/contrib/localnet/orchestrator/smoketest/runner/setup_zeta.go b/contrib/localnet/orchestrator/smoketest/runner/setup_zeta.go index 1a04830556..80a7606ba2 100644 --- a/contrib/localnet/orchestrator/smoketest/runner/setup_zeta.go +++ b/contrib/localnet/orchestrator/smoketest/runner/setup_zeta.go @@ -6,7 +6,6 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/btcsuite/btcutil" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/systemcontract.sol" "github.com/zeta-chain/protocol-contracts/pkg/contracts/zevm/zrc20.sol" @@ -38,7 +37,8 @@ func (sm *SmokeTestRunner) SetTSSAddresses() { } tssAddress := ethcommon.HexToAddress(res.Eth) - btcTSSAddress, err := btcutil.DecodeAddress(res.Btc, common.BitcoinRegnetParams) + + btcTSSAddress, err := common.DecodeBtcAddress(res.Btc, common.BtcRegtestChain().ChainId) if err != nil { panic(err) } diff --git a/x/crosschain/keeper/evm_hooks.go b/x/crosschain/keeper/evm_hooks.go index 85a7c1d8a0..e4c3ed5d4b 100644 --- a/x/crosschain/keeper/evm_hooks.go +++ b/x/crosschain/keeper/evm_hooks.go @@ -289,11 +289,7 @@ func (k Keeper) ParseZRC20WithdrawalEvent(ctx sdk.Context, log ethtypes.Log) (*z if event.Value.Cmp(big.NewInt(0)) <= 0 { return nil, fmt.Errorf("ParseZRC20WithdrawalEvent: invalid amount %s", event.Value.String()) } - btcChainParams, err := common.GetBTCChainParams(chainID) - if err != nil { - return nil, err - } - addr, err := btcutil.DecodeAddress(string(event.To), btcChainParams) + addr, err := common.DecodeBtcAddress(string(event.To), chainID) if err != nil { return nil, fmt.Errorf("ParseZRC20WithdrawalEvent: invalid address %s: %s", event.To, err) } diff --git a/zetaclient/bitcoin_client.go b/zetaclient/bitcoin_client.go index 9a427a3701..4e0651b45e 100644 --- a/zetaclient/bitcoin_client.go +++ b/zetaclient/bitcoin_client.go @@ -758,11 +758,7 @@ func (ob *BitcoinChainClient) FetchUTXOS() error { // List unspent. tssAddr := ob.Tss.BTCAddress() - bitcoinNetParams, err := common.BitcoinNetParamsFromChainID(ob.chain.ChainId) - if err != nil { - return fmt.Errorf("btc: error getting bitcoin net params : %v", err) - } - address, err := btcutil.DecodeAddress(tssAddr, bitcoinNetParams) + address, err := common.DecodeBtcAddress(tssAddr, ob.chain.ChainId) if err != nil { return fmt.Errorf("btc: error decoding wallet address (%s) : %s", tssAddr, err.Error()) } diff --git a/zetaclient/btc_signer.go b/zetaclient/btc_signer.go index 761d233829..d175c9af71 100644 --- a/zetaclient/btc_signer.go +++ b/zetaclient/btc_signer.go @@ -301,8 +301,7 @@ func (signer *BTCSigner) TryProcessOutTx( logger.Error().Err(err).Msgf("cannot get bitcoin net params%v", err) return } - - addr, err := btcutil.DecodeAddress(params.Receiver, bitcoinNetParams) + addr, err := common.DecodeBtcAddress(params.Receiver, params.ReceiverChainId) if err != nil { logger.Error().Err(err).Msgf("cannot decode address %s ", params.Receiver) return