mirror of
https://github.com/logos-blockchain/logos-execution-zone.git
synced 2026-07-11 08:19:35 +00:00
Merge branch 'dev' into Pravdyvy/pda-account-id-generation
This commit is contained in:
commit
9d1e873344
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
@ -2,6 +2,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- "!.github/workflows/*.yml"
|
||||
@ -94,7 +95,7 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: ci-rust-cache
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
|
||||
|
||||
- name: Lint workspace
|
||||
env:
|
||||
@ -125,7 +126,7 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: ci-rust-cache
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
|
||||
|
||||
- name: Install nextest
|
||||
run: cargo install --locked cargo-nextest
|
||||
@ -156,7 +157,7 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: ci-rust-cache
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
|
||||
|
||||
- name: Install nextest
|
||||
run: cargo install --locked cargo-nextest
|
||||
@ -245,7 +246,7 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: ci-rust-cache
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
|
||||
|
||||
- name: Test valid proof
|
||||
env:
|
||||
@ -268,7 +269,7 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: ci-rust-cache
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
|
||||
|
||||
- name: Install just
|
||||
run: cargo install --locked just
|
||||
|
||||
@ -57,12 +57,16 @@ Before merging a PR, consider squashing non-meaningful commits. E.g.:
|
||||
|
||||
Could be squashed to an empty commit if they belong to the same PR.
|
||||
|
||||
## Default branch
|
||||
|
||||
By default all PRs must be directed into the `dev` branch. This helps us to keep releases stable.
|
||||
|
||||
## Branch workflow
|
||||
|
||||
When bringing your feature branch up to date, prefer rebasing on top of `main`.
|
||||
When bringing your feature branch up to date, prefer rebasing on top of `dev`.
|
||||
|
||||
- Preferred: `git rebase main`
|
||||
- Avoid: `git merge main` in feature branches
|
||||
- Preferred: `git rebase dev`
|
||||
- Avoid: `git merge dev` in feature branches
|
||||
|
||||
This keeps commit history cleaner and makes reviews easier.
|
||||
|
||||
|
||||
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -206,9 +206,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.102"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
@ -8953,8 +8953,10 @@ name = "sequencer_service_protocol"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"common",
|
||||
"hex",
|
||||
"lee",
|
||||
"lee_core",
|
||||
"serde_with",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -98,6 +98,66 @@ async fn public_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn public_bridge_deposit_with_zero_amount_is_rejected() -> anyhow::Result<()> {
|
||||
let ctx = TestContext::new().await?;
|
||||
|
||||
let recipient_id = ctx.existing_public_accounts()[0];
|
||||
let bridge_account_id = system_accounts::bridge_account_id();
|
||||
let vault_program_id = programs::vault().id();
|
||||
let recipient_vault_id = vault_core::compute_vault_account_id(vault_program_id, recipient_id);
|
||||
|
||||
let message = public_transaction::Message::try_new(
|
||||
programs::bridge().id(),
|
||||
vec![bridge_account_id, recipient_vault_id],
|
||||
vec![],
|
||||
bridge_core::Instruction::Deposit {
|
||||
l1_deposit_op_id: [0_u8; 32],
|
||||
vault_program_id,
|
||||
recipient_id,
|
||||
amount: 0,
|
||||
},
|
||||
)
|
||||
.context("Failed to build zero-amount public bridge deposit transaction")?;
|
||||
|
||||
let attack_tx = LeeTransaction::Public(lee::PublicTransaction::new(
|
||||
message,
|
||||
lee::public_transaction::WitnessSet::from_raw_parts(vec![]),
|
||||
));
|
||||
|
||||
let bridge_balance_before = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(bridge_account_id)
|
||||
.await?;
|
||||
let vault_balance_before = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(recipient_vault_id)
|
||||
.await?;
|
||||
|
||||
let tx_hash = ctx.sequencer_client().send_transaction(attack_tx).await?;
|
||||
|
||||
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
|
||||
|
||||
let bridge_balance_after = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(bridge_account_id)
|
||||
.await?;
|
||||
let vault_balance_after = ctx
|
||||
.sequencer_client()
|
||||
.get_account_balance(recipient_vault_id)
|
||||
.await?;
|
||||
let tx_on_chain = ctx.sequencer_client().get_transaction(tx_hash).await?;
|
||||
|
||||
assert_eq!(bridge_balance_after, bridge_balance_before);
|
||||
assert_eq!(vault_balance_after, vault_balance_before);
|
||||
assert!(
|
||||
tx_on_chain.is_none(),
|
||||
"Public bridge::Deposit with zero amount should be rejected"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
async fn private_bridge_deposit_invocation_is_dropped() -> anyhow::Result<()> {
|
||||
let ctx = TestContext::new().await?;
|
||||
|
||||
@ -173,7 +173,7 @@ impl LeeTransaction {
|
||||
balance: pre.balance,
|
||||
..post.clone()
|
||||
};
|
||||
(expected_pre == pre) && (pre.balance <= post.balance)
|
||||
(expected_pre == pre) && (pre.balance < post.balance)
|
||||
};
|
||||
|
||||
if only_balance_increased {
|
||||
|
||||
@ -4,7 +4,7 @@ use anyhow::{Context as _, Result, anyhow};
|
||||
use common::block::Block;
|
||||
use log::{info, warn};
|
||||
pub use logos_blockchain_core::mantle::ops::channel::MsgId;
|
||||
use logos_blockchain_core::mantle::ops::channel::inscribe::Inscription;
|
||||
use logos_blockchain_core::mantle::ops::channel::{ChannelId, inscribe::Inscription};
|
||||
pub use logos_blockchain_key_management_system_service::keys::{Ed25519Key, ZkKey};
|
||||
pub use logos_blockchain_zone_sdk::sequencer::SequencerCheckpoint;
|
||||
use logos_blockchain_zone_sdk::{
|
||||
@ -61,11 +61,14 @@ pub trait BlockPublisherTrait: Clone {
|
||||
/// Fire-and-forget publish. Zone-sdk drives the actual submission and
|
||||
/// retries internally; this just hands the payload off.
|
||||
async fn publish_block(&self, block: &Block, withdrawals: Vec<WithdrawArg>) -> Result<()>;
|
||||
|
||||
fn channel_id(&self) -> ChannelId;
|
||||
}
|
||||
|
||||
/// Real block publisher backed by zone-sdk's `ZoneSequencer`.
|
||||
#[derive(Clone)]
|
||||
pub struct ZoneSdkPublisher {
|
||||
channel_id: ChannelId,
|
||||
publish_tx: mpsc::Sender<(Inscription, Vec<WithdrawArg>)>,
|
||||
// Aborts the drive task when the last clone is dropped.
|
||||
_drive_task: Arc<DriveTaskGuard>,
|
||||
@ -192,6 +195,7 @@ impl BlockPublisherTrait for ZoneSdkPublisher {
|
||||
.context("Zone-sdk readiness channel closed before becoming ready")?;
|
||||
|
||||
Ok(Self {
|
||||
channel_id: config.channel_id,
|
||||
publish_tx,
|
||||
_drive_task: Arc::new(DriveTaskGuard(drive_task)),
|
||||
})
|
||||
@ -210,6 +214,10 @@ impl BlockPublisherTrait for ZoneSdkPublisher {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn channel_id(&self) -> ChannelId {
|
||||
self.channel_id
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserialize inscription payload as a `Block` and return it's`block_id`.
|
||||
|
||||
@ -729,26 +729,23 @@ fn extract_bridge_withdraw_data(tx: &LeeTransaction) -> Option<WithdrawArg> {
|
||||
risc0_zkvm::serde::from_slice::<bridge_core::Instruction, u32>(&message.instruction_data)
|
||||
.ok()?;
|
||||
|
||||
match instruction {
|
||||
bridge_core::Instruction::Withdraw {
|
||||
amount,
|
||||
bedrock_account_pk,
|
||||
} => {
|
||||
let recipient_pk =
|
||||
logos_blockchain_key_management_system_service::keys::ZkPublicKey::from(
|
||||
BigUint::from_bytes_le(&bedrock_account_pk),
|
||||
);
|
||||
let bridge_core::Instruction::Withdraw {
|
||||
amount,
|
||||
bedrock_account_pk,
|
||||
} = instruction
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
Some(WithdrawArg {
|
||||
outputs: logos_blockchain_core::mantle::ledger::Outputs::new(
|
||||
logos_blockchain_core::mantle::Note::new(amount, recipient_pk),
|
||||
),
|
||||
})
|
||||
}
|
||||
bridge_core::Instruction::Deposit { .. } => unreachable!(
|
||||
"Deposit instructions from users should never pass validation, and thus should never be seen here"
|
||||
let recipient_pk = logos_blockchain_key_management_system_service::keys::ZkPublicKey::from(
|
||||
BigUint::from_bytes_le(&bedrock_account_pk),
|
||||
);
|
||||
|
||||
Some(WithdrawArg {
|
||||
outputs: logos_blockchain_core::mantle::ledger::Outputs::new(
|
||||
logos_blockchain_core::mantle::Note::new(amount, recipient_pk),
|
||||
),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn withdraw_event_reconciliation_key(
|
||||
|
||||
@ -2,6 +2,7 @@ use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
use common::block::Block;
|
||||
use logos_blockchain_core::mantle::ops::channel::ChannelId;
|
||||
use logos_blockchain_key_management_system_service::keys::Ed25519Key;
|
||||
use logos_blockchain_zone_sdk::sequencer::WithdrawArg;
|
||||
|
||||
@ -16,11 +17,13 @@ use crate::{
|
||||
pub type SequencerCoreWithMockClients = crate::SequencerCore<MockBlockPublisher>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MockBlockPublisher;
|
||||
pub struct MockBlockPublisher {
|
||||
channel_id: ChannelId,
|
||||
}
|
||||
|
||||
impl BlockPublisherTrait for MockBlockPublisher {
|
||||
async fn new(
|
||||
_config: &BedrockConfig,
|
||||
config: &BedrockConfig,
|
||||
_bedrock_signing_key: Ed25519Key,
|
||||
_resubmit_interval: Duration,
|
||||
_initial_checkpoint: Option<SequencerCheckpoint>,
|
||||
@ -29,7 +32,9 @@ impl BlockPublisherTrait for MockBlockPublisher {
|
||||
_on_deposit_event: OnDepositEventSink,
|
||||
_on_withdraw_event: OnWithdrawEventSink,
|
||||
) -> Result<Self> {
|
||||
Ok(Self)
|
||||
Ok(Self {
|
||||
channel_id: config.channel_id,
|
||||
})
|
||||
}
|
||||
|
||||
async fn publish_block(
|
||||
@ -39,4 +44,8 @@ impl BlockPublisherTrait for MockBlockPublisher {
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn channel_id(&self) -> ChannelId {
|
||||
self.channel_id
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,3 +11,6 @@ workspace = true
|
||||
common.workspace = true
|
||||
lee.workspace = true
|
||||
lee_core.workspace = true
|
||||
|
||||
hex.workspace = true
|
||||
serde_with.workspace = true
|
||||
|
||||
@ -1,5 +1,28 @@
|
||||
//! Reexports of types used by sequencer rpc specification.
|
||||
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
pub use common::{HashType, block::Block, transaction::LeeTransaction};
|
||||
pub use lee::{Account, AccountId, ProgramId};
|
||||
pub use lee_core::{BlockId, Commitment, MembershipProof, account::Nonce};
|
||||
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, SerializeDisplay, DeserializeFromStr)]
|
||||
pub struct ChannelId(pub [u8; 32]);
|
||||
|
||||
impl Display for ChannelId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let hex_string = hex::encode(self.0);
|
||||
write!(f, "{hex_string}")
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ChannelId {
|
||||
type Err = hex::FromHexError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut bytes = [0_u8; 32];
|
||||
hex::decode_to_slice(s, &mut bytes)?;
|
||||
Ok(Self(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,8 +6,8 @@ use jsonrpsee::types::ErrorObjectOwned;
|
||||
#[cfg(feature = "client")]
|
||||
pub use jsonrpsee::{core::ClientError, http_client::HttpClientBuilder as SequencerClientBuilder};
|
||||
use sequencer_service_protocol::{
|
||||
Account, AccountId, Block, BlockId, Commitment, HashType, LeeTransaction, MembershipProof,
|
||||
Nonce, ProgramId,
|
||||
Account, AccountId, Block, BlockId, ChannelId, Commitment, HashType, LeeTransaction,
|
||||
MembershipProof, Nonce, ProgramId,
|
||||
};
|
||||
|
||||
#[cfg(all(not(feature = "server"), not(feature = "client")))]
|
||||
@ -88,5 +88,8 @@ pub trait Rpc {
|
||||
#[method(name = "getProgramIds")]
|
||||
async fn get_program_ids(&self) -> Result<BTreeMap<String, ProgramId>, ErrorObjectOwned>;
|
||||
|
||||
#[method(name = "getChannelId")]
|
||||
async fn get_channel_id(&self) -> Result<ChannelId, ErrorObjectOwned>;
|
||||
|
||||
// =============================================================================================
|
||||
}
|
||||
|
||||
@ -12,7 +12,8 @@ use sequencer_core::{
|
||||
DbError, SequencerCore, TransactionOrigin, block_publisher::BlockPublisherTrait,
|
||||
};
|
||||
use sequencer_service_protocol::{
|
||||
Account, AccountId, Block, BlockId, Commitment, HashType, MembershipProof, Nonce, ProgramId,
|
||||
Account, AccountId, Block, BlockId, ChannelId, Commitment, HashType, MembershipProof, Nonce,
|
||||
ProgramId,
|
||||
};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
@ -176,6 +177,11 @@ impl<BC: BlockPublisherTrait + Send + 'static> sequencer_service_rpc::RpcServer
|
||||
);
|
||||
Ok(program_ids)
|
||||
}
|
||||
|
||||
async fn get_channel_id(&self) -> Result<ChannelId, ErrorObjectOwned> {
|
||||
let channel_id = self.sequencer.lock().await.block_publisher().channel_id();
|
||||
Ok(ChannelId(*channel_id.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
fn internal_error(err: &DbError) -> ErrorObjectOwned {
|
||||
|
||||
@ -265,7 +265,7 @@ pub unsafe extern "C" fn wallet_ffi_get_all_labels_for_account(
|
||||
.storage()
|
||||
.labels_for_account(account_id_with_privacy.into())
|
||||
{
|
||||
let Ok(label_c) = CString::from_str(label.inner()) else {
|
||||
let Ok(label_c) = CString::from_str(label.as_ref()) else {
|
||||
print_error(format!("Failed to cast label into C string: {label}"));
|
||||
return LabelList::error(WalletFfiError::InternalError);
|
||||
};
|
||||
|
||||
@ -19,9 +19,10 @@ impl Label {
|
||||
pub fn new(label: impl ToString) -> Self {
|
||||
Self(label.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn inner(&self) -> &str {
|
||||
impl AsRef<str> for Label {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user