<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StateSyncConfig {
    // Size of chunk to request for state synchronization
    pub chunk_limit: u64,
    // The timeout of the state sync client to process a commit notification (in milliseconds)
    pub client_commit_timeout_ms: u64,
    // default timeout used for long polling to remote peer
    pub long_poll_timeout_ms: u64,
    // valid maximum chunk limit for sanity check
    pub max_chunk_limit: u64,
    // valid maximum timeout limit for sanity check
    pub max_timeout_ms: u64,
    // The timeout of the state sync coordinator to receive a commit ack from mempool (in milliseconds)
    pub mempool_commit_timeout_ms: u64,
    // default timeout to make state sync progress by sending chunk requests to a certain number of networks
    // if no progress is made by sending chunk requests to a number of networks,
    // the next sync request will be multicasted, i.e. sent to more networks
    pub multicast_timeout_ms: u64,
    // The timeout for ensuring sync requests are making progress (i.e., the maximum time between
    // commits when processing a sync request).
    pub sync_request_timeout_ms: u64,
    // interval used for checking state synchronization progress
    pub tick_interval_ms: u64,

    // Everything above belongs to state sync v1 and will be removed in the future.
    pub data_streaming_service: DataStreamingServiceConfig,
    pub aptos_data_client: AptosDataClientConfig,
    pub state_sync_driver: StateSyncDriverConfig,
    pub storage_service: StorageServiceConfig,
}

impl Default for StateSyncConfig {
    fn default() -> Self {
        Self {
            chunk_limit: 1000,
            client_commit_timeout_ms: 5_000,
            long_poll_timeout_ms: 10_000,
            max_chunk_limit: 1000,
            max_timeout_ms: 120_000,
            mempool_commit_timeout_ms: 5_000,
            multicast_timeout_ms: 30_000,
            sync_request_timeout_ms: 60_000,
            tick_interval_ms: 100,
            data_streaming_service: DataStreamingServiceConfig::default(),
            aptos_data_client: AptosDataClientConfig::default(),
            state_sync_driver: StateSyncDriverConfig::default(),
            storage_service: StorageServiceConfig::default(),
        }
    }
}

/// The bootstrapping mode determines how the node will bootstrap to the latest
/// blockchain state, e.g., directly download the latest account states.
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
pub enum BootstrappingMode {
    ApplyTransactionOutputsFromGenesis, // Applies transaction outputs (starting at genesis)
    DownloadLatestAccountStates,        // Downloads the account states (at the latest version)
    ExecuteTransactionsFromGenesis,     // Executes transactions (starting at genesis)
}

/// The continuous syncing mode determines how the node will stay up-to-date
/// once it has bootstrapped and the blockchain continues to grow, e.g.,
/// continuously executing all transactions.
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
pub enum ContinuousSyncingMode {
    ExecuteTransactions,     // Executes transactions to stay up-to-date
    ApplyTransactionOutputs, // Applies transaction outputs to stay up-to-date
}

#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StateSyncDriverConfig {
    pub bootstrapping_mode: BootstrappingMode, // The mode by which to bootstrap
    pub enable_state_sync_v2: bool,            // If the node should sync with state sync v2
    pub continuous_syncing_mode: ContinuousSyncingMode, // The mode by which to sync after bootstrapping
    pub progress_check_interval_ms: u64, // The interval (ms) at which to check state sync progress
    pub max_connection_deadline_secs: u64, // The max time (secs) to wait for connections from peers
    pub max_consecutive_stream_notifications: u64, // The max number of notifications to process per driver loop
    pub max_pending_data_chunks: u64, // The max number of data chunks pending execution or commit
    pub max_stream_wait_time_ms: u64, // The max time (ms) to wait for a data stream notification
}

/// The default state sync driver config will be the one that gets (and keeps)
/// the node up-to-date as quickly and cheaply as possible.
impl Default for StateSyncDriverConfig {
    fn default() -> Self {
        Self {
            bootstrapping_mode: BootstrappingMode::ApplyTransactionOutputsFromGenesis,
            enable_state_sync_v2: true,
            continuous_syncing_mode: ContinuousSyncingMode::ApplyTransactionOutputs,
            progress_check_interval_ms: 100,
            max_connection_deadline_secs: 10,
            max_consecutive_stream_notifications: 10,
            max_pending_data_chunks: 100,
            max_stream_wait_time_ms: 10_000,
        }
    }
}

#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StorageServiceConfig {
    pub max_account_states_chunk_sizes: u64, // Max num of accounts per chunk
    pub max_concurrent_requests: u64,        // Max num of concurrent storage server tasks
    pub max_epoch_chunk_size: u64,           // Max num of epoch ending ledger infos per chunk
    pub max_network_channel_size: u64,       // Max num of pending network messages
    pub max_transaction_chunk_size: u64,     // Max num of transactions per chunk
    pub max_transaction_output_chunk_size: u64, // Max num of transaction outputs per chunk
    pub storage_summary_refresh_interval_ms: u64, // The interval (ms) to refresh the storage summary
}

impl Default for StorageServiceConfig {
    fn default() -> Self {
        Self {
            max_account_states_chunk_sizes: 1000,
            max_concurrent_requests: 4000,
            max_epoch_chunk_size: 100,
            max_network_channel_size: 1000,
            max_transaction_chunk_size: 1000,
            max_transaction_output_chunk_size: 1000,
            storage_summary_refresh_interval_ms: 100,
        }
    }
}

#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DataStreamingServiceConfig {
    // The interval (milliseconds) at which to refresh the global data summary.
    pub global_summary_refresh_interval_ms: u64,

    // Maximum number of concurrent data client requests (per stream).
    pub max_concurrent_requests: u64,

    // Maximum channel sizes for each data stream listener. If messages are not
    // consumed, they will be dropped (oldest messages first). The remaining
    // messages will be retrieved using FIFO ordering.
    pub max_data_stream_channel_sizes: u64,

    // Maximum number of retries for a single client request before a data
    // stream will terminate.
    pub max_request_retry: u64,

    // Maximum number of notification ID to response context mappings held in
    // memory. Once the number grows beyond this value, garbage collection occurs.
    pub max_notification_id_mappings: u64,

    // The interval (milliseconds) at which to check the progress of each stream.
    pub progress_check_interval_ms: u64,
}

impl Default for DataStreamingServiceConfig {
    fn default() -> Self {
        Self {
            global_summary_refresh_interval_ms: 100,
            max_concurrent_requests: 1,
            max_data_stream_channel_sizes: 1000,
            max_request_retry: 3,
            max_notification_id_mappings: 2000,
            progress_check_interval_ms: 100,
        }
    }
}

#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AptosDataClientConfig {
    pub response_timeout_ms: u64, // Timeout (in milliseconds) when waiting for a response
    pub summary_poll_interval_ms: u64, // Interval (in milliseconds) between data summary polls
}

impl Default for AptosDataClientConfig {
    fn default() -> Self {
        Self {
            response_timeout_ms: 10000,
            summary_poll_interval_ms: 100,
        }
    }
}
<commit_msg>[State Sync] Reduce cache latency.<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StateSyncConfig {
    // Size of chunk to request for state synchronization
    pub chunk_limit: u64,
    // The timeout of the state sync client to process a commit notification (in milliseconds)
    pub client_commit_timeout_ms: u64,
    // default timeout used for long polling to remote peer
    pub long_poll_timeout_ms: u64,
    // valid maximum chunk limit for sanity check
    pub max_chunk_limit: u64,
    // valid maximum timeout limit for sanity check
    pub max_timeout_ms: u64,
    // The timeout of the state sync coordinator to receive a commit ack from mempool (in milliseconds)
    pub mempool_commit_timeout_ms: u64,
    // default timeout to make state sync progress by sending chunk requests to a certain number of networks
    // if no progress is made by sending chunk requests to a number of networks,
    // the next sync request will be multicasted, i.e. sent to more networks
    pub multicast_timeout_ms: u64,
    // The timeout for ensuring sync requests are making progress (i.e., the maximum time between
    // commits when processing a sync request).
    pub sync_request_timeout_ms: u64,
    // interval used for checking state synchronization progress
    pub tick_interval_ms: u64,

    // Everything above belongs to state sync v1 and will be removed in the future.
    pub data_streaming_service: DataStreamingServiceConfig,
    pub aptos_data_client: AptosDataClientConfig,
    pub state_sync_driver: StateSyncDriverConfig,
    pub storage_service: StorageServiceConfig,
}

impl Default for StateSyncConfig {
    fn default() -> Self {
        Self {
            chunk_limit: 1000,
            client_commit_timeout_ms: 5_000,
            long_poll_timeout_ms: 10_000,
            max_chunk_limit: 1000,
            max_timeout_ms: 120_000,
            mempool_commit_timeout_ms: 5_000,
            multicast_timeout_ms: 30_000,
            sync_request_timeout_ms: 60_000,
            tick_interval_ms: 100,
            data_streaming_service: DataStreamingServiceConfig::default(),
            aptos_data_client: AptosDataClientConfig::default(),
            state_sync_driver: StateSyncDriverConfig::default(),
            storage_service: StorageServiceConfig::default(),
        }
    }
}

/// The bootstrapping mode determines how the node will bootstrap to the latest
/// blockchain state, e.g., directly download the latest account states.
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
pub enum BootstrappingMode {
    ApplyTransactionOutputsFromGenesis, // Applies transaction outputs (starting at genesis)
    DownloadLatestAccountStates,        // Downloads the account states (at the latest version)
    ExecuteTransactionsFromGenesis,     // Executes transactions (starting at genesis)
}

/// The continuous syncing mode determines how the node will stay up-to-date
/// once it has bootstrapped and the blockchain continues to grow, e.g.,
/// continuously executing all transactions.
#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
pub enum ContinuousSyncingMode {
    ExecuteTransactions,     // Executes transactions to stay up-to-date
    ApplyTransactionOutputs, // Applies transaction outputs to stay up-to-date
}

#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StateSyncDriverConfig {
    pub bootstrapping_mode: BootstrappingMode, // The mode by which to bootstrap
    pub enable_state_sync_v2: bool,            // If the node should sync with state sync v2
    pub continuous_syncing_mode: ContinuousSyncingMode, // The mode by which to sync after bootstrapping
    pub progress_check_interval_ms: u64, // The interval (ms) at which to check state sync progress
    pub max_connection_deadline_secs: u64, // The max time (secs) to wait for connections from peers
    pub max_consecutive_stream_notifications: u64, // The max number of notifications to process per driver loop
    pub max_pending_data_chunks: u64, // The max number of data chunks pending execution or commit
    pub max_stream_wait_time_ms: u64, // The max time (ms) to wait for a data stream notification
}

