-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add quote guards to ServerInfo
related extrinsics
#1123
base: master
Are you sure you want to change the base?
Changes from all commits
fe47faa
6c18434
641134b
ae73f0d
b2e5d01
8613c80
fdff83a
abec5a9
9296095
d5f1545
8daaa24
2e39ce9
edc2f7c
ed5db5e
cc71358
68c8f4c
f7571c5
5a0283c
3518cd0
70da5e3
a4f41c7
f70db4b
c68a4ed
f237a73
212628d
21bdfa7
492b9d6
2434d4d
1628f8a
fe7aadd
5a7ab4d
dd695c9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -341,8 +341,10 @@ pub async fn change_endpoint( | |
rpc: &LegacyRpcMethods<EntropyConfig>, | ||
user_keypair: sr25519::Pair, | ||
new_endpoint: String, | ||
quote: Vec<u8>, | ||
) -> anyhow::Result<EndpointChanged> { | ||
let change_endpoint_tx = entropy::tx().staking_extension().change_endpoint(new_endpoint.into()); | ||
let change_endpoint_tx = | ||
entropy::tx().staking_extension().change_endpoint(new_endpoint.into(), quote); | ||
let in_block = | ||
submit_transaction_with_pair(api, rpc, &user_keypair, &change_endpoint_tx, None).await?; | ||
let result_event = in_block | ||
|
@@ -358,13 +360,18 @@ pub async fn change_threshold_accounts( | |
user_keypair: sr25519::Pair, | ||
new_tss_account: String, | ||
new_x25519_public_key: String, | ||
new_pck_certificate_chain: Vec<Vec<u8>>, | ||
quote: Vec<u8>, | ||
) -> anyhow::Result<ThresholdAccountChanged> { | ||
let tss_account = SubxtAccountId32::from_str(&new_tss_account)?; | ||
let x25519_public_key = hex::decode(new_x25519_public_key)? | ||
.try_into() | ||
.map_err(|_| anyhow!("X25519 pub key needs to be 32 bytes"))?; | ||
let change_threshold_accounts = entropy::tx().staking_extension().change_threshold_accounts( | ||
tss_account, | ||
hex::decode(new_x25519_public_key)? | ||
.try_into() | ||
.map_err(|_| anyhow!("X25519 pub key needs to be 32 bytes"))?, | ||
x25519_public_key, | ||
new_pck_certificate_chain, | ||
quote, | ||
); | ||
let in_block = | ||
submit_transaction_with_pair(api, rpc, &user_keypair, &change_threshold_accounts, None) | ||
|
@@ -414,3 +421,33 @@ async fn jumpstart_inner( | |
|
||
Ok(()) | ||
} | ||
|
||
/// 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<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>()? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thats nice that you can get the nonce like this - i didnt understand that from the last PR. |
||
.ok_or(crate::errors::SubstrateError::NoEvent)?; | ||
|
||
let nonce = result_event.0; | ||
|
||
Ok(nonce) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,17 +11,23 @@ use crate::{ | |
}, | ||
get_api, get_rpc, EntropyConfig, | ||
}, | ||
change_endpoint, change_threshold_accounts, register, remove_program, store_program, | ||
change_endpoint, change_threshold_accounts, register, remove_program, request_attestation, | ||
store_program, | ||
substrate::query_chain, | ||
update_programs, | ||
}; | ||
|
||
use entropy_testing_utils::{ | ||
constants::{TEST_PROGRAM_WASM_BYTECODE, TSS_ACCOUNTS}, | ||
helpers::{derive_mock_pck_verifying_key, encode_verifying_key}, | ||
constants::{TEST_PROGRAM_WASM_BYTECODE, TSS_ACCOUNTS, X25519_PUBLIC_KEYS}, | ||
helpers::encode_verifying_key, | ||
jump_start_network, spawn_testing_validators, | ||
substrate_context::test_context_stationary, | ||
test_node_process_testing_state, ChainSpecType, | ||
}; | ||
use rand::{ | ||
rngs::{OsRng, StdRng}, | ||
SeedableRng, | ||
}; | ||
use serial_test::serial; | ||
use sp_core::{sr25519, Pair, H256}; | ||
use sp_keyring::AccountKeyring; | ||
|
@@ -36,7 +42,33 @@ async fn test_change_endpoint() { | |
let api = get_api(&substrate_context.node_proc.ws_url).await.unwrap(); | ||
let rpc = get_rpc(&substrate_context.node_proc.ws_url).await.unwrap(); | ||
|
||
let result = change_endpoint(&api, &rpc, one.into(), "new_endpoint".to_string()).await.unwrap(); | ||
// By using this `Alice` account we can skip the `request_attestation` step since this is | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah ok, i was thinking we were going to ditch having the pending attestation set at genesis because it wasn't used anymore |
||
// already set up at genesis. | ||
let tss_account_id = &TSS_ACCOUNTS[0]; | ||
let x25519_public_key = X25519_PUBLIC_KEYS[0]; | ||
|
||
// This nonce is what was used in the genesis config for `Alice`. | ||
let nonce = [0; 32]; | ||
|
||
let quote = { | ||
let signing_key = tdx_quote::SigningKey::random(&mut OsRng); | ||
let public_key = sr25519::Public(tss_account_id.0); | ||
|
||
// We need to add `1` here since the quote is being checked in the next block | ||
let block_number = rpc.chain_get_header(None).await.unwrap().unwrap().number + 1; | ||
|
||
let input_data = | ||
entropy_shared::QuoteInputData::new(public_key, x25519_public_key, nonce, block_number); | ||
|
||
let mut pck_seeder = StdRng::from_seed(public_key.0); | ||
let pck = tdx_quote::SigningKey::random(&mut pck_seeder); | ||
|
||
tdx_quote::Quote::mock(signing_key.clone(), pck, input_data.0).as_bytes().to_vec() | ||
}; | ||
|
||
let result = | ||
change_endpoint(&api, &rpc, one.into(), "new_endpoint".to_string(), quote).await.unwrap(); | ||
|
||
assert_eq!( | ||
format!("{:?}", result), | ||
format!( | ||
|
@@ -57,33 +89,83 @@ async fn test_change_threshold_accounts() { | |
|
||
let api = get_api(&substrate_context.node_proc.ws_url).await.unwrap(); | ||
let rpc = get_rpc(&substrate_context.node_proc.ws_url).await.unwrap(); | ||
let x25519_public_key = [0u8; 32]; | ||
let result = change_threshold_accounts( | ||
|
||
// We need to use an account that's not a validator (so not our default development/test accounts) | ||
// otherwise we're not able to update the TSS and X25519 keys for our existing validator. | ||
let non_validator_seed = | ||
"gospel prosper cactus remember snap enact refuse review bind rescue guard sock"; | ||
let (tss_signer_pair, x25519_secret) = | ||
entropy_testing_utils::get_signer_and_x25519_secret_from_mnemonic(non_validator_seed) | ||
.unwrap(); | ||
|
||
let tss_public_key = tss_signer_pair.signer().public(); | ||
let x25519_public_key = x25519_dalek::PublicKey::from(&x25519_secret); | ||
|
||
// We need to give our new TSS account some funds before it can request an attestation. | ||
let dest = tss_signer_pair.account_id().clone().into(); | ||
let amount = 10 * entropy_shared::MIN_BALANCE; | ||
let balance_transfer_tx = entropy::tx().balances().transfer_allow_death(dest, amount); | ||
let _transfer_result = crate::substrate::submit_transaction_with_pair( | ||
&api, | ||
&rpc, | ||
one.into(), | ||
AccountId32(one.pair().public().0.into()).to_string(), | ||
hex::encode(x25519_public_key), | ||
&one.pair(), | ||
&balance_transfer_tx, | ||
None, | ||
) | ||
.await | ||
.unwrap(); | ||
|
||
let provisioning_certification_key = { | ||
let key = derive_mock_pck_verifying_key(&TSS_ACCOUNTS[0]); | ||
BoundedVec(encode_verifying_key(&key).unwrap().to_vec()) | ||
// 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: [u8; 32] = nonce.try_into().unwrap(); | ||
|
||
let mut pck_seeder = StdRng::from_seed(tss_public_key.0.clone()); | ||
let pck = tdx_quote::SigningKey::random(&mut pck_seeder); | ||
let encoded_pck = encode_verifying_key(&pck.verifying_key()).unwrap().to_vec(); | ||
|
||
// Our runtime is using the mock `PckCertChainVerifier`, which means that the expected | ||
// "certificate" basically is just our TSS account ID. This account needs to match the one | ||
// used to sign the following `quote`. | ||
let pck_certificate_chain = vec![tss_public_key.0.to_vec()]; | ||
|
||
let quote = { | ||
// We need to add `1` here since the quote is being checked in the next block | ||
let block_number = rpc.chain_get_header(None).await.unwrap().unwrap().number + 1; | ||
|
||
let input_data = entropy_shared::QuoteInputData::new( | ||
tss_public_key, | ||
*x25519_public_key.as_bytes(), | ||
nonce, | ||
block_number, | ||
); | ||
|
||
let signing_key = tdx_quote::SigningKey::random(&mut OsRng); | ||
tdx_quote::Quote::mock(signing_key.clone(), pck.clone(), input_data.0).as_bytes().to_vec() | ||
}; | ||
|
||
let result = change_threshold_accounts( | ||
&api, | ||
&rpc, | ||
one.into(), | ||
tss_public_key.to_string(), | ||
hex::encode(*x25519_public_key.as_bytes()), | ||
pck_certificate_chain, | ||
quote, | ||
) | ||
.await | ||
.unwrap(); | ||
|
||
assert_eq!( | ||
format!("{:?}", result), | ||
format!( | ||
"{:?}", | ||
events::ThresholdAccountChanged( | ||
AccountId32(one.pair().public().0), | ||
ServerInfo { | ||
tss_account: AccountId32(one.pair().public().0), | ||
x25519_public_key, | ||
tss_account: AccountId32(tss_public_key.0), | ||
x25519_public_key: *x25519_public_key.as_bytes(), | ||
endpoint: "127.0.0.1:3001".as_bytes().to_vec(), | ||
provisioning_certification_key, | ||
provisioning_certification_key: BoundedVec(encoded_pck), | ||
} | ||
) | ||
) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -145,6 +145,11 @@ enum CliCommand { | |
ChangeEndpoint { | ||
/// New endpoint to change to (ex. "127.0.0.1:3001") | ||
new_endpoint: String, | ||
/// The Intel TDX quote used to prove that this TSS is running on TDX hardware. | ||
/// | ||
/// The quote format is specified in: | ||
/// https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_TDX_DCAP_Quoting_Library_API.pdf | ||
quote: String, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For a followup: Maybe we wanna have these be given as hex. I think otherwise they need to be given as It will be easiest if we have them be given in whatever format the TSS server gives them to the operator in the quote generation endpoint, which doesn't yet exist. So probably we can fix this once it does exist. |
||
/// The mnemonic for the validator stash account to use for the call, should be stash address | ||
#[arg(short, long)] | ||
mnemonic_option: Option<String>, | ||
|
@@ -155,6 +160,13 @@ enum CliCommand { | |
new_tss_account: String, | ||
/// New x25519 public key | ||
new_x25519_public_key: String, | ||
/// The new Provisioning Certification Key (PCK) certificate chain to be used for the TSS. | ||
new_pck_certificate_chain: Vec<String>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For followup: If given as strings, it probably makes sense for them to be PEM encoded as this is the standard for encoding x509 certificates as utf-8. This would mean adding a dependency to the test-cli to decode them. Otherwise we could have them be read in from files i guess. We can probably leave this until we develop tooling for operators to be able to retrieve these certificates and see what makes most sense then. |
||
/// The Intel TDX quote used to prove that this TSS is running on TDX hardware. | ||
/// | ||
/// The quote format is specified in: | ||
/// https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_TDX_DCAP_Quoting_Library_API.pdf | ||
quote: String, | ||
/// The mnemonic for the validator stash account to use for the call, should be stash address | ||
#[arg(short, long)] | ||
mnemonic_option: Option<String>, | ||
|
@@ -433,7 +445,7 @@ pub async fn run_command( | |
|
||
Ok("Got status".to_string()) | ||
}, | ||
CliCommand::ChangeEndpoint { new_endpoint, mnemonic_option } => { | ||
CliCommand::ChangeEndpoint { new_endpoint, quote, mnemonic_option } => { | ||
let mnemonic = if let Some(mnemonic_option) = mnemonic_option { | ||
mnemonic_option | ||
} else { | ||
|
@@ -443,13 +455,16 @@ pub async fn run_command( | |
let user_keypair = <sr25519::Pair as Pair>::from_string(&mnemonic, None)?; | ||
println!("User account for current call: {}", user_keypair.public()); | ||
|
||
let result_event = change_endpoint(&api, &rpc, user_keypair, new_endpoint).await?; | ||
let result_event = | ||
change_endpoint(&api, &rpc, user_keypair, new_endpoint, quote.into()).await?; | ||
println!("Event result: {:?}", result_event); | ||
Ok("Endpoint changed".to_string()) | ||
}, | ||
CliCommand::ChangeThresholdAccounts { | ||
new_tss_account, | ||
new_x25519_public_key, | ||
new_pck_certificate_chain, | ||
quote, | ||
mnemonic_option, | ||
} => { | ||
let mnemonic = if let Some(mnemonic_option) = mnemonic_option { | ||
|
@@ -460,12 +475,16 @@ pub async fn run_command( | |
let user_keypair = <sr25519::Pair as Pair>::from_string(&mnemonic, None)?; | ||
println!("User account for current call: {}", user_keypair.public()); | ||
|
||
let new_pck_certificate_chain = | ||
new_pck_certificate_chain.iter().cloned().map(|i| i.into()).collect::<_>(); | ||
let result_event = change_threshold_accounts( | ||
&api, | ||
&rpc, | ||
user_keypair, | ||
new_tss_account, | ||
new_x25519_public_key, | ||
new_pck_certificate_chain, | ||
quote.into(), | ||
) | ||
.await?; | ||
println!("Event result: {:?}", result_event); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For followup: If this is going to also be called by
entropy-tss
, which i think we probably will want to when we add a generate quote endpoint - it will need to go in a different module as stuff in this one is behind thefull-client
feature flag.