-
Notifications
You must be signed in to change notification settings - Fork 2
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
Attestation pallet #1003
Attestation pallet #1003
Changes from 35 commits
55c9af2
c5df03f
09e877e
6fe7070
8cc3457
0a7ee3c
d9d4a80
d106227
430b372
5a2206f
305120b
7425039
905359d
254a89a
2c462b8
37d08d0
f57303b
fb5a4b7
69dd18e
992a4d1
74a83ab
e27262a
175166b
c7db0ef
01213d1
19c7a7a
8d1f3ca
33797ef
b6f69b8
314dc9a
7843545
76d95eb
3ef7fe8
fe93e35
0abb8be
cdd22b4
0e75228
d76bb0f
8ad0c8d
dba2907
9379469
2aca946
8d07bf9
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 |
---|---|---|
|
@@ -13,57 +13,98 @@ | |
// You should have received a copy of the GNU Affero General Public License | ||
// along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
||
use crate::{attestation::errors::AttestationErr, AppState}; | ||
use crate::{ | ||
attestation::errors::AttestationErr, | ||
chain_api::{entropy, get_api, get_rpc, EntropyConfig}, | ||
get_signer_and_x25519_secret, | ||
helpers::substrate::{query_chain, submit_transaction}, | ||
AppState, | ||
}; | ||
use axum::{body::Bytes, extract::State, http::StatusCode}; | ||
use entropy_shared::OcwMessageAttestationRequest; | ||
use parity_scale_codec::Decode; | ||
use sp_core::Pair; | ||
use subxt::tx::PairSigner; | ||
use x25519_dalek::StaticSecret; | ||
|
||
/// HTTP POST endpoint to initiate a TDX attestation. | ||
/// Not yet implemented. | ||
#[cfg(not(any(test, feature = "unsafe")))] | ||
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. Before we had a production and mock version of the endpoint function - but that is now replaced with a common endpoint function which internally calls either a mock or production version of the |
||
pub async fn attest( | ||
State(_app_state): State<AppState>, | ||
_input: Bytes, | ||
) -> Result<StatusCode, AttestationErr> { | ||
// Non-mock attestation (the real thing) will go here | ||
Err(AttestationErr::NotImplemented) | ||
} | ||
|
||
/// HTTP POST endpoint to initiate a mock TDX attestation for testing on non-TDX hardware. | ||
/// The body of the request should be a 32 byte random nonce used to show 'freshness' of the | ||
/// quote. | ||
/// The response body contains a mock TDX v4 quote serialized as described in the | ||
/// [Index TDX DCAP Quoting Library API](https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_TDX_DCAP_Quoting_Library_API.pdf). | ||
#[cfg(any(test, feature = "unsafe"))] | ||
pub async fn attest( | ||
State(app_state): State<AppState>, | ||
input: Bytes, | ||
) -> Result<(StatusCode, Bytes), AttestationErr> { | ||
use crate::{chain_api::get_rpc, get_signer_and_x25519_secret}; | ||
use rand_core::OsRng; | ||
use sp_core::Pair; | ||
) -> Result<StatusCode, AttestationErr> { | ||
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. we will have to validate this message agains blockchain data (can be in a later PR but needs an issue 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. I'll put it in the issue - i am adding a bunch of things into the once issue for this feature. But since we check below with the chain that there is a pending attestation, im not sure what we gain by also checking the |
||
let (signer, x25519_secret) = get_signer_and_x25519_secret(&app_state.kv_store).await?; | ||
let attestaion_requests = OcwMessageAttestationRequest::decode(&mut input.as_ref())?; | ||
ameba23 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// TODO (#982) confirm with the chain that an attestation should be happenning | ||
let nonce = input.as_ref().try_into()?; | ||
// Check whether there is an attestion request for us | ||
if !attestaion_requests.tss_account_ids.contains(&signer.signer().public().0) { | ||
return Ok(StatusCode::OK); | ||
} | ||
|
||
let api = get_api(&app_state.configuration.endpoint).await?; | ||
let rpc = get_rpc(&app_state.configuration.endpoint).await?; | ||
|
||
// Get the input nonce for this attestation | ||
let nonce = { | ||
let pending_attestation_query = | ||
entropy::storage().attestation().pending_attestations(signer.account_id()); | ||
query_chain(&api, &rpc, pending_attestation_query, None) | ||
.await? | ||
.ok_or_else(|| AttestationErr::Unexpected)? | ||
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. wouldn't hate a better error message here, a generalize one for querying chains maybe that takes a string of a better description |
||
}; | ||
|
||
// 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?; | ||
|
||
// Submit the quote | ||
let attest_tx = entropy::tx().attestation().attest(quote.clone()); | ||
submit_transaction(&api, &rpc, &signer, &attest_tx, None).await?; | ||
|
||
Ok(StatusCode::OK) | ||
} | ||
|
||
/// Create a mock quote for testing on non-TDX hardware | ||
#[cfg(any(test, feature = "unsafe"))] | ||
pub async fn create_quote( | ||
block_number: u32, | ||
nonce: [u8; 32], | ||
signer: &PairSigner<EntropyConfig, sp_core::sr25519::Pair>, | ||
x25519_secret: &StaticSecret, | ||
) -> Result<Vec<u8>, AttestationErr> { | ||
use rand_core::OsRng; | ||
use sp_core::Pair; | ||
|
||
// In the real thing this is the hardware key used in the quoting enclave | ||
let signing_key = tdx_quote::SigningKey::random(&mut OsRng); | ||
|
||
let (signer, x25519_secret) = get_signer_and_x25519_secret(&app_state.kv_store).await?; | ||
let public_key = x25519_dalek::PublicKey::from(&x25519_secret); | ||
let public_key = x25519_dalek::PublicKey::from(x25519_secret); | ||
|
||
let input_data = entropy_shared::QuoteInputData::new( | ||
signer.signer().public().into(), | ||
signer.signer().public(), | ||
*public_key.as_bytes(), | ||
nonce, | ||
block_number, | ||
); | ||
|
||
let quote = tdx_quote::Quote::mock(signing_key.clone(), input_data.0); | ||
// Here we would submit an attest extrinsic to the chain - but for now we just include it in the | ||
// response | ||
Ok((StatusCode::OK, Bytes::from(quote.as_bytes().to_vec()))) | ||
let quote = tdx_quote::Quote::mock(signing_key.clone(), input_data.0).as_bytes().to_vec(); | ||
Ok(quote) | ||
} | ||
|
||
/// Once implemented, this will create a TDX quote in production | ||
#[cfg(not(any(test, feature = "unsafe")))] | ||
pub async fn create_quote( | ||
_block_number: u32, | ||
_nonce: [u8; 32], | ||
_signer: &PairSigner<EntropyConfig, sp_core::sr25519::Pair>, | ||
_x25519_secret: &StaticSecret, | ||
) -> Result<Vec<u8>, AttestationErr> { | ||
// Non-mock attestation (the real thing) will go here | ||
Err(AttestationErr::NotImplemented) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,12 +12,16 @@ | |
// | ||
// You should have received a copy of the GNU Affero General Public License | ||
// along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
use crate::helpers::tests::{initialize_test_logger, spawn_testing_validators}; | ||
use crate::{ | ||
chain_api::{entropy, get_api, get_rpc}, | ||
helpers::{ | ||
substrate::query_chain, | ||
tests::{initialize_test_logger, run_to_block, spawn_testing_validators}, | ||
}, | ||
}; | ||
use entropy_kvdb::clean_tests; | ||
use entropy_shared::QuoteInputData; | ||
use entropy_testing_utils::{ | ||
constants::{TSS_ACCOUNTS, X25519_PUBLIC_KEYS}, | ||
substrate_context::test_node_process_testing_state, | ||
constants::TSS_ACCOUNTS, substrate_context::test_node_process_stationary, | ||
}; | ||
use serial_test::serial; | ||
|
||
|
@@ -27,25 +31,34 @@ async fn test_attest() { | |
initialize_test_logger().await; | ||
clean_tests(); | ||
|
||
let _cxt = test_node_process_testing_state(false).await; | ||
let cxt = test_node_process_stationary().await; | ||
let (_validator_ips, _validator_ids) = spawn_testing_validators(false).await; | ||
let api = get_api(&cxt.ws_url).await.unwrap(); | ||
let rpc = get_rpc(&cxt.ws_url).await.unwrap(); | ||
|
||
// Check that there is an attestation request at block 3 from the genesis config | ||
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. Why block 3? 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. This was one of those problems which kind of got sorted but i don't completely understand why. The propagation pallet's off chain worker hooks which run every block don't run on the genesis block. It seems to me that they also don't run on block one - although me and Jesse were getting different behavior. In the end i've ended up with block 3, and also am not subtracting 1 from the block number. Probably this could be either changed to 2, or the subtract 1 could be put back in. |
||
let attestation_requests_query = entropy::storage().attestation().attestation_requests(3); | ||
query_chain(&api, &rpc, attestation_requests_query, None).await.unwrap().unwrap(); | ||
|
||
let nonce = [0; 32]; | ||
let client = reqwest::Client::new(); | ||
let res = client | ||
.post(format!("http://127.0.0.1:3001/attest")) | ||
.body(nonce.to_vec()) | ||
.send() | ||
.await | ||
.unwrap(); | ||
assert_eq!(res.status(), 200); | ||
let quote = res.bytes().await.unwrap(); | ||
// Get the nonce from the pending attestation from the genesis config | ||
let nonce = { | ||
let pending_attestation_query = | ||
entropy::storage().attestation().pending_attestations(&TSS_ACCOUNTS[0]); | ||
query_chain(&api, &rpc, pending_attestation_query, None).await.unwrap().unwrap() | ||
}; | ||
assert_eq!(nonce, [0; 32]); | ||
|
||
// This internally verifies the signature in the quote | ||
let quote = tdx_quote::Quote::from_bytes("e).unwrap(); | ||
// Wait for the attestation to be handled | ||
for _ in 0..10 { | ||
let block_number = rpc.chain_get_header(None).await.unwrap().unwrap().number; | ||
run_to_block(&rpc, block_number + 1).await; | ||
|
||
// Check the input data of the quote | ||
let expected_input_data = | ||
QuoteInputData::new(TSS_ACCOUNTS[0].0, X25519_PUBLIC_KEYS[0], nonce, 0); | ||
assert_eq!(quote.report_input_data(), expected_input_data.0); | ||
// There should be no more pending attestation as the attestation has been handled | ||
let pending_attestation_query = | ||
entropy::storage().attestation().pending_attestations(&TSS_ACCOUNTS[0]); | ||
if query_chain(&api, &rpc, pending_attestation_query, None).await.unwrap().is_none() { | ||
return; | ||
} | ||
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. This test isn't ideal - but currently the only outcome of a successful attestation is that there is no longer a pending attestation. |
||
} | ||
panic!("Waited 10 blocks and attestation is still pending"); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,10 +17,10 @@ use crate::chain_spec::{get_account_id_from_seed, ChainSpec}; | |
use crate::endowed_accounts::endowed_accounts_dev; | ||
|
||
use entropy_runtime::{ | ||
constants::currency::*, wasm_binary_unwrap, AuthorityDiscoveryConfig, BabeConfig, | ||
BalancesConfig, ElectionsConfig, GrandpaConfig, ImOnlineConfig, IndicesConfig, MaxNominations, | ||
ParametersConfig, ProgramsConfig, RegistryConfig, SessionConfig, StakerStatus, StakingConfig, | ||
StakingExtensionConfig, SudoConfig, TechnicalCommitteeConfig, | ||
constants::currency::*, wasm_binary_unwrap, AttestationConfig, AuthorityDiscoveryConfig, | ||
BabeConfig, BalancesConfig, ElectionsConfig, GrandpaConfig, ImOnlineConfig, IndicesConfig, | ||
MaxNominations, ParametersConfig, ProgramsConfig, RegistryConfig, SessionConfig, StakerStatus, | ||
StakingConfig, StakingExtensionConfig, SudoConfig, TechnicalCommitteeConfig, | ||
}; | ||
use entropy_runtime::{AccountId, Balance}; | ||
use entropy_shared::{ | ||
|
@@ -34,7 +34,7 @@ use pallet_im_online::sr25519::AuthorityId as ImOnlineId; | |
use sc_service::ChainType; | ||
use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; | ||
use sp_consensus_babe::AuthorityId as BabeId; | ||
use sp_core::sr25519; | ||
use sp_core::{sr25519, ByteArray}; | ||
use sp_runtime::{BoundedVec, Perbill}; | ||
|
||
pub fn devnet_three_node_initial_tss_servers( | ||
|
@@ -336,5 +336,9 @@ pub fn development_genesis_config( | |
10, | ||
)], | ||
}, | ||
"attestation": AttestationConfig { | ||
initial_attestation_requests: vec![(3, vec![crate::chain_spec::tss_account_id::ALICE.to_raw_vec()])], | ||
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. Do you mind using an account instead of 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. This is a block number, not an account id |
||
initial_pending_attestations: vec![(crate::chain_spec::tss_account_id::ALICE.clone(), [0; 32])], | ||
}, | ||
}) | ||
} |
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.
This is just so it can be used with mock account ids in the attestation pallet test.