/// The default state sync driver config will be the one that gets (and keeps)
/// the node up-to-date as quickly and cheaply as possible.
impl Default for StateSyncDriverConfig {
    fn default() -> Self {
        Self {
            bootstrapping_mode: BootstrappingMode::ApplyTransactionOutputsFromGenesis,
            enable_state_sync_v2: true,
            continuous_syncing_mode: ContinuousSyncingMode::ApplyTransactionOutputs,
            progress_check_interval_ms: 100,
            max_connection_deadline_secs: 10,
            max_consecutive_stream_notifications: 10,
            max_pending_data_chunks: 100,
            max_stream_wait_time_ms: 10_000,
        }
    }
}

#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct StorageServiceConfig {
    pub max_account_states_chunk_sizes: u64, // Max num of accounts per chunk
    pub max_concurrent_requests: u64,        // Max num of concurrent storage server tasks
    pub max_epoch_chunk_size: u64,           // Max num of epoch ending ledger infos per chunk
    pub max_network_channel_size: u64,       // Max num of pending network messages
    pub max_transaction_chunk_size: u64,     // Max num of transactions per chunk
    pub max_transaction_output_chunk_size: u64, // Max num of transaction outputs per chunk
    pub storage_summary_refresh_interval_ms: u64, // The interval (ms) to refresh the storage summary
}

impl Default for StorageServiceConfig {
    fn default() -> Self {
        Self {
            max_account_states_chunk_sizes: 1000,
            max_concurrent_requests: 4000,
            max_epoch_chunk_size: 100,
            max_network_channel_size: 4000,
            max_transaction_chunk_size: 1000,
            max_transaction_output_chunk_size: 1000,
            storage_summary_refresh_interval_ms: 50,
        }
    }
}

#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct DataStreamingServiceConfig {
    // The interval (milliseconds) at which to refresh the global data summary.
    pub global_summary_refresh_interval_ms: u64,

    // Maximum number of concurrent data client requests (per stream).
    pub max_concurrent_requests: u64,

    // Maximum channel sizes for each data stream listener. If messages are not
    // consumed, they will be dropped (oldest messages first). The remaining
    // messages will be retrieved using FIFO ordering.
    pub max_data_stream_channel_sizes: u64,

    // Maximum number of retries for a single client request before a data
    // stream will terminate.
    pub max_request_retry: u64,

    // Maximum number of notification ID to response context mappings held in
    // memory. Once the number grows beyond this value, garbage collection occurs.
    pub max_notification_id_mappings: u64,

    // The interval (milliseconds) at which to check the progress of each stream.
    pub progress_check_interval_ms: u64,
}

impl Default for DataStreamingServiceConfig {
    fn default() -> Self {
        Self {
            global_summary_refresh_interval_ms: 50,
            max_concurrent_requests: 1,
            max_data_stream_channel_sizes: 1000,
            max_request_retry: 3,
            max_notification_id_mappings: 2000,
            progress_check_interval_ms: 100,
        }
    }
}

#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct AptosDataClientConfig {
    pub response_timeout_ms: u64, // Timeout (in milliseconds) when waiting for a response
    pub summary_poll_interval_ms: u64, // Interval (in milliseconds) between data summary polls
}

impl Default for AptosDataClientConfig {
    fn default() -> Self {
        Self {
            response_timeout_ms: 10000,
            summary_poll_interval_ms: 100,
        }
    }
}
<|endoftext|>


<commit_before>extern crate pkg_config;
extern crate cmake;

use std::env;
use std::fs::{self};
use std::path::PathBuf;
use std::process::Command;

pub fn find_or_build(lib: &str) -> Result<(), String> {
    // force behavior for static libraries (risks over-linking, but better than missing transitive dependencies)
    env::set_var("PKG_CONFIG_ALL_STATIC", "");
    match pkg_config::find_library(lib) {
        Ok(_) => Ok(()),
        Err(_) => {
            // get source and build directories
            let src = PathBuf::from(&env::var_os("CARGO_MANIFEST_DIR").unwrap())
                                   .parent().unwrap().join("energymon");
            let build = PathBuf::from(&env::var_os("OUT_DIR").unwrap()).join("build");
            let mut cmake_config = cmake::Config::new(&src);
            // always remake the build directory
            fs::remove_dir_all(&build).ok();
            fs::create_dir_all(&build).unwrap();
            // set CMake parameters
            cmake_config.build_target(&lib);
            cmake_config.define("BUILD_SHARED_LIBS", "false");
            if let Some(default_impl) = env::var_os("ENERGYMON_DEFAULT_IMPL") {
                cmake_config.define("DEFAULT", default_impl.to_str().unwrap());
            }
            if let Ok(msystem) = env::var("MSYSTEM") {
                if msystem.contains("MINGW") {
                    cmake_config.generator("MSYS Makefiles");
                }
            }
            // run the build commands
            cmake_config.build();
            // run pkg-config on compiled dir to get any transitive dependencies of static lib
            set_pkg_config_path(build);
            // this might be a cross-compile, so we need to force the search
            env::set_var("PKG_CONFIG_ALLOW_CROSS", "1");
            pkg_config::find_library(lib).map(|_| ())
        },
    }
}

fn set_pkg_config_path(build: PathBuf) {
    let var = match env::var_os("PKG_CONFIG_PATH") {
        Some(p) => {
            let mut paths = env::split_paths(&p).collect::<Vec<_>>();
            paths.push(build);
            env::join_paths(paths).unwrap()
        },
        None => build.into_os_string(),
    };
    env::set_var("PKG_CONFIG_PATH", &var);
}

fn run(cmd: &mut Command) {
    match cmd.status() {
        Ok(status) => assert!(status.success()),
        Err(e) => panic!("Unable to execute {:?}! {}", cmd, e),
    }
}
<commit_msg>Remove now-unused function<commit_after>extern crate pkg_config;
extern crate cmake;

use std::env;
use std::fs::{self};
use std::path::PathBuf;

pub fn find_or_build(lib: &str) -> Result<(), String> {
    // force behavior for static libraries (risks over-linking, but better than missing transitive dependencies)
    env::set_var("PKG_CONFIG_ALL_STATIC", "");
    match pkg_config::find_library(lib) {
        Ok(_) => Ok(()),
        Err(_) => {
            // get source and build directories
            let src = PathBuf::from(&env::var_os("CARGO_MANIFEST_DIR").unwrap())
                                   .parent().unwrap().join("energymon");
            let build = PathBuf::from(&env::var_os("OUT_DIR").unwrap()).join("build");
            let mut cmake_config = cmake::Config::new(&src);
            // always remake the build directory
            fs::remove_dir_all(&build).ok();
            fs::create_dir_all(&build).unwrap();
            // set CMake parameters
            cmake_config.build_target(&lib);
            cmake_config.define("BUILD_SHARED_LIBS", "false");
            if let Some(default_impl) = env::var_os("ENERGYMON_DEFAULT_IMPL") {
                cmake_config.define("DEFAULT", default_impl.to_str().unwrap());
            }
            if let Ok(msystem) = env::var("MSYSTEM") {
                if msystem.contains("MINGW") {
                    cmake_config.generator("MSYS Makefiles");
                }
            }
            // run the build commands
            cmake_config.build();
            // run pkg-config on compiled dir to get any transitive dependencies of static lib
            set_pkg_config_path(build);
            // this might be a cross-compile, so we need to force the search
            env::set_var("PKG_CONFIG_ALLOW_CROSS", "1");
            pkg_config::find_library(lib).map(|_| ())
        },
    }
}

fn set_pkg_config_path(build: PathBuf) {
    let var = match env::var_os("PKG_CONFIG_PATH") {
        Some(p) => {
            let mut paths = env::split_paths(&p).collect::<Vec<_>>();
            paths.push(build);
            env::join_paths(paths).unwrap()
        },
        None => build.into_os_string(),
    };
    env::set_var("PKG_CONFIG_PATH", &var);
}
<|endoftext|>


<commit_before>use std::mem::MaybeUninit;

use nodejs_sys as napi;

use raw::{Env, Local};

pub unsafe extern "C" fn new(out: &mut Local, env: Env) {
    napi::napi_create_object(env, out as *mut _);
}

pub unsafe extern "C" fn get_own_property_names(out: &mut Local, env: Env, object: Local) -> bool {
    let status = napi::napi_get_property_names(env, object, out as *mut _);

    status == napi::napi_status::napi_ok
}

// Unused.
pub unsafe extern "C" fn get_isolate(_obj: Local) -> Env { unimplemented!() }

pub unsafe extern "C" fn get_index(out: &mut Local, env: Env, object: Local, index: u32) -> bool {
    let status = napi::napi_get_element(env, object, index, out as *mut _);

    status == napi::napi_status::napi_ok
}

pub unsafe extern "C" fn set_index(out: &mut bool, env: Env, object: Local, index: u32, val: Local) -> bool {
    let status = napi::napi_set_element(env, object, index, val);
    *out = status == napi::napi_status::napi_ok;

    *out
}

pub unsafe extern "C" fn get_string(env: Env, out: &mut Local, object: Local, key: *const u8, len: i32) -> bool {
    let mut key_val = MaybeUninit::uninit();

    // Not using `crate::string::new()` because it requires a _reference_ to a Local,
    // while we only have uninitialized memory.
    if napi::napi_create_string_utf8(
        env,
        key as *const i8,
        len as usize,
        key_val.as_mut_ptr(),
    ) != napi::napi_status::napi_ok {
        return false;
    }

    // Not using napi_get_named_property() because the `key` may not be null terminated.
    if napi::napi_get_property(
        env,
        object,
        key_val.assume_init(),
        out as *mut _,
    ) != napi::napi_status::napi_ok {
        return false;
    }

    true
}

pub unsafe extern "C" fn set_string(
    env: Env,
    out: &mut bool,
    object: Local,
    key: *const u8,
    len: i32,
    val: Local,
) -> bool {
    let mut key_val = MaybeUninit::uninit();

    *out = true;

    if napi::napi_create_string_utf8(
        env,
        key as *const i8,
        len as usize,
        key_val.as_mut_ptr(),
    ) != napi::napi_status::napi_ok {
        *out = false;
        return false;
    }

    if napi::napi_set_property(
        env,
        object,
        key_val.assume_init(),
        val,
    ) != napi::napi_status::napi_ok {
        *out = false;
        return false;
    }

    true
}

pub unsafe extern "C" fn get(out: &mut Local, env: Env, object: Local, key: Local) -> bool {
    let status = napi::napi_get_property(env, object, key, out as *mut _);

    status == napi::napi_status::napi_ok
}

pub unsafe extern "C" fn set(out: &mut bool, env: Env, object: Local, key: Local, val: Local) -> bool {
    let status = napi::napi_set_property(env, object, key, val);
    *out = status == napi::napi_status::napi_ok;

    *out
}
<commit_msg>Document out param in `neon_runtime::nan::object::set_{index,string}`.<commit_after>use std::mem::MaybeUninit;

