Skip to content

Commit

Permalink
test: allow running Bitcoin tests on live networks (tentative) (#2303)
Browse files Browse the repository at this point in the history
* add local check

* improve mineblocks

* only generate on local bitcoin

* simplify some functions

* try removing min amount

* simplify error message

* refactor utxos list

* comments
  • Loading branch information
lumtis authored Jun 6, 2024
1 parent cc18372 commit aec6ac2
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 121 deletions.
12 changes: 3 additions & 9 deletions e2e/e2etests/helper_bitcoin.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,7 @@ func withdrawBTCZRC20(r *runner.E2ERunner, to btcutil.Address, amount *big.Int)
}

// mine blocks if testing on regnet
var stop chan struct{}
isRegnet := chains.IsBitcoinRegnet(r.GetBitcoinChainID())
if isRegnet {
stop = r.MineBlocks()
}
stop := r.MineBlocksIfLocalBitcoin()

// withdraw 'amount' of BTC from ZRC20 to BTC address
tx, err = r.BTCZRC20.Withdraw(r.ZEVMAuth, []byte(to.EncodeAddress()), amount)
Expand All @@ -81,7 +77,7 @@ func withdrawBTCZRC20(r *runner.E2ERunner, to btcutil.Address, amount *big.Int)
}

// mine 10 blocks to confirm the withdraw tx
_, err = r.BtcRPCClient.GenerateToAddress(10, to, nil)
_, err = r.GenerateToAddressIfLocalBitcoin(10, to)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -118,9 +114,7 @@ func withdrawBTCZRC20(r *runner.E2ERunner, to btcutil.Address, amount *big.Int)
}

// stop mining
if isRegnet {
stop <- struct{}{}
}
stop()

return rawTx
}
11 changes: 2 additions & 9 deletions e2e/e2etests/test_bitcoin_withdraw_invalid_address.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

"github.com/zeta-chain/zetacore/e2e/runner"
"github.com/zeta-chain/zetacore/e2e/utils"
"github.com/zeta-chain/zetacore/pkg/chains"
)

func TestBitcoinWithdrawToInvalidAddress(r *runner.E2ERunner, args []string) {
Expand Down Expand Up @@ -47,11 +46,7 @@ func withdrawToInvalidAddress(r *runner.E2ERunner, amount *big.Int) {
}

// mine blocks if testing on regnet
var stop chan struct{}
isRegnet := chains.IsBitcoinRegnet(r.GetBitcoinChainID())
if isRegnet {
stop = r.MineBlocks()
}
stop := r.MineBlocksIfLocalBitcoin()

// withdraw amount provided as test arg BTC from ZRC20 to BTC legacy address
// the address "1EYVvXLusCxtVuEwoYvWRyN5EZTXwPVvo3" is for mainnet, not regtest
Expand All @@ -65,7 +60,5 @@ func withdrawToInvalidAddress(r *runner.E2ERunner, amount *big.Int) {
}

// stop mining
if isRegnet {
stop <- struct{}{}
}
stop()
}
4 changes: 2 additions & 2 deletions e2e/e2etests/test_bitcoin_withdraw_multiple.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ package e2etests
// go func() {
// for {
// time.Sleep(3 * time.Second)
// _, err = r.BtcRPCClient.GenerateToAddress(1, r.BTCDeployerAddress, nil)
// _, err = r.GenerateToAddressIfLocalBitcoin(1, r.BTCDeployerAddress)
// if err != nil {
// panic(err)
// }
Expand All @@ -64,7 +64,7 @@ package e2etests
// if receipt.Status != 1 {
// panic(fmt.Errorf("withdraw receipt status is not 1"))
// }
// _, err = r.BtcRPCClient.GenerateToAddress(10, r.BTCDeployerAddress, nil)
// _, err = r.GenerateToAddressIfLocalBitcoin(10, r.BTCDeployerAddress)
// if err != nil {
// panic(err)
// }
Expand Down
35 changes: 7 additions & 28 deletions e2e/e2etests/test_crosschain_swap.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (

"github.com/zeta-chain/zetacore/e2e/runner"
"github.com/zeta-chain/zetacore/e2e/utils"
"github.com/zeta-chain/zetacore/pkg/chains"
"github.com/zeta-chain/zetacore/x/crosschain/types"
)

