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

feat: add persistent secret storage #396

Merged
merged 7 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,214 changes: 545 additions & 669 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion contract/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ edition = "2021"
crate-type = ["cdylib", "lib"]

[dependencies]
near-sdk = "4.1.1"
borsh = "1.3.0"
near-sdk = "5.0.0-alpha.1"
serde = { version = "1", features = ["derive"] }
schemars = "0.8"

Expand Down
13 changes: 12 additions & 1 deletion contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pub struct ResharingContractState {

#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Debug)]
pub enum ProtocolContractState {
NotInitialized,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need one more status?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a stop-gap for now, otherwise it's very hard to deploy multichain to GCP.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will document this behaviour

Initializing(InitializingContractState),
Running(RunningContractState),
Resharing(ResharingContractState),
Expand All @@ -77,7 +78,6 @@ pub struct MpcContract {

#[near_bindgen]
impl MpcContract {
// TODO: only add ignore_state for dev environments
#[init(ignore_state)]
pub fn init(threshold: usize, participants: BTreeMap<AccountId, ParticipantInfo>) -> Self {
MpcContract {
Expand Down Expand Up @@ -296,4 +296,15 @@ impl MpcContract {

#[allow(unused_variables)]
pub fn respond(&mut self, receipt_id: [u8; 32], big_r: String, s: String) {}

#[private]
#[init(ignore_state)]
pub fn clean(keys: Vec<near_sdk::json_types::Base64VecU8>) -> Self {
for key in keys.iter() {
env::storage_remove(&key.0);
ChaoticTempest marked this conversation as resolved.
Show resolved Hide resolved
}
Self {
protocol_state: ProtocolContractState::NotInitialized,
}
}
}
8 changes: 8 additions & 0 deletions infra/modules/multichain/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ resource "google_cloud_run_v2_service" "node" {
name = "AWS_DEFAULT_REGION"
value = var.indexer_options.s3_region
}
env {
name = "MPC_RECOVERY_GCP_PROJECT_ID"
value = var.project
}
env {
name = "MPC_RECOVERY_SK_SHARE_SECRET_ID"
Comment on lines +105 to +109
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we rename these to be multichain instead of mpc-recovery?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmm, not sure, maybe? I don't have a strong opinion so I just kept it as is for now

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made a ticket for this one too: #407

value = var.sk_share_secret_id
}

env {
name = "MPC_RECOVERY_WEB_PORT"
Expand Down
8 changes: 8 additions & 0 deletions infra/modules/multichain/variables.tf
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
variable "project" {
type = string
}

variable "region" {
}

Expand Down Expand Up @@ -67,3 +71,7 @@ variable "aws_access_key_secret_id" {
variable "aws_secret_key_secret_id" {
type = string
}

variable "sk_share_secret_id" {
type = string
}
22 changes: 21 additions & 1 deletion infra/multichain-dev/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,28 @@ resource "google_secret_manager_secret_iam_member" "aws_secret_key_secret_access
member = "serviceAccount:${google_service_account.service_account.email}"
}

resource "google_secret_manager_secret_iam_member" "sk_share_secret_access" {
count = length(var.node_configs)

secret_id = var.node_configs[count.index].sk_share_secret_id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_service_account.service_account.email}"
}

resource "google_secret_manager_secret_iam_member" "sk_share_secret_manager" {
count = length(var.node_configs)

secret_id = var.node_configs[count.index].sk_share_secret_id
role = "roles/secretmanager.secretVersionManager"
member = "serviceAccount:${google_service_account.service_account.email}"
}

module "node" {
count = length(var.node_configs)
source = "../modules/multichain"

service_name = "multichain-${var.env}-${count.index}"
project = var.project
region = var.region
service_account_email = google_service_account.service_account.email
docker_image = var.docker_image
Expand All @@ -103,11 +120,14 @@ module "node" {
cipher_sk_secret_id = var.node_configs[count.index].cipher_sk_secret_id
aws_access_key_secret_id = var.aws_access_key_secret_id
aws_secret_key_secret_id = var.aws_secret_key_secret_id
sk_share_secret_id = var.node_configs[count.index].sk_share_secret_id

depends_on = [
google_secret_manager_secret_iam_member.account_sk_secret_access,
google_secret_manager_secret_iam_member.cipher_sk_secret_access,
google_secret_manager_secret_iam_member.aws_access_key_secret_access,
google_secret_manager_secret_iam_member.aws_secret_key_secret_access
google_secret_manager_secret_iam_member.aws_secret_key_secret_access,
google_secret_manager_secret_iam_member.sk_share_secret_access,
google_secret_manager_secret_iam_member.sk_share_secret_manager
]
}
5 changes: 4 additions & 1 deletion infra/multichain-dev/terraform-dev.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ indexer_options = {
s3_bucket = "near-lake-data-testnet"
s3_region = "eu-central-1"
s3_url = null
start_block_height = 149685857
start_block_height = 152754054
}

aws_access_key_secret_id = "multichain-indexer-aws-access-key"
Expand All @@ -19,19 +19,22 @@ node_configs = [
address = "https://multichain-dev-0-7tk2cmmtcq-ue.a.run.app"
account_sk_secret_id = "multichain-account-sk-dev-0"
cipher_sk_secret_id = "multichain-cipher-sk-dev-0"
sk_share_secret_id = "multichain-sk-share-dev-0"
},
{
account = "multichain-node-dev-1.testnet"
cipher_pk = "349f89f6717a02aaf7a649b98ac7af09e6517ffd4cb7ea8a9f6edee8f84a330c"
address = "https://multichain-dev-1-7tk2cmmtcq-ue.a.run.app"
account_sk_secret_id = "multichain-account-sk-dev-1"
cipher_sk_secret_id = "multichain-cipher-sk-dev-1"
sk_share_secret_id = "multichain-sk-share-dev-1"
},
{
account = "multichain-node-dev-2.testnet"
cipher_pk = "78ae67d38afaa6d329bba0175c200a8c248a5a82d78fbb8f71e060e6c186d800"
address = "https://multichain-dev-2-7tk2cmmtcq-ue.a.run.app"
account_sk_secret_id = "multichain-account-sk-dev-2"
cipher_sk_secret_id = "multichain-cipher-sk-dev-2"
sk_share_secret_id = "multichain-sk-share-dev-2"
}
]
1 change: 1 addition & 0 deletions infra/multichain-dev/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ variable "node_configs" {
address = string
account_sk_secret_id = string
cipher_sk_secret_id = string
sk_share_secret_id = string
}))
}

Expand Down
3 changes: 1 addition & 2 deletions integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ serde_json = "1"
testcontainers = { version = "0.15", features = ["experimental"] }
tokio = { version = "1.28", features = ["full"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
near-workspaces = "0.8.0"
near-workspaces = { git = "https://github.com/near/near-workspaces-rs.git", branch = "main" }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

workspaces 0.8.0/0.9.0 depends on NEAR SDK 4.x which is no longer compilable due to a yanked dependency. See near/near-sdk-rs#1119.

So we have to use the unreleased version of workspaces for now

toml = "0.8.1"
sha2 = "0.10.8"

Expand All @@ -42,7 +42,6 @@ near-jsonrpc-client = "0.6"
near-primitives = "0.17"
near-lake-framework = { git = "https://github.com/near/near-lake-framework-rs.git", branch = "daniyar/upgrade-sdk" }
near-lake-primitives = { git = "https://github.com/near/near-lake-framework-rs.git", branch = "daniyar/upgrade-sdk" }
near-units = "0.2.0"

mpc-contract = { path = "../contract" }
mpc-keys = { path = "../keys" }
Expand Down
10 changes: 6 additions & 4 deletions integration-tests/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use bollard::exec::{CreateExecOptions, StartExecResults};
use futures::StreamExt;
use near_crypto::KeyFile;
use near_units::parse_near;
use near_workspaces::{
network::{Sandbox, ValidatorKey},
types::SecretKey,
Account, Worker,
};

use crate::env::containers::{self, LocalStack};
use near_workspaces::types::NearToken;
use testcontainers::{Container, GenericImage};

pub mod env;
Expand Down Expand Up @@ -156,13 +156,15 @@ pub async fn initialize_relayer<'a>(
sandbox::initialize_linkdrop(&worker).await?;
tracing::info!("Initializing relayer accounts...");
let relayer_account =
sandbox::create_account(&worker, "relayer", parse_near!("1000 N")).await?;
sandbox::create_account(&worker, "relayer", NearToken::from_near(1000)).await?;
let relayer_account_keys = sandbox::gen_rotating_keys(&relayer_account, 5).await?;

let creator_account = sandbox::create_account(&worker, "creator", parse_near!("200 N")).await?;
let creator_account =
sandbox::create_account(&worker, "creator", NearToken::from_near(200)).await?;
let creator_account_keys = sandbox::gen_rotating_keys(&creator_account, 5).await?;

let social_account = sandbox::create_account(&worker, "social", parse_near!("1000 N")).await?;
let social_account =
sandbox::create_account(&worker, "social", NearToken::from_near(1000)).await?;
tracing::info!(
"Relayer accounts initialized. Relayer account: {}, Creator account: {}, Social account: {}",
relayer_account.id(),
Expand Down
4 changes: 4 additions & 0 deletions integration-tests/src/multichain/containers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ impl<'a> Node<'a> {
start_block_height: 0,
},
my_address: None,
storage_options: mpc_recovery_node::storage::Options {
gcp_project_id: None,
sk_share_secret_id: None,
},
}
.into_str_args();
let image: GenericImage = GenericImage::new("near/mpc-recovery-node", "latest")
Expand Down
4 changes: 4 additions & 0 deletions integration-tests/src/multichain/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ impl Node {
start_block_height: 0,
},
my_address: None,
storage_options: mpc_recovery_node::storage::Options {
gcp_project_id: None,
sk_share_secret_id: None,
},
};

let mpc_node_id = format!("multichain/{node_id}");
Expand Down
3 changes: 2 additions & 1 deletion integration-tests/src/sandbox.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use near_workspaces::types::NearToken;
use near_workspaces::{network::Sandbox, types::SecretKey, AccessKey, Account, Contract, Worker};

const BATCH_COUNT_LIMIT: usize = 100;
Expand Down Expand Up @@ -41,7 +42,7 @@ pub async fn initialize_linkdrop(worker: &Worker<Sandbox>) -> anyhow::Result<()>
pub async fn create_account(
worker: &Worker<Sandbox>,
prefix: &str,
initial_balance: u128,
initial_balance: NearToken,
) -> anyhow::Result<Account> {
tracing::info!("Creating account with random account_id...");
let new_account = worker
Expand Down
3 changes: 2 additions & 1 deletion node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ cait-sith = { git = "https://github.com/LIT-Protocol/cait-sith.git", features =
"k256",
] }
clap = { version = "4.2", features = ["derive", "env"] }
google-secretmanager1 = "5"
hex = "0.4.3"
hkdf = "0.12.4"
k256 = { version = "0.13.1", features = ["sha256", "ecdsa", "serde"] }
Expand All @@ -40,7 +41,7 @@ near-fetch = "0.0.12"
near-lake-framework = { git = "https://github.com/near/near-lake-framework-rs.git", branch = "daniyar/upgrade-sdk" }
near-lake-primitives = { git = "https://github.com/near/near-lake-framework-rs.git", branch = "daniyar/upgrade-sdk" }
near-primitives = "0.17"
near-sdk = "4.1.1"
near-sdk = "5.0.0-alpha.1"

mpc-contract = { path = "../contract" }
mpc-keys = { path = "../keys" }
10 changes: 9 additions & 1 deletion node/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::protocol::{MpcSignProtocol, SignQueue};
use crate::{indexer, web};
use crate::{indexer, storage, web};
use cait_sith::protocol::Participant;
use clap::Parser;
use local_ip_address::local_ip;
Expand Down Expand Up @@ -51,6 +51,9 @@ pub enum Cli {
/// Local address that other peers can use to message this node.
#[arg(long, env("MPC_RECOVERY_LOCAL_ADDRESS"))]
my_address: Option<Url>,
/// Storage options
#[clap(flatten)]
storage_options: storage::Options,
},
}

Expand All @@ -73,6 +76,7 @@ impl Cli {
cipher_sk,
indexer_options,
my_address,
storage_options,
} => {
let mut args = vec![
"start".to_string(),
Expand All @@ -97,6 +101,7 @@ impl Cli {
args.extend(vec!["--my-address".to_string(), my_address.to_string()]);
}
args.extend(indexer_options.into_str_args());
args.extend(storage_options.into_str_args());
args
}
}
Expand Down Expand Up @@ -128,6 +133,7 @@ pub fn run(cmd: Cli) -> anyhow::Result<()> {
cipher_sk,
indexer_options,
my_address,
storage_options,
} => {
let sign_queue = Arc::new(RwLock::new(SignQueue::new()));
let a = indexer_options.clone();
Expand All @@ -141,6 +147,7 @@ pub fn run(cmd: Cli) -> anyhow::Result<()> {
.build()?
.block_on(async {
let (sender, receiver) = mpsc::channel(16384);
let key_storage = storage::init(&storage_options).await?;

let my_address = my_address.unwrap_or_else(|| {
let my_ip = local_ip().unwrap();
Expand All @@ -159,6 +166,7 @@ pub fn run(cmd: Cli) -> anyhow::Result<()> {
receiver,
sign_queue.clone(),
hpke::PublicKey::try_from_bytes(&hex::decode(cipher_pk)?)?,
key_storage,
);
tracing::debug!("protocol initialized");
let protocol_handle = tokio::spawn(async move { protocol.run().await });
Expand Down
3 changes: 2 additions & 1 deletion node/src/indexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::RwLock;

/// Configures exporter of span and trace data.
/// Configures indexer.
#[derive(Debug, Clone, clap::Parser)]
#[group(id = "indexer_options")]
pub struct Options {
/// AWS S3 bucket name for NEAR Lake Indexer
#[clap(
Expand Down
1 change: 1 addition & 0 deletions node/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod indexer;
pub mod kdf;
pub mod protocol;
pub mod rpc_client;
pub mod storage;
pub mod types;
pub mod util;
pub mod web;
12 changes: 8 additions & 4 deletions node/src/protocol/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use crate::protocol::presignature::PresignatureManager;
use crate::protocol::signature::SignatureManager;
use crate::protocol::state::{GeneratingState, ResharingState};
use crate::protocol::triple::TripleManager;
use crate::types::PrivateKeyShare;
use crate::storage::{SecretNodeStorageBox, SecretStorageError};
use crate::types::SecretKeyShare;
use crate::util::AffinePointExt;
use crate::{http_client, rpc_client};
use async_trait::async_trait;
Expand All @@ -34,6 +35,7 @@ pub trait ConsensusCtx {
fn cipher_pk(&self) -> &hpke::PublicKey;
fn sign_pk(&self) -> near_crypto::PublicKey;
fn sign_sk(&self) -> &near_crypto::SecretKey;
fn secret_storage(&self) -> &SecretNodeStorageBox;
}

#[derive(thiserror::Error, Debug)]
Expand All @@ -52,6 +54,8 @@ pub enum ConsensusError {
HasBeenKicked,
#[error("cait-sith initialization error: {0}")]
CaitSithInitializationError(#[from] InitializationError),
#[error("secret storage error: {0}")]
SecretStorageError(#[from] SecretStorageError),
}

#[async_trait]
Expand Down Expand Up @@ -607,8 +611,8 @@ impl ConsensusProtocol for NodeState {
) -> Result<NodeState, ConsensusError> {
match self {
NodeState::Starting => {
// TODO: Load from persistent storage
Ok(NodeState::Started(StartedState(None)))
let node_data = ctx.secret_storage().load().await?;
Ok(NodeState::Started(StartedState(node_data)))
}
NodeState::Started(state) => state.advance(ctx, contract_state).await,
NodeState::Generating(state) => state.advance(ctx, contract_state).await,
Expand All @@ -621,7 +625,7 @@ impl ConsensusProtocol for NodeState {
}

fn start_resharing<C: ConsensusCtx>(
private_share: Option<PrivateKeyShare>,
private_share: Option<SecretKeyShare>,
ctx: C,
contract_state: ResharingContractState,
) -> Result<NodeState, ConsensusError> {
Expand Down
1 change: 1 addition & 0 deletions node/src/protocol/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ impl TryFrom<ProtocolContractState> for ProtocolState {

fn try_from(value: ProtocolContractState) -> Result<Self, Self::Error> {
match value {
ProtocolContractState::NotInitialized => Err(()),
ProtocolContractState::Initializing(state) => {
Ok(ProtocolState::Initializing(state.into()))
}
Expand Down
Loading
Loading