use nodejs_sys as napi;

use raw::{Env, Local};

pub unsafe extern "C" fn new(out: &mut Local, env: Env) {
    napi::napi_create_object(env, out as *mut _);
}

pub unsafe extern "C" fn get_own_property_names(out: &mut Local, env: Env, object: Local) -> bool {
    let status = napi::napi_get_property_names(env, object, out as *mut _);

    status == napi::napi_status::napi_ok
}

// Unused.
pub unsafe extern "C" fn get_isolate(_obj: Local) -> Env {
    unimplemented!()
}

pub unsafe extern "C" fn get_index(out: &mut Local, env: Env, object: Local, index: u32) -> bool {
    let status = napi::napi_get_element(env, object, index, out as *mut _);

    status == napi::napi_status::napi_ok
}

/// Sets the key value of a `napi_value` at the `index` provided. Returns `true` if the set
/// succeeded.
///
/// The `out` parameter and the return value contain the same information for historical reasons,
/// see [discussion].
///
/// [discussion]: https://github.com/neon-bindings/neon/pull/458#discussion_r344827965
pub unsafe extern "C" fn set_index(out: &mut bool, env: Env, object: Local, index: u32, val: Local) -> bool {
    let status = napi::napi_set_element(env, object, index, val);
    *out = status == napi::napi_status::napi_ok;

    *out
}

pub unsafe extern "C" fn get_string(env: Env, out: &mut Local, object: Local, key: *const u8, len: i32) -> bool {
    let mut key_val = MaybeUninit::uninit();

    // Not using `crate::string::new()` because it requires a _reference_ to a Local,
    // while we only have uninitialized memory.
    if napi::napi_create_string_utf8(env, key as *const i8, len as usize, key_val.as_mut_ptr())
        != napi::napi_status::napi_ok
    {
        return false;
    }

    // Not using napi_get_named_property() because the `key` may not be null terminated.
    if napi::napi_get_property(env, object, key_val.assume_init(), out as *mut _)
        != napi::napi_status::napi_ok
    {
        return false;
    }

    true
}

/// Sets the key value of a `napi_value` at a named key. Returns `true` if the set succeeded.
///
/// The `out` parameter and the return value contain the same information for historical reasons,
/// see [discussion].
///
/// [discussion]: https://github.com/neon-bindings/neon/pull/458#discussion_r344827965
pub unsafe extern "C" fn set_string(env: Env, out: &mut bool, object: Local, key: *const u8, len: i32, val: Local) -> bool {
    let mut key_val = MaybeUninit::uninit();

    *out = true;

    if napi::napi_create_string_utf8(env, key as *const i8, len as usize, key_val.as_mut_ptr())
        != napi::napi_status::napi_ok
    {
        *out = false;
        return false;
    }

    if napi::napi_set_property(env, object, key_val.assume_init(), val)
        != napi::napi_status::napi_ok
    {
        *out = false;
        return false;
    }

    true
}

pub unsafe extern "C" fn get(out: &mut Local, env: Env, object: Local, key: Local) -> bool {
    let status = napi::napi_get_property(env, object, key, out as *mut _);

    status == napi::napi_status::napi_ok
}

/// Sets the property value of an `napi_value` object, named by another `napi_value` `key`. Returns `true` if the set succeeded.
///
/// The `out` parameter and the return value contain the same information for historical reasons,
/// see [discussion].
///
/// [discussion]: https://github.com/neon-bindings/neon/pull/458#discussion_r344827965
pub unsafe extern "C" fn set(out: &mut bool, env: Env, object: Local, key: Local, val: Local) -> bool {
    let status = napi::napi_set_property(env, object, key, val);
    *out = status == napi::napi_status::napi_ok;

    *out
}
<|endoftext|>


<commit_before>//@ignore-target-windows: Concurrency on Windows is not supported yet.
//@compile-flags: -Zmiri-disable-isolation -Zmiri-strict-provenance

use std::sync::{Arc, Barrier, Condvar, Mutex, Once, RwLock};
use std::thread;
use std::time::{Duration, Instant};

// Check if Rust barriers are working.

/// This test is taken from the Rust documentation.
fn check_barriers() {
    let mut handles = Vec::with_capacity(10);
    let barrier = Arc::new(Barrier::new(10));
    for _ in 0..10 {
        let c = barrier.clone();
        // The same messages will be printed together.
        // You will NOT see any interleaving.
        handles.push(thread::spawn(move || {
            println!("before wait");
            c.wait();
            println!("after wait");
        }));
    }
    // Wait for other threads to finish.
    for handle in handles {
        handle.join().unwrap();
    }
}

// Check if Rust conditional variables are working.

/// The test taken from the Rust documentation.
fn check_conditional_variables_notify_one() {
    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let pair2 = pair.clone();

    // Spawn a new thread.
    let t = thread::spawn(move || {
        thread::yield_now();
        let (lock, cvar) = &*pair2;
        let mut started = lock.lock().unwrap();
        *started = true;
        // We notify the condvar that the value has changed.
        cvar.notify_one();
    });

    // Wait for the thread to fully start up.
    let (lock, cvar) = &*pair;
    let mut started = lock.lock().unwrap();
    while !*started {
        started = cvar.wait(started).unwrap();
    }

    t.join().unwrap();
}

/// Test that waiting on a conditional variable with a timeout does not
/// deadlock.
fn check_conditional_variables_timed_wait_timeout() {
    let lock = Mutex::new(());
    let cvar = Condvar::new();
    let guard = lock.lock().unwrap();
    let now = Instant::now();
    let (_guard, timeout) = cvar.wait_timeout(guard, Duration::from_millis(100)).unwrap();
    assert!(timeout.timed_out());
    let elapsed_time = now.elapsed().as_millis();
    assert!(100 <= elapsed_time && elapsed_time <= 1000);
}

/// Test that signaling a conditional variable when waiting with a timeout works
/// as expected.
fn check_conditional_variables_timed_wait_notimeout() {
    let pair = Arc::new((Mutex::new(()), Condvar::new()));
    let pair2 = pair.clone();

    let (lock, cvar) = &*pair;
    let guard = lock.lock().unwrap();

    let handle = thread::spawn(move || {
        let (_lock, cvar) = &*pair2;
        cvar.notify_one();
    });

    let (_guard, timeout) = cvar.wait_timeout(guard, Duration::from_millis(500)).unwrap();
    assert!(!timeout.timed_out());
    handle.join().unwrap();
}

// Check if locks are working.

fn check_mutex() {
    let data = Arc::new(Mutex::new(0));
    let mut threads = Vec::new();

    for _ in 0..3 {
        let data = Arc::clone(&data);
        let thread = thread::spawn(move || {
            let mut data = data.lock().unwrap();
            thread::yield_now();
            *data += 1;
        });
        threads.push(thread);
    }

    for thread in threads {
        thread.join().unwrap();
    }

    assert!(data.try_lock().is_ok());

    let data = Arc::try_unwrap(data).unwrap().into_inner().unwrap();
    assert_eq!(data, 3);
}

fn check_rwlock_write() {
    let data = Arc::new(RwLock::new(0));
    let mut threads = Vec::new();

    for _ in 0..3 {
        let data = Arc::clone(&data);
        let thread = thread::spawn(move || {
            let mut data = data.write().unwrap();
            thread::yield_now();
            *data += 1;
        });
        threads.push(thread);
    }

    for thread in threads {
        thread.join().unwrap();
    }

    assert!(data.try_write().is_ok());

    let data = Arc::try_unwrap(data).unwrap().into_inner().unwrap();
    assert_eq!(data, 3);
}

fn check_rwlock_read_no_deadlock() {
    let l1 = Arc::new(RwLock::new(0));
    let l2 = Arc::new(RwLock::new(0));

    let l1_copy = Arc::clone(&l1);
    let l2_copy = Arc::clone(&l2);
    let _guard1 = l1.read().unwrap();
    let handle = thread::spawn(move || {
        let _guard2 = l2_copy.read().unwrap();
        thread::yield_now();
        let _guard1 = l1_copy.read().unwrap();
    });
    thread::yield_now();
    let _guard2 = l2.read().unwrap();
    handle.join().unwrap();
}

// Check if Rust once statics are working.

static mut VAL: usize = 0;
static INIT: Once = Once::new();

fn get_cached_val() -> usize {
    unsafe {
        INIT.call_once(|| {
            VAL = expensive_computation();
        });
        VAL
    }
}

fn expensive_computation() -> usize {
    let mut i = 1;
    let mut c = 1;
    while i < 1000 {
        i *= c;
        c += 1;
    }
    i
}

/// The test taken from the Rust documentation.
fn check_once() {
    let handles: Vec<_> = (0..10)
        .map(|_| {
            thread::spawn(|| {
                thread::yield_now();
                let val = get_cached_val();
                assert_eq!(val, 5040);
            })
        })
        .collect();
    for handle in handles {
        handle.join().unwrap();
    }
}

fn park_timeout() {
    let start = Instant::now();

    thread::park_timeout(Duration::from_millis(200));
    // Normally, waiting in park/park_timeout may spuriously wake up early, but we
    // know Miri's timed synchronization primitives do not do that.

    assert!((200..1000).contains(&start.elapsed().as_millis()));
}

fn park_unpark() {
    let t1 = thread::current();
    let t2 = thread::spawn(move || {
        thread::park();
        thread::sleep(Duration::from_millis(200));
        t1.unpark();
    });

    let start = Instant::now();

    t2.thread().unpark();
    thread::park();
    // Normally, waiting in park/park_timeout may spuriously wake up early, but we
    // know Miri's timed synchronization primitives do not do that.

    assert!((200..1000).contains(&start.elapsed().as_millis()));

    t2.join().unwrap();
}

fn main() {
    check_barriers();
    check_conditional_variables_notify_one();
    check_conditional_variables_timed_wait_timeout();
    check_conditional_variables_timed_wait_notimeout();
    check_mutex();
    check_rwlock_write();
    check_rwlock_read_no_deadlock();
    check_once();
    park_timeout();
    park_unpark();
}
<commit_msg>give macOS more time<commit_after>//@ignore-target-windows: Concurrency on Windows is not supported yet.
//@compile-flags: -Zmiri-disable-isolation -Zmiri-strict-provenance

use std::sync::{Arc, Barrier, Condvar, Mutex, Once, RwLock};
use std::thread;
use std::time::{Duration, Instant};

// Check if Rust barriers are working.

/// This test is taken from the Rust documentation.
fn check_barriers() {
    let mut handles = Vec::with_capacity(10);
    let barrier = Arc::new(Barrier::new(10));
    for _ in 0..10 {
        let c = barrier.clone();
        // The same messages will be printed together.
        // You will NOT see any interleaving.
        handles.push(thread::spawn(move || {
            println!("before wait");
            c.wait();
            println!("after wait");
        }));
    }
    // Wait for other threads to finish.
    for handle in handles {
        handle.join().unwrap();
    }
}

