Skip to content

Commit

Permalink
Support providing a key over the env for the Serai node
Browse files Browse the repository at this point in the history
  • Loading branch information
kayabaNerve committed Feb 8, 2024
1 parent cc1d4f4 commit 51edd82
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 27 deletions.
6 changes: 6 additions & 0 deletions Cargo.lock

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

9 changes: 9 additions & 0 deletions substrate/node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ workspace = true
name = "serai-node"

[dependencies]
zeroize = "1"
rand_core = "0.6"
hex = "0.4"

schnorrkel = "0.11"

sp-core = { git = "https://github.com/serai-dex/substrate" }
sp-keystore = { git = "https://github.com/serai-dex/substrate" }
sp-timestamp = { git = "https://github.com/serai-dex/substrate" }
sp-io = { git = "https://github.com/serai-dex/substrate" }
sp-blockchain = { git = "https://github.com/serai-dex/substrate" }
Expand Down Expand Up @@ -61,6 +68,8 @@ sc-rpc-api = { git = "https://github.com/serai-dex/substrate" }
substrate-frame-rpc-system = { git = "https://github.com/serai-dex/substrate" }
pallet-transaction-payment-rpc = { git = "https://github.com/serai-dex/substrate" }

serai-env = { path = "../../common/env" }

[build-dependencies]
substrate-build-script-utils = { git = "https://github.com/serai-dex/substrate" }

