Skip to content

Commit

Permalink
func-test: withdraw BOSD OP_RETURN
Browse files Browse the repository at this point in the history
  • Loading branch information
storopoli committed Jan 15, 2025
1 parent e49d108 commit 03309d9
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 14 deletions.
53 changes: 42 additions & 11 deletions functional-tests/envs/testenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import flexitest
from strata_utils import (
deposit_request_transaction,
extract_p2tr_pubkey,
get_address,
get_recovery_address,
xonlypk_to_descriptor,
Expand Down Expand Up @@ -107,31 +106,29 @@ def deposit(self, ctx: flexitest.RunContext, el_address, bridge_pk):
error_with="Strata balance after deposit is not as expected",
)

def withdraw(
def withdraw_op_return(
self,
ctx: flexitest.RunContext,
el_address: str,
withdraw_address: str,
payload: str,
):
"""
Perform a withdrawal from the L2 to the given BTC withdraw address.
Perform a withdrawal from the L2 to the BTC using the OP_RETURN with the given payload.
Returns (l2_tx_hash, tx_receipt, total_gas_used).
"""
cfg: RollupConfig = ctx.env.rollup_cfg()
# D BTC
deposit_amount = cfg.deposit_amount
# Build the p2tr pubkey from the withdraw address
change_address_pk = extract_p2tr_pubkey(withdraw_address)
self.debug(f"Change Address PK: {change_address_pk}")
self.debug(f"OP_RETURN payload: {payload}")

# Estimate gas
estimated_withdraw_gas = self.__estimate_withdraw_gas(
deposit_amount, el_address, change_address_pk
estimated_withdraw_gas = self.__estimate_withdraw_gas_op_return(
deposit_amount, el_address, payload
)
self.debug(f"Estimated withdraw gas: {estimated_withdraw_gas}")

l2_tx_hash = self.__make_withdraw(
deposit_amount, el_address, change_address_pk, estimated_withdraw_gas
l2_tx_hash = self.__make_withdraw_op_return(
deposit_amount, el_address, payload, estimated_withdraw_gas
).hex()
self.debug(f"Sent withdrawal transaction with hash: {l2_tx_hash}")

Expand Down Expand Up @@ -176,6 +173,27 @@ def __make_withdraw(
l2_tx_hash = self.web3.eth.send_transaction(transaction)
return l2_tx_hash

def __make_withdraw_op_return(
self,
deposit_amount,
el_address,
payload,
gas,
):
"""
Withdrawal Request Transaction in Strata's EVM.
"""

transaction = {
"from": el_address,
"to": PRECOMPILE_BRIDGEOUT_ADDRESS,
"value": deposit_amount * SATS_TO_WEI,
"gas": gas,
"data": payload,
}
l2_tx_hash = self.web3.eth.send_transaction(transaction)
return l2_tx_hash

def __estimate_withdraw_gas(self, deposit_amount, el_address, change_address_pk):
"""
Estimate the gas for the withdrawal transaction.
Expand All @@ -191,6 +209,19 @@ def __estimate_withdraw_gas(self, deposit_amount, el_address, change_address_pk)
}
return self.web3.eth.estimate_gas(transaction)

def __estimate_withdraw_gas_op_return(self, deposit_amount, el_address, payload):
"""
Estimate the gas for the withdrawal transaction using an OP_RETURN
"""

transaction = {
"from": el_address,
"to": PRECOMPILE_BRIDGEOUT_ADDRESS,
"value": deposit_amount * SATS_TO_WEI,
"data": payload,
}
return self.web3.eth.estimate_gas(transaction)

def make_drt(self, ctx: flexitest.RunContext, el_address, musig_bridge_pk):
"""
Deposit Request Transaction
Expand Down
103 changes: 103 additions & 0 deletions functional-tests/tests/bridge_withdraw_happy_op_return.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import flexitest
from bitcoinlib.services.bitcoind import BitcoindClient
from strata_utils import get_balance, string_to_opreturn_descriptor

from envs import net_settings, testenv
from utils import generate_n_blocks, get_bridge_pubkey, wait_until

# Local constants
# Gas for the withdrawal transaction
WITHDRAWAL_GAS_FEE = 22_000 # technically is 21_000
# Ethereum Private Key
# NOTE: don't use this private key in production
ETH_PRIVATE_KEY = "0x0000000000000000000000000000000000000000000000000000000000000001"


@flexitest.register
class BridgeWithdrawHappyOpReturnTest(testenv.BridgeTestBase):
"""
Makes two DRT deposits to the same EL address, then makes a withdrawal to an OP_RETURN.
Checks if the balance of the EL address is expected
and if the BTC has an OP_RETURN block.
"""

def __init__(self, ctx: flexitest.InitContext):
fast_batch_settings = net_settings.get_fast_batch_settings()
ctx.set_env(
testenv.BasicEnvConfig(
pre_generate_blocks=101,
rollup_settings=fast_batch_settings,
# need to manually control the block generations
auto_generate_blocks=False,
)
)

def main(self, ctx: flexitest.RunContext):
btc = ctx.get_service("bitcoin")
seq = ctx.get_service("sequencer")
# create both btc and sequencer RPC
btcrpc: BitcoindClient = btc.create_rpc()
seqrpc = seq.create_rpc()
# generate 5 btc blocks
generate_n_blocks(btcrpc, 5)

# Wait for seq
wait_until(
lambda: seqrpc.strata_protocolVersion() is not None,
error_with="Sequencer did not start on time",
)
generate_n_blocks(btcrpc, 5)

# Generate addresses
address = ctx.env.gen_ext_btc_address()
withdraw_address = ctx.env.gen_ext_btc_address()
el_address = self.eth_account.address
payload = "hello world"
bosd = string_to_opreturn_descriptor(payload)
self.debug(f"BOSD: {bosd}")

self.debug(f"Address: {address}")
self.debug(f"Change Address: {withdraw_address}")
self.debug(f"EL Address: {el_address}")

# Original BTC balance
btc_url = self.btcrpc.base_url
btc_user = self.btc.get_prop("rpc_user")
btc_password = self.btc.get_prop("rpc_password")
original_balance = get_balance(withdraw_address, btc_url, btc_user, btc_password)
self.debug(f"BTC balance before withdraw: {original_balance}")

# Make sure starting ETH balance is 0
check_initial_eth_balance(self.rethrpc, el_address, self.debug)

bridge_pk = get_bridge_pubkey(self.seqrpc)
self.debug(f"Bridge pubkey: {bridge_pk}")

# make two deposits
self.deposit(ctx, el_address, bridge_pk)
self.deposit(ctx, el_address, bridge_pk)

# Withdraw
self.withdraw_op_return(ctx, el_address, bosd)

# Move forward a single block
block = generate_n_blocks(btcrpc, 1)[0] # There's only one
last_block = btcrpc.getblock(block)

# Get the output of the tx
# OP_RETURN is the second output
outputs = last_block["txs"][0].as_dict()["outputs"]
op_return_output = outputs[1]
self.debug(f"OP_RETURN output: {op_return_output}")
op_return_script_type = op_return_output["script_type"]
assert op_return_script_type == "nulldata", "OP_RETURN not found"

return True


def check_initial_eth_balance(rethrpc, address, debug_fn=print):
"""Asserts that the initial ETH balance for `address` is zero."""
balance = int(rethrpc.eth_getBalance(address), 16)
debug_fn(f"Strata Balance before deposits: {balance}")
assert balance == 0, "Strata balance is not expected (should be zero initially)"
7 changes: 4 additions & 3 deletions functional-tests/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def generate_n_blocks(bitcoin_rpc: BitcoindClient, n: int):
try:
blk = bitcoin_rpc.proxy.generatetoaddress(n, addr)
print(f"made blocks {blk}")
return blk
except Exception as ex:
logging.warning(f"{ex} while generating address")
return
Expand Down Expand Up @@ -156,9 +157,9 @@ def check_nth_checkpoint_finalized(
timeout=3,
)

assert (
syncstat["finalized_block_id"] != batch_info["l2_blockid"]
), "Checkpoint block should not yet finalize"
assert syncstat["finalized_block_id"] != batch_info["l2_blockid"], (
"Checkpoint block should not yet finalize"
)
assert batch_info["idx"] == idx
checkpoint_info_next = seqrpc.strata_getCheckpointInfo(idx + 1)
assert checkpoint_info_next is None, f"There should be no checkpoint info for {idx + 1} index"
Expand Down

0 comments on commit 03309d9

Please sign in to comment.