// Check if Rust conditional variables are working.

/// The test taken from the Rust documentation.
fn check_conditional_variables_notify_one() {
    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let pair2 = pair.clone();

    // Spawn a new thread.
    let t = thread::spawn(move || {
        thread::yield_now();
        let (lock, cvar) = &*pair2;
        let mut started = lock.lock().unwrap();
        *started = true;
        // We notify the condvar that the value has changed.
        cvar.notify_one();
    });

    // Wait for the thread to fully start up.
    let (lock, cvar) = &*pair;
    let mut started = lock.lock().unwrap();
    while !*started {
        started = cvar.wait(started).unwrap();
    }

    t.join().unwrap();
}

/// Test that waiting on a conditional variable with a timeout does not
/// deadlock.
fn check_conditional_variables_timed_wait_timeout() {
    let lock = Mutex::new(());
    let cvar = Condvar::new();
    let guard = lock.lock().unwrap();
    let now = Instant::now();
    let (_guard, timeout) = cvar.wait_timeout(guard, Duration::from_millis(100)).unwrap();
    assert!(timeout.timed_out());
    let elapsed_time = now.elapsed().as_millis();
    assert!(100 <= elapsed_time && elapsed_time <= 1000);
}

/// Test that signaling a conditional variable when waiting with a timeout works
/// as expected.
fn check_conditional_variables_timed_wait_notimeout() {
    let pair = Arc::new((Mutex::new(()), Condvar::new()));
    let pair2 = pair.clone();

    let (lock, cvar) = &*pair;
    let guard = lock.lock().unwrap();

    let handle = thread::spawn(move || {
        let (_lock, cvar) = &*pair2;
        cvar.notify_one();
    });

    let (_guard, timeout) = cvar.wait_timeout(guard, Duration::from_millis(1000)).unwrap();
    assert!(!timeout.timed_out());
    handle.join().unwrap();
}

// Check if locks are working.

fn check_mutex() {
    let data = Arc::new(Mutex::new(0));
    let mut threads = Vec::new();

    for _ in 0..3 {
        let data = Arc::clone(&data);
        let thread = thread::spawn(move || {
            let mut data = data.lock().unwrap();
            thread::yield_now();
            *data += 1;
        });
        threads.push(thread);
    }

    for thread in threads {
        thread.join().unwrap();
    }

    assert!(data.try_lock().is_ok());

    let data = Arc::try_unwrap(data).unwrap().into_inner().unwrap();
    assert_eq!(data, 3);
}

fn check_rwlock_write() {
    let data = Arc::new(RwLock::new(0));
    let mut threads = Vec::new();

    for _ in 0..3 {
        let data = Arc::clone(&data);
        let thread = thread::spawn(move || {
            let mut data = data.write().unwrap();
            thread::yield_now();
            *data += 1;
        });
        threads.push(thread);
    }

    for thread in threads {
        thread.join().unwrap();
    }

    assert!(data.try_write().is_ok());

    let data = Arc::try_unwrap(data).unwrap().into_inner().unwrap();
    assert_eq!(data, 3);
}

fn check_rwlock_read_no_deadlock() {
    let l1 = Arc::new(RwLock::new(0));
    let l2 = Arc::new(RwLock::new(0));

    let l1_copy = Arc::clone(&l1);
    let l2_copy = Arc::clone(&l2);
    let _guard1 = l1.read().unwrap();
    let handle = thread::spawn(move || {
        let _guard2 = l2_copy.read().unwrap();
        thread::yield_now();
        let _guard1 = l1_copy.read().unwrap();
    });
    thread::yield_now();
    let _guard2 = l2.read().unwrap();
    handle.join().unwrap();
}

// Check if Rust once statics are working.

static mut VAL: usize = 0;
static INIT: Once = Once::new();

fn get_cached_val() -> usize {
    unsafe {
        INIT.call_once(|| {
            VAL = expensive_computation();
        });
        VAL
    }
}

fn expensive_computation() -> usize {
    let mut i = 1;
    let mut c = 1;
    while i < 1000 {
        i *= c;
        c += 1;
    }
    i
}

/// The test taken from the Rust documentation.
fn check_once() {
    let handles: Vec<_> = (0..10)
        .map(|_| {
            thread::spawn(|| {
                thread::yield_now();
                let val = get_cached_val();
                assert_eq!(val, 5040);
            })
        })
        .collect();
    for handle in handles {
        handle.join().unwrap();
    }
}

fn park_timeout() {
    let start = Instant::now();

    thread::park_timeout(Duration::from_millis(200));
    // Normally, waiting in park/park_timeout may spuriously wake up early, but we
    // know Miri's timed synchronization primitives do not do that.

    assert!((200..1000).contains(&start.elapsed().as_millis()));
}

fn park_unpark() {
    let t1 = thread::current();
    let t2 = thread::spawn(move || {
        thread::park();
        thread::sleep(Duration::from_millis(200));
        t1.unpark();
    });

    let start = Instant::now();

    t2.thread().unpark();
    thread::park();
    // Normally, waiting in park/park_timeout may spuriously wake up early, but we
    // know Miri's timed synchronization primitives do not do that.

    assert!((200..1000).contains(&start.elapsed().as_millis()));

    t2.join().unwrap();
}

fn main() {
    check_barriers();
    check_conditional_variables_notify_one();
    check_conditional_variables_timed_wait_timeout();
    check_conditional_variables_timed_wait_notimeout();
    check_mutex();
    check_rwlock_write();
    check_rwlock_read_no_deadlock();
    check_once();
    park_timeout();
    park_unpark();
}
<|endoftext|>


<commit_before>#![no_std]
#![feature(alloc_error_handler)]

extern crate alloc;

use core::alloc::Layout;
use core::cell::Cell;
use core::convert::TryInto;
use core::fmt::{self, Debug, Error, Formatter};
use core::time::Duration;
use ctap2_authenticator::credentials::{RpCredential, UserCredential};
use ctap2_authenticator::usbhid::{
    CtapHidCapabilities, CtapHidPlatform, KeepaliveResponse, TransactionProcessor,
};
use ctap2_authenticator::Authenticator;
use ctap2_authenticator::{
    AuthenticatorPlatform, CredentialDescriptorList, CtapOptions, PublicKey, Signature,
};
use generic_array::GenericArray;
use libtock::ctap::{CtapRecvBuffer, CtapSendBuffer};
use libtock::hmac::{HmacDataBuffer, HmacDestBuffer, HmacDriverFactory, HmacKeyBuffer};
use libtock::println;
use libtock::result::TockResult;
use libtock::syscalls;
use p256::ecdsa::{signature::Signer, SigningKey};
use p256::elliptic_curve::ff::PrimeField;
use p256::{Scalar, SecretKey};
use subtle::{Choice, ConditionallySelectable};

#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub struct PrivateKey(Scalar);

impl ConditionallySelectable for PrivateKey {
    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
        Self(ConditionallySelectable::conditional_select(
            &a.0, &b.0, choice,
        ))
    }
}

impl Default for PrivateKey {
    fn default() -> Self {
        Self(Scalar::default())
    }
}

impl PrivateKey {
    pub fn from_bytes(bytes: &[u8; 32]) -> Option<Self> {
        Scalar::from_repr(GenericArray::clone_from_slice(bytes)).map(|s| Self(s))
    }
}

/// This is the provided implementation of `CtapHidPlatform` to be used in the `UsbContext`
pub(crate) struct UsbKeyHidPlatform {}

impl UsbKeyHidPlatform {
    pub fn new() -> Self {
        Self {}
    }
}

impl CtapHidPlatform for UsbKeyHidPlatform {
    // Should be kept in sync with the Crates version if possible
    const MAJOR_VERSION: u8 = 0;
    const MINOR_VERSION: u8 = 0;
    const BUILD_VERSION: u8 = 0;
    const CAPABILITIES: CtapHidCapabilities = CtapHidCapabilities {
        wink: true,
        cbor: true,
        msg: false,
    };

    fn wink(&mut self) {}

    fn cancel(&mut self) {
        println!("cancel");
        unimplemented!()
    }

    fn keepalive_needed(&mut self) -> KeepaliveResponse {
        println!("keepalive_needed");
        unimplemented!()
    }

    fn start_timer(&mut self) {
        println!("start_timer");
    }

    fn has_timed_out(&mut self) -> bool {
        println!("has_timed_out");
        false
    }
}

#[derive(Clone, Copy)]
pub struct HmacKeyCredential(pub(crate) [u8; 40]);

impl HmacKeyCredential {
    fn new(mac: &[u8; 32], nonce: [u8; 8]) -> Self {
        let mut new = [0; 40];
        new[..32].copy_from_slice(&mac[..]);
        new[32..].copy_from_slice(&nonce[..]);
        Self(new)
    }

    fn get_mac(&self) -> [u8; 32] {
        let mut mac = [0; 32];
        mac.copy_from_slice(&self.0[..32]);
        mac
    }
}

impl AsRef<[u8]> for HmacKeyCredential {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl Debug for HmacKeyCredential {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        write!(
            f,
            "HmacKeyCredential {{ mac: {:?}, nonce: {:?} }}",
            &self.0[..32],
            &self.0[32..]
        )
    }
}

impl PartialEq for HmacKeyCredential {
    fn eq(&self, other: &Self) -> bool {
        self.0[..] == other.0[..]
    }
}

impl Eq for HmacKeyCredential {}

const ITERATOR_MAX_LENGTH: usize = 8;

pub struct HmacCredentialQueue {
    index: usize,
    length: usize,
    list: [Option<(HmacKeyCredential, u32)>; ITERATOR_MAX_LENGTH],
}

impl HmacCredentialQueue {
    pub(crate) fn new() -> Self {
        Self {
            index: 0,
            length: 0,
            list: [None; ITERATOR_MAX_LENGTH],
        }
    }

    pub(crate) fn push(&mut self, credential: HmacKeyCredential, counter: u32) -> Result<(), ()> {
        if self.length < ITERATOR_MAX_LENGTH {
            self.list[self.length] = Some((credential, counter));
            self.length += 1;
            Ok(())
        } else {
            Err(())
        }
    }

    pub(crate) fn len(&self) -> usize {
        self.length
    }
}

impl Iterator for HmacCredentialQueue {
    type Item = (HmacKeyCredential, u32);

    fn next(&mut self) -> Option<Self::Item> {
        if self.list[self.index].is_some() {
            self.index += 1;
            self.list[self.index - 1]
        } else {
            None
        }
    }
}

pub(crate) struct CtapPlatform {
    hmac: HmacDriverFactory,
}

impl CtapPlatform {
    fn new(hmac: HmacDriverFactory) -> Self {
        Self { hmac }
    }
}