Expand Down
11 changes: 6 additions & 5 deletions substrate/node/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,23 +58,23 @@ pub fn run() -> sc_cli::Result<()> {

Some(Subcommand::CheckBlock(cmd)) => cli.create_runner(cmd)?.async_run(|config| {
let PartialComponents { client, task_manager, import_queue, .. } =
service::new_partial(&config)?;
service::new_partial(&config)?.0;
Ok((cmd.run(client, import_queue), task_manager))
}),

Some(Subcommand::ExportBlocks(cmd)) => cli.create_runner(cmd)?.async_run(|config| {
let PartialComponents { client, task_manager, .. } = service::new_partial(&config)?;
let PartialComponents { client, task_manager, .. } = service::new_partial(&config)?.0;
Ok((cmd.run(client, config.database), task_manager))
}),

Some(Subcommand::ExportState(cmd)) => cli.create_runner(cmd)?.async_run(|config| {
let PartialComponents { client, task_manager, .. } = service::new_partial(&config)?;
let PartialComponents { client, task_manager, .. } = service::new_partial(&config)?.0;
Ok((cmd.run(client, config.chain_spec), task_manager))
}),

Some(Subcommand::ImportBlocks(cmd)) => cli.create_runner(cmd)?.async_run(|config| {
let PartialComponents { client, task_manager, import_queue, .. } =
service::new_partial(&config)?;
service::new_partial(&config)?.0;
Ok((cmd.run(client, import_queue), task_manager))
}),

Expand All @@ -83,7 +83,8 @@ pub fn run() -> sc_cli::Result<()> {
}

Some(Subcommand::Revert(cmd)) => cli.create_runner(cmd)?.async_run(|config| {
let PartialComponents { client, task_manager, backend, .. } = service::new_partial(&config)?;
let PartialComponents { client, task_manager, backend, .. } =
service::new_partial(&config)?.0;
let aux_revert = Box::new(|client: Arc<FullClient>, backend, blocks| {
sc_consensus_babe::revert(client.clone(), backend, blocks)?;
sc_consensus_grandpa::revert(client, blocks)?;
Expand Down
107 changes: 107 additions & 0 deletions substrate/node/src/keystore.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use zeroize::Zeroize;
use rand_core::RngCore;

use sp_core::{crypto::*, ed25519, sr25519};
use sp_keystore::*;

pub struct Keystore(sr25519::Pair);

impl Keystore {
pub fn from_env() -> Option<Self> {
let mut key_hex = serai_env::var("KEY")?;
let mut key = hex::decode(&key_hex).expect("KEY from environment wasn't hex");
key_hex.zeroize();

assert_eq!(key.len(), 32, "KEY from environment wasn't 32 bytes");
key.extend([0; 32]);
rand_core::OsRng.fill_bytes(&mut key[32 ..]);

let res = Self(sr25519::Pair::from(schnorrkel::SecretKey::from_bytes(&key).unwrap()));
key.zeroize();
Some(res)
}
}

impl sp_keystore::Keystore for Keystore {
fn sr25519_public_keys(&self, _: KeyTypeId) -> Vec<sr25519::Public> {
vec![self.0.public()]
}

fn sr25519_generate_new(&self, _: KeyTypeId, _: Option<&str>) -> Result<sr25519::Public, Error> {
panic!("asked to generate an sr25519 key");
}

fn sr25519_sign(
&self,
_: KeyTypeId,
public: &sr25519::Public,
msg: &[u8],
) -> Result<Option<sr25519::Signature>, Error> {
if public == &self.0.public() {
Ok(Some(self.0.sign(msg)))
} else {
Ok(None)
}
}

fn sr25519_vrf_sign(
&self,
_: KeyTypeId,
public: &sr25519::Public,
data: &sr25519::vrf::VrfSignData,
) -> Result<Option<sr25519::vrf::VrfSignature>, Error> {
if public == &self.0.public() {
Ok(Some(self.0.vrf_sign(data)))
} else {
Ok(None)
}
}

fn sr25519_vrf_output(
&self,
_: KeyTypeId,
public: &sr25519::Public,
input: &sr25519::vrf::VrfInput,
) -> Result<Option<sr25519::vrf::VrfOutput>, Error> {
if public == &self.0.public() {
Ok(Some(self.0.vrf_output(input)))
} else {
Ok(None)
}
}

fn ed25519_public_keys(&self, _: KeyTypeId) -> Vec<ed25519::Public> {
panic!("asked for ed25519 keys");
}

fn ed25519_generate_new(&self, _: KeyTypeId, _: Option<&str>) -> Result<ed25519::Public, Error> {
panic!("asked to generate an ed25519 key");
}

fn ed25519_sign(
&self,
_: KeyTypeId,
_: &ed25519::Public,
_: &[u8],
) -> Result<Option<ed25519::Signature>, Error> {
panic!("asked to produce an ed25519 signature");
}

fn insert(&self, _: KeyTypeId, _: &str, _: &[u8]) -> Result<(), ()> {
panic!("asked to insert a key");
}

fn keys(&self, _: KeyTypeId) -> Result<Vec<Vec<u8>>, Error> {
Ok(vec![self.0.public().0.to_vec()])
}

fn has_keys(&self, public_keys: &[(Vec<u8>, KeyTypeId)]) -> bool {
let our_key = self.0.public().0;
for (public_key, _) in public_keys {
if our_key != public_key.as_slice() {
return false;
}
}
true
}
}
2 changes: 2 additions & 0 deletions substrate/node/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod keystore;

mod chain_spec;
mod service;

Expand Down
59 changes: 37 additions & 22 deletions substrate/node/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ fn create_inherent_data_providers(
(BabeInherent::from_timestamp_and_slot_duration(*timestamp, slot_duration), timestamp)
}

pub fn new_partial(config: &Configuration) -> Result<PartialComponents, ServiceError> {
pub fn new_partial(
config: &Configuration,
) -> Result<(PartialComponents, Arc<dyn sp_keystore::Keystore>), ServiceError> {
let telemetry = config
.telemetry_endpoints
.clone()
Expand Down Expand Up @@ -87,6 +89,13 @@ pub fn new_partial(config: &Configuration) -> Result<PartialComponents, ServiceE
)?;
let client = Arc::new(client);

let keystore: Arc<dyn sp_keystore::Keystore> =
if let Some(keystore) = crate::keystore::Keystore::from_env() {
Arc::new(keystore)
} else {
keystore_container.keystore()
};

let telemetry = telemetry.map(|(worker, telemetry)| {
task_manager.spawn_handle().spawn("telemetry", None, worker.run());
telemetry
Expand Down Expand Up @@ -137,29 +146,35 @@ pub fn new_partial(config: &Configuration) -> Result<PartialComponents, ServiceE
// This won't grow in size, so forgetting this isn't a disastrous memleak
std::mem::forget(babe_handle);

Ok(sc_service::PartialComponents {
client,
backend,
task_manager,
keystore_container,
select_chain,
import_queue,
transaction_pool,
other: (block_import, babe_link, grandpa_link, grandpa::SharedVoterState::empty(), telemetry),
})
Ok((
sc_service::PartialComponents {
client,
backend,
task_manager,
keystore_container,
select_chain,
import_queue,
transaction_pool,
other: (block_import, babe_link, grandpa_link, grandpa::SharedVoterState::empty(), telemetry),
},
keystore,
))
}

pub fn new_full(config: Configuration) -> Result<TaskManager, ServiceError> {
let sc_service::PartialComponents {
client,
backend,
mut task_manager,
import_queue,
let (
sc_service::PartialComponents {
client,
backend,
mut task_manager,
keystore_container: _,
import_queue,
select_chain,
transaction_pool,
other: (block_import, babe_link, grandpa_link, shared_voter_state, mut telemetry),
},
keystore_container,
select_chain,
transaction_pool,
other: (block_import, babe_link, grandpa_link, shared_voter_state, mut telemetry),
} = new_partial(&config)?;
) = new_partial(&config)?;

let mut net_config = sc_network::config::FullNetworkConfiguration::new(&config.network);
let grandpa_protocol_name =
Expand Down Expand Up @@ -195,7 +210,7 @@ pub fn new_full(config: Configuration) -> Result<TaskManager, ServiceError> {
sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions {
runtime_api_provider: client.clone(),
is_validator: config.role.is_authority(),
keystore: Some(keystore_container.keystore()),
keystore: Some(keystore_container.clone()),
offchain_db: backend.offchain_storage(),
transaction_pool: Some(OffchainTransactionPoolFactory::new(transaction_pool.clone())),
network_provider: network.clone(),
Expand All @@ -207,7 +222,7 @@ pub fn new_full(config: Configuration) -> Result<TaskManager, ServiceError> {
}

let role = config.role.clone();
let keystore = keystore_container.keystore();
let keystore = keystore_container;
let prometheus_registry = config.prometheus_registry().cloned();

// TODO: Ensure we're considered as an authority is a validator of an external network
Expand Down

0 comments on commit 51edd82

Please sign in to comment.