Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,17 @@
use async_trait::async_trait;
use blockifier_reexecution::state_reader::rpc_objects::BlockId;
use mockito::Server;
use serde_json::{json, Map, Value};
use starknet_api::invoke_tx_args;
use starknet_api::rpc_transaction::RpcTransaction;
use starknet_api::test_utils::invoke::rpc_invoke_tx;
use starknet_api::transaction::fields::ValidResourceBounds;
use starknet_api::transaction::InvokeTransaction;
use url::Url;

use super::virtual_snos_prover::VirtualSnosProver;
use crate::blocking_check::BlockingCheckClient;
use crate::errors::{RunnerError, VirtualSnosProverError};
use crate::proving::virtual_snos_prover::{ProveTransactionResult, VirtualSnosProver};
use crate::running::runner::{RunnerOutput, VirtualSnosRunner};
use crate::test_utils::resource_bounds_for_client_side_tx;

Expand Down Expand Up @@ -124,6 +125,73 @@ async fn test_check_allowed_proceeds_to_proving() {
assert_runner_error(&result);
}

#[tokio::test]
async fn test_check_allowed_with_additional_data_proceeds_to_proving() {
let mut server = Server::new_async().await;
let _mock = server
.mock("POST", "/")
.with_status(200)
.with_body(
r#"{"jsonrpc":"2.0","result":{"allowed":true,"additional_data":{"signature":{"issued_at":1716579600,"sig_r":"0x1","sig_s":"0x2"}}},"id":1}"#,
)
.create_async()
.await;

let url = Url::parse(&server.url()).unwrap();
let client = BlockingCheckClient::new(url, TEST_TIMEOUT_MILLIS, true);
let prover = build_prover(MockRunner, Some(client));

// The MockRunner errors before producing a result, so the verbatim relay is
// covered by the serde tests below; this confirms an allow carrying
// additional_data routes to proving rather than blocking.
let result = prove(&prover).await;
assert_runner_error(&result);
}

fn sample_additional_data() -> Map<String, Value> {
serde_json::from_value(json!({
"signature": { "issued_at": 1716579600, "sig_r": "0x6e6f63c8", "sig_s": "0x58a68a71" }
}))
.unwrap()
}

#[test]
fn test_additional_data_relays_object_verbatim() {
// The prover does not interpret additional_data; an arbitrary object,
// including keys it has never heard of, round-trips unchanged.
let additional_data: Map<String, Value> =
serde_json::from_value(json!({ "signature": { "sig_r": "0x1" }, "future_key": [1, 2, 3] }))
.unwrap();
let fixture = include_str!("../../resources/mock_proving_rpc/prove_transaction_result.json");
let mut result: ProveTransactionResult = serde_json::from_str(fixture).unwrap();
result.additional_data = Some(additional_data.clone());

let json = serde_json::to_value(&result).unwrap();
assert_eq!(json["additional_data"], Value::Object(additional_data));
}

#[test]
fn test_empty_additional_data_is_omitted_from_prove_result_json() {
// The committed mock fixture carries no additional_data; the field must
// deserialize to `None` and stay absent on re-serialization.
let fixture = include_str!("../../resources/mock_proving_rpc/prove_transaction_result.json");
let result: ProveTransactionResult = serde_json::from_str(fixture).unwrap();
assert!(result.additional_data.is_none());

let json = serde_json::to_value(&result).unwrap();
assert!(json.get("additional_data").is_none());
}

#[test]
fn test_populated_additional_data_is_present_in_prove_result_json() {
let fixture = include_str!("../../resources/mock_proving_rpc/prove_transaction_result.json");
let mut result: ProveTransactionResult = serde_json::from_str(fixture).unwrap();
result.additional_data = Some(sample_additional_data());

let json = serde_json::to_value(&result).unwrap();
assert_eq!(json["additional_data"]["signature"]["sig_r"], "0x6e6f63c8");
}

#[tokio::test]
async fn test_inconclusive_fail_open_proceeds_to_proving() {
let mut server = Server::new_async().await;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use blockifier_reexecution::utils::get_chain_info;
#[cfg(feature = "stwo_proving")]
use privacy_prove::{prepare_recursive_prover_precomputes, RecursiveProverPrecomputes};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use starknet_api::block::GasPrice;
use starknet_api::execution_resources::GasAmount;
use starknet_api::rpc_transaction::{RpcInvokeTransaction, RpcInvokeTransactionV3, RpcTransaction};
Expand All @@ -37,6 +38,13 @@ pub struct ProveTransactionResult {
pub proof_facts: ProofFacts,
/// Messages sent from L2 to L1 during execution.
pub l2_to_l1_messages: Vec<MessageToL1>,
/// Opaque side-channel relayed verbatim from the blocking check's allow
/// response; omitted from the JSON response when absent. The prover does not
/// interpret its contents (a screened deposit carries a screening signature
/// under `signature`, but that is the screening domain's concern, not the
/// prover's).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub additional_data: Option<Map<String, Value>>,
}

/// Virtual SNOS prover for Starknet transactions.
Expand Down Expand Up @@ -234,22 +242,24 @@ impl<R: VirtualSnosRunner + 'static> VirtualSnosProver<R> {
tokio::time::timeout(timeout_duration, client.check_transaction(block_id, transaction))
.await;

let allow = match check_outcome {
// A transaction allowed via the fail-open policy (inconclusive check or
// timeout) carries no additional_data.
let (allow, additional_data) = match check_outcome {
Ok(BlockingCheckResult::Blocked) => {
info!("Transaction blocked by external check");
false
(false, None)
}
Ok(BlockingCheckResult::Allowed(_)) => {
Ok(BlockingCheckResult::Allowed(additional_data)) => {
info!("Transaction allowed by external check");
true
(true, additional_data)
}
Ok(BlockingCheckResult::Inconclusive) => {
info!(fail_open = client.fail_open, "Blocking check inconclusive");
client.fail_open
(client.fail_open, None)
}
Err(_) => {
info!(fail_open = client.fail_open, "Blocking check timed out");
client.fail_open
(client.fail_open, None)
}
};

Expand All @@ -258,11 +268,13 @@ impl<R: VirtualSnosRunner + 'static> VirtualSnosProver<R> {
return Err(VirtualSnosProverError::TransactionBlocked);
}

match prove_handle.await {
Ok(result) => result,
let mut result = match prove_handle.await {
Ok(prove_result) => prove_result?,
Err(err) if err.is_panic() => std::panic::resume_unwind(err.into_panic()),
Err(err) => unreachable!("prove task cancelled unexpectedly: {err}"),
}
};
result.additional_data = additional_data;
Ok(result)
}

/// Proves a Virtual Starknet OS run from its output.
Expand Down Expand Up @@ -329,6 +341,7 @@ async fn prove_virtual_snos_run_with_precomputes(
proof: prover_output.proof,
proof_facts,
l2_to_l1_messages: runner_output.l2_to_l1_messages,
additional_data: None,
})
}

Expand Down
Loading