Skip to content

Commit

Permalink
new_acc load tests (#325)
Browse files Browse the repository at this point in the history
* new_acc load tests

* sign logic added to load tests

* short load scenarion updated

* recovery pk set in session

* fore load scenarios

* remove unwraps

* use tracing
  • Loading branch information
volovyks authored Oct 23, 2023
1 parent bc48a73 commit 17c6649
Show file tree
Hide file tree
Showing 9 changed files with 410 additions and 13 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target
.direnv
.DS_Store
tmp
14 changes: 12 additions & 2 deletions Cargo.lock

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

10 changes: 10 additions & 0 deletions load-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,13 @@ goose = "^0.17"
goose-eggs = "0.5.1"
tokio = "^1.12"
reqwest = "^0.11"
jsonwebtoken = "8.3.0"
near-primitives = "0.17.0"
serde = "1.0.130"
serde_json = "1.0.68"
chrono = "0.4.19"
near-crypto = "0.17.0"
mpc-recovery = { path = "../mpc-recovery" }
rand = "0.8.4"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
2 changes: 1 addition & 1 deletion load-tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This directory contains load tests for the MPC Recovery service. It is build usi
To run the tests, you need to have Rust installed. You can install Rust using [rustup](https://rustup.rs/).
To start the tests, run the following command:
```bash
cargo run --release -- --host <host> --report-file=load_test_results.html --test-plan "$(cat ./test_plans/short.txt)"
RUST_LOG=info cargo run --release -- --host <host> --report-file=load_test_results.html --test-plan "$(cat ./test_plans/short.txt)" --scenarios simpleMpcPublicKey
```
You can run Load Tests against your local development environment (check `/integration-tests` for more info) or against the staging environment by setting the `--host` parameter.

Expand Down
3 changes: 3 additions & 0 deletions load-tests/src/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub const VALID_OIDC_PROVIDER_KEY: &str = "-----BEGIN RSA PRIVATE KEY-----MIIJKAIBAAKCAgEAg6UuFBM3QmtQID8cOHltjM8WF/XpFj2d5feVShG19jan76n6kEQPIhbqC4gweqWWdKdwbOmJKvDzV7qER5BC2tKB7ViKRFpsEc5pSp2vc4w81Wni9Dzicpz1R0Qr2p3lkqLuG6G/nJaD6s0KMfiyPiIBSBOgd1gaGIZcN2MtZm4bT2cVBxgBBW9L3bkpyONf0JHtia+6M7LPwzKwd29LYuirPFU31psCBejXwuWst/KBncEeHASEW/LK0UJS4tJVH05mNuicBjKkYJ8Q+UTVZPA+8bgkrWEzScAoedVn+QwbwUxZ+C0r1NunllwU+e29s9rpf9wifzX43vA4FGPYdDuEPiGmaNqFTsV/Z8oOMLDuAt/QqFVrp24S6DyHy/aWAZcJzcAbwckP0B5GsrvbAogcWzPpRzFLFkPmsQ1IMG/MK382AJ04rh+u0jomXxImLYiDFvzEXTelNsiDICHY6PQ1Fd/OfxuKVFl4cVVx5VeyWOIAjRePaeMaijHr0KrxKDZiz+Umx8UJTwbjAfPx9fM5mvBXlmsXYAm/hmnp74xDlr/s8c4fAyXmuqRocu8jq0GkMDjYJKj2QQSZSLQUMxmeF6gRIFpjK8mawsSvM88Kiu6o/pZD3i0e3QL5OBwYjcd0muxY23yvcmdVmLeTds+wB0xAtA8wkWEu8N8SGXcCAwEAAQKCAgBaJCHAF0RQU4DjA7PEK8lKkIY1U+oNk5Vp4TS1KhlphRVK8x4h6KhgFEagLNndMUMrj3dY7DRDVgeaO5nWEr7kbR4QMf9DPJMhQjAwqnZ37T++dim0SXhZOIZvDQvmPxXyaWQXQZMdmqargUiI3RzXlJtCCkZnUclUn7PHLT7qE1zZ6uCoIdSZLxNIuEAXUTHLdBCtpckfG0JOC4hvz6JUELMntcZtSWiCOWR8DJ5OulvsdE60qpcjCsW7sellbNZigGFXGcG0MLsDege6V1qzKho/k3Jx0cu3pT9R5UGzc4oRusEkQXHw55MCTv0CAbtSywP1y/tHFeLabKxJsfCE6BciR7PCIuB0DD+4cP82AD3xu2HbJuw1ata8PnDSk1SwgCHnnj1Qh5ExVyPLQa6vlEqRI7gA52xB6q56YNWpEiLeEPWvnky4rq/w3xTEFoG9N4XkjQGD3PRLngdm/u3YKQ4uVrp2GwiNTsjN6eOcZYfffH2YNH4qf4tKmDInBmig4dQE/brXLAU7mh7x6gUH8EMm5lUaeQhKYfpSnJPdAJEKFZ5UYnMEKuDYUDIhs9yn9Vlzr4acIlnRvu/nM00NUwjZfWJDTbmbktRQANKQdnC41WcqCh9p1+zSbBlzmTSSIGXu+dnfTtKzswU7fFoMgS8FWfV+u5v1wjPO6GXUIQKCAQEA9ZbiE3oghHK3qQHseHllyxWShUY0xVa4K1nd1fHUDOwWR9/qW8V/m+c7tu8yya95DngWvK5zFhzgygP49QRc30W+CTZPTQ5UHEvmyzD3CuL5XCAXPSi+C+hpt6vAdM4ZkHSwAT5Ce1KjzN49xQS33H0QZA9CR6/gcnUoJJx1tdMPghHjJAOTlQaNPJVK+OXJmQIxDvJL7MB0UK084ELYeP+o6Qlt0aC+zAfMwMVAxpc+O/4QBig6d2a1+mi6jJYvFtH1UAWbE8WbQtEX1Lql2rxkJCGe6TYCY2rm2muVuYda5yYbr4CkzUCM8vNecgpuU82aVIsp/p0n7zO2FZ29BwKCAQEAiTnIqEfNmdX3a5rRNfX78c8A3rAK5jiiBiHHcu40Fd5ETGT/Fm2BsY+xfX+Ldgv4oc7RDTZReJPXr1Y0l9ht+0LUvY4BX5ym3ADImxwQ/tCV+U/El0ffDL+cNtuIR8XOHMP9WnuajqSo2I33a79r09jGbAMZNAAmoUTIsFXtB51CVEcHM/mMZpGMddpu6yvtEW9XhorCxANIAzqdyqB9/e9jChkIG/bGqMLzv2vZYxUxNTfnhYYhK5xmqvTyGxPKOLHa61e561FBnbom3EslIq8IkorkGqUtRby7w+NiSGpr+ChkmQiyfzSOhBs5Pc7areUXqLvQ9+MyO9/aG4wUEQKCAQAXtZxX0weGoeiXOWdR7i5kn82IblGz535aOQ/QksstADHaeISQnY2HSJicPZWCoR0nx3Iyfwj/ToRpHF8RkH1C1OHW09ZuEv8NyEocvbpr46O9QB/eOKu4TJTANaWb4TXYm1tOk2spqr3DjoUaGy2A7NYDQvHcJ9+cTTE176Dxj9HEdeOe23WJApvqCGO3ib+ftPV1gvDPh3jzPPZOlEV/0PbGoLFodoNVAT/EMIbjZUCN3CZB4epbEqBo72lrHyimpFhxhEkHbKFjnvoVAHv4lQ1564EC9MLgRDbLSW2n/qhI/oXXuKywYBX7coFgsx8ZmhTXKqRAP33WewCOL69LAoIBAE2nM1N2/nPVTuPHgihFAMN/XoCloiVRWu6ZYuI4xaSyWHfalzc71K6EH+5ipKqyb4oxHL+bQ1M2ZlFEORLMWMBcu0Jg/4n5fbr1fo+3vC5WHugsKZVqCGCQdXfdlyr2VoKUrePsGjQqHZoeDCse8Ye6Hd61iieRBkswP1j55t3uMcC7SOoyhy7rok52w1m1S7wYA7GRCFIfgTrCitRFKcbvFl56d8pLRXPujjx+bU/SiDwTXKKEmnSxVq/bWL3V3xNiIf4XcJAnNThqRN9YbrVH01QJ4LbrTcku2hoprE5KWrrdMMAg2dF+Dj/Xn/bH/Zt2DoNfdQsxuBWFwUjhZeECggEBANTpwOCTpEIv9AwFs7q3vvYl/dqVcjAliQLk7fq7U0C1pL5f51jrLQWwWLhpJhkQvnmVhUFAOqWxKFvvpJ4NQbjHldQzIou9rBofsHPju42yo0NC1zwyQy4SGl644Fg5jL5KxE2AdOsTkk47uBxdPfEcZOaF5oqY6yVk3x4qNOqfxqt/MUwyDviEHgd/TfHIvNcpLl7l1CcaHv/eobSB3XPjNXcXy1MTyolH0pg662eW8Su3h7qAhP4m7ArizpgnFgHEdarXF/g3OrMDgj2IPAzalHnGSuuSjLYE7fdjGcqZ9R6+ZUpk4Vwaba6tjzB1f/SU2Myampd4H+tkHbLyJJE=-----END RSA PRIVATE KEY-----";
pub const VALID_OIDC_AUD: &str = "test_audience";
pub const VALID_OIDC_ISS: &str = "https://securetoken.google.com/test_audience";
309 changes: 300 additions & 9 deletions load-tests/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,165 @@
use std::time::Duration;
mod constants;
mod primitives;
pub mod utils;

use goose::prelude::*;
use goose_eggs::{validate_and_load_static_assets, Validate};
use core::panic;
use reqwest::{header::CONTENT_TYPE, Body};
use std::{time::Duration, vec};
use tracing_subscriber::{filter, prelude::*};

use constants::VALID_OIDC_PROVIDER_KEY;
use goose::prelude::*;
use mpc_recovery::{
msg::{
ClaimOidcRequest, MpcPkRequest, NewAccountRequest, SignRequest, UserCredentialsRequest,
UserCredentialsResponse,
},
sign_node::oidc::OidcToken,
transaction::CreateAccountOptions,
utils::{
claim_oidc_request_digest, sign_digest, sign_request_digest,
user_credentials_request_digest,
},
};
use near_crypto::SecretKey;
use near_primitives::{
account::{AccessKey, AccessKeyPermission},
borsh::BorshSerialize,
delegate_action::DelegateAction,
transaction::{Action, AddKeyAction},
types::AccountId,
};
use primitives::UserSession;
use rand::{distributions::Alphanumeric, Rng};
use utils::build_send_and_check_request;

#[tokio::main]
async fn main() -> Result<(), GooseError> {
let stdout_log = tracing_subscriber::fmt::layer().pretty();

tracing_subscriber::registry()
.with(stdout_log.with_filter(filter::LevelFilter::INFO))
.init();

GooseAttack::initialize()?
.register_scenario(
scenario!("simple_mpc_public_key").register_transaction(transaction!(mpc_public_key)),
scenario!("registration")
.register_transaction(transaction!(prepare_user_credentials).set_sequence(1))
.register_transaction(transaction!(claim_oidc).set_sequence(2))
.register_transaction(transaction!(new_account).set_sequence(3)),
)
.register_scenario(
scenario!("registrationAndSign")
.register_transaction(transaction!(prepare_user_credentials).set_sequence(1))
.register_transaction(transaction!(claim_oidc).set_sequence(2))
.register_transaction(transaction!(new_account).set_sequence(3))
.register_transaction(transaction!(user_credentials).set_sequence(4))
.register_transaction(
transaction!(sign)
.set_sequence(5)
.set_weight(1000) // In this scenario we are mostly testing /sign functionality
.expect("Failed to set weight"),
),
)
.register_scenario(
scenario!("simpleClaimOidc")
.register_transaction(transaction!(prepare_user_credentials).set_sequence(1))
.register_transaction(
transaction!(claim_oidc)
.set_sequence(2)
.set_weight(100)
.expect("Failed to set weight"),
),
)
.register_scenario(
scenario!("simpleUserCredentials")
.register_transaction(transaction!(prepare_user_credentials).set_sequence(1))
.register_transaction(transaction!(claim_oidc).set_sequence(2))
.register_transaction(
transaction!(user_credentials)
.set_sequence(3)
.set_weight(100)
.expect("Failed to set weight"),
),
)
.register_scenario(
scenario!("simpleMpcPublicKey").register_transaction(transaction!(mpc_public_key)),
)
.execute()
.await?;

Ok(())
}

async fn mpc_public_key(user: &mut GooseUser) -> TransactionResult {
async fn prepare_user_credentials(user: &mut GooseUser) -> TransactionResult {
tracing::info!("prepare_user_credentials");
// Generate 2 key pairs
let fa_sk = SecretKey::from_random(near_crypto::KeyType::ED25519);
let la_sk = SecretKey::from_random(near_crypto::KeyType::ED25519);

// Create JWT with random sub (usually done by OIDC Provider)
let oidc_token = OidcToken::new(&utils::create_jwt_token(
VALID_OIDC_PROVIDER_KEY,
constants::VALID_OIDC_AUD,
constants::VALID_OIDC_ISS,
None,
));

// Generate random near account id
let account_id_rand: String = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(10)
.map(char::from)
.collect();

let near_account_id: AccountId = format!("acc-{}.near", account_id_rand.to_lowercase())
.try_into()
.expect("Failed to generate random account Id");

let session = UserSession {
jwt_token: oidc_token,
near_account_id,
fa_sk,
la_sk,
recovery_pk: None,
};

user.set_session_data(session);

Ok(())
}

async fn user_credentials(user: &mut GooseUser) -> TransactionResult {
tracing::info!("user_credentials");
let sesion = user
.get_session_data::<UserSession>()
.expect("Session Data must be set");

let oidc_token = sesion.jwt_token.clone();
let fa_sk = sesion.fa_sk.clone();
let fa_pk = fa_sk.public_key();
let la_sk = sesion.la_sk.clone();
let near_account_id = sesion.near_account_id.clone();

let user_credentials_request_digest =
user_credentials_request_digest(&oidc_token, &fa_pk).expect("Failed to create digest");

let user_credentials_frp_signature =
sign_digest(&user_credentials_request_digest, &fa_sk).expect("Failed to sign digest");

let user_credentials_request = UserCredentialsRequest {
oidc_token: oidc_token.clone(),
frp_public_key: fa_pk,
frp_signature: user_credentials_frp_signature,
};

let body_json =
serde_json::to_string(&user_credentials_request).expect("json serialization failed");

let body = Body::from(body_json.to_owned());
let request_builder = user
.get_request_builder(&GooseMethod::Post, "mpc_public_key")?
.body(Body::from("{}"))
.get_request_builder(&GooseMethod::Post, "user_credentials")?
.body(body)
.header(CONTENT_TYPE, "application/json")
.timeout(Duration::from_secs(10));

Expand All @@ -29,8 +169,159 @@ async fn mpc_public_key(user: &mut GooseUser) -> TransactionResult {

let goose_responce = user.request(goose_request).await?;

let validate = &Validate::builder().status(200).build();
validate_and_load_static_assets(user, goose_responce, validate).await?;
let response = goose_responce.response.expect("Expected response ... .");

let user_credentials_response = response
.json::<UserCredentialsResponse>()
.await
.expect("Failed to parse user credentials response");

if let UserCredentialsResponse::Ok { recovery_pk } = user_credentials_response {
tracing::info!("UserCredentialsResponce has Ok, setting session data");
let session = UserSession {
jwt_token: oidc_token,
near_account_id,
fa_sk,
la_sk,
recovery_pk: Some(recovery_pk),
};
user.set_session_data(session);
} else {
panic!(
"UserCredentialsResponce has Error: {:?}",
user_credentials_response
);
}

Ok(())
}

async fn mpc_public_key(user: &mut GooseUser) -> TransactionResult {
tracing::info!("mpc_public_key");
let body_json = serde_json::to_string(&MpcPkRequest {}).expect("json serialization failed");
build_send_and_check_request(user, "mpc_public_key", &body_json).await
}

async fn claim_oidc(user: &mut GooseUser) -> TransactionResult {
tracing::info!("claim_oidc");
let sesion = user
.get_session_data::<UserSession>()
.expect("Session Data must be set");
let oidc_token_hash = sesion.jwt_token.digest_hash();
let frp_secret_key = sesion.fa_sk.clone();
let frp_public_key = frp_secret_key.public_key();

let request_digest = claim_oidc_request_digest(&oidc_token_hash, &frp_public_key)
.expect("Failed to create digest");
let frp_signature =
sign_digest(&request_digest, &frp_secret_key).expect("Failed to sign digest");

let claim_oidc_request = ClaimOidcRequest {
oidc_token_hash: oidc_token_hash.to_owned(),
frp_public_key,
frp_signature,
};

let body_json = serde_json::to_string(&claim_oidc_request).expect("json serialization failed");

build_send_and_check_request(user, "claim_oidc", &body_json).await
}

async fn new_account(user: &mut GooseUser) -> TransactionResult {
let sesion = user
.get_session_data::<UserSession>()
.expect("Session Data must be set");
let oidc_token = sesion.jwt_token.clone();
let fa_secret_key = sesion.fa_sk.clone();
let fa_public_key = fa_secret_key.public_key();
let user_account_id = sesion.near_account_id.clone();

let create_account_options = CreateAccountOptions {
full_access_keys: Some(vec![fa_public_key.clone()]),
limited_access_keys: None,
contract_bytes: None,
};

let user_credentials_request_digest =
user_credentials_request_digest(&oidc_token, &fa_public_key)
.expect("Failed to create digest");

let user_credentials_frp_signature =
sign_digest(&user_credentials_request_digest, &fa_secret_key)
.expect("Failed to sign digest");

let new_account_request = NewAccountRequest {
near_account_id: user_account_id,
create_account_options,
oidc_token: sesion.jwt_token.clone(),
user_credentials_frp_signature,
frp_public_key: fa_public_key,
};

let body_json = serde_json::to_string(&new_account_request).expect("json serialization failed");
build_send_and_check_request(user, "new_account", &body_json).await
}

async fn sign(user: &mut GooseUser) -> TransactionResult {
tracing::info!("sign");
let session = user
.get_session_data::<UserSession>()
.expect("Session Data must be set");
let oidc_token = session.jwt_token.clone();
let fa_secret_key = session.fa_sk.clone();
let fa_public_key = fa_secret_key.public_key();
let account_id = session.near_account_id.clone();
let recovery_pk = session
.recovery_pk
.clone()
.expect("Recovery PK must be set before calling /sign");

let new_secret_key = SecretKey::from_random(near_crypto::KeyType::ED25519);
let new_public_key = new_secret_key.public_key();

let nonce = 0; // Set real nonce in case transaction is entend to be executed
let block_height = 0; // Set real block height in case transaction is entend to be executed

let add_key_delegate_action = DelegateAction {
sender_id: account_id.clone(),
receiver_id: account_id.clone(),
actions: vec![Action::AddKey(AddKeyAction {
public_key: new_public_key.clone(),
access_key: AccessKey {
nonce: 0,
permission: AccessKeyPermission::FullAccess,
},
})
.try_into()
.unwrap()],
nonce,
max_block_height: block_height + 100,
public_key: recovery_pk,
};

let sign_request_digest =
sign_request_digest(&add_key_delegate_action, &oidc_token, &fa_public_key)
.expect("Failed to create digest");
let sign_request_frp_signature =
sign_digest(&sign_request_digest, &fa_secret_key).expect("Failed to sign digest");

let user_credentials_request_digest =
user_credentials_request_digest(&oidc_token, &fa_public_key)
.expect("Failed to create digest");
let user_credentials_frp_signature =
sign_digest(&user_credentials_request_digest, &fa_secret_key)
.expect("Failed to sign digest");

let sign_request = SignRequest {
delegate_action: add_key_delegate_action
.try_to_vec()
.expect("Failed to serialize delegate action"),
oidc_token,
frp_signature: sign_request_frp_signature,
user_credentials_frp_signature,
frp_public_key: fa_public_key,
};

let body_json = serde_json::to_string(&sign_request).expect("json serialization failed");
build_send_and_check_request(user, "sign", &body_json).await
}
Loading

0 comments on commit 17c6649

Please sign in to comment.