impl fmt::Debug for CtapPlatform {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        Ok(())
    }
}

impl CtapPlatform {
    fn generate_key_seed(
        &mut self,
        rp_id: &str,
        nonce: &[u8; 8],
    ) -> [u8; libtock::hmac::DEST_BUFFER_SIZE] {
        let hmac_driver = self.hmac.init_driver().unwrap();

        let mut key_buffer = HmacKeyBuffer::default();
        for (i, d) in rp_id.as_bytes().iter().enumerate() {
            key_buffer[i] = *d;
        }
        let _key_buffer = hmac_driver.init_key_buffer(&mut key_buffer).unwrap();

        let mut data_buffer = HmacDataBuffer::default();
        for (i, d) in nonce.iter().enumerate() {
            data_buffer[i] = *d;
        }
        let _data_buffer = hmac_driver.init_data_buffer(&mut data_buffer).unwrap();

        let mut seed_buffer = HmacDestBuffer::default();
        let seed_buffer = hmac_driver.init_dest_buffer(&mut seed_buffer).unwrap();

        let mut callback = |_result, _digest| {};

        let _subscription = hmac_driver.subscribe(&mut callback).unwrap();

        hmac_driver.run().unwrap();

        // Yield waiting for the HMAC callback
        unsafe {
            syscalls::raw::yieldk();
        }

        let mut temp_buffer = [0; libtock::hmac::DEST_BUFFER_SIZE];
        seed_buffer.read_bytes(&mut temp_buffer[..]);
        temp_buffer
    }

    /// Generates a Credential from a CredentialId and checks that the Id is valid
    fn checked_generate_hmac_cred(
        &mut self,
        rp_id: &str,
        credential_id: &[u8],
    ) -> Option<HmacKeyCredential> {
        if credential_id.len() != 40 {
            return None;
        }

        let received_mac = &credential_id[..32];

        let seed_buffer = self.generate_key_seed(rp_id, &credential_id[32..40].try_into().unwrap());

        let credential =
            HmacKeyCredential::new(&seed_buffer, credential_id[32..].try_into().unwrap());

        let generated_mac = &credential.0[..32];

        if generated_mac == received_mac {
            Some(credential)
        } else {
            None
        }
    }
}

impl AuthenticatorPlatform for CtapPlatform {
    const AAGUID: [u8; 16] = [
        0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc,
        0x7d,
    ];
    const CERTIFICATE: Option<&'static [u8]> = None;
    const MAX_MSG_LENGTH: u16 = 7609;
    const SUPPORTED_OPTIONS: CtapOptions = CtapOptions {
        plat: None,
        rk: Some(true),
        client_pin: None,
        up: Some(true),
        uv: Some(false),
    };
    type CredentialId = HmacKeyCredential;
    type PublicKeyBuffer = [u8; 32];
    type SignatureBuffer = [u8; 32];
    type CredentialIterator = HmacCredentialQueue;

    fn reset(&mut self) -> Result<(), ()> {
        unimplemented!()
    }

    fn check_exclude_list(&mut self, _rp_id: &str, _list: CredentialDescriptorList) -> bool {
        unimplemented!()
    }

    fn locate_credentials(
        &mut self,
        rp_id: &str,
        list: Option<CredentialDescriptorList>,
    ) -> (u16, Self::CredentialIterator) {
        match list {
            None => (0, HmacCredentialQueue::new()),
            Some(list) => {
                let mut result = HmacCredentialQueue::new();

                for descriptor in list {
                    match self.checked_generate_hmac_cred(rp_id, descriptor.get_id()) {
                        None => (),
                        Some(credential) => {
                            match result.push(credential, 0) {
                                Ok(()) => (),
                                // NOTE: We just truncate if we don't have enough space to store all
                                // fitting credentials
                                Err(()) => break,
                            }
                        }
                    }
                }

                (result.len() as u16, result)
            }
        }
    }

    fn create_credential(
        &mut self,
        rp: RpCredential<&[u8], &str>,
        _user: UserCredential<&[u8], &str>,
    ) -> (Self::CredentialId, PublicKey<Self::PublicKeyBuffer>, u32) {
        println!("create_credential");
        let rp_id = core::str::from_utf8(rp.rp_id()).unwrap();
        // This nonce is static!!!!
        // This is really bad cryptowise, let's print a warning
        // TODO: Convert this to generate a nonce from Tock's TRNG
        println!("WARNING!!! The nonce is static, this key is insecure");
        println!("WARNING!!! Do not use this anywhere important");
        let nonce = [0; 8];

        let seed_buffer = self.generate_key_seed(rp_id, &nonce);

        let credential = HmacKeyCredential::new(&seed_buffer, nonce.try_into().unwrap());

        let secret_key = SecretKey::from_bytes(&seed_buffer[0..32]).unwrap();

        let pub_key = p256::EncodedPoint::from_secret_key(&secret_key, false);

        let x: [u8; 32] = pub_key.as_bytes()[1..33].try_into().unwrap();
        let y: [u8; 32] = pub_key.as_bytes()[33..].try_into().unwrap();

        let ret_key = PublicKey::nistp256(x, y);

        (credential, ret_key, 0)
    }

    fn attest(
        &mut self,
        id: &Self::CredentialId,
        data: &[u8],
    ) -> Option<Signature<Self::SignatureBuffer>> {
        // TODO: Should attest be different then sign?
        let attest = {
            let secret_key = SecretKey::from_bytes(id.get_mac()).unwrap();

            let signer = SigningKey::from(&secret_key);

            let sig = signer.sign(data);

            let mut r: [u8; 32] = [0; 32];
            r.clone_from_slice(&sig.r().as_ref().to_bytes());
            let mut s: [u8; 32] = [0; 32];
            s.clone_from_slice(&sig.s().as_ref().to_bytes());

            Signature::nistp256(r, s)
        };

        Some(attest)
    }

    fn sign(
        &mut self,
        id: &Self::CredentialId,
        data: &[u8],
    ) -> Option<Signature<Self::SignatureBuffer>> {
        let attest = {
            let secret_key = SecretKey::from_bytes(id.get_mac()).unwrap();
            let signer = SigningKey::from(&secret_key);
            let sig = signer.sign(data);

            let mut r: [u8; 32] = [0; 32];
            r.clone_from_slice(&sig.r().as_ref().to_bytes());
            let mut s: [u8; 32] = [0; 32];
            s.clone_from_slice(&sig.s().as_ref().to_bytes());

            Signature::nistp256(r, s)
        };

        Some(attest)
    }

    fn start_timeout(&mut self) {
        unimplemented!()
    }

    fn has_timed_out(&mut self, _time: Duration) -> bool {
        unimplemented!()
    }
}

#[alloc_error_handler]
unsafe fn alloc_error_handler(_: Layout) -> ! {
    println!("alloc_error_handler called");
    loop {
        syscalls::raw::yieldk();
    }
}

#[libtock::main]
async fn main() -> TockResult<()> {
    let mut tp: Cell<TransactionProcessor<&'static mut [u8], UsbKeyHidPlatform>>;
    let mut temp_buffer = [0; libtock::ctap::RECV_BUFFER_SIZE];
    let mut drivers = libtock::retrieve_drivers()?;
    drivers.console.create_console();

    println!("Starting CTAP feature example");

    let ctap_driver = drivers.ctap.init_driver()?;

    let mut recv_buffer = CtapRecvBuffer::default();
    let recv_buffer = ctap_driver.init_recv_buffer(&mut recv_buffer)?;

    let mut send_buffer = CtapSendBuffer::default();
    let mut send_buffer = ctap_driver.init_send_buffer(&mut send_buffer)?;

    static mut BUFFER: &'static mut [u8] = &mut [0; 2048];
    unsafe {
        tp = Cell::new(TransactionProcessor::new(BUFFER, UsbKeyHidPlatform::new()));
    }

    let mut authenticator = Authenticator::create(CtapPlatform::new(drivers.hmac));

    let mut callback = |sent, _| {
        let mut looping = true;
        recv_buffer.read_bytes(&mut temp_buffer[..]);

        let temp_tp = tp.get_mut();
        let mut poll_msg = if sent == 0 { Some(&temp_buffer) } else { None };

        while looping {
            looping = false;
            let (maybe_request, maybe_data) = temp_tp.poll(poll_msg);

            if let Some(request) = maybe_request {
                let response = authenticator.process(request);
                temp_tp.response(response);
                looping = true;
            }

            if let Some(mut data) = maybe_data {
                send_buffer.write_bytes(&mut data[0..]);
                let _ = ctap_driver.send_data();
            }

            poll_msg = None;
        }
        let _ = ctap_driver.allow_receive();
    };

    let _subscription = ctap_driver.subscribe(&mut callback)?;
    ctap_driver.allow_receive()?;

    loop {
        unsafe { syscalls::raw::yieldk() };
    }
}
<commit_msg>examples-features/ctap: Don't call unwarp() in callbacks<commit_after>#![no_std]
#![feature(alloc_error_handler)]

extern crate alloc;

use core::alloc::Layout;
use core::cell::Cell;
use core::convert::TryInto;
use core::fmt::{self, Debug, Error, Formatter};
use core::time::Duration;
use ctap2_authenticator::credentials::{RpCredential, UserCredential};
use ctap2_authenticator::usbhid::{
    CtapHidCapabilities, CtapHidPlatform, KeepaliveResponse, TransactionProcessor,
};
use ctap2_authenticator::Authenticator;
use ctap2_authenticator::{
    AuthenticatorPlatform, CredentialDescriptorList, CtapOptions, PublicKey, Signature,
};
use generic_array::GenericArray;
use libtock::ctap::{CtapRecvBuffer, CtapSendBuffer};
use libtock::hmac::{HmacDataBuffer, HmacDestBuffer, HmacDriverFactory, HmacKeyBuffer};
use libtock::println;
use libtock::result::TockResult;
use libtock::syscalls;
use p256::ecdsa::{signature::Signer, SigningKey};
use p256::elliptic_curve::ff::PrimeField;
use p256::{Scalar, SecretKey};
use subtle::{Choice, ConditionallySelectable};

#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub struct PrivateKey(Scalar);

impl ConditionallySelectable for PrivateKey {
    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
        Self(ConditionallySelectable::conditional_select(
            &a.0, &b.0, choice,
        ))
    }
}

impl Default for PrivateKey {
    fn default() -> Self {
        Self(Scalar::default())
    }
}

impl PrivateKey {
    pub fn from_bytes(bytes: &[u8; 32]) -> Option<Self> {
        Scalar::from_repr(GenericArray::clone_from_slice(bytes)).map(|s| Self(s))
    }
}

/// This is the provided implementation of `CtapHidPlatform` to be used in the `UsbContext`
pub(crate) struct UsbKeyHidPlatform {}

