Skip to content
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

Split jumpstart and register flows #952

Merged
merged 11 commits into from
Jul 19, 2024
9 changes: 5 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@ At the moment this project **does not** adhere to

## [Unreleased](https://github.com/entropyxyz/entropy-core/compare/release/v0.2.0...master)

### Added
- Jumpstart network ([#918](https://github.com/entropyxyz/entropy-core/pull/918))
- Add Signer groups and rotation ([#938](https://github.com/entropyxyz/entropy-core/pull/938))

### Breaking Changes
- In [#938](https://github.com/entropyxyz/entropy-core/pull/938), the chainspec got a couple of new
fields, `pallet_staking_extension::initial_signers`, `pallet_parameters::total_signers`, and
`pallet_parameters::threshold`, which are used to set up the initial threshold signing
configuration for the network.

### Added
- Jumpstart network ([#918](https://github.com/entropyxyz/entropy-core/pull/918))
- Add Signer groups and rotation ([#938](https://github.com/entropyxyz/entropy-core/pull/938))
- Split jumpstart and register flows ([#952](https://github.com/entropyxyz/entropy-core/pull/952))

## [0.2.0](https://github.com/entropyxyz/entropy-core/compare/release/v0.1.0...release/v0.2.0) - 2024-07-11

### Breaking Changes
Expand Down
Binary file modified crates/client/entropy_metadata.scale
Binary file not shown.
3 changes: 2 additions & 1 deletion crates/threshold-signature-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,9 @@ impl AppState {

pub fn app(app_state: AppState) -> Router {
let mut routes = Router::new()
.route("/user/sign_tx", post(sign_tx))
.route("/generate_network_key", post(generate_network_key))
.route("/user/new", post(new_user))
.route("/user/sign_tx", post(sign_tx))
.route("/signer/proactive_refresh", post(proactive_refresh))
.route("/healthz", get(healthz))
.route("/version", get(get_version))
Expand Down
51 changes: 47 additions & 4 deletions crates/threshold-signature-server/src/user/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ use crate::{
pub use entropy_client::user::{get_signers_from_chain, UserSignatureRequest};
pub const REQUEST_KEY_HEADER: &str = "REQUESTS";

/// Used to differentiate different flows which perform distributed key generation.
enum DkgFlow {
Jumpstart,
Registration,
}

/// Type for validators to send user key's back and forth
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq)]
Expand Down Expand Up @@ -228,7 +234,25 @@ pub async fn sign_tx(
Ok((StatusCode::OK, Body::from_stream(response_rx)))
}

/// HTTP POST endpoint called by the off-chain worker (propagation pallet) during user registration.
/// HTTP POST endpoint called by the off-chain worker (Propagation pallet) during the network
/// jumpstart.
///
/// The HTTP request takes a Parity SCALE encoded [OcwMessageDkg] which indicates which validators
/// are in the validator group.
///
/// This will trigger the Distributed Key Generation (DKG) process.
#[tracing::instrument(skip_all, fields(block_number))]
pub async fn generate_network_key(
State(app_state): State<AppState>,
encoded_data: Bytes,
) -> Result<StatusCode, UserErr> {
let data = OcwMessageDkg::decode(&mut encoded_data.as_ref())?;
tracing::Span::current().record("block_number", data.block_number);

distributed_key_generation(app_state, data, DkgFlow::Jumpstart).await
}

/// HTTP POST endpoint called by the off-chain worker (Propagation pallet) during user registration.
///
/// The HTTP request takes a Parity SCALE encoded [OcwMessageDkg] which indicates which validators
/// are in the validator group.
Expand All @@ -242,12 +266,25 @@ pub async fn new_user(
let data = OcwMessageDkg::decode(&mut encoded_data.as_ref())?;
tracing::Span::current().record("block_number", data.block_number);

distributed_key_generation(app_state, data, DkgFlow::Registration).await
}

/// An internal helper which kicks off the distributed key generation (DKG) process.
///
/// Since the jumpstart and registration flows are both doing DKG at the moment, we've split this
/// out. In the future though, only the jumpstart flow will require this.
async fn distributed_key_generation(
app_state: AppState,
data: OcwMessageDkg,
flow: DkgFlow,
) -> Result<StatusCode, UserErr> {
if data.sig_request_accounts.is_empty() {
return Ok(StatusCode::NO_CONTENT);
}
let api = get_api(&app_state.configuration.endpoint).await?;
let rpc = get_rpc(&app_state.configuration.endpoint).await?;
let (signer, x25519_secret_key) = get_signer_and_x25519_secret(&app_state.kv_store).await?;

let in_registration_group =
check_in_registration_group(&data.validators_info, signer.account_id());

Expand All @@ -261,7 +298,7 @@ pub async fn new_user(
return Ok(StatusCode::MISDIRECTED_REQUEST);
}

validate_new_user(&data, &api, &rpc, &app_state.kv_store).await?;
validate_new_user(&data, &api, &rpc, &app_state.kv_store, flow).await?;

// Do the DKG protocol in another task, so we can already respond
tokio::spawn(async move {
Expand Down Expand Up @@ -308,6 +345,7 @@ async fn setup_dkg(
data.block_number,
)
.await?;

let verifying_key = key_share.verifying_key().to_encoded_point(true).as_bytes().to_vec();
let string_verifying_key = if sig_request_account == NETWORK_PARENT_KEY.encode() {
hex::encode(NETWORK_PARENT_KEY)
Expand Down Expand Up @@ -396,11 +434,12 @@ pub async fn confirm_registered(

/// Validates new user endpoint
/// Checks the chain for validity of data and block number of data matches current block
pub async fn validate_new_user(
async fn validate_new_user(
chain_data: &OcwMessageDkg,
api: &OnlineClient<EntropyConfig>,
rpc: &LegacyRpcMethods<EntropyConfig>,
kv_manager: &KvManager,
flow: DkgFlow,
) -> Result<(), UserErr> {
let last_block_number_recorded = kv_manager.kv().get(LATEST_BLOCK_NUMBER_NEW_USER).await?;
if u32::from_be_bytes(
Expand Down Expand Up @@ -428,7 +467,11 @@ pub async fn validate_new_user(
let chain_data_hash = hasher_chain_data.finalize();
let mut hasher_verifying_data = Blake2s256::new();

let verifying_data_query = entropy::storage().registry().dkg(chain_data.block_number);
let verifying_data_query = match flow {
DkgFlow::Jumpstart => entropy::storage().registry().jumpstart_dkg(chain_data.block_number),
DkgFlow::Registration => entropy::storage().registry().dkg(chain_data.block_number),
};

let verifying_data = query_chain(api, rpc, verifying_data_query, None).await?;
hasher_verifying_data.update(verifying_data.encode());

Expand Down
2 changes: 1 addition & 1 deletion crates/threshold-signature-server/src/user/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ async fn test_jumpstart_network() {
.iter()
.map(|port| {
client
.post(format!("http://127.0.0.1:{}/user/new", port))
.post(format!("http://127.0.0.1:{}/generate_network_key", port))
.body(onchain_user_request.clone().encode())
.send()
})
Expand Down
5 changes: 5 additions & 0 deletions node/cli/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,11 @@ pub fn new_full_base(
offchain_db.local_storage_set(
sp_core::offchain::StorageKind::PERSISTENT,
b"propagation",
&format!("{}/generate_network_key", endpoint).into_bytes(),
);
offchain_db.local_storage_set(
sp_core::offchain::StorageKind::PERSISTENT,
b"registration",
&format!("{}/user/new", endpoint).into_bytes(),
);
offchain_db.local_storage_set(
Expand Down
61 changes: 60 additions & 1 deletion pallets/propagation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub mod pallet {
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn offchain_worker(block_number: BlockNumberFor<T>) {
let _ = Self::post_dkg(block_number);
let _ = Self::post_user_registration(block_number);
let _ = Self::post_proactive_refresh(block_number);
}

Expand Down Expand Up @@ -83,12 +84,70 @@ pub mod pallet {

impl<T: Config> Pallet<T> {
pub fn post_dkg(block_number: BlockNumberFor<T>) -> Result<(), http::Error> {
HCastano marked this conversation as resolved.
Show resolved Hide resolved
let messages = pallet_registry::Pallet::<T>::jumpstart_dkg(
block_number.saturating_sub(1u32.into()),
);

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"propagation")
.unwrap_or_else(|| b"http://localhost:3001/generate_network_key".to_vec());
let url =
str::from_utf8(&from_local).unwrap_or("http://localhost:3001/generate_network_key");

log::warn!("propagation::post::messages: {:?}", &messages);
let converted_block_number: u32 =
BlockNumberFor::<T>::try_into(block_number).unwrap_or_default();
let servers_info =
pallet_registry::Pallet::<T>::get_validators_info().unwrap_or_default();
let validators_info = servers_info
.iter()
.map(|server_info| ValidatorInfo {
x25519_public_key: server_info.x25519_public_key,
ip_address: server_info.endpoint.clone(),
tss_account: server_info.tss_account.encode(),
})
.collect::<Vec<_>>();
// the data is serialized / encoded to Vec<u8> by parity-scale-codec::encode()
let req_body = OcwMessageDkg {
// subtract 1 from blocknumber since the request is from the last block
block_number: converted_block_number.saturating_sub(1),
sig_request_accounts: messages,
validators_info,
};

log::warn!("propagation::post::req_body: {:?}", &[req_body.encode()]);
// We construct the request
// important: the header->Content-Type must be added and match that of the receiving
// party!!
let pending = http::Request::post(url, vec![req_body.encode()])
.deadline(deadline)
.send()
.map_err(|_| http::Error::IoError)?;

// We await response, same as in fn get()
let response =
pending.try_wait(deadline).map_err(|_| http::Error::DeadlineReached)??;

// check response code
if response.code != 200 {
log::warn!("Unexpected status code: {}", response.code);
return Err(http::Error::Unknown);
}
let _res_body = response.body().collect::<Vec<u8>>();

Self::deposit_event(Event::DkgMessagePassed(req_body));

Ok(())
}

pub fn post_user_registration(block_number: BlockNumberFor<T>) -> Result<(), http::Error> {
HCastano marked this conversation as resolved.
Show resolved Hide resolved
let messages =
pallet_registry::Pallet::<T>::dkg(block_number.saturating_sub(1u32.into()));

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"propagation")
let from_local = sp_io::offchain::local_storage_get(kind, b"registration")
.unwrap_or_else(|| b"http://localhost:3001/user/new".to_vec());
let url = str::from_utf8(&from_local).unwrap_or("http://localhost:3001/user/new");

Expand Down
19 changes: 14 additions & 5 deletions pallets/registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,11 @@ pub mod pallet {
pub type Registering<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, RegisteringDetails<T>, OptionQuery>;

#[pallet::storage]
HCastano marked this conversation as resolved.
Show resolved Hide resolved
#[pallet::getter(fn jumpstart_dkg)]
pub type JumpstartDkg<T: Config> =
StorageMap<_, Blake2_128Concat, BlockNumberFor<T>, Vec<Vec<u8>>, ValueQuery>;

#[pallet::storage]
#[pallet::getter(fn dkg)]
pub type Dkg<T: Config> =
Expand Down Expand Up @@ -284,11 +289,15 @@ pub mod pallet {
},
_ => return Err(Error::<T>::JumpStartProgressNotReady.into()),
};

// TODO (#923): Add checks for network state.
Dkg::<T>::try_mutate(current_block_number, |messages| -> Result<_, DispatchError> {
messages.push(NETWORK_PARENT_KEY.encode());
Ok(())
})?;
JumpstartDkg::<T>::try_mutate(
current_block_number,
|messages| -> Result<_, DispatchError> {
messages.push(NETWORK_PARENT_KEY.encode());
Ok(())
},
)?;
JumpStartProgress::<T>::put(JumpStartDetails {
jump_start_status: JumpStartStatus::InProgress(converted_block_number),
confirmations: vec![],
Expand Down Expand Up @@ -373,7 +382,7 @@ pub mod pallet {
/// network.
#[pallet::call_index(2)]
#[pallet::weight({
<T as Config>::WeightInfo::register( <T as Config>::MaxProgramHashes::get())
<T as Config>::WeightInfo::register(<T as Config>::MaxProgramHashes::get())
})]
pub fn register(
origin: OriginFor<T>,
Expand Down