mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-06-03 15:49:48 +00:00
Merge b8471c0f0ef39aec3d55fde4225a376e5c743b58 into d3390efc6db215cef35ba1d6d1f5e13277fe9597
This commit is contained in:
commit
b80a469dfe
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -8868,7 +8868,9 @@ dependencies = [
|
|||||||
"mempool",
|
"mempool",
|
||||||
"nssa",
|
"nssa",
|
||||||
"nssa_core",
|
"nssa_core",
|
||||||
|
"num-bigint 0.4.6",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
|
"risc0-zkvm",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"storage",
|
"storage",
|
||||||
|
|||||||
@ -131,6 +131,7 @@ chrono = "0.4.41"
|
|||||||
borsh = "1.5.7"
|
borsh = "1.5.7"
|
||||||
base58 = "0.2.0"
|
base58 = "0.2.0"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
|
num-bigint = "0.4.6"
|
||||||
url = { version = "2.5.4", features = ["serde"] }
|
url = { version = "2.5.4", features = ["serde"] }
|
||||||
tokio-retry = "0.3.0"
|
tokio-retry = "0.3.0"
|
||||||
schemars = "1.2"
|
schemars = "1.2"
|
||||||
|
|||||||
@ -90,14 +90,16 @@ impl NSSATransaction {
|
|||||||
}
|
}
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
let system_accounts = nssa::CLOCK_PROGRAM_ACCOUNT_IDS.iter().copied().chain([
|
let system_accounts = nssa::CLOCK_PROGRAM_ACCOUNT_IDS
|
||||||
nssa::system_faucet_account_id(),
|
.iter()
|
||||||
nssa::system_bridge_account_id(),
|
.copied()
|
||||||
]);
|
.chain(std::iter::once(nssa::system_faucet_account_id()));
|
||||||
for account_id in system_accounts {
|
for account_id in system_accounts {
|
||||||
validate_doesnt_modify_account(state, &diff, account_id)?;
|
validate_doesnt_modify_account(state, &diff, account_id)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validate_bridge_account_modification(state, &diff)?;
|
||||||
|
|
||||||
Ok(diff)
|
Ok(diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,3 +190,40 @@ fn validate_doesnt_modify_account(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn validate_bridge_account_modification(
|
||||||
|
state: &V03State,
|
||||||
|
diff: &ValidatedStateDiff,
|
||||||
|
) -> Result<(), nssa::error::NssaError> {
|
||||||
|
let bridge_account_id = nssa::system_bridge_account_id();
|
||||||
|
let pre = state.get_account_by_id(bridge_account_id);
|
||||||
|
let Some(post) = diff.public_diff().get(&bridge_account_id).cloned() else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
let nssa::Account {
|
||||||
|
balance: pre_balance,
|
||||||
|
program_owner: pre_program_owner,
|
||||||
|
data: pre_data,
|
||||||
|
nonce: pre_nonce,
|
||||||
|
} = pre;
|
||||||
|
let nssa::Account {
|
||||||
|
balance: post_balance,
|
||||||
|
program_owner: post_program_owner,
|
||||||
|
data: post_data,
|
||||||
|
nonce: post_nonce,
|
||||||
|
} = post;
|
||||||
|
|
||||||
|
let only_balance_increased = post_balance >= pre_balance
|
||||||
|
&& post_program_owner == pre_program_owner
|
||||||
|
&& post_data == pre_data
|
||||||
|
&& post_nonce == pre_nonce;
|
||||||
|
|
||||||
|
if only_balance_increased {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(nssa::error::NssaError::InvalidInput(format!(
|
||||||
|
"Transaction modifies restricted system account {bridge_account_id}"
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ use borsh::BorshSerialize;
|
|||||||
use common::transaction::NSSATransaction;
|
use common::transaction::NSSATransaction;
|
||||||
use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext};
|
use integration_tests::{TIME_TO_WAIT_FOR_BLOCK_SECONDS, TestContext};
|
||||||
use log::info;
|
use log::info;
|
||||||
use logos_blockchain_core::mantle::{Value, ledger::Inputs, ops::channel::deposit::DepositOp};
|
use logos_blockchain_core::mantle::{ledger::Inputs, ops::channel::deposit::DepositOp};
|
||||||
use logos_blockchain_http_api_common::bodies::{
|
use logos_blockchain_http_api_common::bodies::{
|
||||||
channel::ChannelDepositRequestBody,
|
channel::ChannelDepositRequestBody,
|
||||||
wallet::{
|
wallet::{
|
||||||
@ -195,7 +195,7 @@ async fn private_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> {
|
|||||||
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: u64,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
#[derive(BorshSerialize)]
|
#[derive(BorshSerialize)]
|
||||||
struct DepositMetadata {
|
struct DepositMetadata {
|
||||||
@ -208,9 +208,6 @@ async fn submit_bedrock_deposit(
|
|||||||
|
|
||||||
let funding_key = "2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26";
|
let funding_key = "2e03b2eff5a45478e7e79668d2a146cf2c5c7925bce927f2b1c67f2ab4fc0d26";
|
||||||
|
|
||||||
let amount: Value = amount
|
|
||||||
.try_into()
|
|
||||||
.context("Deposit amount does not fit Bedrock Value type")?;
|
|
||||||
let channel_id = integration_tests::config::bedrock_channel_id();
|
let channel_id = integration_tests::config::bedrock_channel_id();
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
|||||||
@ -62,12 +62,40 @@ fn main() {
|
|||||||
vec![bridge_for_vault, recipient_vault],
|
vec![bridge_for_vault, recipient_vault],
|
||||||
&vault_core::Instruction::Transfer {
|
&vault_core::Instruction::Transfer {
|
||||||
recipient_id,
|
recipient_id,
|
||||||
amount,
|
amount: u128::from(amount),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.with_pda_seeds(vec![bridge_core::compute_bridge_seed()]),
|
.with_pda_seeds(vec![bridge_core::compute_bridge_seed()]),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Instruction::Withdraw {
|
||||||
|
amount,
|
||||||
|
bedrock_account_pk: _,
|
||||||
|
} => {
|
||||||
|
let [sender, bridge] = pre_states
|
||||||
|
.try_into()
|
||||||
|
.expect("Withdraw requires exactly 2 accounts");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
bridge.account_id,
|
||||||
|
bridge_core::compute_bridge_account_id(self_program_id),
|
||||||
|
"Second account must be bridge PDA"
|
||||||
|
);
|
||||||
|
|
||||||
|
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"
|
||||||
|
);
|
||||||
|
|
||||||
|
vec![ChainedCall::new(
|
||||||
|
auth_transfer_program_id,
|
||||||
|
vec![sender, bridge],
|
||||||
|
&authenticated_transfer_core::Instruction::Transfer {
|
||||||
|
amount: u128::from(amount),
|
||||||
|
},
|
||||||
|
)]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ProgramOutput::new(
|
ProgramOutput::new(
|
||||||
|
|||||||
@ -14,7 +14,20 @@ pub enum Instruction {
|
|||||||
Deposit {
|
Deposit {
|
||||||
vault_program_id: ProgramId,
|
vault_program_id: ProgramId,
|
||||||
recipient_id: AccountId,
|
recipient_id: AccountId,
|
||||||
amount: u128,
|
amount: u64,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Transfers native tokens from a user account to the bridge PDA account.
|
||||||
|
///
|
||||||
|
/// Required accounts (2):
|
||||||
|
/// - Sender account
|
||||||
|
/// - Bridge PDA account
|
||||||
|
///
|
||||||
|
/// `bedrock_account_pk` is consumed by the Sequencer and is not used by the Bridge program
|
||||||
|
/// logic.
|
||||||
|
Withdraw {
|
||||||
|
amount: u64,
|
||||||
|
bedrock_account_pk: [u8; 32],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,8 @@ faucet_core.workspace = true
|
|||||||
bridge_core.workspace = true
|
bridge_core.workspace = true
|
||||||
vault_core.workspace = true
|
vault_core.workspace = true
|
||||||
|
|
||||||
|
logos-blockchain-key-management-system-service.workspace = true
|
||||||
|
logos-blockchain-core.workspace = true
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
@ -27,13 +29,13 @@ tempfile.workspace = true
|
|||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
||||||
logos-blockchain-key-management-system-service.workspace = true
|
|
||||||
logos-blockchain-core.workspace = true
|
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
borsh.workspace = true
|
borsh.workspace = true
|
||||||
bytesize.workspace = true
|
bytesize.workspace = true
|
||||||
hex.workspace = true
|
hex.workspace = true
|
||||||
url.workspace = true
|
url.workspace = true
|
||||||
|
num-bigint.workspace = true
|
||||||
|
risc0-zkvm.workspace = true
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|||||||
@ -3,8 +3,8 @@ use std::{pin::Pin, sync::Arc, time::Duration};
|
|||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use common::block::Block;
|
use common::block::Block;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
pub use logos_blockchain_core::mantle::ops::channel::MsgId;
|
use logos_blockchain_core::mantle::{Note, ledger::Outputs};
|
||||||
pub use logos_blockchain_key_management_system_service::keys::Ed25519Key;
|
pub use logos_blockchain_key_management_system_service::keys::{Ed25519Key, ZkKey};
|
||||||
pub use logos_blockchain_zone_sdk::sequencer::SequencerCheckpoint;
|
pub use logos_blockchain_zone_sdk::sequencer::SequencerCheckpoint;
|
||||||
use logos_blockchain_zone_sdk::{
|
use logos_blockchain_zone_sdk::{
|
||||||
CommonHttpClient,
|
CommonHttpClient,
|
||||||
@ -12,9 +12,10 @@ use logos_blockchain_zone_sdk::{
|
|||||||
sequencer::{Event, SequencerConfig as ZoneSdkSequencerConfig, SequencerHandle, ZoneSequencer},
|
sequencer::{Event, SequencerConfig as ZoneSdkSequencerConfig, SequencerHandle, ZoneSequencer},
|
||||||
state::{DepositInfo, FinalizedOp, InscriptionInfo},
|
state::{DepositInfo, FinalizedOp, InscriptionInfo},
|
||||||
};
|
};
|
||||||
|
use num_bigint::BigUint;
|
||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
use crate::config::BedrockConfig;
|
use crate::{BridgeWithdrawData, config::BedrockConfig};
|
||||||
|
|
||||||
/// Sink for `Event::Published` checkpoints emitted by the drive task.
|
/// Sink for `Event::Published` checkpoints emitted by the drive task.
|
||||||
/// Caller is responsible for persistence (e.g. writing to rocksdb).
|
/// Caller is responsible for persistence (e.g. writing to rocksdb).
|
||||||
@ -43,7 +44,11 @@ pub trait BlockPublisherTrait: Clone {
|
|||||||
|
|
||||||
/// Fire-and-forget publish. Zone-sdk drives the actual submission and
|
/// Fire-and-forget publish. Zone-sdk drives the actual submission and
|
||||||
/// retries internally; this just hands the payload off.
|
/// retries internally; this just hands the payload off.
|
||||||
async fn publish_block(&self, block: &Block) -> Result<()>;
|
async fn publish_block(
|
||||||
|
&self,
|
||||||
|
block: &Block,
|
||||||
|
bridge_withdrawals: Vec<BridgeWithdrawData>,
|
||||||
|
) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Real block publisher backed by zone-sdk's `ZoneSequencer`.
|
/// Real block publisher backed by zone-sdk's `ZoneSequencer`.
|
||||||
@ -124,17 +129,42 @@ impl BlockPublisherTrait for ZoneSdkPublisher {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn publish_block(&self, block: &Block) -> Result<()> {
|
async fn publish_block(
|
||||||
|
&self,
|
||||||
|
block: &Block,
|
||||||
|
bridge_withdrawals: Vec<BridgeWithdrawData>,
|
||||||
|
) -> Result<()> {
|
||||||
let data = borsh::to_vec(block).context("Failed to serialize block")?;
|
let data = borsh::to_vec(block).context("Failed to serialize block")?;
|
||||||
let data_bounded = data
|
let data_bounded = data
|
||||||
.try_into()
|
.try_into()
|
||||||
.context("Block data exceeds maximum allowed size")?;
|
.context("Block data exceeds maximum allowed size")?;
|
||||||
|
|
||||||
self.handle
|
if bridge_withdrawals.is_empty() {
|
||||||
.publish_message(data_bounded)
|
self.handle
|
||||||
.await
|
.publish_message(data_bounded)
|
||||||
.context("Failed to publish block")?;
|
.await
|
||||||
|
.context("Failed to publish block")?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let withdraws = bridge_withdrawals
|
||||||
|
.into_iter()
|
||||||
|
.map(|withdrawal| {
|
||||||
|
let recipient_pk =
|
||||||
|
logos_blockchain_key_management_system_service::keys::ZkPublicKey::from(
|
||||||
|
BigUint::from_bytes_le(&withdrawal.bedrock_account_pk),
|
||||||
|
);
|
||||||
|
|
||||||
|
logos_blockchain_zone_sdk::sequencer::WithdrawArg {
|
||||||
|
outputs: Outputs::new(vec![Note::new(withdrawal.amount, recipient_pk)]),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
self.handle
|
||||||
|
.publish_atomic_withdraw(data_bounded, withdraws)
|
||||||
|
.await
|
||||||
|
.context("Failed to publish block with withdrawals")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -199,7 +199,9 @@ impl<BP: BlockPublisherTrait> SequencerCore<BP> {
|
|||||||
// genesis block so the indexer can find the channel start. After the
|
// genesis block so the indexer can find the channel start. After the
|
||||||
// first publish, zone-sdk's checkpoint persistence covers further
|
// first publish, zone-sdk's checkpoint persistence covers further
|
||||||
// restarts.
|
// restarts.
|
||||||
if is_fresh_start && let Err(err) = block_publisher.publish_block(&genesis_block).await {
|
if is_fresh_start
|
||||||
|
&& let Err(err) = block_publisher.publish_block(&genesis_block, vec![]).await
|
||||||
|
{
|
||||||
error!("Failed to publish genesis block: {err:#}");
|
error!("Failed to publish genesis block: {err:#}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,7 +219,10 @@ impl<BP: BlockPublisherTrait> SequencerCore<BP> {
|
|||||||
|
|
||||||
/// Produces a new block from mempool transactions and publishes it via zone-sdk.
|
/// Produces a new block from mempool transactions and publishes it via zone-sdk.
|
||||||
pub async fn produce_new_block(&mut self) -> Result<u64> {
|
pub async fn produce_new_block(&mut self) -> Result<u64> {
|
||||||
let block = self
|
let BlockWithMeta {
|
||||||
|
block,
|
||||||
|
bridge_withdrawals,
|
||||||
|
} = self
|
||||||
.build_block_from_mempool()
|
.build_block_from_mempool()
|
||||||
.context("Failed to build block from mempool transactions")?;
|
.context("Failed to build block from mempool transactions")?;
|
||||||
|
|
||||||
@ -225,7 +230,11 @@ impl<BP: BlockPublisherTrait> SequencerCore<BP> {
|
|||||||
// zone-sdk manages L1 settlement state via its own checkpoint.
|
// zone-sdk manages L1 settlement state via its own checkpoint.
|
||||||
let placeholder_msg_id = [0_u8; 32];
|
let placeholder_msg_id = [0_u8; 32];
|
||||||
|
|
||||||
if let Err(err) = self.block_publisher.publish_block(&block).await {
|
if let Err(err) = self
|
||||||
|
.block_publisher
|
||||||
|
.publish_block(&block, bridge_withdrawals)
|
||||||
|
.await
|
||||||
|
{
|
||||||
error!("Failed to publish block to Bedrock with error: {err:#}");
|
error!("Failed to publish block to Bedrock with error: {err:#}");
|
||||||
}
|
}
|
||||||
self.store.update(&block, placeholder_msg_id, &self.state)?;
|
self.store.update(&block, placeholder_msg_id, &self.state)?;
|
||||||
@ -235,12 +244,16 @@ impl<BP: BlockPublisherTrait> SequencerCore<BP> {
|
|||||||
|
|
||||||
/// Builds a new block from transactions in the mempool.
|
/// Builds a new block from transactions in the mempool.
|
||||||
/// Does NOT publish or store the block — the caller is responsible for that.
|
/// Does NOT publish or store the block — the caller is responsible for that.
|
||||||
pub fn build_block_from_mempool(&mut self) -> Result<Block> {
|
///
|
||||||
|
/// Returns the built block together with bridge withdraw data extracted
|
||||||
|
/// from included bridge withdraw transactions.
|
||||||
|
fn build_block_from_mempool(&mut self) -> Result<BlockWithMeta> {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
|
|
||||||
let new_block_height = self.next_block_id();
|
let new_block_height = self.next_block_id();
|
||||||
|
|
||||||
let mut valid_transactions = vec![];
|
let mut valid_transactions = vec![];
|
||||||
|
let mut bridge_withdrawals = vec![];
|
||||||
|
|
||||||
let max_block_size = usize::try_from(self.sequencer_config.max_block_size.as_u64())
|
let max_block_size = usize::try_from(self.sequencer_config.max_block_size.as_u64())
|
||||||
.expect("`max_block_size` should fit into usize");
|
.expect("`max_block_size` should fit into usize");
|
||||||
@ -305,6 +318,10 @@ impl<BP: BlockPublisherTrait> SequencerCore<BP> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(withdraw_data) = extract_bridge_withdraw_data(&tx) {
|
||||||
|
bridge_withdrawals.push(withdraw_data);
|
||||||
|
}
|
||||||
|
|
||||||
self.state.apply_state_diff(validated_diff);
|
self.state.apply_state_diff(validated_diff);
|
||||||
}
|
}
|
||||||
TransactionOrigin::Sequencer => {
|
TransactionOrigin::Sequencer => {
|
||||||
@ -322,6 +339,7 @@ impl<BP: BlockPublisherTrait> SequencerCore<BP> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
valid_transactions.push(tx);
|
valid_transactions.push(tx);
|
||||||
|
|
||||||
info!("Validated transaction with hash {tx_hash}, including it in block");
|
info!("Validated transaction with hash {tx_hash}, including it in block");
|
||||||
if valid_transactions.len() >= self.sequencer_config.max_num_tx_in_block {
|
if valid_transactions.len() >= self.sequencer_config.max_num_tx_in_block {
|
||||||
break;
|
break;
|
||||||
@ -355,7 +373,11 @@ impl<BP: BlockPublisherTrait> SequencerCore<BP> {
|
|||||||
hashable_data.transactions.len(),
|
hashable_data.transactions.len(),
|
||||||
now.elapsed().as_secs()
|
now.elapsed().as_secs()
|
||||||
);
|
);
|
||||||
Ok(block)
|
|
||||||
|
Ok(BlockWithMeta {
|
||||||
|
block,
|
||||||
|
bridge_withdrawals,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn state(&self) -> &nssa::V03State {
|
pub const fn state(&self) -> &nssa::V03State {
|
||||||
@ -411,6 +433,17 @@ impl<BP: BlockPublisherTrait> SequencerCore<BP> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct BlockWithMeta {
|
||||||
|
block: Block,
|
||||||
|
bridge_withdrawals: Vec<BridgeWithdrawData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct BridgeWithdrawData {
|
||||||
|
pub amount: u64,
|
||||||
|
pub bedrock_account_pk: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
/// Builds the initial genesis state from `testnet_initial_state` plus configured genesis
|
/// Builds the initial genesis state from `testnet_initial_state` plus configured genesis
|
||||||
/// transactions. Returns the final state and the list of [`NSSATransaction`]s that should be
|
/// transactions. Returns the final state and the list of [`NSSATransaction`]s that should be
|
||||||
/// committed to the genesis block so external observers can replay them.
|
/// committed to the genesis block so external observers can replay them.
|
||||||
@ -503,7 +536,7 @@ fn build_bridge_deposit_tx(
|
|||||||
bridge_core::Instruction::Deposit {
|
bridge_core::Instruction::Deposit {
|
||||||
vault_program_id,
|
vault_program_id,
|
||||||
recipient_id: metadata.recipient_id,
|
recipient_id: metadata.recipient_id,
|
||||||
amount: u128::from(deposit.amount),
|
amount: deposit.amount,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.context("Failed to build bridge deposit message")?;
|
.context("Failed to build bridge deposit message")?;
|
||||||
@ -515,6 +548,35 @@ fn build_bridge_deposit_tx(
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn extract_bridge_withdraw_data(tx: &NSSATransaction) -> Option<BridgeWithdrawData> {
|
||||||
|
let NSSATransaction::Public(tx) = tx else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let message = tx.message();
|
||||||
|
if message.program_id != nssa::program::Program::bridge().id() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let instruction =
|
||||||
|
risc0_zkvm::serde::from_slice::<bridge_core::Instruction, u32>(&message.instruction_data)
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
match instruction {
|
||||||
|
bridge_core::Instruction::Withdraw {
|
||||||
|
amount,
|
||||||
|
bedrock_account_pk,
|
||||||
|
} => Some(BridgeWithdrawData {
|
||||||
|
amount,
|
||||||
|
bedrock_account_pk,
|
||||||
|
}),
|
||||||
|
bridge_core::Instruction::Deposit { .. } => unreachable!(
|
||||||
|
"Deposit instructions from users should never pass validation, and thus should never be seen here"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Load signing key from file or generate a new one if it doesn't exist.
|
/// Load signing key from file or generate a new one if it doesn't exist.
|
||||||
fn load_or_create_signing_key(path: &Path) -> Result<Ed25519Key> {
|
fn load_or_create_signing_key(path: &Path) -> Result<Ed25519Key> {
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ use common::block::Block;
|
|||||||
use logos_blockchain_key_management_system_service::keys::Ed25519Key;
|
use logos_blockchain_key_management_system_service::keys::Ed25519Key;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
BridgeWithdrawData,
|
||||||
block_publisher::{
|
block_publisher::{
|
||||||
BlockPublisherTrait, CheckpointSink, FinalizedBlockSink, OnDepositEventSink,
|
BlockPublisherTrait, CheckpointSink, FinalizedBlockSink, OnDepositEventSink,
|
||||||
SequencerCheckpoint,
|
SequencerCheckpoint,
|
||||||
@ -30,7 +31,11 @@ impl BlockPublisherTrait for MockBlockPublisher {
|
|||||||
Ok(Self)
|
Ok(Self)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn publish_block(&self, _block: &Block) -> Result<()> {
|
async fn publish_block(
|
||||||
|
&self,
|
||||||
|
_block: &Block,
|
||||||
|
_bridge_withdrawals: Vec<BridgeWithdrawData>,
|
||||||
|
) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user