impl UsbKeyHidPlatform {
    pub fn new() -> Self {
        Self {}
    }
}

impl CtapHidPlatform for UsbKeyHidPlatform {
    // Should be kept in sync with the Crates version if possible
    const MAJOR_VERSION: u8 = 0;
    const MINOR_VERSION: u8 = 0;
    const BUILD_VERSION: u8 = 0;
    const CAPABILITIES: CtapHidCapabilities = CtapHidCapabilities {
        wink: true,
        cbor: true,
        msg: false,
    };

    fn wink(&mut self) {}

    fn cancel(&mut self) {
        println!("cancel");
        unimplemented!()
    }

    fn keepalive_needed(&mut self) -> KeepaliveResponse {
        println!("keepalive_needed");
        unimplemented!()
    }

    fn start_timer(&mut self) {
        println!("start_timer");
    }

    fn has_timed_out(&mut self) -> bool {
        println!("has_timed_out");
        false
    }
}

#[derive(Clone, Copy)]
pub struct HmacKeyCredential(pub(crate) [u8; 40]);

impl HmacKeyCredential {
    fn new(mac: &[u8; 32], nonce: [u8; 8]) -> Self {
        let mut new = [0; 40];
        new[..32].copy_from_slice(&mac[..]);
        new[32..].copy_from_slice(&nonce[..]);
        Self(new)
    }

    fn get_mac(&self) -> [u8; 32] {
        let mut mac = [0; 32];
        mac.copy_from_slice(&self.0[..32]);
        mac
    }
}

impl AsRef<[u8]> for HmacKeyCredential {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl Debug for HmacKeyCredential {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        write!(
            f,
            "HmacKeyCredential {{ mac: {:?}, nonce: {:?} }}",
            &self.0[..32],
            &self.0[32..]
        )
    }
}

impl PartialEq for HmacKeyCredential {
    fn eq(&self, other: &Self) -> bool {
        self.0[..] == other.0[..]
    }
}

impl Eq for HmacKeyCredential {}

const ITERATOR_MAX_LENGTH: usize = 8;

pub struct HmacCredentialQueue {
    index: usize,
    length: usize,
    list: [Option<(HmacKeyCredential, u32)>; ITERATOR_MAX_LENGTH],
}

impl HmacCredentialQueue {
    pub(crate) fn new() -> Self {
        Self {
            index: 0,
            length: 0,
            list: [None; ITERATOR_MAX_LENGTH],
        }
    }

    pub(crate) fn push(&mut self, credential: HmacKeyCredential, counter: u32) -> Result<(), ()> {
        if self.length < ITERATOR_MAX_LENGTH {
            self.list[self.length] = Some((credential, counter));
            self.length += 1;
            Ok(())
        } else {
            Err(())
        }
    }

    pub(crate) fn len(&self) -> usize {
        self.length
    }
}

impl Iterator for HmacCredentialQueue {
    type Item = (HmacKeyCredential, u32);

    fn next(&mut self) -> Option<Self::Item> {
        if self.list[self.index].is_some() {
            self.index += 1;
            self.list[self.index - 1]
        } else {
            None
        }
    }
}

pub(crate) struct CtapPlatform {
    hmac: HmacDriverFactory,
}

impl CtapPlatform {
    fn new(hmac: HmacDriverFactory) -> Self {
        Self { hmac }
    }
}

impl fmt::Debug for CtapPlatform {
    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
        Ok(())
    }
}

impl CtapPlatform {
    fn generate_key_seed(
        &mut self,
        rp_id: &str,
        nonce: &[u8; 8],
    ) -> [u8; libtock::hmac::DEST_BUFFER_SIZE] {
        let hmac_driver;
        match self.hmac.init_driver() {
            Err(_) => {
                panic!("Hmac init error");
            }
            Ok(driver) => {
                hmac_driver = driver;
            }
        }

        let mut key_buffer = HmacKeyBuffer::default();
        for (i, d) in rp_id.as_bytes().iter().enumerate() {
            key_buffer[i] = *d;
        }
        // We need to make sure this isn't dropped straight away
        let key_buffer_ret = hmac_driver.init_key_buffer(&mut key_buffer);
        if key_buffer_ret.is_err() {
            panic!("Hmac key buffer init error");
        }

        let mut data_buffer = HmacDataBuffer::default();
        for (i, d) in nonce.iter().enumerate() {
            data_buffer[i] = *d;
        }
        // We need to make sure this isn't dropped straight away
        let data_buffer_ret = hmac_driver.init_data_buffer(&mut data_buffer);
        if data_buffer_ret.is_err() {
            panic!("Hmac data buffer init error");
        }

        let mut dest_buffer = HmacDestBuffer::default();
        let seed_buffer;
        match hmac_driver.init_dest_buffer(&mut dest_buffer) {
            Err(_) => {
                panic!("Hmac subscribe error");
            }
            Ok(buffer) => {
                seed_buffer = buffer;
            }
        }

        let mut callback = |_result, _digest| {};

        // We need to make sure this isn't dropped straight away
        let subscribe = hmac_driver.subscribe(&mut callback);
        if subscribe.is_err() {
            panic!("Hmac subscribe error");
        }

        if hmac_driver.run().is_err() {
            panic!("Hmac run error");
        }

        // Yield waiting for the HMAC callback
        unsafe {
            syscalls::raw::yieldk();
        }

        let mut temp_buffer = [0; libtock::hmac::DEST_BUFFER_SIZE];
        seed_buffer.read_bytes(&mut temp_buffer[..]);
        temp_buffer
    }

    /// Generates a Credential from a CredentialId and checks that the Id is valid
    fn checked_generate_hmac_cred(
        &mut self,
        rp_id: &str,
        credential_id: &[u8],
    ) -> Option<HmacKeyCredential> {
        if credential_id.len() != 40 {
            return None;
        }

        let received_mac = &credential_id[..32];

        let seed_buffer = self.generate_key_seed(rp_id, &credential_id[32..40].try_into().unwrap());

        let credential =
            HmacKeyCredential::new(&seed_buffer, credential_id[32..].try_into().unwrap());

        let generated_mac = &credential.0[..32];

        if generated_mac == received_mac {
            Some(credential)
        } else {
            None
        }
    }
}

impl AuthenticatorPlatform for CtapPlatform {
    const AAGUID: [u8; 16] = [
        0xf8, 0xa0, 0x11, 0xf3, 0x8c, 0x0a, 0x4d, 0x15, 0x80, 0x06, 0x17, 0x11, 0x1f, 0x9e, 0xdc,
        0x7d,
    ];
    const CERTIFICATE: Option<&'static [u8]> = None;
    const MAX_MSG_LENGTH: u16 = 7609;
    const SUPPORTED_OPTIONS: CtapOptions = CtapOptions {
        plat: None,
        rk: Some(true),
        client_pin: None,
        up: Some(true),
        uv: Some(false),
    };
    type CredentialId = HmacKeyCredential;
    type PublicKeyBuffer = [u8; 32];
    type SignatureBuffer = [u8; 32];
    type CredentialIterator = HmacCredentialQueue;

    fn reset(&mut self) -> Result<(), ()> {
        unimplemented!()
    }

    fn check_exclude_list(&mut self, _rp_id: &str, _list: CredentialDescriptorList) -> bool {
        unimplemented!()
    }

    fn locate_credentials(
        &mut self,
        rp_id: &str,
        list: Option<CredentialDescriptorList>,
    ) -> (u16, Self::CredentialIterator) {
        match list {
            None => (0, HmacCredentialQueue::new()),
            Some(list) => {
                let mut result = HmacCredentialQueue::new();

                for descriptor in list {
                    match self.checked_generate_hmac_cred(rp_id, descriptor.get_id()) {
                        None => (),
                        Some(credential) => {
                            match result.push(credential, 0) {
                                Ok(()) => (),
                                // NOTE: We just truncate if we don't have enough space to store all
                                // fitting credentials
                                Err(()) => break,
                            }
                        }
                    }
                }

                (result.len() as u16, result)
            }
        }
    }

    fn create_credential(
        &mut self,
        rp: RpCredential<&[u8], &str>,
        _user: UserCredential<&[u8], &str>,
    ) -> (Self::CredentialId, PublicKey<Self::PublicKeyBuffer>, u32) {
        println!("create_credential");
        let rp_id = core::str::from_utf8(rp.rp_id()).unwrap();
        // This nonce is static!!!!
        // This is really bad cryptowise, let's print a warning
        // TODO: Convert this to generate a nonce from Tock's TRNG
        println!("WARNING!!! The nonce is static, this key is insecure");
        println!("WARNING!!! Do not use this anywhere important");
        let nonce = [0; 8];

        let seed_buffer = self.generate_key_seed(rp_id, &nonce);

        let credential = HmacKeyCredential::new(&seed_buffer, nonce.try_into().unwrap());

        let secret_key = SecretKey::from_bytes(&seed_buffer[0..32]).unwrap();

        let pub_key = p256::EncodedPoint::from_secret_key(&secret_key, false);

        let x: [u8; 32] = pub_key.as_bytes()[1..33].try_into().unwrap();
        let y: [u8; 32] = pub_key.as_bytes()[33..].try_into().unwrap();

        let ret_key = PublicKey::nistp256(x, y);

        (credential, ret_key, 0)
    }

    fn attest(
        &mut self,
        id: &Self::CredentialId,
        data: &[u8],
    ) -> Option<Signature<Self::SignatureBuffer>> {
        // TODO: Should attest be different then sign?
        let attest = {
            let secret_key = SecretKey::from_bytes(id.get_mac()).unwrap();

            let signer = SigningKey::from(&secret_key);

            let sig = signer.sign(data);

            let mut r: [u8; 32] = [0; 32];
            r.clone_from_slice(&sig.r().as_ref().to_bytes());
            let mut s: [u8; 32] = [0; 32];
            s.clone_from_slice(&sig.s().as_ref().to_bytes());

            Signature::nistp256(r, s)
        };

        Some(attest)
    }

    fn sign(
        &mut self,
        id: &Self::CredentialId,
        data: &[u8],
    ) -> Option<Signature<Self::SignatureBuffer>> {
        let attest = {
            let secret_key = SecretKey::from_bytes(id.get_mac()).unwrap();
            let signer = SigningKey::from(&secret_key);
            let sig = signer.sign(data);

            let mut r: [u8; 32] = [0; 32];
            r.clone_from_slice(&sig.r().as_ref().to_bytes());
            let mut s: [u8; 32] = [0; 32];
            s.clone_from_slice(&sig.s().as_ref().to_bytes());

            Signature::nistp256(r, s)
        };

        Some(attest)
    }

    fn start_timeout(&mut self) {
        unimplemented!()
    }

    fn has_timed_out(&mut self, _time: Duration) -> bool {
        unimplemented!()
    }
}

