-
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
Attestation pallet #1003
Attestation pallet #1003
Conversation
* master: TSS attestation endpoint (#1001)
* master: Bump serde_json from 1.0.124 to 1.0.125 in the patch-dependencies group (#1007) Signing flow with derived accounts (#990) Add `network-jumpstart` command to `entropy-test-cli` (#1004) Refactor reshare (#994) Migrate circle-ci workflow to github actions (#991) Delete old keyshare if not in next_signers (#999)
…pallet to call it
@@ -105,14 +114,14 @@ pub type EncodedVerifyingKey = [u8; VERIFICATION_KEY_LENGTH as usize]; | |||
pub struct QuoteInputData(pub [u8; 64]); | |||
|
|||
impl QuoteInputData { | |||
pub fn new( | |||
tss_account_id: [u8; 32], | |||
pub fn new<T: Encode>( |
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.
|
||
/// 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 comment
The 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 create_quote
function
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 comment
The 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.
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.
Looks good a few issues need to be made
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 comment
The 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 comment
The 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 OcwMessageAttestationRequest
matches on chain.
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 comment
The 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
/// attestation, used to make attestation requests via an offchain worker | ||
#[pallet::storage] | ||
#[pallet::getter(fn attestation_requests)] | ||
pub type AttestationRequests<T: Config> = |
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.
idea here, we can make a trailing request of X blocks on an on init hook that boots a validator if they did not complete their attestation
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.
I've added this to the TODOs in the issue
pallets/attestation/src/lib.rs
Outdated
); | ||
|
||
// Remove the entry from PendingAttestations | ||
PendingAttestations::<T>::remove(&who); |
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 should be below the other two probably
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.
I've moved it lower down for now but Im not sure about this.
I think once we have some specific outcome for a failed attestation, we will still want to remove the pending attestation even if it is bad. So pending attestations will just be ones that haven't been responded to yet and we will have some other way of representing ones which have been responded to but were bad.
pallets/attestation/src/lib.rs
Outdated
|
||
// Remove the entry from PendingAttestations | ||
PendingAttestations::<T>::remove(&who); | ||
|
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.
Idea: we put in the validator info when their last successful attestation was made to chain
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.
Left some comments and questions, but nothing major. Good work 👍
Make sure to add a CHANGELOG
entry before merging
// 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/>. | ||
|
||
//! # Attestation Pallet |
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.
Some docs about what this is would be nice
#[derive(frame_support::DefaultNoBound)] | ||
pub struct GenesisConfig<T: Config> { | ||
pub initial_pending_attestations: Vec<(T::AccountId, [u8; 32])>, | ||
pub initial_attestation_requests: Vec<(BlockNumberFor<T>, Vec<Vec<u8>>)>, |
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.
pub initial_attestation_requests: Vec<(BlockNumberFor<T>, Vec<Vec<u8>>)>, | |
pub initial_attestation_requests: Vec<(BlockNumberFor<T>, Vec<T::AccountId>)>, |
Why not do something like this instead?
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.
Good point. I thought it is not possible to have a <T::AccountId> as a value in a StorageMap
. As in all our other pallets, we use Vec<u8>
. But actually i just tried it, and it compiled fine.
So now the issue is that in entropy_shared::OcwMessageAttestationRequest
we represent this as a [u8; 32]
. Since theres no way to turn a generic T::AccountId
into a [u8; 32]
, we would have to make OcwMessageAttestationRequest
generic for chain config.
I'd be happy to do this, but i have the feeling it there might be issues with deriving Serialize
and Deserialize
and probably it would make sense to also do it to all the other types we have in entropy-shared
which represent account ids with Vec<u8>
. So maybe its for another PR.
pallets/attestation/src/lib.rs
Outdated
#[pallet::storage] | ||
#[pallet::getter(fn pending_attestations)] | ||
pub type PendingAttestations<T: Config> = | ||
StorageMap<_, Blake2_128Concat, T::AccountId, [u8; 32], OptionQuery>; |
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.
We could typedef this as type Nonce = [u8; 32]
pallets/attestation/src/lib.rs
Outdated
BadQuote, | ||
UnexpectedAttestation, | ||
IncorrectInputData, | ||
NoStashAccount, | ||
NoServerInfo, |
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.
I know we don't do it for the other pallets, but let's try and add doc comments before it's too late lol
impl<T: Config> Pallet<T> { | ||
#[pallet::call_index(0)] | ||
#[pallet::weight({100})] | ||
pub fn attest(origin: OriginFor<T>, quote: Vec<u8>) -> DispatchResult { |
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.
Missing doc comments. Would be helpful to know what a quote
should look like for example
assert_last_event::<T>( | ||
Event::<T>::AttestationMade.into() | ||
); |
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.
Here we can also test for expected behaviour.
For example, we can check that PendingAttestations
doesn't have the validator ID anymore, or that expected events were emitted
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.
Good idea. I've added the check that there is no longer a pending attestation. Not sure what other events i could check for.
|
||
let deadline = sp_io::offchain::timestamp().add(Duration::from_millis(2_000)); | ||
let kind = sp_core::offchain::StorageKind::PERSISTENT; | ||
let from_local = sp_io::offchain::local_storage_get(kind, b"attest") |
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.
I think we also need to reflect this in the service
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.
Ah ok yep will do.
Although i find it strange that we store a bunch of these urls for each endpoint, rather than just a top level one.
@@ -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 comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mind using an account instead of 3
here? Either a typedef or the result from one of the other helpful functions is fine
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 a block number, not an account id
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 comment
The 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 comment
The 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.
Co-authored-by: Hernando Castano <[email protected]>
This is part of #982
This adds a pallet for TDX attestation.
The pallet stores the nonces of all pending (requested) attestations, storing them under associated TSS account ID. So there may be at most one pending attestation per TS server. The nonce is just a random 32 bytes, which is included in the input data to the TDX quote, to prove that this is a freshly made quote.
An attestation request is responded to by submitting the quote using the
attest
extrinsic. If there was a pending attestation for the caller, the quote is verified. Verification currently just means checking that the quote parses correctly and has a valid signature. But this would eventually also check the build-time or run-time measurement details match our current release and that the public key matches the corresponding PCK certificate. PCK certificates will be handled in a separate PR.If the quote fails to verify, something should happen to the validator - eg: remove them from the signing committee or block them from joining. This is outside of the scope of this PR and can be handled as part of the slashing feature.
The attestation pallet also stores a mapping of block number to TSS account IDs of nodes for who an attestation request should be initiated. This is used by the propagation pallet to make a POST request to the TS server's
/attest
endpoint whenever there are requests to be made. Currently, the only place these attestation requests are initiated is in the genesis config for testing.