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

refactor: replace docker-based inscription builder sidecar with Golang implementation #3082

Merged
merged 10 commits into from
Nov 4, 2024
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
* [2899](https://github.com/zeta-chain/node/pull/2899) - remove btc deposit fee v1 and improve unit tests
* [2952](https://github.com/zeta-chain/node/pull/2952) - add error_message to cctx.status
* [3039](https://github.com/zeta-chain/node/pull/3039) - use `btcd` native APIs to handle Bitcoin Taproot address
* [3082](https://github.com/zeta-chain/node/pull/3082) - replace docker-based bitcoin sidecar inscription build with Golang implementation

### Tests

Expand Down
1 change: 1 addition & 0 deletions cmd/zetae2e/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ func localE2ETest(cmd *cobra.Command, _ []string) {
e2etests.TestBitcoinStdMemoDepositAndCallName,
e2etests.TestBitcoinStdMemoDepositAndCallRevertName,
e2etests.TestBitcoinStdMemoDepositAndCallRevertOtherAddressName,
e2etests.TestBitcoinStdMemoInscribedDepositAndCallName,
e2etests.TestBitcoinWithdrawSegWitName,
e2etests.TestBitcoinWithdrawInvalidAddressName,
e2etests.TestZetaWithdrawBTCRevertName,
Expand Down
12 changes: 0 additions & 12 deletions contrib/localnet/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -227,18 +227,6 @@ services:
-rpcauth=smoketest:63acf9b8dccecce914d85ff8c044b78b$$5892f9bbc84f4364e79f0970039f88bdd823f168d4acc76099ab97b14a766a99
-txindex=1

bitcoin-node-sidecar:
image: ghcr.io/zeta-chain/node-localnet-bitcoin-sidecar:e0205d7
container_name: bitcoin-node-sidecar
hostname: bitcoin-node-sidecar
networks:
mynetwork:
ipv4_address: 172.20.0.111
environment:
- PORT=8000
ports:
- "8000:8000"

solana:
image: solana-local:latest
container_name: solana
Expand Down
18 changes: 10 additions & 8 deletions e2e/e2etests/e2etests.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ const (
TestBitcoinStdMemoDepositAndCallName = "bitcoin_std_memo_deposit_and_call"
TestBitcoinStdMemoDepositAndCallRevertName = "bitcoin_std_memo_deposit_and_call_revert"
TestBitcoinStdMemoDepositAndCallRevertOtherAddressName = "bitcoin_std_memo_deposit_and_call_revert_other_address"
TestBitcoinStdMemoInscribedDepositAndCallName = "bitcoin_std_memo_inscribed_deposit_and_call"
TestBitcoinWithdrawSegWitName = "bitcoin_withdraw_segwit"
TestBitcoinWithdrawTaprootName = "bitcoin_withdraw_taproot"
TestBitcoinWithdrawMultipleName = "bitcoin_withdraw_multiple"
Expand All @@ -89,7 +90,6 @@ const (
TestBitcoinWithdrawP2SHName = "bitcoin_withdraw_p2sh"
TestBitcoinWithdrawInvalidAddressName = "bitcoin_withdraw_invalid"
TestBitcoinWithdrawRestrictedName = "bitcoin_withdraw_restricted"
TestExtractBitcoinInscriptionMemoName = "bitcoin_memo_from_inscription"

/*
Application tests
Expand Down Expand Up @@ -490,13 +490,6 @@ var AllE2ETests = []runner.E2ETest{
},
TestBitcoinDonation,
),
runner.NewE2ETest(
TestExtractBitcoinInscriptionMemoName,
"extract memo from BTC inscription", []runner.ArgDefinition{
{Description: "amount in btc", DefaultValue: "0.1"},
},
TestExtractBitcoinInscriptionMemo,
),
runner.NewE2ETest(
TestBitcoinDepositName,
"deposit Bitcoin into ZEVM",
Expand Down Expand Up @@ -552,6 +545,15 @@ var AllE2ETests = []runner.E2ETest{
},
TestBitcoinStdMemoDepositAndCallRevertOtherAddress,
),
runner.NewE2ETest(
TestBitcoinStdMemoInscribedDepositAndCallName,
"deposit Bitcoin into ZEVM and call a contract with inscribed standard memo",
ws4charlie marked this conversation as resolved.
Show resolved Hide resolved
[]runner.ArgDefinition{
{Description: "amount in btc", DefaultValue: "0.1"},
{Description: "fee rate", DefaultValue: "10"},
},
TestBitcoinStdMemoInscribedDepositAndCall,
),
runner.NewE2ETest(
TestBitcoinWithdrawSegWitName,
"withdraw BTC from ZEVM to a SegWit address",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package e2etests

import (
"math/big"

"github.com/stretchr/testify/require"

"github.com/zeta-chain/node/e2e/runner"
"github.com/zeta-chain/node/e2e/utils"
"github.com/zeta-chain/node/pkg/memo"
testcontract "github.com/zeta-chain/node/testutil/contracts"
crosschaintypes "github.com/zeta-chain/node/x/crosschain/types"
zetabitcoin "github.com/zeta-chain/node/zetaclient/chains/bitcoin"
)

func TestBitcoinStdMemoInscribedDepositAndCall(r *runner.E2ERunner, args []string) {
// ARRANGE
// Given BTC address
r.SetBtcAddress(r.Name, false)

// Start mining blocks
stop := r.MineBlocksIfLocalBitcoin()
defer stop()

// Given amount to send and fee rate
require.Len(r, args, 2)
amount := parseFloat(r, args[0])
feeRate := parseInt(r, args[1])
ws4charlie marked this conversation as resolved.
Show resolved Hide resolved

// deploy an example contract in ZEVM
contractAddr, _, contract, err := testcontract.DeployExample(r.ZEVMAuth, r.ZEVMClient)
require.NoError(r, err)

// create a standard memo > 80 bytes
memo := &memo.InboundMemo{
Header: memo.Header{
Version: 0,
EncodingFmt: memo.EncodingFmtCompactShort,
OpCode: memo.OpCodeDepositAndCall,
},
FieldsV0: memo.FieldsV0{
Receiver: contractAddr,
Payload: []byte("for use case that passes a large memo > 80 bytes, inscripting the memo is the way to go"),
},
}
memoBytes, err := memo.EncodeToBytes()
require.NoError(r, err)
ws4charlie marked this conversation as resolved.
Show resolved Hide resolved

// ACT
// Send BTC to TSS address with memo
txHash, depositAmount := r.InscribeToTSSFromDeployerWithMemo(amount, memoBytes, int64(feeRate))

// ASSERT
// wait for the cctx to be mined
cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, txHash.String(), r.CctxClient, r.Logger, r.CctxTimeout)
r.Logger.CCTX(*cctx, "bitcoin_std_memo_inscribed_deposit_and_call")
utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined)

// check if example contract has been called, 'bar' value should be set to correct amount
depositFeeSats, err := zetabitcoin.GetSatoshis(zetabitcoin.DefaultDepositorFee)
require.NoError(r, err)
receiveAmount := depositAmount - depositFeeSats
utils.MustHaveCalledExampleContract(r, contract, big.NewInt(receiveAmount))
}
57 changes: 0 additions & 57 deletions e2e/e2etests/test_extract_bitcoin_inscription_memo.go

This file was deleted.

2 changes: 1 addition & 1 deletion e2e/runner/accounting.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (r *E2ERunner) CheckBtcTSSBalance() error {
)
}
// #nosec G115 test - always in range
r.Logger.Print(
r.Logger.Info(
"BTC: Balance (%d) >= ZRC20 TotalSupply (%d)",
int64(tssTotalBalance*1e8),
zrc20Supply.Int64()-10000000,
Expand Down
54 changes: 28 additions & 26 deletions e2e/runner/bitcoin.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"encoding/hex"
"fmt"
"net/http"
"sort"
"time"

Expand Down Expand Up @@ -331,44 +330,47 @@ func (r *E2ERunner) sendToAddrFromDeployerWithMemo(
// InscribeToTSSFromDeployerWithMemo creates an inscription that is sent to the tss address with the corresponding memo
func (r *E2ERunner) InscribeToTSSFromDeployerWithMemo(
amount float64,
inputUTXOs []btcjson.ListUnspentResult,
memo []byte,
) *chainhash.Hash {
// TODO: replace builder with Go function to enable instructions
// https://github.com/zeta-chain/node/issues/2759
builder := InscriptionBuilder{sidecarURL: "http://bitcoin-node-sidecar:8000", client: http.Client{}}

address, err := builder.GenerateCommitAddress(memo)
require.NoError(r, err)
r.Logger.Info("received inscription commit address %s", address)

receiver, err := chains.DecodeBtcAddress(address, r.GetBitcoinChainID())
feeRate int64,
) (*chainhash.Hash, int64) {
ws4charlie marked this conversation as resolved.
Show resolved Hide resolved
// list deployer utxos
utxos, err := r.ListDeployerUTXOs()
require.NoError(r, err)

txnHash, err := r.sendToAddrFromDeployerWithMemo(amount, receiver, inputUTXOs, []byte(constant.DonationMessage))
// generate commit address
builder := NewTapscriptSpender(r.BitcoinParams)
receiver, err := builder.GenerateCommitAddress(memo)
require.NoError(r, err)
r.Logger.Info("obtained inscription commit txn hash %s", txnHash.String())
r.Logger.Info("received inscription commit address: %s", receiver)
ws4charlie marked this conversation as resolved.
Show resolved Hide resolved

// sendToAddrFromDeployerWithMemo makes sure index is 0
outpointIdx := 0
hexTx, err := builder.GenerateRevealTxn(r.BTCTSSAddress.String(), txnHash.String(), outpointIdx, amount)
// send funds to the commit address
commitTxHash, err := r.sendToAddrFromDeployerWithMemo(amount, receiver, utxos, nil)
require.NoError(r, err)
r.Logger.Info("obtained inscription commit txn hash: %s", commitTxHash.String())

// Decode the hex string into raw bytes
rawTxBytes, err := hex.DecodeString(hexTx)
// parameters to build the reveal transaction
commitOutputIdx := uint32(0)
commitAmount, err := zetabitcoin.GetSatoshis(amount)
require.NoError(r, err)

// Deserialize the raw bytes into a wire.MsgTx structure
msgTx := wire.NewMsgTx(wire.TxVersion)
err = msgTx.Deserialize(bytes.NewReader(rawTxBytes))
// build the reveal transaction to spend above funds
revealTx, err := builder.BuildRevealTxn(
r.BTCTSSAddress,
wire.OutPoint{
Hash: *commitTxHash,
Index: commitOutputIdx,
},
commitAmount,
feeRate,
)
require.NoError(r, err)
r.Logger.Info("recovered inscription reveal txn %s", hexTx)

txid, err := r.BtcRPCClient.SendRawTransaction(msgTx, true)
// submit the reveal transaction
txid, err := r.BtcRPCClient.SendRawTransaction(revealTx, true)
require.NoError(r, err)
r.Logger.Info("txid: %+v", txid)
r.Logger.Info("reveal txid: %s", txid.String())

return txid
return txid, revealTx.TxOut[0].Value
ws4charlie marked this conversation as resolved.
Show resolved Hide resolved
}

// GetBitcoinChainID gets the bitcoin chain ID from the network params
Expand Down
Loading
Loading