Skip to content

Commit

Permalink
Merge master
Browse files Browse the repository at this point in the history
  • Loading branch information
ameba23 committed Nov 21, 2024
2 parents 778a644 + fd1047f commit f48c59d
Show file tree
Hide file tree
Showing 17 changed files with 168 additions and 36 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ runtime
### Added
- Protocol message versioning ([#1140](https://github.com/entropyxyz/entropy-core/pull/1140))
- CLI command to get oracle headings ([#1170](https://github.com/entropyxyz/entropy-core/pull/1170))
- Add TSS endpoint to get TDX quote ([#1173](https://github.com/entropyxyz/entropy-core/pull/1173))

### Changed
- Use correct key rotation endpoint in OCW ([#1104](https://github.com/entropyxyz/entropy-core/pull/1104))
Expand Down
24 changes: 13 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 9 additions & 16 deletions crates/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ use crate::{
},
client::entropy::staking_extension::events::{EndpointChanged, ThresholdAccountChanged},
substrate::{get_registered_details, submit_transaction_with_pair},
user::{get_all_signers_from_chain, get_validators_not_signer_for_relay, UserSignatureRequest},
user::{
self, get_all_signers_from_chain, get_validators_not_signer_for_relay, UserSignatureRequest,
},
Hasher,
};

Expand Down Expand Up @@ -427,6 +429,9 @@ async fn jumpstart_inner(
/// some point in the near future.
///
/// The returned `nonce` must be used when generating a `quote` for the chain.
///
/// This wraps [user::request_attestation] to convert the error to a [ClientError] consistant with
/// other functions in this module
#[tracing::instrument(
skip_all,
fields(
Expand All @@ -436,21 +441,9 @@ async fn jumpstart_inner(
pub async fn request_attestation(
api: &OnlineClient<EntropyConfig>,
rpc: &LegacyRpcMethods<EntropyConfig>,
attestee: sr25519::Pair,
) -> Result<Vec<u8>, ClientError> {
tracing::debug!("{} is requesting an attestation.", attestee.public());

let request_attestation = entropy::tx().attestation().request_attestation();

let result =
submit_transaction_with_pair(api, rpc, &attestee, &request_attestation, None).await?;
let result_event = result
.find_first::<entropy::attestation::events::AttestationIssued>()?
.ok_or(crate::errors::SubstrateError::NoEvent)?;

let nonce = result_event.0;

Ok(nonce)
attestee: &sr25519::Pair,
) -> Result<[u8; 32], ClientError> {
Ok(user::request_attestation(api, rpc, attestee).await?)
}

/// Get oracle data headings
Expand Down
13 changes: 13 additions & 0 deletions crates/client/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@ pub enum SubgroupGetError {
JoinError(#[from] tokio::task::JoinError),
}

/// An error when making an attestation request
#[derive(Debug, Error)]
pub enum AttestationRequestError {
#[error("Generic Substrate error: {0}")]
GenericSubstrate(#[from] subxt::error::Error),
#[error("Substrate client: {0}")]
SubstrateClient(#[from] crate::substrate::SubstrateError),
#[error("Recieved nonce is not 32 bytes")]
BadNonce,
}

#[cfg(feature = "full-client")]
#[derive(Debug, Error)]
pub enum ClientError {
Expand Down Expand Up @@ -110,4 +121,6 @@ pub enum ClientError {
NoNonSigningValidators,
#[error("Scale decode: {0}")]
Codec(#[from] parity_scale_codec::Error),
#[error("Attestation request: {0}")]
AttestationRequest(#[from] AttestationRequestError),
}
2 changes: 1 addition & 1 deletion crates/client/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ async fn test_change_threshold_accounts() {
.unwrap();

// When we request an attestation we get a nonce back that we must use when generating our quote.
let nonce = request_attestation(&api, &rpc, tss_signer_pair.signer().clone()).await.unwrap();
let nonce = request_attestation(&api, &rpc, tss_signer_pair.signer()).await.unwrap();
let nonce: [u8; 32] = nonce.try_into().unwrap();

let mut pck_seeder = StdRng::from_seed(tss_public_key.0.clone());
Expand Down
35 changes: 33 additions & 2 deletions crates/client/src/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@
//! User interaction related
use crate::{
chain_api::{entropy, EntropyConfig},
substrate::query_chain,
substrate::{query_chain, submit_transaction_with_pair},
};
use entropy_shared::{user::ValidatorInfo, BlockNumber, HashingAlgorithm};
use serde::{Deserialize, Serialize};
use sp_core::{sr25519, Pair};
use subxt::{backend::legacy::LegacyRpcMethods, OnlineClient};

pub use crate::errors::SubgroupGetError;
pub use crate::errors::{AttestationRequestError, SubgroupGetError};

/// Represents an unparsed, transaction request coming from the client.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
Expand Down Expand Up @@ -143,3 +144,33 @@ pub async fn get_all_signers_from_chain(

Ok(all_signers)
}

/// An extrinsic to indicate to the chain that it should expect an attestation from the `signer` at
/// some point in the near future.
///
/// The returned `nonce` must be used when generating a `quote` for the chain.
#[tracing::instrument(
skip_all,
fields(
attestee = ?attestee.public(),
)
)]
pub async fn request_attestation(
api: &OnlineClient<EntropyConfig>,
rpc: &LegacyRpcMethods<EntropyConfig>,
attestee: &sr25519::Pair,
) -> Result<[u8; 32], AttestationRequestError> {
tracing::debug!("{} is requesting an attestation.", attestee.public());

let request_attestation = entropy::tx().attestation().request_attestation();

let result =
submit_transaction_with_pair(api, rpc, attestee, &request_attestation, None).await?;
let result_event = result
.find_first::<entropy::attestation::events::AttestationIssued>()?
.ok_or(crate::errors::SubstrateError::NoEvent)?;

let nonce = result_event.0.try_into().map_err(|_| AttestationRequestError::BadNonce)?;

Ok(nonce)
}
2 changes: 1 addition & 1 deletion crates/shared/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ edition ='2021'

[dependencies]
codec ={ package="parity-scale-codec", version="3.0.0", default-features=false }
scale-info ={ version='2.11.5', default-features=false, features=['derive'] }
scale-info ={ version='2.11.6', default-features=false, features=['derive'] }
serde ={ version="1.0", default-features=false, features=["derive"] }
serde_derive="1.0.147"
strum ="0.26.3"
Expand Down
1 change: 1 addition & 0 deletions crates/test-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ sp-runtime ={ version="32.0.0", default-features=false }
entropy-shared={ version="0.3.0", path="../shared" }
serde_json ="1.0.133"
serde ={ version="1.0.215", features=["derive"] }
reqwest ="0.12.9"
20 changes: 20 additions & 0 deletions crates/test-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,14 @@ enum CliCommand {
///
/// This is useful for program developers to know what oracle data is available.
GetOracleHeadings,
/// Request a TDX quote from a TSS server and write it to a file.
GetTdxQuote {
/// The socket address of the TS server, eg: `127.0.0.1:3002`
tss_endpoint: String,
/// The filename to write the quote to. Defaults to `quote.dat`
#[arg(long)]
output_filename: Option<String>,
},
}

impl Cli {
Expand Down Expand Up @@ -566,6 +574,18 @@ pub async fn run_command(
let headings = get_oracle_headings(&api, &rpc).await?;
Ok(serde_json::to_string_pretty(&headings)?)
},
CliCommand::GetTdxQuote { tss_endpoint, output_filename } => {
let quote_bytes =
reqwest::get(format!("http://{}/attest", tss_endpoint)).await?.bytes().await?;
let output_filename = output_filename.unwrap_or("quote.dat".into());

std::fs::write(&output_filename, quote_bytes)?;
if cli.json {
Ok("{}".to_string())
} else {
Ok(format!("Succesfully written quote to {}", output_filename))
}
},
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/testing-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ subxt ="0.35.3"
sp-keyring ="34.0.0"
project-root ="0.2.2"
sp-core ={ version="31.0.0", default-features=false }
parity-scale-codec="3.6.12"
parity-scale-codec="3.7.0"
lazy_static ="1.5.0"
hex-literal ="0.4.1"
tokio ={ version="1.41", features=["macros", "fs", "rt-multi-thread", "io-util", "process"] }
Expand Down
4 changes: 2 additions & 2 deletions crates/threshold-signature-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ axum ={ version="0.7.9", features=["ws"] }

# Substrate
subxt ="0.35.3"
parity-scale-codec="3.6.12"
parity-scale-codec="3.7.0"
sp-core ={ version="31.0.0", default-features=false }

# Entropy
Expand All @@ -52,7 +52,7 @@ entropy-programs-runtime={ git="https://github.com/entropyxyz/programs.git", bra
tracing ="0.1.37"
tracing-subscriber ={ version="0.3.18", features=["env-filter", "json"] }
tracing-loki ="0.2"
tower-http ={ version="0.6.1", features=["trace", "cors"] }
tower-http ={ version="0.6.2", features=["trace", "cors"] }
tracing-bunyan-formatter="0.3.9"
uuid ={ version="1.11.0", features=["v4"] }

Expand Down
26 changes: 26 additions & 0 deletions crates/threshold-signature-server/src/attestation/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use crate::{
AppState,
};
use axum::{body::Bytes, extract::State, http::StatusCode};
use entropy_client::user::request_attestation;
use entropy_kvdb::kv_manager::KvManager;
use entropy_shared::OcwMessageAttestationRequest;
use parity_scale_codec::Decode;
Expand Down Expand Up @@ -77,6 +78,31 @@ pub async fn attest(
Ok(StatusCode::OK)
}

/// Retrieve a quote by requesting a nonce from the chain and return the quote in the HTTP response
/// body.
///
/// This is used by node operators to get a quote for use in the `validate`, `change_endpoint`
/// and `change_tss_accounts` extrinsics.
pub async fn get_attest(
State(app_state): State<AppState>,
) -> Result<(StatusCode, Vec<u8>), AttestationErr> {
let (signer, x25519_secret) = get_signer_and_x25519_secret(&app_state.kv_store).await?;
let api = get_api(&app_state.configuration.endpoint).await?;
let rpc = get_rpc(&app_state.configuration.endpoint).await?;

// Request attestation to get nonce
let nonce = request_attestation(&api, &rpc, signer.signer()).await?;

// We also need the current block number as input
let block_number =
rpc.chain_get_header(None).await?.ok_or_else(|| AttestationErr::BlockNumber)?.number;

// We add 1 to the block number as this will be processed in the next block
let quote = create_quote(block_number + 1, nonce, &signer, &x25519_secret).await?;

Ok((StatusCode::OK, quote))
}

/// Create a mock quote for testing on non-TDX hardware
#[cfg(not(feature = "production"))]
pub async fn create_quote(
Expand Down
2 changes: 2 additions & 0 deletions crates/threshold-signature-server/src/attestation/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ pub enum AttestationErr {
Kv(#[from] entropy_kvdb::kv_manager::error::KvError),
#[error("Data is stale")]
StaleData,
#[error("Attestation request: {0}")]
AttestationRequest(#[from] entropy_client::errors::AttestationRequestError),
}

impl IntoResponse for AttestationErr {
Expand Down
32 changes: 31 additions & 1 deletion crates/threshold-signature-server/src/attestation/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,40 @@ use crate::{
use entropy_kvdb::clean_tests;
use entropy_shared::OcwMessageAttestationRequest;
use entropy_testing_utils::{
constants::TSS_ACCOUNTS,
constants::{BOB_STASH_ADDRESS, TSS_ACCOUNTS},
substrate_context::{test_context_stationary, test_node_process_stationary},
};
use serial_test::serial;
use subxt::utils::AccountId32;
use tdx_quote::{decode_verifying_key, Quote};

#[tokio::test]
#[serial]
async fn test_get_attest() {
initialize_test_logger().await;
clean_tests();

let cxt = test_node_process_stationary().await;
let (_validator_ips, _validator_ids) =
spawn_testing_validators(ChainSpecType::Integration).await;

let api = get_api(&cxt.ws_url).await.unwrap();
let rpc = get_rpc(&cxt.ws_url).await.unwrap();

let quote_bytes =
reqwest::get("http://127.0.0.1:3002/attest").await.unwrap().bytes().await.unwrap();
let quote = Quote::from_bytes(&quote_bytes).unwrap();

let query =
entropy::storage().staking_extension().threshold_servers(&AccountId32(BOB_STASH_ADDRESS.0));
let server_info = query_chain(&api, &rpc, query, None).await.unwrap().unwrap();

let provisioning_certification_key =
decode_verifying_key(&server_info.provisioning_certification_key.0.try_into().unwrap())
.unwrap();

assert!(quote.verify_with_pck(provisioning_certification_key).is_ok())
}

#[ignore]
#[tokio::test]
Expand Down
Loading

0 comments on commit f48c59d

Please sign in to comment.