mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-08-01 12:33:24 +00:00
feat(sequencer): disable withdraws
This commit is contained in:
parent
50ac64c37d
commit
93a93c6b96
112
Cargo.lock
generated
112
Cargo.lock
generated
@ -1220,7 +1220,6 @@ dependencies = [
|
||||
name = "bridge_program"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"authenticated_transfer_core",
|
||||
"bridge_core",
|
||||
"lee_core",
|
||||
"vault_core",
|
||||
@ -3037,15 +3036,6 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared 0.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.5.0"
|
||||
@ -3053,7 +3043,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
|
||||
dependencies = [
|
||||
"foreign-types-macros",
|
||||
"foreign-types-shared 0.3.1",
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3067,12 +3057,6 @@ dependencies = [
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.3.1"
|
||||
@ -3852,22 +3836,6 @@ dependencies = [
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.20"
|
||||
@ -3886,11 +3854,9 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"socket2 0.6.4",
|
||||
"system-configuration 0.7.0",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"windows-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4304,7 +4270,6 @@ dependencies = [
|
||||
"anyhow",
|
||||
"associated_token_account_core",
|
||||
"authenticated_transfer_core",
|
||||
"borsh",
|
||||
"bridge_core",
|
||||
"bridge_lock_core",
|
||||
"bytesize",
|
||||
@ -4312,7 +4277,6 @@ dependencies = [
|
||||
"cross_zone_inbox_core",
|
||||
"cross_zone_outbox_core",
|
||||
"faucet_core",
|
||||
"futures",
|
||||
"hex",
|
||||
"indexer_ffi",
|
||||
"indexer_service_protocol",
|
||||
@ -4322,12 +4286,9 @@ dependencies = [
|
||||
"lee_core",
|
||||
"log",
|
||||
"logos-blockchain-core",
|
||||
"logos-blockchain-http-api-common",
|
||||
"logos-blockchain-key-management-system-service",
|
||||
"logos-blockchain-zone-sdk",
|
||||
"ping_core",
|
||||
"programs",
|
||||
"reqwest",
|
||||
"risc0-zkvm",
|
||||
"sequencer_core",
|
||||
"sequencer_service_rpc",
|
||||
@ -6676,7 +6637,7 @@ dependencies = [
|
||||
"bitflags 2.12.1",
|
||||
"block",
|
||||
"core-graphics-types",
|
||||
"foreign-types 0.5.0",
|
||||
"foreign-types",
|
||||
"log",
|
||||
"objc",
|
||||
"paste",
|
||||
@ -6839,23 +6800,6 @@ dependencies = [
|
||||
"unsigned-varint 0.7.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "natpmp"
|
||||
version = "0.5.0"
|
||||
@ -7240,49 +7184,12 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967"
|
||||
dependencies = [
|
||||
"bitflags 2.12.1",
|
||||
"cfg-if",
|
||||
"foreign-types 0.3.2",
|
||||
"libc",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.116"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opentelemetry"
|
||||
version = "0.31.0"
|
||||
@ -8440,7 +8347,6 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
@ -8450,12 +8356,9 @@ dependencies = [
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"hyper-tls",
|
||||
"hyper-util",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
@ -8466,7 +8369,6 @@ dependencies = [
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
@ -10537,16 +10439,6 @@ dependencies = [
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.4"
|
||||
|
||||
@ -38,16 +38,11 @@ programs.workspace = true
|
||||
test_programs.workspace = true
|
||||
testnet_initial_state.workspace = true
|
||||
|
||||
logos-blockchain-http-api-common.workspace = true
|
||||
logos-blockchain-core.workspace = true
|
||||
logos-blockchain-zone-sdk.workspace = true
|
||||
logos-blockchain-key-management-system-service.workspace = true
|
||||
anyhow.workspace = true
|
||||
log.workspace = true
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||
futures.workspace = true
|
||||
hex.workspace = true
|
||||
tempfile.workspace = true
|
||||
bytesize.workspace = true
|
||||
reqwest.workspace = true
|
||||
borsh.workspace = true
|
||||
|
||||
@ -1,42 +1,23 @@
|
||||
#![expect(
|
||||
clippy::tests_outside_test_module,
|
||||
clippy::arithmetic_side_effects,
|
||||
reason = "We don't care about these in tests"
|
||||
)]
|
||||
|
||||
use std::{collections::HashSet, ops::Deref as _, time::Duration};
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Context as _;
|
||||
use borsh::BorshSerialize;
|
||||
use common::transaction::LeeTransaction;
|
||||
use futures::StreamExt as _;
|
||||
use integration_tests::{
|
||||
TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext, account_balance, get_account,
|
||||
wait_for_indexer_to_catch_up,
|
||||
};
|
||||
use lee::{
|
||||
AccountId, execute_and_prove, privacy_preserving_transaction, program::Program,
|
||||
public_transaction,
|
||||
execute_and_prove, privacy_preserving_transaction, program::Program, public_transaction,
|
||||
};
|
||||
use lee_core::{InputAccountIdentity, account::AccountWithMetadata};
|
||||
use log::info;
|
||||
use logos_blockchain_core::mantle::{ledger::Inputs, ops::channel::deposit::DepositOp};
|
||||
use logos_blockchain_http_api_common::bodies::{
|
||||
channel::ChannelDepositRequestBody,
|
||||
wallet::{
|
||||
balance::WalletBalanceResponseBody,
|
||||
transfer_funds::{WalletTransferFundsRequestBody, WalletTransferFundsResponseBody},
|
||||
},
|
||||
};
|
||||
use logos_blockchain_zone_sdk::{
|
||||
CommonHttpClient, ZoneMessage, adapter::NodeHttpClient, indexer::ZoneIndexer,
|
||||
};
|
||||
use sequencer_service_rpc::RpcClient as _;
|
||||
use test_fixtures::public_mention;
|
||||
use tokio::test;
|
||||
use wallet::cli::{Command, execute_subcommand, programs::bridge::BridgeSubcommand};
|
||||
|
||||
const TIME_TO_FINALIZE_DEPOSIT_EVENT_ON_BEDROCK: Duration = Duration::from_mins(2);
|
||||
// const TIME_TO_FINALIZE_DEPOSIT_EVENT_ON_BEDROCK: Duration = Duration::from_mins(2);
|
||||
|
||||
#[test]
|
||||
async fn public_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> {
|
||||
@ -248,384 +229,386 @@ async fn private_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn submit_bedrock_deposit(
|
||||
bedrock_addr: std::net::SocketAddr,
|
||||
bedrock_account_pk: &str,
|
||||
recipient_id: AccountId,
|
||||
amount: u64,
|
||||
) -> anyhow::Result<()> {
|
||||
#[derive(BorshSerialize)]
|
||||
struct DepositMetadata {
|
||||
recipient_id: AccountId,
|
||||
}
|
||||
// async fn submit_bedrock_deposit(
|
||||
// bedrock_addr: std::net::SocketAddr,
|
||||
// bedrock_account_pk: &str,
|
||||
// recipient_id: AccountId,
|
||||
// amount: u64,
|
||||
// ) -> anyhow::Result<()> {
|
||||
// #[derive(BorshSerialize)]
|
||||
// struct DepositMetadata {
|
||||
// recipient_id: AccountId,
|
||||
// }
|
||||
|
||||
// Encode deposit metadata
|
||||
let metadata = borsh::to_vec(&DepositMetadata { recipient_id })
|
||||
.context("Failed to encode deposit metadata")?
|
||||
.try_into()
|
||||
.context("Encoded metadata is too big")?;
|
||||
// // Encode deposit metadata
|
||||
// let metadata = borsh::to_vec(&DepositMetadata { recipient_id })
|
||||
// .context("Failed to encode deposit metadata")?
|
||||
// .try_into()
|
||||
// .context("Encoded metadata is too big")?;
|
||||
|
||||
let channel_id = integration_tests::config::bedrock_channel_id();
|
||||
let client = reqwest::Client::new();
|
||||
// let channel_id = integration_tests::config::bedrock_channel_id();
|
||||
// let client = reqwest::Client::new();
|
||||
|
||||
let mut balance = bedrock_wallet_balance(bedrock_addr, bedrock_account_pk).await?;
|
||||
// let mut balance = bedrock_wallet_balance(bedrock_addr, bedrock_account_pk).await?;
|
||||
|
||||
info!(
|
||||
"Queried Bedrock balance for key {bedrock_account_pk}: {:?}",
|
||||
balance.balance
|
||||
);
|
||||
// info!(
|
||||
// "Queried Bedrock balance for key {bedrock_account_pk}: {:?}",
|
||||
// balance.balance
|
||||
// );
|
||||
|
||||
if balance.balance < amount {
|
||||
anyhow::bail!(
|
||||
"Bedrock wallet with key {bedrock_account_pk} has insufficient balance {:?} for deposit amount {:?}",
|
||||
balance.balance,
|
||||
amount
|
||||
);
|
||||
}
|
||||
// if balance.balance < amount {
|
||||
// anyhow::bail!(
|
||||
// "Bedrock wallet with key {bedrock_account_pk} has insufficient balance {:?} for
|
||||
// deposit amount {:?}", balance.balance,
|
||||
// amount
|
||||
// );
|
||||
// }
|
||||
|
||||
let mut selected_note_id = balance
|
||||
.notes
|
||||
.iter()
|
||||
.find_map(|(note_id, value)| (*value == amount).then_some(*note_id));
|
||||
// let mut selected_note_id = balance
|
||||
// .notes
|
||||
// .iter()
|
||||
// .find_map(|(note_id, value)| (*value == amount).then_some(*note_id));
|
||||
|
||||
if selected_note_id.is_none() {
|
||||
let transfer_body = WalletTransferFundsRequestBody {
|
||||
tip: None,
|
||||
change_public_key: balance.address,
|
||||
funding_public_keys: vec![balance.address],
|
||||
recipient_public_key: balance.address,
|
||||
amount,
|
||||
};
|
||||
// if selected_note_id.is_none() {
|
||||
// let transfer_body = WalletTransferFundsRequestBody {
|
||||
// tip: None,
|
||||
// change_public_key: balance.address,
|
||||
// funding_public_keys: vec![balance.address],
|
||||
// recipient_public_key: balance.address,
|
||||
// amount,
|
||||
// };
|
||||
|
||||
let transfer_response = client
|
||||
.post(format!(
|
||||
"http://{bedrock_addr}/wallet/transactions/transfer-funds"
|
||||
))
|
||||
.json(&transfer_body)
|
||||
.send()
|
||||
.await
|
||||
.context("Failed to submit Bedrock transfer-funds request")?;
|
||||
let transfer_response = check_response_success(transfer_response).await?;
|
||||
// let transfer_response = client
|
||||
// .post(format!(
|
||||
// "http://{bedrock_addr}/wallet/transactions/transfer-funds"
|
||||
// ))
|
||||
// .json(&transfer_body)
|
||||
// .send()
|
||||
// .await
|
||||
// .context("Failed to submit Bedrock transfer-funds request")?;
|
||||
// let transfer_response = check_response_success(transfer_response).await?;
|
||||
|
||||
let transfer: WalletTransferFundsResponseBody = transfer_response
|
||||
.json()
|
||||
.await
|
||||
.context("Failed to decode Bedrock transfer-funds response")?;
|
||||
// let transfer: WalletTransferFundsResponseBody = transfer_response
|
||||
// .json()
|
||||
// .await
|
||||
// .context("Failed to decode Bedrock transfer-funds response")?;
|
||||
|
||||
info!(
|
||||
"Submitted transfer-funds to create exact deposit note, tx hash {:?}",
|
||||
transfer.hash
|
||||
);
|
||||
// info!(
|
||||
// "Submitted transfer-funds to create exact deposit note, tx hash {:?}",
|
||||
// transfer.hash
|
||||
// );
|
||||
|
||||
let mut found_note = None;
|
||||
for _ in 0..20 {
|
||||
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||
balance = bedrock_wallet_balance(bedrock_addr, bedrock_account_pk).await?;
|
||||
found_note = balance
|
||||
.notes
|
||||
.iter()
|
||||
.find_map(|(note_id, value)| (*value == amount).then_some(*note_id));
|
||||
if found_note.is_some() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// let mut found_note = None;
|
||||
// for _ in 0..20 {
|
||||
// tokio::time::sleep(Duration::from_millis(500)).await;
|
||||
// balance = bedrock_wallet_balance(bedrock_addr, bedrock_account_pk).await?;
|
||||
// found_note = balance
|
||||
// .notes
|
||||
// .iter()
|
||||
// .find_map(|(note_id, value)| (*value == amount).then_some(*note_id));
|
||||
// if found_note.is_some() {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
selected_note_id = found_note;
|
||||
}
|
||||
// selected_note_id = found_note;
|
||||
// }
|
||||
|
||||
let Some(selected_note_id) = selected_note_id else {
|
||||
anyhow::bail!(
|
||||
"Failed to locate exact-value note {amount:?} for Bedrock deposit; available notes: {:?}",
|
||||
balance.notes,
|
||||
);
|
||||
};
|
||||
// let Some(selected_note_id) = selected_note_id else {
|
||||
// anyhow::bail!(
|
||||
// "Failed to locate exact-value note {amount:?} for Bedrock deposit; available notes:
|
||||
// {:?}", balance.notes,
|
||||
// );
|
||||
// };
|
||||
|
||||
let body = ChannelDepositRequestBody {
|
||||
tip: None,
|
||||
deposit: DepositOp {
|
||||
channel_id,
|
||||
inputs: Inputs::new(selected_note_id),
|
||||
metadata,
|
||||
},
|
||||
change_public_key: balance.address,
|
||||
funding_public_keys: vec![balance.address],
|
||||
max_tx_fee: u64::MAX.into(),
|
||||
};
|
||||
// let body = ChannelDepositRequestBody {
|
||||
// tip: None,
|
||||
// deposit: DepositOp {
|
||||
// channel_id,
|
||||
// inputs: Inputs::new(selected_note_id),
|
||||
// metadata,
|
||||
// },
|
||||
// change_public_key: balance.address,
|
||||
// funding_public_keys: vec![balance.address],
|
||||
// max_tx_fee: u64::MAX.into(),
|
||||
// };
|
||||
|
||||
let response = client
|
||||
.post(format!("http://{bedrock_addr}/channel/deposit"))
|
||||
.json(&body)
|
||||
.send()
|
||||
.await
|
||||
.context("Failed to submit Bedrock deposit request")?;
|
||||
let response = check_response_success(response).await?;
|
||||
// let response = client
|
||||
// .post(format!("http://{bedrock_addr}/channel/deposit"))
|
||||
// .json(&body)
|
||||
// .send()
|
||||
// .await
|
||||
// .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}",
|
||||
);
|
||||
// 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(())
|
||||
}
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
/// The Bedrock wallet state of `bedrock_account_pk`: its total balance and the
|
||||
/// notes it owns, keyed by note id.
|
||||
async fn bedrock_wallet_balance(
|
||||
bedrock_addr: std::net::SocketAddr,
|
||||
bedrock_account_pk: &str,
|
||||
) -> anyhow::Result<WalletBalanceResponseBody> {
|
||||
let response = reqwest::Client::new()
|
||||
.get(format!(
|
||||
"http://{bedrock_addr}/wallet/{bedrock_account_pk}/balance"
|
||||
))
|
||||
.send()
|
||||
.await
|
||||
.context("Failed to query Bedrock wallet balance")?;
|
||||
// /// The Bedrock wallet state of `bedrock_account_pk`: its total balance and the
|
||||
// /// notes it owns, keyed by note id.
|
||||
// async fn bedrock_wallet_balance(
|
||||
// bedrock_addr: std::net::SocketAddr,
|
||||
// bedrock_account_pk: &str,
|
||||
// ) -> anyhow::Result<WalletBalanceResponseBody> {
|
||||
// let response = reqwest::Client::new()
|
||||
// .get(format!(
|
||||
// "http://{bedrock_addr}/wallet/{bedrock_account_pk}/balance"
|
||||
// ))
|
||||
// .send()
|
||||
// .await
|
||||
// .context("Failed to query Bedrock wallet balance")?;
|
||||
|
||||
check_response_success(response)
|
||||
.await?
|
||||
.json::<WalletBalanceResponseBody>()
|
||||
.await
|
||||
.context("Failed to decode Bedrock balance response")
|
||||
}
|
||||
// check_response_success(response)
|
||||
// .await?
|
||||
// .json::<WalletBalanceResponseBody>()
|
||||
// .await
|
||||
// .context("Failed to decode Bedrock balance response")
|
||||
// }
|
||||
|
||||
async fn check_response_success(response: reqwest::Response) -> anyhow::Result<reqwest::Response> {
|
||||
if response.status().is_success() {
|
||||
Ok(response)
|
||||
} else {
|
||||
let status = response.status();
|
||||
let body_text = response.text().await.unwrap_or_default();
|
||||
anyhow::bail!("Request failed with status {status} and body {body_text}");
|
||||
}
|
||||
}
|
||||
// async fn check_response_success(response: reqwest::Response) -> anyhow::Result<reqwest::Response>
|
||||
// { if response.status().is_success() {
|
||||
// Ok(response)
|
||||
// } else {
|
||||
// let status = response.status();
|
||||
// let body_text = response.text().await.unwrap_or_default();
|
||||
// anyhow::bail!("Request failed with status {status} and body {body_text}");
|
||||
// }
|
||||
// }
|
||||
|
||||
async fn wait_for_vault_balance(
|
||||
ctx: &TestContext,
|
||||
vault_id: AccountId,
|
||||
expected_balance: u128,
|
||||
) -> 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 {
|
||||
loop {
|
||||
let balance = account_balance(ctx, vault_id).await?;
|
||||
if balance == expected_balance {
|
||||
return Ok(());
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("Timed out waiting for vault {vault_id} balance to reach {expected_balance}")
|
||||
})?
|
||||
}
|
||||
// async fn wait_for_vault_balance(
|
||||
// ctx: &TestContext,
|
||||
// vault_id: AccountId,
|
||||
// expected_balance: u128,
|
||||
// ) -> 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 {
|
||||
// loop {
|
||||
// let balance = account_balance(ctx, vault_id).await?;
|
||||
// if balance == expected_balance {
|
||||
// return Ok(());
|
||||
// }
|
||||
// tokio::time::sleep(Duration::from_millis(500)).await;
|
||||
// }
|
||||
// })
|
||||
// .await
|
||||
// .with_context(|| {
|
||||
// format!("Timed out waiting for vault {vault_id} balance to reach {expected_balance}")
|
||||
// })?
|
||||
// }
|
||||
|
||||
/// Test deposit and withdraw round trip.
|
||||
///
|
||||
/// Implemented as one test instead of two separate tests for deposit and withdraw, because the
|
||||
/// withdraw test depends on the deposit to set up the necessary state (funds in vault) for testing
|
||||
/// withdraw functionality.
|
||||
#[test]
|
||||
async fn bedrock_deposit_claim_and_withdraw_round_trip_succeeds() -> anyhow::Result<()> {
|
||||
let mut ctx = TestContext::new().await?;
|
||||
// /// Test deposit and withdraw round trip.
|
||||
// ///
|
||||
// /// Implemented as one test instead of two separate tests for deposit and withdraw, because the
|
||||
// /// withdraw test depends on the deposit to set up the necessary state (funds in vault) for
|
||||
// testing /// withdraw functionality.
|
||||
// #[test]
|
||||
// async fn bedrock_deposit_claim_and_withdraw_round_trip_succeeds() -> anyhow::Result<()> {
|
||||
// let mut ctx = TestContext::new().await?;
|
||||
|
||||
let bedrock_account_pk = "2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26";
|
||||
let recipient_id = ctx.existing_public_accounts()[0];
|
||||
let amount = 1_u64;
|
||||
let vault_program_id = programs::vault().id();
|
||||
let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient_id);
|
||||
// let bedrock_account_pk = "2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26";
|
||||
// let recipient_id = ctx.existing_public_accounts()[0];
|
||||
// let amount = 1_u64;
|
||||
// let vault_program_id = programs::vault().id();
|
||||
// let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id,
|
||||
// recipient_id);
|
||||
|
||||
let vault_balance_before = account_balance(&ctx, recipient_vault_id).await?;
|
||||
let recipient_balance_before = account_balance(&ctx, recipient_id).await?;
|
||||
// let vault_balance_before = account_balance(&ctx, recipient_vault_id).await?;
|
||||
// let recipient_balance_before = account_balance(&ctx, recipient_id).await?;
|
||||
|
||||
// Submit deposit to Bedrock
|
||||
submit_bedrock_deposit(ctx.bedrock_addr(), bedrock_account_pk, recipient_id, amount)
|
||||
.await
|
||||
.context("Failed to submit Bedrock deposit for round-trip setup")?;
|
||||
// // Submit deposit to Bedrock
|
||||
// submit_bedrock_deposit(ctx.bedrock_addr(), bedrock_account_pk, recipient_id, amount)
|
||||
// .await
|
||||
// .context("Failed to submit Bedrock deposit for round-trip setup")?;
|
||||
|
||||
// Wait for vault to receive the deposit (minted from bridge to vault)
|
||||
wait_for_vault_balance(
|
||||
&ctx,
|
||||
recipient_vault_id,
|
||||
vault_balance_before + u128::from(amount),
|
||||
)
|
||||
.await?;
|
||||
// // Wait for vault to receive the deposit (minted from bridge to vault)
|
||||
// wait_for_vault_balance(
|
||||
// &ctx,
|
||||
// recipient_vault_id,
|
||||
// vault_balance_before + u128::from(amount),
|
||||
// )
|
||||
// .await?;
|
||||
|
||||
// Now claim funds from vault back to recipient
|
||||
let nonces = ctx
|
||||
.wallet()
|
||||
.get_accounts_nonces(&[recipient_id])
|
||||
.await
|
||||
.context("Failed to get nonce for vault claim")?;
|
||||
// // Now claim funds from vault back to recipient
|
||||
// let nonces = ctx
|
||||
// .wallet()
|
||||
// .get_accounts_nonces(&[recipient_id])
|
||||
// .await
|
||||
// .context("Failed to get nonce for vault claim")?;
|
||||
|
||||
let signing_key = ctx
|
||||
.wallet()
|
||||
.storage()
|
||||
.key_chain()
|
||||
.pub_account_signing_key(recipient_id)
|
||||
.with_context(|| format!("Missing signing key for account {recipient_id}"))?;
|
||||
// let signing_key = ctx
|
||||
// .wallet()
|
||||
// .storage()
|
||||
// .key_chain()
|
||||
// .pub_account_signing_key(recipient_id)
|
||||
// .with_context(|| format!("Missing signing key for account {recipient_id}"))?;
|
||||
|
||||
let claim_message = public_transaction::Message::try_new(
|
||||
vault_program_id,
|
||||
vec![recipient_id, recipient_vault_id],
|
||||
nonces,
|
||||
vault_core::Instruction::Claim {
|
||||
amount: u128::from(amount),
|
||||
},
|
||||
)
|
||||
.context("Failed to build vault claim message")?;
|
||||
// let claim_message = public_transaction::Message::try_new(
|
||||
// vault_program_id,
|
||||
// vec![recipient_id, recipient_vault_id],
|
||||
// nonces,
|
||||
// vault_core::Instruction::Claim {
|
||||
// amount: u128::from(amount),
|
||||
// },
|
||||
// )
|
||||
// .context("Failed to build vault claim message")?;
|
||||
|
||||
let claim_witness_set =
|
||||
public_transaction::WitnessSet::for_message(&claim_message, &[signing_key]);
|
||||
let claim_tx = LeeTransaction::Public(lee::PublicTransaction::new(
|
||||
claim_message,
|
||||
claim_witness_set,
|
||||
));
|
||||
// let claim_witness_set =
|
||||
// public_transaction::WitnessSet::for_message(&claim_message, &[signing_key]);
|
||||
// let claim_tx = LeeTransaction::Public(lee::PublicTransaction::new(
|
||||
// claim_message,
|
||||
// claim_witness_set,
|
||||
// ));
|
||||
|
||||
let claim_hash = ctx.sequencer_client().send_transaction(claim_tx).await?;
|
||||
// let claim_hash = ctx.sequencer_client().send_transaction(claim_tx).await?;
|
||||
|
||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
// tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
let claim_on_chain = ctx.sequencer_client().get_transaction(claim_hash).await?;
|
||||
let vault_balance_after_claim = account_balance(&ctx, recipient_vault_id).await?;
|
||||
let recipient_balance_after_claim = account_balance(&ctx, recipient_id).await?;
|
||||
// let claim_on_chain = ctx.sequencer_client().get_transaction(claim_hash).await?;
|
||||
// let vault_balance_after_claim = account_balance(&ctx, recipient_vault_id).await?;
|
||||
// let recipient_balance_after_claim = account_balance(&ctx, recipient_id).await?;
|
||||
|
||||
assert!(
|
||||
claim_on_chain.is_some(),
|
||||
"Vault claim transaction must be included on-chain"
|
||||
);
|
||||
assert_eq!(
|
||||
vault_balance_after_claim, vault_balance_before,
|
||||
"Vault balance should return to initial state after claim"
|
||||
);
|
||||
assert_eq!(
|
||||
recipient_balance_after_claim,
|
||||
recipient_balance_before + u128::from(amount),
|
||||
"Recipient balance should increase by claimed amount"
|
||||
);
|
||||
// assert!(
|
||||
// claim_on_chain.is_some(),
|
||||
// "Vault claim transaction must be included on-chain"
|
||||
// );
|
||||
// assert_eq!(
|
||||
// vault_balance_after_claim, vault_balance_before,
|
||||
// "Vault balance should return to initial state after claim"
|
||||
// );
|
||||
// assert_eq!(
|
||||
// recipient_balance_after_claim,
|
||||
// recipient_balance_before + u128::from(amount),
|
||||
// "Recipient balance should increase by claimed amount"
|
||||
// );
|
||||
|
||||
// The indexer must replay the deposit and claim blocks and reach the same
|
||||
// state as the sequencer — including the bridge system account the deposit
|
||||
// modifies, which is the case the hot fix unblocks.
|
||||
wait_for_indexer_to_catch_up(&ctx).await?;
|
||||
let bridge_account_id = system_accounts::bridge_account_id();
|
||||
for account_id in [recipient_id, recipient_vault_id, bridge_account_id] {
|
||||
let indexer_account = indexer_service_rpc::RpcClient::get_account(
|
||||
// `deref` is needed for correct trait resolution
|
||||
// of the async `get_account` method on `RpcClient`
|
||||
ctx.indexer_client().deref(),
|
||||
account_id.into(),
|
||||
)
|
||||
.await?;
|
||||
let sequencer_account = get_account(&ctx, account_id).await?;
|
||||
assert_eq!(
|
||||
indexer_account,
|
||||
sequencer_account.into(),
|
||||
"Indexer and sequencer diverged for account {account_id} after deposit"
|
||||
);
|
||||
}
|
||||
// // The indexer must replay the deposit and claim blocks and reach the same
|
||||
// // state as the sequencer — including the bridge system account the deposit
|
||||
// // modifies, which is the case the hot fix unblocks.
|
||||
// wait_for_indexer_to_catch_up(&ctx).await?;
|
||||
// let bridge_account_id = system_accounts::bridge_account_id();
|
||||
// for account_id in [recipient_id, recipient_vault_id, bridge_account_id] {
|
||||
// let indexer_account = indexer_service_rpc::RpcClient::get_account(
|
||||
// // `deref` is needed for correct trait resolution
|
||||
// // of the async `get_account` method on `RpcClient`
|
||||
// ctx.indexer_client().deref(),
|
||||
// account_id.into(),
|
||||
// )
|
||||
// .await?;
|
||||
// let sequencer_account = get_account(&ctx, account_id).await?;
|
||||
// assert_eq!(
|
||||
// indexer_account,
|
||||
// sequencer_account.into(),
|
||||
// "Indexer and sequencer diverged for account {account_id} after deposit"
|
||||
// );
|
||||
// }
|
||||
|
||||
// Withdraw back to Bedrock and wait for finalized withdraw event.
|
||||
let sender_id = recipient_id;
|
||||
// // Withdraw back to Bedrock and wait for finalized withdraw event.
|
||||
// let sender_id = recipient_id;
|
||||
|
||||
let observer = create_zone_indexer_observer(ctx.bedrock_addr())?;
|
||||
let observe_fut =
|
||||
wait_for_finalized_withdraw_op(&observer, ctx.bedrock_addr(), amount, bedrock_account_pk);
|
||||
// let observer = create_zone_indexer_observer(ctx.bedrock_addr())?;
|
||||
// let observe_fut =
|
||||
// wait_for_finalized_withdraw_op(&observer, ctx.bedrock_addr(), amount,
|
||||
// bedrock_account_pk);
|
||||
|
||||
let withdraw_fut = execute_subcommand(
|
||||
ctx.wallet_mut(),
|
||||
Command::Bridge(BridgeSubcommand::Withdraw {
|
||||
from: public_mention(sender_id),
|
||||
amount,
|
||||
bedrock_account_pk: bedrock_account_pk.to_owned(),
|
||||
}),
|
||||
);
|
||||
// let withdraw_fut = execute_subcommand(
|
||||
// ctx.wallet_mut(),
|
||||
// Command::Bridge(BridgeSubcommand::Withdraw {
|
||||
// from: public_mention(sender_id),
|
||||
// amount,
|
||||
// bedrock_account_pk: bedrock_account_pk.to_owned(),
|
||||
// }),
|
||||
// );
|
||||
|
||||
let (observe_result, withdraw_result) = tokio::join!(observe_fut, withdraw_fut);
|
||||
// let (observe_result, withdraw_result) = tokio::join!(observe_fut, withdraw_fut);
|
||||
|
||||
withdraw_result.context("Failed to execute wallet bridge withdraw command")?;
|
||||
// withdraw_result.context("Failed to execute wallet bridge withdraw command")?;
|
||||
|
||||
observe_result
|
||||
.context("Failed while waiting for finalized withdraw event from zone indexer")?;
|
||||
// observe_result
|
||||
// .context("Failed while waiting for finalized withdraw event from zone indexer")?;
|
||||
|
||||
// Sleep to observe sequencer log about validated withdraw event
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
// // Sleep to observe sequencer log about validated withdraw event
|
||||
// tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
fn create_zone_indexer_observer(
|
||||
bedrock_addr: std::net::SocketAddr,
|
||||
) -> anyhow::Result<ZoneIndexer<NodeHttpClient>> {
|
||||
let bedrock_url = integration_tests::config::addr_to_url(
|
||||
integration_tests::config::UrlProtocol::Http,
|
||||
bedrock_addr,
|
||||
)
|
||||
.context("Failed to convert Bedrock addr to URL for zone indexer observer")?;
|
||||
// fn create_zone_indexer_observer(
|
||||
// bedrock_addr: std::net::SocketAddr,
|
||||
// ) -> anyhow::Result<ZoneIndexer<NodeHttpClient>> {
|
||||
// let bedrock_url = integration_tests::config::addr_to_url(
|
||||
// integration_tests::config::UrlProtocol::Http,
|
||||
// bedrock_addr,
|
||||
// )
|
||||
// .context("Failed to convert Bedrock addr to URL for zone indexer observer")?;
|
||||
|
||||
let node = NodeHttpClient::new(CommonHttpClient::new(None), bedrock_url);
|
||||
// let node = NodeHttpClient::new(CommonHttpClient::new(None), bedrock_url);
|
||||
|
||||
Ok(ZoneIndexer::new(
|
||||
integration_tests::config::bedrock_channel_id(),
|
||||
node,
|
||||
))
|
||||
}
|
||||
// Ok(ZoneIndexer::new(
|
||||
// integration_tests::config::bedrock_channel_id(),
|
||||
// node,
|
||||
// ))
|
||||
// }
|
||||
|
||||
/// Waits for a finalized withdraw that pays `expected_amount` to `receiver_pk`.
|
||||
///
|
||||
/// A withdraw op releases channel-owned notes and carries nothing but their
|
||||
/// ids — the value and recipient live in the note itself. A released note keeps
|
||||
/// its id, value and public key, so the pairing is checked on the receiver's
|
||||
/// Bedrock wallet: one of the released notes must land there with the expected
|
||||
/// value.
|
||||
async fn wait_for_finalized_withdraw_op(
|
||||
observer: &ZoneIndexer<NodeHttpClient>,
|
||||
bedrock_addr: std::net::SocketAddr,
|
||||
expected_amount: u64,
|
||||
receiver_pk: &str,
|
||||
) -> anyhow::Result<()> {
|
||||
let timeout = TIME_TO_FINALIZE_DEPOSIT_EVENT_ON_BEDROCK
|
||||
+ Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS);
|
||||
// /// Waits for a finalized withdraw that pays `expected_amount` to `receiver_pk`.
|
||||
// ///
|
||||
// /// A withdraw op releases channel-owned notes and carries nothing but their
|
||||
// /// ids — the value and recipient live in the note itself. A released note keeps
|
||||
// /// its id, value and public key, so the pairing is checked on the receiver's
|
||||
// /// Bedrock wallet: one of the released notes must land there with the expected
|
||||
// /// value.
|
||||
// async fn wait_for_finalized_withdraw_op(
|
||||
// observer: &ZoneIndexer<NodeHttpClient>,
|
||||
// bedrock_addr: std::net::SocketAddr,
|
||||
// expected_amount: u64,
|
||||
// receiver_pk: &str,
|
||||
// ) -> 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 {
|
||||
// The wallet can trail the channel event, so released notes accumulate
|
||||
// across polls instead of being checked once when first observed.
|
||||
let mut released_notes = HashSet::new();
|
||||
// tokio::time::timeout(timeout, async {
|
||||
// // The wallet can trail the channel event, so released notes accumulate
|
||||
// // across polls instead of being checked once when first observed.
|
||||
// let mut released_notes = HashSet::new();
|
||||
|
||||
loop {
|
||||
let stream = observer
|
||||
.follow()
|
||||
.await
|
||||
.context("Failed to read zone indexer message batch")?;
|
||||
let mut stream = std::pin::pin!(stream);
|
||||
// loop {
|
||||
// let stream = observer
|
||||
// .follow()
|
||||
// .await
|
||||
// .context("Failed to read zone indexer message batch")?;
|
||||
// let mut stream = std::pin::pin!(stream);
|
||||
|
||||
while let Some(message) = stream.next().await {
|
||||
info!("Observed zone message {message:?}");
|
||||
// while let Some(message) = stream.next().await {
|
||||
// info!("Observed zone message {message:?}");
|
||||
|
||||
if let ZoneMessage::Withdraw(withdraw) = message {
|
||||
released_notes.extend(withdraw.inputs.iter().copied());
|
||||
}
|
||||
}
|
||||
// if let ZoneMessage::Withdraw(withdraw) = message {
|
||||
// released_notes.extend(withdraw.inputs.iter().copied());
|
||||
// }
|
||||
// }
|
||||
|
||||
if !released_notes.is_empty() {
|
||||
let balance = bedrock_wallet_balance(bedrock_addr, receiver_pk).await?;
|
||||
if released_notes
|
||||
.iter()
|
||||
.any(|note_id| balance.notes.get(note_id) == Some(&expected_amount))
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
// if !released_notes.is_empty() {
|
||||
// let balance = bedrock_wallet_balance(bedrock_addr, receiver_pk).await?;
|
||||
// if released_notes
|
||||
// .iter()
|
||||
// .any(|note_id| balance.notes.get(note_id) == Some(&expected_amount))
|
||||
// {
|
||||
// return Ok(());
|
||||
// }
|
||||
// }
|
||||
|
||||
tokio::time::sleep(Duration::from_millis(500)).await;
|
||||
}
|
||||
})
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("Timed out waiting for finalized withdraw message with amount {expected_amount}")
|
||||
})?
|
||||
}
|
||||
// tokio::time::sleep(Duration::from_millis(500)).await;
|
||||
// }
|
||||
// })
|
||||
// .await
|
||||
// .with_context(|| {
|
||||
// format!("Timed out waiting for finalized withdraw message with amount {expected_amount}")
|
||||
// })?
|
||||
// }
|
||||
|
||||
@ -170,13 +170,13 @@ unsafe extern "C" {
|
||||
|
||||
fn wallet_ffi_free_transfer_result(result: *mut FfiTransferResult);
|
||||
|
||||
fn wallet_ffi_bridge_withdraw(
|
||||
handle: *mut WalletHandle,
|
||||
from: *const FfiBytes32,
|
||||
amount: u64,
|
||||
bedrock_account_pk: *const FfiBytes32,
|
||||
out_result: *mut FfiTransferResult,
|
||||
) -> error::WalletFfiError;
|
||||
// fn wallet_ffi_bridge_withdraw(
|
||||
// handle: *mut WalletHandle,
|
||||
// from: *const FfiBytes32,
|
||||
// amount: u64,
|
||||
// bedrock_account_pk: *const FfiBytes32,
|
||||
// out_result: *mut FfiTransferResult,
|
||||
// ) -> error::WalletFfiError;
|
||||
|
||||
fn wallet_ffi_get_vault_balance(
|
||||
handle: *mut WalletHandle,
|
||||
@ -1521,68 +1521,68 @@ fn restore_keys_from_seed_ffi() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_bridge_withdraw() -> Result<()> {
|
||||
let ctx = BlockingTestContext::new()?;
|
||||
let home = tempfile::tempdir()?;
|
||||
let FfiCreateWalletOutput {
|
||||
wallet: wallet_ffi_handle,
|
||||
mnemonic: _,
|
||||
} = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
||||
let from: FfiBytes32 = ctx.ctx().existing_public_accounts()[0].into();
|
||||
let bridge_account: FfiBytes32 = system_accounts::bridge_account_id().into();
|
||||
let bedrock_account_pk = FfiBytes32::from_bytes([0x42; 32]);
|
||||
let amount = 100_u64;
|
||||
// #[test]
|
||||
// fn test_wallet_ffi_bridge_withdraw() -> Result<()> {
|
||||
// let ctx = BlockingTestContext::new()?;
|
||||
// let home = tempfile::tempdir()?;
|
||||
// let FfiCreateWalletOutput {
|
||||
// wallet: wallet_ffi_handle,
|
||||
// mnemonic: _,
|
||||
// } = new_wallet_ffi_with_test_context_config(&ctx, home.path())?;
|
||||
// let from: FfiBytes32 = ctx.ctx().existing_public_accounts()[0].into();
|
||||
// let bridge_account: FfiBytes32 = system_accounts::bridge_account_id().into();
|
||||
// let bedrock_account_pk = FfiBytes32::from_bytes([0x42; 32]);
|
||||
// let amount = 100_u64;
|
||||
|
||||
let mut transfer_result = FfiTransferResult::default();
|
||||
unsafe {
|
||||
wallet_ffi_bridge_withdraw(
|
||||
wallet_ffi_handle,
|
||||
&raw const from,
|
||||
amount,
|
||||
&raw const bedrock_account_pk,
|
||||
&raw mut transfer_result,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
// let mut transfer_result = FfiTransferResult::default();
|
||||
// unsafe {
|
||||
// wallet_ffi_bridge_withdraw(
|
||||
// wallet_ffi_handle,
|
||||
// &raw const from,
|
||||
// amount,
|
||||
// &raw const bedrock_account_pk,
|
||||
// &raw mut transfer_result,
|
||||
// )
|
||||
// .unwrap();
|
||||
// }
|
||||
|
||||
info!("Waiting for next block creation");
|
||||
std::thread::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS));
|
||||
// info!("Waiting for next block creation");
|
||||
// std::thread::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS));
|
||||
|
||||
let from_balance = unsafe {
|
||||
let mut out_balance: [u8; 16] = [0; 16];
|
||||
wallet_ffi_get_balance(
|
||||
wallet_ffi_handle,
|
||||
&raw const from,
|
||||
true,
|
||||
&raw mut out_balance,
|
||||
)
|
||||
.unwrap();
|
||||
u128::from_le_bytes(out_balance)
|
||||
};
|
||||
// let from_balance = unsafe {
|
||||
// let mut out_balance: [u8; 16] = [0; 16];
|
||||
// wallet_ffi_get_balance(
|
||||
// wallet_ffi_handle,
|
||||
// &raw const from,
|
||||
// true,
|
||||
// &raw mut out_balance,
|
||||
// )
|
||||
// .unwrap();
|
||||
// u128::from_le_bytes(out_balance)
|
||||
// };
|
||||
|
||||
let bridge_balance = unsafe {
|
||||
let mut out_balance: [u8; 16] = [0; 16];
|
||||
wallet_ffi_get_balance(
|
||||
wallet_ffi_handle,
|
||||
&raw const bridge_account,
|
||||
true,
|
||||
&raw mut out_balance,
|
||||
)
|
||||
.unwrap();
|
||||
u128::from_le_bytes(out_balance)
|
||||
};
|
||||
// let bridge_balance = unsafe {
|
||||
// let mut out_balance: [u8; 16] = [0; 16];
|
||||
// wallet_ffi_get_balance(
|
||||
// wallet_ffi_handle,
|
||||
// &raw const bridge_account,
|
||||
// true,
|
||||
// &raw mut out_balance,
|
||||
// )
|
||||
// .unwrap();
|
||||
// u128::from_le_bytes(out_balance)
|
||||
// };
|
||||
|
||||
assert_eq!(from_balance, 9900);
|
||||
assert_eq!(bridge_balance, 1_000_100);
|
||||
// assert_eq!(from_balance, 9900);
|
||||
// assert_eq!(bridge_balance, 1_000_100);
|
||||
|
||||
unsafe {
|
||||
wallet_ffi_free_transfer_result(&raw mut transfer_result);
|
||||
wallet_ffi_destroy(wallet_ffi_handle);
|
||||
}
|
||||
// unsafe {
|
||||
// wallet_ffi_free_transfer_result(&raw mut transfer_result);
|
||||
// wallet_ffi_destroy(wallet_ffi_handle);
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn test_wallet_ffi_transfer_generic_public() -> Result<()> {
|
||||
|
||||
@ -7,5 +7,4 @@ license = { workspace = true }
|
||||
[dependencies]
|
||||
bridge_core.workspace = true
|
||||
vault_core.workspace = true
|
||||
authenticated_transfer_core.workspace = true
|
||||
lee_core.workspace = true
|
||||
|
||||
@ -104,33 +104,35 @@ fn main() {
|
||||
}
|
||||
}
|
||||
Instruction::Withdraw {
|
||||
amount,
|
||||
amount: _,
|
||||
bedrock_account_pk: _,
|
||||
} => {
|
||||
let [sender, bridge] = pre_states
|
||||
.try_into()
|
||||
.expect("Withdraw requires exactly 2 accounts");
|
||||
panic!("Withdraws are disabled in the current version of LEZ");
|
||||
|
||||
assert_eq!(
|
||||
bridge.account_id,
|
||||
bridge_core::compute_bridge_account_id(self_program_id),
|
||||
"Second account must be bridge PDA"
|
||||
);
|
||||
// let [sender, bridge] = pre_states
|
||||
// .try_into()
|
||||
// .expect("Withdraw requires exactly 2 accounts");
|
||||
|
||||
let auth_transfer_program_id = bridge.account.program_owner;
|
||||
assert_eq!(
|
||||
sender.account.program_owner, auth_transfer_program_id,
|
||||
"Sender account must be owned by the authenticated transfer program"
|
||||
);
|
||||
// assert_eq!(
|
||||
// bridge.account_id,
|
||||
// bridge_core::compute_bridge_account_id(self_program_id),
|
||||
// "Second account must be bridge PDA"
|
||||
// );
|
||||
|
||||
let chained_calls = vec![ChainedCall::new(
|
||||
auth_transfer_program_id,
|
||||
vec![sender, bridge],
|
||||
&authenticated_transfer_core::Instruction::Transfer {
|
||||
amount: u128::from(amount),
|
||||
},
|
||||
)];
|
||||
(unchanged_post_states(&pre_states_clone), chained_calls)
|
||||
// let auth_transfer_program_id = bridge.account.program_owner;
|
||||
// assert_eq!(
|
||||
// sender.account.program_owner, auth_transfer_program_id,
|
||||
// "Sender account must be owned by the authenticated transfer program"
|
||||
// );
|
||||
|
||||
// let chained_calls = vec![ChainedCall::new(
|
||||
// auth_transfer_program_id,
|
||||
// vec![sender, bridge],
|
||||
// &authenticated_transfer_core::Instruction::Transfer {
|
||||
// amount: u128::from(amount),
|
||||
// },
|
||||
// )];
|
||||
// (unchanged_post_states(&pre_states_clone), chained_calls)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -8,20 +8,10 @@ use common::{
|
||||
test_utils::sequencer_sign_key_for_testing,
|
||||
transaction::{LeeTransaction, clock_invocation},
|
||||
};
|
||||
use key_protocol::key_management::KeyChain;
|
||||
use lee::{
|
||||
Account, AccountId, Data, PrivacyPreservingTransaction, PrivateKey, PublicKey,
|
||||
PublicTransaction, V03State,
|
||||
error::LeeError,
|
||||
execute_and_prove,
|
||||
privacy_preserving_transaction::{Message, circuit::ProgramWithDependencies},
|
||||
program::Program,
|
||||
};
|
||||
use lee_core::{
|
||||
Commitment, InputAccountIdentity, Nullifier,
|
||||
account::{AccountWithMetadata, Nonce},
|
||||
program::PdaSeed,
|
||||
Account, AccountId, Data, PrivateKey, PublicKey, PublicTransaction, V03State, program::Program,
|
||||
};
|
||||
use lee_core::{account::Nonce, program::PdaSeed};
|
||||
use logos_blockchain_core::{
|
||||
events::DepositRecreatedNotes,
|
||||
mantle::{
|
||||
@ -1479,94 +1469,94 @@ async fn block_production_aborts_when_clock_account_data_is_corrupted() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn private_bridge_withdraw_invocation_is_dropped() {
|
||||
let sender_keys = KeyChain::new_os_random();
|
||||
let sender_account_id = AccountId::for_regular_private_account(
|
||||
&sender_keys.nullifier_public_key,
|
||||
&sender_keys.viewing_public_key,
|
||||
0,
|
||||
);
|
||||
let sender_private_account = Account {
|
||||
program_owner: programs::authenticated_transfer().id(),
|
||||
balance: 100,
|
||||
nonce: Nonce(0xdead_beef),
|
||||
data: Data::default(),
|
||||
};
|
||||
let bridge_account_id = system_accounts::bridge_account_id();
|
||||
// #[test]
|
||||
// fn private_bridge_withdraw_invocation_is_dropped() {
|
||||
// let sender_keys = KeyChain::new_os_random();
|
||||
// let sender_account_id = AccountId::for_regular_private_account(
|
||||
// &sender_keys.nullifier_public_key,
|
||||
// &sender_keys.viewing_public_key,
|
||||
// 0,
|
||||
// );
|
||||
// let sender_private_account = Account {
|
||||
// program_owner: programs::authenticated_transfer().id(),
|
||||
// balance: 100,
|
||||
// nonce: Nonce(0xdead_beef),
|
||||
// data: Data::default(),
|
||||
// };
|
||||
// let bridge_account_id = system_accounts::bridge_account_id();
|
||||
|
||||
let mut state = V03State::new()
|
||||
.with_public_accounts([(bridge_account_id, system_accounts::bridge_account())])
|
||||
.with_private_accounts([(
|
||||
Commitment::new(&sender_account_id, &sender_private_account),
|
||||
Nullifier::for_account_initialization(&sender_account_id),
|
||||
)]);
|
||||
// let mut state = V03State::new()
|
||||
// .with_public_accounts([(bridge_account_id, system_accounts::bridge_account())])
|
||||
// .with_private_accounts([(
|
||||
// Commitment::new(&sender_account_id, &sender_private_account),
|
||||
// Nullifier::for_account_initialization(&sender_account_id),
|
||||
// )]);
|
||||
|
||||
let sender_commitment = Commitment::new(&sender_account_id, &sender_private_account);
|
||||
// let sender_commitment = Commitment::new(&sender_account_id, &sender_private_account);
|
||||
|
||||
let sender_pre = AccountWithMetadata::new(
|
||||
sender_private_account,
|
||||
true,
|
||||
(
|
||||
&sender_keys.nullifier_public_key,
|
||||
&sender_keys.viewing_public_key,
|
||||
0,
|
||||
),
|
||||
);
|
||||
let bridge_pre = AccountWithMetadata::new(
|
||||
state.get_account_by_id(bridge_account_id),
|
||||
false,
|
||||
bridge_account_id,
|
||||
);
|
||||
// let sender_pre = AccountWithMetadata::new(
|
||||
// sender_private_account,
|
||||
// true,
|
||||
// (
|
||||
// &sender_keys.nullifier_public_key,
|
||||
// &sender_keys.viewing_public_key,
|
||||
// 0,
|
||||
// ),
|
||||
// );
|
||||
// let bridge_pre = AccountWithMetadata::new(
|
||||
// state.get_account_by_id(bridge_account_id),
|
||||
// false,
|
||||
// bridge_account_id,
|
||||
// );
|
||||
|
||||
let instruction = Program::serialize_instruction(bridge_core::Instruction::Withdraw {
|
||||
amount: 1,
|
||||
bedrock_account_pk: [0; 32],
|
||||
})
|
||||
.unwrap();
|
||||
// let instruction = Program::serialize_instruction(bridge_core::Instruction::Withdraw {
|
||||
// amount: 1,
|
||||
// bedrock_account_pk: [0; 32],
|
||||
// })
|
||||
// .unwrap();
|
||||
|
||||
let program_with_deps = ProgramWithDependencies::new(
|
||||
programs::bridge(),
|
||||
[(
|
||||
programs::authenticated_transfer().id(),
|
||||
programs::authenticated_transfer(),
|
||||
)]
|
||||
.into(),
|
||||
);
|
||||
// let program_with_deps = ProgramWithDependencies::new(
|
||||
// programs::bridge(),
|
||||
// [(
|
||||
// programs::authenticated_transfer().id(),
|
||||
// programs::authenticated_transfer(),
|
||||
// )]
|
||||
// .into(),
|
||||
// );
|
||||
|
||||
let (output, proof) = execute_and_prove(
|
||||
vec![sender_pre, bridge_pre],
|
||||
instruction,
|
||||
vec![
|
||||
InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
vpk: sender_keys.viewing_public_key.clone(),
|
||||
random_seed: [0; 32],
|
||||
view_tag: 0,
|
||||
nsk: sender_keys.private_key_holder.nullifier_secret_key,
|
||||
membership_proof: state
|
||||
.get_proof_for_commitment(&sender_commitment)
|
||||
.expect("sender commitment must be in state"),
|
||||
identifier: 0,
|
||||
},
|
||||
InputAccountIdentity::Public,
|
||||
],
|
||||
&program_with_deps,
|
||||
)
|
||||
.expect("Execution should succeed");
|
||||
// let (output, proof) = execute_and_prove(
|
||||
// vec![sender_pre, bridge_pre],
|
||||
// instruction,
|
||||
// vec![
|
||||
// InputAccountIdentity::PrivateAuthorizedUpdate {
|
||||
// vpk: sender_keys.viewing_public_key.clone(),
|
||||
// random_seed: [0; 32],
|
||||
// view_tag: 0,
|
||||
// nsk: sender_keys.private_key_holder.nullifier_secret_key,
|
||||
// membership_proof: state
|
||||
// .get_proof_for_commitment(&sender_commitment)
|
||||
// .expect("sender commitment must be in state"),
|
||||
// identifier: 0,
|
||||
// },
|
||||
// InputAccountIdentity::Public,
|
||||
// ],
|
||||
// &program_with_deps,
|
||||
// )
|
||||
// .expect("Execution should succeed");
|
||||
|
||||
let message = Message::try_from_circuit_output(vec![bridge_account_id], vec![], output)
|
||||
.expect("Message construction should succeed");
|
||||
let witness_set =
|
||||
lee::privacy_preserving_transaction::WitnessSet::for_message(&message, proof, &[]);
|
||||
let tx =
|
||||
LeeTransaction::PrivacyPreserving(PrivacyPreservingTransaction::new(message, witness_set));
|
||||
let res = tx.execute_check_on_state(&mut state, 1, 0);
|
||||
// let message = Message::try_from_circuit_output(vec![bridge_account_id], vec![], output)
|
||||
// .expect("Message construction should succeed");
|
||||
// let witness_set =
|
||||
// lee::privacy_preserving_transaction::WitnessSet::for_message(&message, proof, &[]);
|
||||
// let tx =
|
||||
// LeeTransaction::PrivacyPreserving(PrivacyPreservingTransaction::new(message,
|
||||
// witness_set)); let res = tx.execute_check_on_state(&mut state, 1, 0);
|
||||
|
||||
assert!(
|
||||
matches!(res, Err(LeeError::InvalidInput(_))),
|
||||
"Bridge withdraw invocation should be rejected in private execution"
|
||||
);
|
||||
}
|
||||
// assert!(
|
||||
// matches!(res, Err(LeeError::InvalidInput(_))),
|
||||
// "Bridge withdraw invocation should be rejected in private execution"
|
||||
// );
|
||||
// }
|
||||
|
||||
/// Builds a [`V03State`] with the clock program and `program` registered, the three clock
|
||||
/// accounts initialized, and the clock advanced to `clock_timestamp` so that reads of the
|
||||
|
||||
@ -10,7 +10,7 @@ use chain_state::ChainState;
|
||||
use common::block::Block;
|
||||
use logos_blockchain_core::mantle::ops::channel::{MsgId, inscribe::Inscription};
|
||||
use logos_blockchain_zone_sdk::{Slot, ZoneBlock, ZoneMessage};
|
||||
use storage::sequencer::sequencer_cells::{WithdrawalReconciliationKey, ZoneAnchorRecord};
|
||||
use storage::sequencer::sequencer_cells::ZoneAnchorRecord;
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
@ -298,229 +298,230 @@ fn deposit_event_record(
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a signed public bridge `Withdraw` transaction (the normal user path).
|
||||
fn build_public_withdraw_tx(
|
||||
sender: lee::AccountId,
|
||||
nonce: u128,
|
||||
amount: u64,
|
||||
bedrock_account_pk: [u8; 32],
|
||||
signing_key: &lee::PrivateKey,
|
||||
) -> LeeTransaction {
|
||||
let message = lee::public_transaction::Message::try_new(
|
||||
programs::bridge().id(),
|
||||
vec![sender, system_accounts::bridge_account_id()],
|
||||
vec![nonce.into()],
|
||||
bridge_core::Instruction::Withdraw {
|
||||
amount,
|
||||
bedrock_account_pk,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let witness_set = lee::public_transaction::WitnessSet::for_message(&message, &[signing_key]);
|
||||
LeeTransaction::Public(lee::PublicTransaction::new(message, witness_set))
|
||||
}
|
||||
// /// Builds a signed public bridge `Withdraw` transaction (the normal user path).
|
||||
// fn build_public_withdraw_tx(
|
||||
// sender: lee::AccountId,
|
||||
// nonce: u128,
|
||||
// amount: u64,
|
||||
// bedrock_account_pk: [u8; 32],
|
||||
// signing_key: &lee::PrivateKey,
|
||||
// ) -> LeeTransaction {
|
||||
// let message = lee::public_transaction::Message::try_new(
|
||||
// programs::bridge().id(),
|
||||
// vec![sender, system_accounts::bridge_account_id()],
|
||||
// vec![nonce.into()],
|
||||
// bridge_core::Instruction::Withdraw {
|
||||
// amount,
|
||||
// bedrock_account_pk,
|
||||
// },
|
||||
// )
|
||||
// .unwrap();
|
||||
// let witness_set = lee::public_transaction::WitnessSet::for_message(&message, &[signing_key]);
|
||||
// LeeTransaction::Public(lee::PublicTransaction::new(message, witness_set))
|
||||
// }
|
||||
|
||||
/// The reconciliation key a produced block carries for `withdraw_tx`, keyed on
|
||||
/// the note [`MockBlockPublisher`] reports as released for it.
|
||||
fn produced_withdraw_key(withdraw_tx: &LeeTransaction) -> WithdrawalReconciliationKey {
|
||||
let withdraw_arg = crate::extract_bridge_withdraw_data(withdraw_tx).expect("withdraw data");
|
||||
let [note_id] = crate::mock::mock_released_notes(std::slice::from_ref(&withdraw_arg))[..]
|
||||
else {
|
||||
panic!("A bridge withdraw releases exactly one note");
|
||||
};
|
||||
// /// The reconciliation key a produced block carries for `withdraw_tx`, keyed on
|
||||
// /// the note [`MockBlockPublisher`] reports as released for it.
|
||||
// fn produced_withdraw_key(withdraw_tx: &LeeTransaction) -> WithdrawalReconciliationKey {
|
||||
// let withdraw_arg = crate::extract_bridge_withdraw_data(withdraw_tx).expect("withdraw data");
|
||||
// let [note_id] = crate::mock::mock_released_notes(std::slice::from_ref(&withdraw_arg))[..]
|
||||
// else {
|
||||
// panic!("A bridge withdraw releases exactly one note");
|
||||
// };
|
||||
|
||||
crate::withdrawal_reconciliation_key(¬e_id)
|
||||
}
|
||||
// crate::withdrawal_reconciliation_key(¬e_id)
|
||||
// }
|
||||
|
||||
/// Cold-start backfill re-records an already-finalized deposit event as a
|
||||
/// pending record before reconstruction replays the same deposit block.
|
||||
/// Reconstruction must drop that record — its mint is permanently reflected in
|
||||
/// the reconstructed state (the receipt PDA) — so the next production neither
|
||||
/// re-mints the vault nor emits a stray deposit tx.
|
||||
#[tokio::test]
|
||||
async fn reconstructed_deposit_is_not_reminted_after_backfill_redelivery() {
|
||||
let recipient = initial_public_user_accounts()[0].account_id;
|
||||
let deposit_amount = 500_u64;
|
||||
let withdraw_amount = 100_u64;
|
||||
let bedrock_account_pk = [0x22_u8; 32];
|
||||
let deposit_op_id = [0x0d_u8; 32];
|
||||
// /// Cold-start backfill re-records an already-finalized deposit event as a
|
||||
// /// pending record before reconstruction replays the same deposit block.
|
||||
// /// Reconstruction must drop that record — its mint is permanently reflected in
|
||||
// /// the reconstructed state (the receipt PDA) — so the next production neither
|
||||
// /// re-mints the vault nor emits a stray deposit tx.
|
||||
// #[tokio::test]
|
||||
// async fn reconstructed_deposit_is_not_reminted_after_backfill_redelivery() {
|
||||
// let recipient = initial_public_user_accounts()[0].account_id;
|
||||
// let deposit_amount = 500_u64;
|
||||
// let withdraw_amount = 100_u64;
|
||||
// let bedrock_account_pk = [0x22_u8; 32];
|
||||
// let deposit_op_id = [0x0d_u8; 32];
|
||||
|
||||
// Sequencer A produces a deposit block then a withdraw block.
|
||||
let config_a = bridge_funded_config();
|
||||
let (mut seq_a, mempool_a) =
|
||||
SequencerCoreWithMockClients::start_from_config(config_a.clone()).await;
|
||||
// // Sequencer A produces a deposit block then a withdraw block.
|
||||
// let config_a = bridge_funded_config();
|
||||
// let (mut seq_a, mempool_a) =
|
||||
// SequencerCoreWithMockClients::start_from_config(config_a.clone()).await;
|
||||
|
||||
let deposit_record = deposit_event_record(deposit_op_id, deposit_amount, recipient);
|
||||
let deposit_tx =
|
||||
crate::build_bridge_deposit_tx_from_event(&deposit_record).expect("build deposit tx");
|
||||
mempool_a
|
||||
.push((TransactionOrigin::Sequencer, deposit_tx))
|
||||
.await
|
||||
.unwrap();
|
||||
seq_a.produce_new_block().await.unwrap();
|
||||
// let deposit_record = deposit_event_record(deposit_op_id, deposit_amount, recipient);
|
||||
// let deposit_tx =
|
||||
// crate::build_bridge_deposit_tx_from_event(&deposit_record).expect("build deposit tx");
|
||||
// mempool_a
|
||||
// .push((TransactionOrigin::Sequencer, deposit_tx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// seq_a.produce_new_block().await.unwrap();
|
||||
|
||||
let withdraw_tx = build_public_withdraw_tx(
|
||||
recipient,
|
||||
0,
|
||||
withdraw_amount,
|
||||
bedrock_account_pk,
|
||||
&create_signing_key_for_account1(),
|
||||
);
|
||||
mempool_a
|
||||
.push((TransactionOrigin::User, withdraw_tx.clone()))
|
||||
.await
|
||||
.unwrap();
|
||||
seq_a.produce_new_block().await.unwrap();
|
||||
// let withdraw_tx = build_public_withdraw_tx(
|
||||
// recipient,
|
||||
// 0,
|
||||
// withdraw_amount,
|
||||
// bedrock_account_pk,
|
||||
// &create_signing_key_for_account1(),
|
||||
// );
|
||||
// mempool_a
|
||||
// .push((TransactionOrigin::User, withdraw_tx.clone()))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// seq_a.produce_new_block().await.unwrap();
|
||||
|
||||
let tip_a = seq_a.block_store().latest_block_meta().unwrap().unwrap();
|
||||
let messages = channel_from_store(seq_a.block_store(), 10);
|
||||
let tip_slot = messages.last().unwrap().1;
|
||||
let channel_id = config_a.bedrock_config.channel_id;
|
||||
// let tip_a = seq_a.block_store().latest_block_meta().unwrap().unwrap();
|
||||
// let messages = channel_from_store(seq_a.block_store(), 10);
|
||||
// let tip_slot = messages.last().unwrap().1;
|
||||
// let channel_id = config_a.bedrock_config.channel_id;
|
||||
|
||||
let config_b = bridge_funded_config();
|
||||
let (mut seq_b, _mempool_b) = SequencerCoreWithMockClients::start_from_config(config_b).await;
|
||||
// let config_b = bridge_funded_config();
|
||||
// let (mut seq_b, _mempool_b) =
|
||||
// SequencerCoreWithMockClients::start_from_config(config_b).await;
|
||||
|
||||
// Backfill re-delivery: the deposit event is re-recorded as a pending record
|
||||
// before reconstruction runs. The mint no longer flows through the mempool
|
||||
// (that sink was removed); the store drain is the only source.
|
||||
assert!(
|
||||
seq_b
|
||||
.block_store()
|
||||
.dbio()
|
||||
.add_pending_deposit_event(deposit_record.clone())
|
||||
.unwrap()
|
||||
);
|
||||
// // Backfill re-delivery: the deposit event is re-recorded as a pending record
|
||||
// // before reconstruction runs. The mint no longer flows through the mempool
|
||||
// // (that sink was removed); the store drain is the only source.
|
||||
// assert!(
|
||||
// seq_b
|
||||
// .block_store()
|
||||
// .dbio()
|
||||
// .add_pending_deposit_event(deposit_record.clone())
|
||||
// .unwrap()
|
||||
// );
|
||||
|
||||
let mock_b = MockBlockPublisher::with_canned_channel(channel_id, Some(tip_slot), messages);
|
||||
SequencerCore::<MockBlockPublisher>::verify_and_reconstruct(
|
||||
&mock_b,
|
||||
&seq_b.store,
|
||||
&seq_b.chain,
|
||||
true,
|
||||
)
|
||||
.await
|
||||
.expect("reconstruct");
|
||||
// let mock_b = MockBlockPublisher::with_canned_channel(channel_id, Some(tip_slot), messages);
|
||||
// SequencerCore::<MockBlockPublisher>::verify_and_reconstruct(
|
||||
// &mock_b,
|
||||
// &seq_b.store,
|
||||
// &seq_b.chain,
|
||||
// true,
|
||||
// )
|
||||
// .await
|
||||
// .expect("reconstruct");
|
||||
|
||||
let tip_b = seq_b.block_store().latest_block_meta().unwrap().unwrap();
|
||||
assert_eq!(tip_b.id, tip_a.id);
|
||||
assert_eq!(tip_b.hash, tip_a.hash);
|
||||
// let tip_b = seq_b.block_store().latest_block_meta().unwrap().unwrap();
|
||||
// assert_eq!(tip_b.id, tip_a.id);
|
||||
// assert_eq!(tip_b.hash, tip_a.hash);
|
||||
|
||||
// Reconstruction replays the finalized deposit block, minting the receipt
|
||||
// into state and dropping the re-recorded pending event — so the drain has
|
||||
// nothing left to re-mint. This is the mechanism that protects against the
|
||||
// re-delivery, in place of the removed mempool sink.
|
||||
assert!(
|
||||
seq_b
|
||||
.block_store()
|
||||
.dbio()
|
||||
.get_pending_deposit_events()
|
||||
.unwrap()
|
||||
.is_empty(),
|
||||
"reconstruction must drop the re-delivered pending deposit record"
|
||||
);
|
||||
// // Reconstruction replays the finalized deposit block, minting the receipt
|
||||
// // into state and dropping the re-recorded pending event — so the drain has
|
||||
// // nothing left to re-mint. This is the mechanism that protects against the
|
||||
// // re-delivery, in place of the removed mempool sink.
|
||||
// assert!(
|
||||
// seq_b
|
||||
// .block_store()
|
||||
// .dbio()
|
||||
// .get_pending_deposit_events()
|
||||
// .unwrap()
|
||||
// .is_empty(),
|
||||
// "reconstruction must drop the re-delivered pending deposit record"
|
||||
// );
|
||||
|
||||
seq_b.produce_new_block().await.unwrap();
|
||||
// seq_b.produce_new_block().await.unwrap();
|
||||
|
||||
let vault_id = vault_core::compute_vault_account_id(programs::vault().id(), recipient);
|
||||
let bridge_id = system_accounts::bridge_account_id();
|
||||
let state_b = seq_b.chain().lock().unwrap().head_state().clone();
|
||||
let state_a = seq_a.chain().lock().unwrap().head_state().clone();
|
||||
for account in [vault_id, bridge_id, recipient] {
|
||||
assert_eq!(
|
||||
state_b.get_account_by_id(account).balance,
|
||||
state_a.get_account_by_id(account).balance,
|
||||
"reconstructed balance mismatch for {account:?}",
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
state_b.get_account_by_id(vault_id).balance,
|
||||
u128::from(deposit_amount),
|
||||
"deposit must mint into the recipient vault exactly once, not twice"
|
||||
);
|
||||
// let vault_id = vault_core::compute_vault_account_id(programs::vault().id(), recipient);
|
||||
// let bridge_id = system_accounts::bridge_account_id();
|
||||
// let state_b = seq_b.chain().lock().unwrap().head_state().clone();
|
||||
// let state_a = seq_a.chain().lock().unwrap().head_state().clone();
|
||||
// for account in [vault_id, bridge_id, recipient] {
|
||||
// assert_eq!(
|
||||
// state_b.get_account_by_id(account).balance,
|
||||
// state_a.get_account_by_id(account).balance,
|
||||
// "reconstructed balance mismatch for {account:?}",
|
||||
// );
|
||||
// }
|
||||
// assert_eq!(
|
||||
// state_b.get_account_by_id(vault_id).balance,
|
||||
// u128::from(deposit_amount),
|
||||
// "deposit must mint into the recipient vault exactly once, not twice"
|
||||
// );
|
||||
|
||||
let produced = seq_b
|
||||
.block_store()
|
||||
.get_block_at_id(tip_b.id + 1)
|
||||
.unwrap()
|
||||
.expect("produced block present");
|
||||
assert!(
|
||||
!produced
|
||||
.body
|
||||
.transactions
|
||||
.iter()
|
||||
.any(|tx| crate::extract_bridge_deposit_id(tx) == Some(HashType(deposit_op_id))),
|
||||
"the re-delivered mint must be skipped, not re-included in a block"
|
||||
);
|
||||
// let produced = seq_b
|
||||
// .block_store()
|
||||
// .get_block_at_id(tip_b.id + 1)
|
||||
// .unwrap()
|
||||
// .expect("produced block present");
|
||||
// assert!(
|
||||
// !produced
|
||||
// .body
|
||||
// .transactions
|
||||
// .iter()
|
||||
// .any(|tx| crate::extract_bridge_deposit_id(tx) == Some(HashType(deposit_op_id))),
|
||||
// "the re-delivered mint must be skipped, not re-included in a block"
|
||||
// );
|
||||
|
||||
// A reconstructed withdraw's finalized L1 event was already re-delivered (and
|
||||
// dropped) by cold-start backfill, so it will never be consumed again.
|
||||
// Reconstruction must not count it, or the count stays phantom-inflated forever.
|
||||
let key = produced_withdraw_key(&withdraw_tx);
|
||||
assert!(
|
||||
!seq_b
|
||||
.block_store()
|
||||
.dbio()
|
||||
.consume_unseen_withdraw_count(key)
|
||||
.unwrap(),
|
||||
"reconstruction must not leave a phantom unseen-withdraw count"
|
||||
);
|
||||
}
|
||||
// // A reconstructed withdraw's finalized L1 event was already re-delivered (and
|
||||
// // dropped) by cold-start backfill, so it will never be consumed again.
|
||||
// // Reconstruction must not count it, or the count stays phantom-inflated forever.
|
||||
// let key = produced_withdraw_key(&withdraw_tx);
|
||||
// assert!(
|
||||
// !seq_b
|
||||
// .block_store()
|
||||
// .dbio()
|
||||
// .consume_unseen_withdraw_count(key)
|
||||
// .unwrap(),
|
||||
// "reconstruction must not leave a phantom unseen-withdraw count"
|
||||
// );
|
||||
// }
|
||||
|
||||
/// A reconstructed withdraw block must not touch the unseen-withdraw counter.
|
||||
/// Its finalized L1 Withdraw event was already re-delivered (and dropped as a
|
||||
/// no-op) by cold-start backfill, so counting it during reconstruction would
|
||||
/// leave a permanent phantom that nothing ever consumes.
|
||||
#[tokio::test]
|
||||
async fn reconstructed_withdraw_leaves_no_phantom_unseen_count() {
|
||||
let recipient = initial_public_user_accounts()[0].account_id;
|
||||
let withdraw_amount = 100_u64;
|
||||
let bedrock_account_pk = [0x33_u8; 32];
|
||||
// /// A reconstructed withdraw block must not touch the unseen-withdraw counter.
|
||||
// /// Its finalized L1 Withdraw event was already re-delivered (and dropped as a
|
||||
// /// no-op) by cold-start backfill, so counting it during reconstruction would
|
||||
// /// leave a permanent phantom that nothing ever consumes.
|
||||
// #[tokio::test]
|
||||
// async fn reconstructed_withdraw_leaves_no_phantom_unseen_count() {
|
||||
// let recipient = initial_public_user_accounts()[0].account_id;
|
||||
// let withdraw_amount = 100_u64;
|
||||
// let bedrock_account_pk = [0x33_u8; 32];
|
||||
|
||||
// Sequencer A produces a single withdraw block; treat its chain as the channel.
|
||||
let config_a = bridge_funded_config();
|
||||
let (mut seq_a, mempool_a) =
|
||||
SequencerCoreWithMockClients::start_from_config(config_a.clone()).await;
|
||||
let withdraw_tx = build_public_withdraw_tx(
|
||||
recipient,
|
||||
0,
|
||||
withdraw_amount,
|
||||
bedrock_account_pk,
|
||||
&create_signing_key_for_account1(),
|
||||
);
|
||||
mempool_a
|
||||
.push((TransactionOrigin::User, withdraw_tx.clone()))
|
||||
.await
|
||||
.unwrap();
|
||||
seq_a.produce_new_block().await.unwrap();
|
||||
// // Sequencer A produces a single withdraw block; treat its chain as the channel.
|
||||
// let config_a = bridge_funded_config();
|
||||
// let (mut seq_a, mempool_a) =
|
||||
// SequencerCoreWithMockClients::start_from_config(config_a.clone()).await;
|
||||
// let withdraw_tx = build_public_withdraw_tx(
|
||||
// recipient,
|
||||
// 0,
|
||||
// withdraw_amount,
|
||||
// bedrock_account_pk,
|
||||
// &create_signing_key_for_account1(),
|
||||
// );
|
||||
// mempool_a
|
||||
// .push((TransactionOrigin::User, withdraw_tx.clone()))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// seq_a.produce_new_block().await.unwrap();
|
||||
|
||||
let key = produced_withdraw_key(&withdraw_tx);
|
||||
// Producing the withdraw counts it as unseen, awaiting its L1 event.
|
||||
assert!(
|
||||
seq_a
|
||||
.block_store()
|
||||
.dbio()
|
||||
.consume_unseen_withdraw_count(key)
|
||||
.unwrap(),
|
||||
"producing a withdraw must count it as unseen"
|
||||
);
|
||||
// let key = produced_withdraw_key(&withdraw_tx);
|
||||
// // Producing the withdraw counts it as unseen, awaiting its L1 event.
|
||||
// assert!(
|
||||
// seq_a
|
||||
// .block_store()
|
||||
// .dbio()
|
||||
// .consume_unseen_withdraw_count(key)
|
||||
// .unwrap(),
|
||||
// "producing a withdraw must count it as unseen"
|
||||
// );
|
||||
|
||||
let messages = channel_from_store(seq_a.block_store(), 10);
|
||||
let tip_slot = messages.last().unwrap().1;
|
||||
let channel_id = config_a.bedrock_config.channel_id;
|
||||
// let messages = channel_from_store(seq_a.block_store(), 10);
|
||||
// let tip_slot = messages.last().unwrap().1;
|
||||
// let channel_id = config_a.bedrock_config.channel_id;
|
||||
|
||||
// Sequencer B reconstructs A's chain from a fresh store.
|
||||
let config_b = bridge_funded_config();
|
||||
let (store_b, chain_b) = fresh_store_and_chain(&config_b);
|
||||
let mock_b = MockBlockPublisher::with_canned_channel(channel_id, Some(tip_slot), messages);
|
||||
SequencerCore::<MockBlockPublisher>::verify_and_reconstruct(&mock_b, &store_b, &chain_b, true)
|
||||
.await
|
||||
.expect("reconstruct");
|
||||
// // Sequencer B reconstructs A's chain from a fresh store.
|
||||
// let config_b = bridge_funded_config();
|
||||
// let (store_b, chain_b) = fresh_store_and_chain(&config_b);
|
||||
// let mock_b = MockBlockPublisher::with_canned_channel(channel_id, Some(tip_slot), messages);
|
||||
// SequencerCore::<MockBlockPublisher>::verify_and_reconstruct(&mock_b, &store_b, &chain_b,
|
||||
// true) .await
|
||||
// .expect("reconstruct");
|
||||
|
||||
assert!(
|
||||
!store_b.dbio().consume_unseen_withdraw_count(key).unwrap(),
|
||||
"reconstruction must not leave a phantom unseen-withdraw count"
|
||||
);
|
||||
}
|
||||
// assert!(
|
||||
// !store_b.dbio().consume_unseen_withdraw_count(key).unwrap(),
|
||||
// "reconstruction must not leave a phantom unseen-withdraw count"
|
||||
// );
|
||||
// }
|
||||
|
||||
/// A deposit whose L1 event was observed (an unfulfilled pending record
|
||||
/// exists) and whose L2 mint is already contained in a finalized channel block.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user