Expand Down Expand Up @@ -110,17 +109,13 @@ func TestCrosschainSwap(r *runner.E2ERunner, _ []string) {
}

// mine 10 blocks to confirm the outbound tx
_, err = r.BtcRPCClient.GenerateToAddress(10, r.BTCDeployerAddress, nil)
_, err = r.GenerateToAddressIfLocalBitcoin(10, r.BTCDeployerAddress)
if err != nil {
panic(err)
}

// mine blocks if testing on regnet
var stop chan struct{}
isRegnet := chains.IsBitcoinRegnet(r.GetBitcoinChainID())
if isRegnet {
stop = r.MineBlocks()
}
stop := r.MineBlocksIfLocalBitcoin()

// cctx1 index acts like the inboundHash for the second cctx (the one that withdraws BTC)
cctx2 := utils.WaitCctxMinedByInboundHash(r.Ctx, cctx1.Index, r.CctxClient, r.Logger, r.CctxTimeout)
Expand All @@ -137,8 +132,8 @@ func TestCrosschainSwap(r *runner.E2ERunner, _ []string) {
r.Logger.Info("cctx2 outbound tx hash %s", cctx2.GetCurrentOutboundParam().Hash)

r.Logger.Info("******* Second test: BTC -> ERC20ZRC20")
// list deployer utxos that have at least 1 BTC
utxos, err := r.ListDeployerUTXOs(1.0)
// list deployer utxos
utxos, err := r.ListDeployerUTXOs()
if err != nil {
panic(err)
}
Expand All @@ -151,14 +146,7 @@ func TestCrosschainSwap(r *runner.E2ERunner, _ []string) {
memo = append(r.ZEVMSwapAppAddr.Bytes(), memo...)
r.Logger.Info("memo length %d", len(memo))

txID, err := r.SendToTSSFromDeployerWithMemo(
r.BTCTSSAddress,
0.01,
utxos[0:1],
r.BtcRPCClient,
memo,
r.BTCDeployerAddress,
)
txID, err := r.SendToTSSFromDeployerWithMemo(0.01, utxos[0:1], memo)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -200,14 +188,7 @@ func TestCrosschainSwap(r *runner.E2ERunner, _ []string) {
r.Logger.Info("memo length %d", len(memo))

amount := 0.1
txid, err := r.SendToTSSFromDeployerWithMemo(
r.BTCTSSAddress,
amount,
utxos[1:2],
r.BtcRPCClient,
memo,
r.BTCDeployerAddress,
)
txid, err := r.SendToTSSFromDeployerWithMemo(amount, utxos[1:2], memo)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -239,7 +220,5 @@ func TestCrosschainSwap(r *runner.E2ERunner, _ []string) {
}

// stop mining
if isRegnet {
stop <- struct{}{}
}
stop()
}
125 changes: 54 additions & 71 deletions e2e/runner/bitcoin.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

"github.com/btcsuite/btcd/btcjson"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/rpcclient"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
Expand All @@ -29,8 +28,8 @@ import (

var blockHeaderBTCTimeout = 5 * time.Minute

// ListDeployerUTXOs list the deployer's UTXOs that have at least `minAmount`
func (runner *E2ERunner) ListDeployerUTXOs(minAmount float64) ([]btcjson.ListUnspentResult, error) {
// ListDeployerUTXOs list the deployer's UTXOs
func (runner *E2ERunner) ListDeployerUTXOs() ([]btcjson.ListUnspentResult, error) {
// query UTXOs from node
utxos, err := runner.BtcRPCClient.ListUnspentMinMaxAddresses(
1,
Expand All @@ -41,23 +40,15 @@ func (runner *E2ERunner) ListDeployerUTXOs(minAmount float64) ([]btcjson.ListUns
return nil, err
}

// filter UTXOs by `minAmount`
filtered := []btcjson.ListUnspentResult{}
for _, utxo := range utxos {
if utxo.Amount >= minAmount {
filtered = append(filtered, utxo)
}
}

return filtered, nil
return utxos, nil
}

// DepositBTCWithAmount deposits BTC on ZetaChain with a specific amount
func (runner *E2ERunner) DepositBTCWithAmount(amount float64) (txHash *chainhash.Hash) {
runner.Logger.Print("⏳ depositing BTC into ZEVM")

// list deployer utxos that have at least 1 BTC
utxos, err := runner.ListDeployerUTXOs(1.0)
// list deployer utxos
utxos, err := runner.ListDeployerUTXOs()
if err != nil {
panic(err)
}
Expand All @@ -72,7 +63,11 @@ func (runner *E2ERunner) DepositBTCWithAmount(amount float64) (txHash *chainhash
}

if spendableAmount < amount {
panic(fmt.Errorf("not enough spendable BTC to run the test; have %f", spendableAmount))
panic(fmt.Errorf(
"not enough spendable BTC to run the test; have %f, require %f",
spendableAmount,
amount,
))
}

runner.Logger.Info("ListUnspent:")
Expand All @@ -81,13 +76,7 @@ func (runner *E2ERunner) DepositBTCWithAmount(amount float64) (txHash *chainhash
runner.Logger.Info("Now sending two txs to TSS address...")

amount = amount + zetabitcoin.DefaultDepositorFee
txHash, err = runner.SendToTSSFromDeployerToDeposit(
runner.BTCTSSAddress,
amount,
utxos,
runner.BtcRPCClient,
runner.BTCDeployerAddress,
)
txHash, err = runner.SendToTSSFromDeployerToDeposit(amount, utxos)
if err != nil {
panic(err)
}
Expand All @@ -104,8 +93,8 @@ func (runner *E2ERunner) DepositBTC(testHeader bool) {
runner.Logger.Print("✅ BTC deposited in %s", time.Since(startTime))
}()

// list deployer utxos that have at least 1 BTC
utxos, err := runner.ListDeployerUTXOs(1.0)
// list deployer utxos
utxos, err := runner.ListDeployerUTXOs()
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -133,38 +122,19 @@ func (runner *E2ERunner) DepositBTC(testHeader bool) {

// send two transactions to the TSS address
amount1 := 1.1 + zetabitcoin.DefaultDepositorFee
txHash1, err := runner.SendToTSSFromDeployerToDeposit(
runner.BTCTSSAddress,
amount1,
utxos[:2],
runner.BtcRPCClient,
runner.BTCDeployerAddress,
)
txHash1, err := runner.SendToTSSFromDeployerToDeposit(amount1, utxos[:2])
if err != nil {
panic(err)
}
amount2 := 0.05 + zetabitcoin.DefaultDepositorFee
txHash2, err := runner.SendToTSSFromDeployerToDeposit(
runner.BTCTSSAddress,
amount2,
utxos[2:4],
runner.BtcRPCClient,
runner.BTCDeployerAddress,
)
txHash2, err := runner.SendToTSSFromDeployerToDeposit(amount2, utxos[2:4])
if err != nil {
panic(err)
}

// send a donation to the TSS address to compensate for the funds minted automatically during pool creation
// and prevent accounting errors
_, err = runner.SendToTSSFromDeployerWithMemo(
runner.BTCTSSAddress,
0.11,
utxos[4:5],
runner.BtcRPCClient,
[]byte(constant.DonationMessage),
runner.BTCDeployerAddress,
)
_, err = runner.SendToTSSFromDeployerWithMemo(0.11, utxos[4:5], []byte(constant.DonationMessage))
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -201,31 +171,22 @@ func (runner *E2ERunner) DepositBTC(testHeader bool) {
}
}

func (runner *E2ERunner) SendToTSSFromDeployerToDeposit(
to btcutil.Address,
amount float64,
inputUTXOs []btcjson.ListUnspentResult,
btc *rpcclient.Client,
btcDeployerAddress *btcutil.AddressWitnessPubKeyHash,
) (*chainhash.Hash, error) {
return runner.SendToTSSFromDeployerWithMemo(
to,
amount,
inputUTXOs,
btc,
runner.DeployerAddress.Bytes(),
btcDeployerAddress,
)
func (runner *E2ERunner) SendToTSSFromDeployerToDeposit(amount float64, inputUTXOs []btcjson.ListUnspentResult) (
*chainhash.Hash,
error,
) {
return runner.SendToTSSFromDeployerWithMemo(amount, inputUTXOs, runner.DeployerAddress.Bytes())
}

func (runner *E2ERunner) SendToTSSFromDeployerWithMemo(
to btcutil.Address,
amount float64,
inputUTXOs []btcjson.ListUnspentResult,
btcRPC *rpcclient.Client,
memo []byte,
btcDeployerAddress *btcutil.AddressWitnessPubKeyHash,
) (*chainhash.Hash, error) {
btcRPC := runner.BtcRPCClient
to := runner.BTCTSSAddress
btcDeployerAddress := runner.BTCDeployerAddress

// prepare inputs
inputs := make([]btcjson.TransactionInput, len(inputUTXOs))
inputSats := btcutil.Amount(0)
Expand Down Expand Up @@ -312,7 +273,7 @@ func (runner *E2ERunner) SendToTSSFromDeployerWithMemo(
panic(err)
}
runner.Logger.Info("txid: %+v", txid)
_, err = btcRPC.GenerateToAddress(6, btcDeployerAddress, nil)
_, err = runner.GenerateToAddressIfLocalBitcoin(6, btcDeployerAddress)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -359,25 +320,47 @@ func (runner *E2ERunner) GetBitcoinChainID() int64 {
return chainID
}

// MineBlocks mines blocks on the BTC chain at a rate of 1 blocks every 5 seconds
// IsLocalBitcoin returns true if the runner is running on a local bitcoin network
func (runner *E2ERunner) IsLocalBitcoin() bool {
return runner.BitcoinParams.Name == chains.BitcoinRegnetParams.Name
}

// GenerateToAddressIfLocalBitcoin generates blocks to an address if the runner is interacting
// with a local bitcoin network
func (runner *E2ERunner) GenerateToAddressIfLocalBitcoin(
numBlocks int64,
address btcutil.Address,
) ([]*chainhash.Hash, error) {
// if not local bitcoin network, do nothing
if runner.IsLocalBitcoin() {
return runner.BtcRPCClient.GenerateToAddress(numBlocks, address, nil)
}
return nil, nil
}

// MineBlocksIfLocalBitcoin mines blocks on the local BTC chain at a rate of 1 blocks every 5 seconds
// and returns a channel that can be used to stop the mining
func (runner *E2ERunner) MineBlocks() chan struct{} {
stop := make(chan struct{})
// If the chain is not local, the function does nothing
func (runner *E2ERunner) MineBlocksIfLocalBitcoin() func() {
stopChan := make(chan struct{})
go func() {
for {
select {
case <-stop:
case <-stopChan:
return
default:
_, err := runner.BtcRPCClient.GenerateToAddress(1, runner.BTCDeployerAddress, nil)
_, err := runner.GenerateToAddressIfLocalBitcoin(1, runner.BTCDeployerAddress)
if err != nil {
panic(err)
}
time.Sleep(3 * time.Second)
}
}
}()
return stop

return func() {
close(stopChan)
}
}

// ProveBTCTransaction proves that a BTC transaction is in a block header and that the block header is in ZetaChain
Expand Down
Loading

0 comments on commit aec6ac2

Please sign in to comment.