#[alloc_error_handler]
unsafe fn alloc_error_handler(_: Layout) -> ! {
    println!("alloc_error_handler called");
    loop {
        syscalls::raw::yieldk();
    }
}

#[libtock::main]
async fn main() -> TockResult<()> {
    let mut tp: Cell<TransactionProcessor<&'static mut [u8], UsbKeyHidPlatform>>;
    let mut temp_buffer = [0; libtock::ctap::RECV_BUFFER_SIZE];
    let mut drivers = libtock::retrieve_drivers()?;
    drivers.console.create_console();

    println!("Starting CTAP feature example");

    let ctap_driver = drivers.ctap.init_driver()?;

    let mut recv_buffer = CtapRecvBuffer::default();
    let recv_buffer = ctap_driver.init_recv_buffer(&mut recv_buffer)?;

    let mut send_buffer = CtapSendBuffer::default();
    let mut send_buffer = ctap_driver.init_send_buffer(&mut send_buffer)?;

    static mut BUFFER: &'static mut [u8] = &mut [0; 2048];
    unsafe {
        tp = Cell::new(TransactionProcessor::new(BUFFER, UsbKeyHidPlatform::new()));
    }

    let mut authenticator = Authenticator::create(CtapPlatform::new(drivers.hmac));

    let mut callback = |sent, _| {
        let mut looping = true;
        recv_buffer.read_bytes(&mut temp_buffer[..]);

        let temp_tp = tp.get_mut();
        let mut poll_msg = if sent == 0 { Some(&temp_buffer) } else { None };

        while looping {
            looping = false;
            let (maybe_request, maybe_data) = temp_tp.poll(poll_msg);

            if let Some(request) = maybe_request {
                let response = authenticator.process(request);
                temp_tp.response(response);
                looping = true;
            }

            if let Some(mut data) = maybe_data {
                send_buffer.write_bytes(&mut data[0..]);
                let _ = ctap_driver.send_data();
            }

            poll_msg = None;
        }
        let _ = ctap_driver.allow_receive();
    };

    let _subscription = ctap_driver.subscribe(&mut callback)?;
    ctap_driver.allow_receive()?;

    loop {
        unsafe { syscalls::raw::yieldk() };
    }
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::accept_type::AcceptType;
use crate::context::Context;
use crate::failpoint::fail_point_poem;
use crate::response::{
    build_not_found, BadRequestError, BasicErrorWith404, BasicResponse, BasicResponseStatus,
    BasicResultWith404, InternalError, NotFoundError,
};
use crate::ApiTags;
use anyhow::Context as AnyhowContext;
use aptos_api_types::{
    Address, AsConverter, IdentifierWrapper, MoveModuleBytecode, MoveStructTag, MoveValue,
    TableItemRequest, TransactionId, U128, U64,
};
use aptos_api_types::{LedgerInfo, MoveResource};
use aptos_state_view::StateView;
use aptos_types::access_path::AccessPath;
use aptos_types::state_store::state_key::StateKey;
use aptos_types::state_store::table::TableHandle;
use aptos_vm::data_cache::AsMoveResolver;
use move_deps::move_core_types::language_storage::{ModuleId, ResourceKey, StructTag};
use poem_openapi::param::Query;
use poem_openapi::payload::Json;
use poem_openapi::{param::Path, OpenApi};
use std::convert::TryInto;
use std::sync::Arc;
use storage_interface::state_view::DbStateView;

pub struct StateApi {
    pub context: Arc<Context>,
}

#[OpenApi]
impl StateApi {
    /// Get specific account resource
    ///
    /// This endpoint returns the resource of a specific type residing at a given
    /// account at a specified ledger version (AKA transaction version). If the
    /// ledger version is not specified in the request, the latest ledger version
    /// is used.
    ///
    /// The Aptos nodes prune account state history, via a configurable time window (link).
    /// If the requested data has been pruned, the server responds with a 404.
    #[oai(
        path = "/accounts/:address/resource/:resource_type",
        method = "get",
        operation_id = "get_account_resource",
        tag = "ApiTags::Accounts"
    )]
    async fn get_account_resource(
        &self,
        accept_type: AcceptType,
        address: Path<Address>,
        resource_type: Path<MoveStructTag>,
        ledger_version: Query<Option<U64>>,
    ) -> BasicResultWith404<MoveResource> {
        fail_point_poem("endpoint_get_account_resource")?;
        self.resource(&accept_type, address.0, resource_type.0, ledger_version.0)
    }

    /// Get specific account module
    ///
    /// This endpoint returns the module with a specific name residing at a given
    /// account at a specified ledger version (AKA transaction version). If the
    /// ledger version is not specified in the request, the latest ledger version
    /// is used.
    ///
    /// The Aptos nodes prune account state history, via a configurable time window (link).
    /// If the requested data has been pruned, the server responds with a 404.
    #[oai(
        path = "/accounts/:address/module/:module_name",
        method = "get",
        operation_id = "get_account_module",
        tag = "ApiTags::Accounts"
    )]
    async fn get_account_module(
        &self,
        accept_type: AcceptType,
        address: Path<Address>,
        module_name: Path<IdentifierWrapper>,
        ledger_version: Query<Option<U64>>,
    ) -> BasicResultWith404<MoveModuleBytecode> {
        fail_point_poem("endpoint_get_account_module")?;
        self.module(&accept_type, address.0, module_name.0, ledger_version.0)
    }

    /// Get table item
    ///
    /// Get a table item from the table identified by {table_handle} in the
    /// path and the "key" (TableItemRequest) provided in the request body.
    ///
    /// This is a POST endpoint because the "key" for requesting a specific
    /// table item (TableItemRequest) could be quite complex, as each of its
    /// fields could themselves be composed of other structs. This makes it
    /// impractical to express using query params, meaning GET isn't an option.
    #[oai(
        path = "/tables/:table_handle/item",
        method = "post",
        operation_id = "get_table_item",
        tag = "ApiTags::Tables"
    )]
    async fn get_table_item(
        &self,
        accept_type: AcceptType,
        table_handle: Path<U128>,
        table_item_request: Json<TableItemRequest>,
        ledger_version: Query<Option<U64>>,
    ) -> BasicResultWith404<MoveValue> {
        fail_point_poem("endpoint_get_table_item")?;
        self.table_item(
            &accept_type,
            table_handle.0,
            table_item_request.0,
            ledger_version.0,
        )
    }
}

impl StateApi {
    fn preprocess_request<E: NotFoundError + InternalError>(
        &self,
        requested_ledger_version: Option<U64>,
    ) -> Result<(LedgerInfo, u64, DbStateView), E> {
        let latest_ledger_info = self.context.get_latest_ledger_info()?;
        let ledger_version: u64 = requested_ledger_version
            .map(|v| v.0)
            .unwrap_or_else(|| latest_ledger_info.version());

        if ledger_version > latest_ledger_info.version() {
            return Err(build_not_found(
                "ledger",
                TransactionId::Version(U64::from(ledger_version)),
                latest_ledger_info.version(),
            ));
        }

        let state_view = self.context.state_view_at_version(ledger_version)
            .context(format!("Failed to get state view at version {} even after confirming the ledger has advanced past that version to {}", ledger_version, latest_ledger_info.version()))
            .map_err(E::internal)?;

        Ok((latest_ledger_info, ledger_version, state_view))
    }

    fn resource(
        &self,
        accept_type: &AcceptType,
        address: Address,
        resource_type: MoveStructTag,
        ledger_version: Option<U64>,
    ) -> BasicResultWith404<MoveResource> {
        let resource_type: StructTag = resource_type
            .try_into()
            .context("Failed to parse given resource type")
            .map_err(BasicErrorWith404::bad_request)?;
        let resource_key = ResourceKey::new(address.into(), resource_type.clone());
        let access_path = AccessPath::resource_access_path(resource_key.clone());
        let state_key = StateKey::AccessPath(access_path);
        let (ledger_info, ledger_version, state_view) = self.preprocess_request(ledger_version)?;
        let bytes = state_view
            .get_state_value(&state_key)
            .context(format!("Failed to query DB to check for {:?}", state_key))
            .map_err(BasicErrorWith404::internal)?
            .ok_or_else(|| build_not_found("Resource", resource_key, ledger_version))?;

        let resource = state_view
            .as_move_resolver()
            .as_converter(self.context.db.clone())
            .try_into_resource(&resource_type, &bytes)
            .context("Failed to deserialize resource data retrieved from DB")
            .map_err(BasicErrorWith404::internal)?;

        BasicResponse::try_from_rust_value((
            resource,
            &ledger_info,
            BasicResponseStatus::Ok,
            accept_type,
        ))
    }

    pub fn module(
        &self,
        accept_type: &AcceptType,
        address: Address,
        name: IdentifierWrapper,
        ledger_version: Option<U64>,
    ) -> BasicResultWith404<MoveModuleBytecode> {
        let module_id = ModuleId::new(address.into(), name.into());
        let access_path = AccessPath::code_access_path(module_id.clone());
        let state_key = StateKey::AccessPath(access_path);
        let (ledger_info, ledger_version, state_view) = self.preprocess_request(ledger_version)?;
        let bytes = state_view
            .get_state_value(&state_key)
            .context(format!("Failed to query DB to check for {:?}", state_key))
            .map_err(BasicErrorWith404::internal)?
            .ok_or_else(|| build_not_found("Module", module_id, ledger_version))?;

        let module = MoveModuleBytecode::new(bytes)
            .try_parse_abi()
            .context("Failed to parse move module ABI from bytes retrieved from storage")
            .map_err(BasicErrorWith404::internal)?;

        BasicResponse::try_from_rust_value((
            module,
            &ledger_info,
            BasicResponseStatus::Ok,
            accept_type,
        ))
    }

