Create a vote transaction upon GeneratedKeyPair

This commit is contained in:
Luke Parker 2023-05-10 00:46:51 -04:00
parent c95bdb6752
commit 168f2899f0
No known key found for this signature in database
4 changed files with 90 additions and 11 deletions

2
Cargo.lock generated
View file

@ -1319,6 +1319,7 @@ dependencies = [
"ciphersuite", "ciphersuite",
"flexible-transcript", "flexible-transcript",
"futures", "futures",
"hex",
"lazy_static", "lazy_static",
"log", "log",
"modular-frost", "modular-frost",
@ -1326,6 +1327,7 @@ dependencies = [
"processor-messages", "processor-messages",
"rand_core 0.6.4", "rand_core 0.6.4",
"schnorr-signatures", "schnorr-signatures",
"schnorrkel",
"serai-client", "serai-client",
"serai-db", "serai-db",
"sp-application-crypto", "sp-application-crypto",

View file

@ -29,6 +29,7 @@ frost = { package = "modular-frost", path = "../crypto/frost" }
scale = { package = "parity-scale-codec", version = "3", features = ["derive"] } scale = { package = "parity-scale-codec", version = "3", features = ["derive"] }
schnorrkel = "0.10"
sp-application-crypto = { git = "https://github.com/serai-dex/substrate", default-features = false } sp-application-crypto = { git = "https://github.com/serai-dex/substrate", default-features = false }
serai-db = { path = "../common/db" } serai-db = { path = "../common/db" }
@ -38,6 +39,7 @@ tributary = { package = "tributary-chain", path = "./tributary" }
serai-client = { path = "../substrate/client", features = ["serai"] } serai-client = { path = "../substrate/client", features = ["serai"] }
hex = "0.4"
log = "0.4" log = "0.4"
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }

View file

@ -12,10 +12,22 @@ use std::{
use zeroize::Zeroizing; use zeroize::Zeroizing;
use rand_core::OsRng; use rand_core::OsRng;
use ciphersuite::{group::ff::Field, Ciphersuite, Ristretto}; use blake2::Digest;
use ciphersuite::{
group::{
ff::{Field, PrimeField},
GroupEncoding,
},
Ciphersuite, Ristretto,
};
use serai_db::{DbTxn, Db, MemDb}; use serai_db::{DbTxn, Db, MemDb};
use serai_client::Serai;
use serai_client::{
subxt::{config::extrinsic_params::BaseExtrinsicParamsBuilder, tx::Signer},
Public, PairSigner, Serai,
};
use tokio::{ use tokio::{
sync::{ sync::{
@ -316,8 +328,11 @@ pub async fn handle_p2p<D: Db, P: P2p>(
// missing // missing
// sync_block waiting is preferable since we know the block is valid by its commit, meaning // sync_block waiting is preferable since we know the block is valid by its commit, meaning
// we are the node behind // we are the node behind
// A for 1/2, 1 may be preferable since this message may frequently occur // As for 1/2, 1 may be preferable since this message may frequently occur
// We at least need to check if we take value from this message before running spawn // This is suitably performant, as tokio HTTP servers will even spawn a new task per
// connection
// In order to reduce congestion though, we should at least check if we take value from
// this message before running spawn
// TODO // TODO
tokio::spawn({ tokio::spawn({
let tributaries = tributaries.clone(); let tributaries = tributaries.clone();
@ -362,11 +377,25 @@ pub async fn publish_transaction<D: Db, P: P2p>(
pub async fn handle_processors<D: Db, Pro: Processors, P: P2p>( pub async fn handle_processors<D: Db, Pro: Processors, P: P2p>(
mut db: D, mut db: D,
key: Zeroizing<<Ristretto as Ciphersuite>::F>, key: Zeroizing<<Ristretto as Ciphersuite>::F>,
serai: Serai,
mut processors: Pro, mut processors: Pro,
tributaries: Arc<RwLock<HashMap<[u8; 32], ActiveTributary<D, P>>>>, tributaries: Arc<RwLock<HashMap<[u8; 32], ActiveTributary<D, P>>>>,
) { ) {
let pub_key = Ristretto::generator() * key.deref(); let pub_key = Ristretto::generator() * key.deref();
// TODO: This is cursed. serai_client has to handle this for us
let substrate_signer = {
let mut bytes = Zeroizing::new([0; 96]);
// Private key
bytes[.. 32].copy_from_slice(&key.to_repr());
// Nonce
let nonce = Zeroizing::new(blake2::Blake2s256::digest(&bytes));
bytes[32 .. 64].copy_from_slice(nonce.as_ref());
// Public key
bytes[64 ..].copy_from_slice(&pub_key.to_bytes());
PairSigner::new(schnorrkel::keys::Keypair::from_bytes(bytes.as_ref()).unwrap().into())
};
loop { loop {
let msg = processors.recv().await; let msg = processors.recv().await;
@ -384,15 +413,57 @@ pub async fn handle_processors<D: Db, Pro: Processors, P: P2p>(
}; };
let tx = match msg.msg { let tx = match msg.msg {
ProcessorMessage::KeyGen(msg) => match msg { ProcessorMessage::KeyGen(inner_msg) => match inner_msg {
key_gen::ProcessorMessage::Commitments { id, commitments } => { key_gen::ProcessorMessage::Commitments { id, commitments } => {
Some(Transaction::DkgCommitments(id.attempt, commitments, Transaction::empty_signed())) Some(Transaction::DkgCommitments(id.attempt, commitments, Transaction::empty_signed()))
} }
key_gen::ProcessorMessage::Shares { id, shares } => { key_gen::ProcessorMessage::Shares { id, shares } => {
Some(Transaction::DkgShares(id.attempt, shares, Transaction::empty_signed())) Some(Transaction::DkgShares(id.attempt, shares, Transaction::empty_signed()))
} }
// TODO key_gen::ProcessorMessage::GeneratedKeyPair { id, substrate_key, coin_key } => {
key_gen::ProcessorMessage::GeneratedKeyPair { .. } => todo!(), assert_eq!(
id.set.network, msg.network,
"processor claimed to be a different network than it was for SubstrateBlockAck",
);
// TODO: Also check the other KeyGenId fields
// TODO: Is this safe?
let Ok(nonce) = serai.get_nonce(&substrate_signer.address()).await else {
log::error!("couldn't connect to Serai node to get nonce");
todo!(); // TODO
};
let tx = serai
.sign(
&substrate_signer,
&Serai::vote(
msg.network,
(
Public(substrate_key),
coin_key
.try_into()
.expect("external key from processor exceeded max external key length"),
),
),
nonce,
BaseExtrinsicParamsBuilder::new(),
)
.expect(
"tried to sign an invalid payload despite creating the payload via serai_client",
);
match serai.publish(&tx).await {
Ok(hash) => {
log::info!("voted on key pair for {:?} in TX {}", id.set, hex::encode(hash))
}
Err(e) => {
log::error!("couldn't connect to Serai node to publish TX: {:?}", e);
todo!(); // TODO
}
}
None
}
}, },
ProcessorMessage::Sign(msg) => match msg { ProcessorMessage::Sign(msg) => match msg {
sign::ProcessorMessage::Preprocess { id, preprocess } => { sign::ProcessorMessage::Preprocess { id, preprocess } => {
@ -423,8 +494,8 @@ pub async fn handle_processors<D: Db, Pro: Processors, P: P2p>(
ProcessorMessage::Coordinator(inner_msg) => match inner_msg { ProcessorMessage::Coordinator(inner_msg) => match inner_msg {
coordinator::ProcessorMessage::SubstrateBlockAck { network, block, plans } => { coordinator::ProcessorMessage::SubstrateBlockAck { network, block, plans } => {
assert_eq!( assert_eq!(
msg.network, network, network, msg.network,
"processor claimed to be a different network than it was", "processor claimed to be a different network than it was for SubstrateBlockAck",
); );
// Safe to use its own txn since this is static and just needs to be written before we // Safe to use its own txn since this is static and just needs to be written before we
@ -600,7 +671,7 @@ pub async fn run<D: Db, Pro: Processors, P: P2p>(
tokio::spawn(handle_p2p(Ristretto::generator() * key.deref(), p2p, tributaries.clone())); tokio::spawn(handle_p2p(Ristretto::generator() * key.deref(), p2p, tributaries.clone()));
// Handle all messages from processors // Handle all messages from processors
handle_processors(raw_db, key, processors, tributaries).await; handle_processors(raw_db, key, serai, processors, tributaries).await;
} }
#[tokio::main] #[tokio::main]

View file

@ -4,7 +4,10 @@ use scale::{Encode, Decode, Compact};
mod scale_value; mod scale_value;
pub(crate) use scale_value::{Value, Composite, scale_value, scale_composite}; pub(crate) use scale_value::{Value, Composite, scale_value, scale_composite};
use sp_core::{Pair as PairTrait, sr25519::Pair}; pub use sp_core::{
Pair as PairTrait,
sr25519::{Public, Pair},
};
pub use subxt; pub use subxt;
use subxt::{ use subxt::{
@ -294,6 +297,7 @@ impl Serai {
TxClient::new(self.0.offline()) TxClient::new(self.0.offline())
.create_signed_with_nonce(payload, signer, nonce, params) .create_signed_with_nonce(payload, signer, nonce, params)
.map(|tx| Encoded(tx.into_encoded())) .map(|tx| Encoded(tx.into_encoded()))
// TODO: Don't have this potentially return an error (requires modifying the Payload type)
.map_err(|_| SeraiError::InvalidRuntime) .map_err(|_| SeraiError::InvalidRuntime)
} }