mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-05-26 18:09:33 +00:00
fix: wait for finalized deposit event
This commit is contained in:
parent
708ac37810
commit
10ded0dc23
@ -1,5 +1,6 @@
|
|||||||
#![expect(
|
#![expect(
|
||||||
clippy::tests_outside_test_module,
|
clippy::tests_outside_test_module,
|
||||||
|
clippy::arithmetic_side_effects,
|
||||||
reason = "We don't care about these in tests"
|
reason = "We don't care about these in tests"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
@ -26,6 +27,8 @@ use nssa_core::{InputAccountIdentity, account::AccountWithMetadata};
|
|||||||
use sequencer_service_rpc::RpcClient as _;
|
use sequencer_service_rpc::RpcClient as _;
|
||||||
use tokio::test;
|
use tokio::test;
|
||||||
|
|
||||||
|
const TIME_TO_FINALIZE_DEPOSIT_EVENT_ON_BEDROCK: Duration = Duration::from_mins(6);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
async fn public_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> {
|
async fn public_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> {
|
||||||
let ctx = TestContext::new().await?;
|
let ctx = TestContext::new().await?;
|
||||||
@ -189,16 +192,16 @@ async fn private_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(BorshSerialize)]
|
|
||||||
struct DepositMetadata {
|
|
||||||
recipient_id: AccountId,
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn submit_bedrock_deposit(
|
async fn submit_bedrock_deposit(
|
||||||
bedrock_addr: std::net::SocketAddr,
|
bedrock_addr: std::net::SocketAddr,
|
||||||
recipient_id: AccountId,
|
recipient_id: AccountId,
|
||||||
amount: u128,
|
amount: u128,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
#[derive(BorshSerialize)]
|
||||||
|
struct DepositMetadata {
|
||||||
|
recipient_id: AccountId,
|
||||||
|
}
|
||||||
|
|
||||||
// Encode deposit metadata
|
// Encode deposit metadata
|
||||||
let metadata = borsh::to_vec(&DepositMetadata { recipient_id })
|
let metadata = borsh::to_vec(&DepositMetadata { recipient_id })
|
||||||
.context("Failed to encode deposit metadata")?;
|
.context("Failed to encode deposit metadata")?;
|
||||||
@ -220,15 +223,7 @@ async fn submit_bedrock_deposit(
|
|||||||
.await
|
.await
|
||||||
.context("Failed to query Bedrock wallet balance")?;
|
.context("Failed to query Bedrock wallet balance")?;
|
||||||
|
|
||||||
if !balance_response.status().is_success() {
|
let balance_response = check_response_success(balance_response).await?;
|
||||||
let status = balance_response.status();
|
|
||||||
let body_text = balance_response.text().await.unwrap_or_default();
|
|
||||||
anyhow::bail!(
|
|
||||||
"Bedrock balance query failed with status {} and body {}",
|
|
||||||
status,
|
|
||||||
body_text,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
balance_response
|
balance_response
|
||||||
.json::<WalletBalanceResponseBody>()
|
.json::<WalletBalanceResponseBody>()
|
||||||
@ -273,16 +268,7 @@ async fn submit_bedrock_deposit(
|
|||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.context("Failed to submit Bedrock transfer-funds request")?;
|
.context("Failed to submit Bedrock transfer-funds request")?;
|
||||||
|
let transfer_response = check_response_success(transfer_response).await?;
|
||||||
if !transfer_response.status().is_success() {
|
|
||||||
let status = transfer_response.status();
|
|
||||||
let body_text = transfer_response.text().await.unwrap_or_default();
|
|
||||||
anyhow::bail!(
|
|
||||||
"Bedrock transfer-funds request failed with status {} and body {}",
|
|
||||||
status,
|
|
||||||
body_text,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let transfer: WalletTransferFundsResponseBody = transfer_response
|
let transfer: WalletTransferFundsResponseBody = transfer_response
|
||||||
.json()
|
.json()
|
||||||
@ -312,8 +298,7 @@ async fn submit_bedrock_deposit(
|
|||||||
|
|
||||||
let Some(selected_note_id) = selected_note_id else {
|
let Some(selected_note_id) = selected_note_id else {
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
"Failed to locate exact-value note {:?} for Bedrock deposit; available notes: {:?}",
|
"Failed to locate exact-value note {amount:?} for Bedrock deposit; available notes: {:?}",
|
||||||
amount,
|
|
||||||
balance.notes,
|
balance.notes,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -336,26 +321,26 @@ async fn submit_bedrock_deposit(
|
|||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.context("Failed to submit Bedrock deposit request")?;
|
.context("Failed to submit Bedrock deposit request")?;
|
||||||
|
let response = check_response_success(response).await?;
|
||||||
|
|
||||||
|
let body_text = response
|
||||||
|
.text()
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|_| "<failed to decode>".to_owned());
|
||||||
|
info!(
|
||||||
|
"Successfully submitted Bedrock deposit request for recipient {recipient_id} and amount {amount}, response body: {body_text}",
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn check_response_success(response: reqwest::Response) -> anyhow::Result<reqwest::Response> {
|
||||||
if response.status().is_success() {
|
if response.status().is_success() {
|
||||||
let body_text = response
|
Ok(response)
|
||||||
.text()
|
|
||||||
.await
|
|
||||||
.unwrap_or_else(|_| "<failed to decode>".to_owned());
|
|
||||||
info!(
|
|
||||||
"Successfully submitted Bedrock deposit request for recipient {recipient_id} and amount {amount}, response body: {}",
|
|
||||||
body_text
|
|
||||||
);
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
} else {
|
} else {
|
||||||
let status = response.status();
|
let status = response.status();
|
||||||
let body_text = response.text().await.unwrap_or_default();
|
let body_text = response.text().await.unwrap_or_default();
|
||||||
anyhow::bail!(
|
anyhow::bail!("Request failed with status {status} and body {body_text}");
|
||||||
"Bedrock deposit request failed with status {} and body {}",
|
|
||||||
status,
|
|
||||||
body_text,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,8 +348,9 @@ async fn wait_for_vault_balance(
|
|||||||
ctx: &TestContext,
|
ctx: &TestContext,
|
||||||
vault_id: AccountId,
|
vault_id: AccountId,
|
||||||
expected_balance: u128,
|
expected_balance: u128,
|
||||||
timeout: Duration,
|
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
let timeout = TIME_TO_FINALIZE_DEPOSIT_EVENT_ON_BEDROCK
|
||||||
|
+ Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS);
|
||||||
tokio::time::timeout(timeout, async {
|
tokio::time::timeout(timeout, async {
|
||||||
loop {
|
loop {
|
||||||
let balance = ctx.sequencer_client().get_account_balance(vault_id).await?;
|
let balance = ctx.sequencer_client().get_account_balance(vault_id).await?;
|
||||||
@ -401,13 +387,7 @@ async fn bedrock_deposit_mints_to_vault_then_claim_succeeds() -> anyhow::Result<
|
|||||||
submit_bedrock_deposit(ctx.bedrock_addr(), recipient_id, 1).await?;
|
submit_bedrock_deposit(ctx.bedrock_addr(), recipient_id, 1).await?;
|
||||||
|
|
||||||
// Wait for vault to receive the deposit (minted from bridge to vault)
|
// Wait for vault to receive the deposit (minted from bridge to vault)
|
||||||
wait_for_vault_balance(
|
wait_for_vault_balance(&ctx, recipient_vault_id, vault_balance_before + 1).await?;
|
||||||
&ctx,
|
|
||||||
recipient_vault_id,
|
|
||||||
vault_balance_before + 1,
|
|
||||||
Duration::from_mins(5),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Now claim funds from vault back to recipient
|
// Now claim funds from vault back to recipient
|
||||||
let nonces = ctx
|
let nonces = ctx
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::{collections::HashSet, net::SocketAddr, sync::Arc, time::Duration};
|
use std::{net::SocketAddr, sync::Arc, time::Duration};
|
||||||
|
|
||||||
use anyhow::{Context as _, Result, anyhow};
|
use anyhow::{Context as _, Result, anyhow};
|
||||||
#[cfg(not(feature = "standalone"))]
|
#[cfg(not(feature = "standalone"))]
|
||||||
@ -13,8 +13,10 @@ use jsonrpsee::server::ServerHandle;
|
|||||||
use log::warn;
|
use log::warn;
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
#[cfg(not(feature = "standalone"))]
|
#[cfg(not(feature = "standalone"))]
|
||||||
|
use logos_blockchain_core::mantle::ops::channel::MsgId;
|
||||||
|
#[cfg(not(feature = "standalone"))]
|
||||||
use logos_blockchain_zone_sdk::{
|
use logos_blockchain_zone_sdk::{
|
||||||
CommonHttpClient, ZoneMessage, adapter::Node as _, adapter::NodeHttpClient,
|
CommonHttpClient, Slot, ZoneMessage, adapter::NodeHttpClient, indexer::ZoneIndexer,
|
||||||
};
|
};
|
||||||
use mempool::MemPoolHandle;
|
use mempool::MemPoolHandle;
|
||||||
#[cfg(not(feature = "standalone"))]
|
#[cfg(not(feature = "standalone"))]
|
||||||
@ -252,77 +254,60 @@ async fn bedrock_deposit_loop(
|
|||||||
mempool_handle: MemPoolHandle<NSSATransaction>,
|
mempool_handle: MemPoolHandle<NSSATransaction>,
|
||||||
) -> Result<Never> {
|
) -> Result<Never> {
|
||||||
let basic_auth = bedrock_config.auth.map(Into::into);
|
let basic_auth = bedrock_config.auth.map(Into::into);
|
||||||
let channel_id = bedrock_config.channel_id;
|
|
||||||
let node = NodeHttpClient::new(CommonHttpClient::new(basic_auth), bedrock_config.node_url);
|
let node = NodeHttpClient::new(CommonHttpClient::new(basic_auth), bedrock_config.node_url);
|
||||||
let mut seen_deposits = HashSet::new();
|
let zone_indexer = ZoneIndexer::new(bedrock_config.channel_id, node);
|
||||||
|
|
||||||
|
let mut cursor: Option<(MsgId, Slot)> = None;
|
||||||
|
let poll_interval = Duration::from_secs(1);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let stream = match node.block_stream().await {
|
let stream = match zone_indexer.next_messages(cursor).await {
|
||||||
Ok(stream) => stream,
|
Ok(stream) => stream,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Failed to start Bedrock deposit stream: {err}");
|
error!("Failed to start Bedrock deposit stream: {err}");
|
||||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
tokio::time::sleep(poll_interval).await;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut stream = std::pin::pin!(stream);
|
let mut stream = std::pin::pin!(stream);
|
||||||
while let Some(block_event) = stream.next().await {
|
while let Some((msg, slot)) = stream.next().await {
|
||||||
let block_id = block_event.block.header.id;
|
match msg {
|
||||||
|
ZoneMessage::Block(block) => {
|
||||||
let zone_messages = match node.zone_messages_in_block(block_id, channel_id).await {
|
cursor = Some((block.id, slot));
|
||||||
Ok(messages) => messages,
|
info!("Observed Bedrock channel block id {:?}", block.id);
|
||||||
Err(err) => {
|
|
||||||
warn!("Failed to fetch zone messages for Bedrock block {block_id}: {err}");
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
};
|
ZoneMessage::Deposit(deposit) => {
|
||||||
let mut zone_messages = std::pin::pin!(zone_messages);
|
let metadata = match DepositMetadata::decode(&deposit.metadata) {
|
||||||
|
Ok(metadata) => metadata,
|
||||||
while let Some(msg) = zone_messages.next().await {
|
Err(err) => {
|
||||||
match msg {
|
warn!("Skipping Bedrock Deposit with invalid metadata: {err}");
|
||||||
ZoneMessage::Block(block) => {
|
|
||||||
info!("Observed Bedrock channel block id {:?}", block.id);
|
|
||||||
}
|
|
||||||
ZoneMessage::Deposit(deposit) => {
|
|
||||||
// Dedupe by stable payload to avoid replaying the same deposit.
|
|
||||||
let deposit_fingerprint =
|
|
||||||
format!("{:?}:{:?}", deposit.inputs, deposit.metadata);
|
|
||||||
if !seen_deposits.insert(deposit_fingerprint) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let metadata = match DepositMetadata::decode(&deposit.metadata) {
|
let tx = match build_bridge_deposit_tx(&metadata) {
|
||||||
Ok(metadata) => metadata,
|
Ok(tx) => tx,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
warn!("Skipping Bedrock Deposit with invalid metadata: {err}");
|
warn!("Skipping Bedrock Deposit due to tx build failure: {err:#}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let tx = match build_bridge_deposit_tx(&metadata) {
|
info!(
|
||||||
Ok(tx) => tx,
|
"Observed Bedrock Deposit for recipient {recipient_id}, pushing to mempool",
|
||||||
Err(err) => {
|
recipient_id = metadata.recipient_id
|
||||||
warn!("Skipping Bedrock Deposit due to tx build failure: {err:#}");
|
);
|
||||||
continue;
|
mempool_handle
|
||||||
}
|
.push(tx)
|
||||||
};
|
.await
|
||||||
|
.context("Mempool is closed while pushing Bedrock Deposit transaction")?;
|
||||||
info!(
|
|
||||||
"Observed Bedrock Deposit for recipient {recipient_id}, pushing to mempool",
|
|
||||||
recipient_id = metadata.recipient_id
|
|
||||||
);
|
|
||||||
mempool_handle.push(tx).await.context(
|
|
||||||
"Mempool is closed while pushing Bedrock Deposit transaction",
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
ZoneMessage::Withdraw(_) => {}
|
|
||||||
}
|
}
|
||||||
|
ZoneMessage::Withdraw(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
warn!("Bedrock deposit stream ended unexpectedly, reconnecting");
|
tokio::time::sleep(poll_interval).await;
|
||||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -189,6 +189,7 @@ pub fn addr_to_url(protocol: UrlProtocol, addr: SocketAddr) -> Result<Url> {
|
|||||||
url_string.parse().map_err(Into::into)
|
url_string.parse().map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn bedrock_channel_id() -> ChannelId {
|
pub fn bedrock_channel_id() -> ChannelId {
|
||||||
ChannelId::from([0_u8; 32])
|
ChannelId::from([0_u8; 32])
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user