    pub fn table_item(
        &self,
        accept_type: &AcceptType,
        table_handle: U128,
        table_item_request: TableItemRequest,
        ledger_version: Option<U64>,
    ) -> BasicResultWith404<MoveValue> {
        let key_type = table_item_request
            .key_type
            .try_into()
            .context("Failed to parse key_type")
            .map_err(BasicErrorWith404::bad_request)?;
        let value_type = table_item_request
            .value_type
            .try_into()
            .context("Failed to parse value_type")
            .map_err(BasicErrorWith404::bad_request)?;
        let key = table_item_request.key;

        let (ledger_info, ledger_version, state_view) = self.preprocess_request(ledger_version)?;

        let resolver = state_view.as_move_resolver();
        let converter = resolver.as_converter(self.context.db.clone());

        let vm_key = converter
            .try_into_vm_value(&key_type, key.clone())
            .map_err(BasicErrorWith404::bad_request)?;
        let raw_key = vm_key
            .undecorate()
            .simple_serialize()
            .ok_or_else(|| BasicErrorWith404::internal_str("Failed to serialize table key"))?;

        let state_key = StateKey::table_item(TableHandle(table_handle.0), raw_key);
        let bytes = state_view
            .get_state_value(&state_key)
            .context(format!(
                "Failed when trying to retrieve table item from the DB with key: {}",
                key
            ))
            .map_err(BasicErrorWith404::internal)?
            .ok_or_else(|| build_not_found("table handle or item", key, ledger_version))?;

        let move_value = converter
            .try_into_move_value(&value_type, &bytes)
            .context("Failed to deserialize table item retrieved from DB")
            .map_err(BasicErrorWith404::internal)?;

        BasicResponse::try_from_rust_value((
            move_value,
            &ledger_info,
            BasicResponseStatus::Ok,
            accept_type,
        ))
    }
}
<commit_msg>[api] Disallow BCS on individual object queries<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::accept_type::AcceptType;
use crate::context::Context;
use crate::failpoint::fail_point_poem;
use crate::response::{
    build_not_found, BadRequestError, BasicErrorWith404, BasicResponse, BasicResponseStatus,
    BasicResultWith404, InternalError, NotFoundError,
};
use crate::ApiTags;
use anyhow::{anyhow, Context as AnyhowContext};
use aptos_api_types::{
    Address, AsConverter, IdentifierWrapper, MoveModuleBytecode, MoveStructTag, MoveValue,
    TableItemRequest, TransactionId, U128, U64,
};
use aptos_api_types::{LedgerInfo, MoveResource};
use aptos_state_view::StateView;
use aptos_types::access_path::AccessPath;
use aptos_types::state_store::state_key::StateKey;
use aptos_types::state_store::table::TableHandle;
use aptos_vm::data_cache::AsMoveResolver;
use move_deps::move_core_types::language_storage::{ModuleId, ResourceKey, StructTag};
use poem_openapi::param::Query;
use poem_openapi::payload::Json;
use poem_openapi::{param::Path, OpenApi};
use std::convert::TryInto;
use std::sync::Arc;
use storage_interface::state_view::DbStateView;

pub struct StateApi {
    pub context: Arc<Context>,
}

#[OpenApi]
impl StateApi {
    /// Get specific account resource
    ///
    /// This endpoint returns the resource of a specific type residing at a given
    /// account at a specified ledger version (AKA transaction version). If the
    /// ledger version is not specified in the request, the latest ledger version
    /// is used.
    ///
    /// The Aptos nodes prune account state history, via a configurable time window (link).
    /// If the requested data has been pruned, the server responds with a 404.
    #[oai(
        path = "/accounts/:address/resource/:resource_type",
        method = "get",
        operation_id = "get_account_resource",
        tag = "ApiTags::Accounts"
    )]
    async fn get_account_resource(
        &self,
        accept_type: AcceptType,
        address: Path<Address>,
        resource_type: Path<MoveStructTag>,
        ledger_version: Query<Option<U64>>,
    ) -> BasicResultWith404<MoveResource> {
        fail_point_poem("endpoint_get_account_resource")?;
        self.resource(&accept_type, address.0, resource_type.0, ledger_version.0)
    }

    /// Get specific account module
    ///
    /// This endpoint returns the module with a specific name residing at a given
    /// account at a specified ledger version (AKA transaction version). If the
    /// ledger version is not specified in the request, the latest ledger version
    /// is used.
    ///
    /// The Aptos nodes prune account state history, via a configurable time window (link).
    /// If the requested data has been pruned, the server responds with a 404.
    #[oai(
        path = "/accounts/:address/module/:module_name",
        method = "get",
        operation_id = "get_account_module",
        tag = "ApiTags::Accounts"
    )]
    async fn get_account_module(
        &self,
        accept_type: AcceptType,
        address: Path<Address>,
        module_name: Path<IdentifierWrapper>,
        ledger_version: Query<Option<U64>>,
    ) -> BasicResultWith404<MoveModuleBytecode> {
        fail_point_poem("endpoint_get_account_module")?;
        self.module(&accept_type, address.0, module_name.0, ledger_version.0)
    }

    /// Get table item
    ///
    /// Get a table item from the table identified by {table_handle} in the
    /// path and the "key" (TableItemRequest) provided in the request body.
    ///
    /// This is a POST endpoint because the "key" for requesting a specific
    /// table item (TableItemRequest) could be quite complex, as each of its
    /// fields could themselves be composed of other structs. This makes it
    /// impractical to express using query params, meaning GET isn't an option.
    #[oai(
        path = "/tables/:table_handle/item",
        method = "post",
        operation_id = "get_table_item",
        tag = "ApiTags::Tables"
    )]
    async fn get_table_item(
        &self,
        accept_type: AcceptType,
        table_handle: Path<U128>,
        table_item_request: Json<TableItemRequest>,
        ledger_version: Query<Option<U64>>,
    ) -> BasicResultWith404<MoveValue> {
        fail_point_poem("endpoint_get_table_item")?;
        self.table_item(
            &accept_type,
            table_handle.0,
            table_item_request.0,
            ledger_version.0,
        )
    }
}

impl StateApi {
    fn preprocess_request<E: NotFoundError + InternalError>(
        &self,
        requested_ledger_version: Option<U64>,
    ) -> Result<(LedgerInfo, u64, DbStateView), E> {
        let latest_ledger_info = self.context.get_latest_ledger_info()?;
        let ledger_version: u64 = requested_ledger_version
            .map(|v| v.0)
            .unwrap_or_else(|| latest_ledger_info.version());

        if ledger_version > latest_ledger_info.version() {
            return Err(build_not_found(
                "ledger",
                TransactionId::Version(U64::from(ledger_version)),
                latest_ledger_info.version(),
            ));
        }

        let state_view = self.context.state_view_at_version(ledger_version)
            .context(format!("Failed to get state view at version {} even after confirming the ledger has advanced past that version to {}", ledger_version, latest_ledger_info.version()))
            .map_err(E::internal)?;

        Ok((latest_ledger_info, ledger_version, state_view))
    }

    fn resource(
        &self,
        accept_type: &AcceptType,
        address: Address,
        resource_type: MoveStructTag,
        ledger_version: Option<U64>,
    ) -> BasicResultWith404<MoveResource> {
        // TODO: Determine what is needed to deserialize the storage type
        if accept_type == &AcceptType::Bcs {
            return Err(anyhow!("BCS is not supported for get resource"))
                .map_err(BasicErrorWith404::bad_request);
        }

        let resource_type: StructTag = resource_type
            .try_into()
            .context("Failed to parse given resource type")
            .map_err(BasicErrorWith404::bad_request)?;
        let resource_key = ResourceKey::new(address.into(), resource_type.clone());
        let access_path = AccessPath::resource_access_path(resource_key.clone());
        let state_key = StateKey::AccessPath(access_path);
        let (ledger_info, ledger_version, state_view) = self.preprocess_request(ledger_version)?;
        let bytes = state_view
            .get_state_value(&state_key)
            .context(format!("Failed to query DB to check for {:?}", state_key))
            .map_err(BasicErrorWith404::internal)?
            .ok_or_else(|| build_not_found("Resource", resource_key, ledger_version))?;

        let resource = state_view
            .as_move_resolver()
            .as_converter(self.context.db.clone())
            .try_into_resource(&resource_type, &bytes)
            .context("Failed to deserialize resource data retrieved from DB")
            .map_err(BasicErrorWith404::internal)?;

        BasicResponse::try_from_rust_value((
            resource,
            &ledger_info,
            BasicResponseStatus::Ok,
            accept_type,
        ))
    }

    pub fn module(
        &self,
        accept_type: &AcceptType,
        address: Address,
        name: IdentifierWrapper,
        ledger_version: Option<U64>,
    ) -> BasicResultWith404<MoveModuleBytecode> {
        // TODO: Determine what is needed to deserialize the storage type
        if accept_type == &AcceptType::Bcs {
            return Err(anyhow!("BCS is not supported for get module"))
                .map_err(BasicErrorWith404::bad_request);
        }
        let module_id = ModuleId::new(address.into(), name.into());
        let access_path = AccessPath::code_access_path(module_id.clone());
        let state_key = StateKey::AccessPath(access_path);
        let (ledger_info, ledger_version, state_view) = self.preprocess_request(ledger_version)?;
        let bytes = state_view
            .get_state_value(&state_key)
            .context(format!("Failed to query DB to check for {:?}", state_key))
            .map_err(BasicErrorWith404::internal)?
            .ok_or_else(|| build_not_found("Module", module_id, ledger_version))?;

        let module = MoveModuleBytecode::new(bytes)
            .try_parse_abi()
            .context("Failed to parse move module ABI from bytes retrieved from storage")
            .map_err(BasicErrorWith404::internal)?;

        BasicResponse::try_from_rust_value((
            module,
            &ledger_info,
            BasicResponseStatus::Ok,
            accept_type,
        ))
    }

    pub fn table_item(
        &self,
        accept_type: &AcceptType,
        table_handle: U128,
        table_item_request: TableItemRequest,
        ledger_version: Option<U64>,
    ) -> BasicResultWith404<MoveValue> {
        // TODO: Determine what is needed to deserialize the storage type
        if accept_type == &AcceptType::Bcs {
            return Err(anyhow!("BCS is not supported for get table item"))
                .map_err(BasicErrorWith404::bad_request);
        }
        let key_type = table_item_request
            .key_type
            .try_into()
            .context("Failed to parse key_type")
            .map_err(BasicErrorWith404::bad_request)?;
        let value_type = table_item_request
            .value_type
            .try_into()
            .context("Failed to parse value_type")
            .map_err(BasicErrorWith404::bad_request)?;
        let key = table_item_request.key;

        let (ledger_info, ledger_version, state_view) = self.preprocess_request(ledger_version)?;

        let resolver = state_view.as_move_resolver();
        let converter = resolver.as_converter(self.context.db.clone());

        let vm_key = converter
            .try_into_vm_value(&key_type, key.clone())
            .map_err(BasicErrorWith404::bad_request)?;
        let raw_key = vm_key
            .undecorate()
            .simple_serialize()
            .ok_or_else(|| BasicErrorWith404::internal_str("Failed to serialize table key"))?;

        let state_key = StateKey::table_item(TableHandle(table_handle.0), raw_key);
        let bytes = state_view
            .get_state_value(&state_key)
            .context(format!(
                "Failed when trying to retrieve table item from the DB with key: {}",
                key
            ))
            .map_err(BasicErrorWith404::internal)?
            .ok_or_else(|| build_not_found("table handle or item", key, ledger_version))?;

        let move_value = converter
            .try_into_move_value(&value_type, &bytes)
            .context("Failed to deserialize table item retrieved from DB")
            .map_err(BasicErrorWith404::internal)?;

        BasicResponse::try_from_rust_value((
            move_value,
            &ledger_info,
            BasicResponseStatus::Ok,
            accept_type,
        ))
    }
}
<|endoftext|>