From a8159e90703cc28cad3db9606f5ae4a1604c136a Mon Sep 17 00:00:00 2001
From: Luke Parker <lukeparker5132@gmail.com>
Date: Wed, 11 Sep 2024 03:23:00 -0400
Subject: [PATCH] Bitcoin Key Gen

---
 Cargo.lock                       |  2 ++
 processor/bitcoin/Cargo.toml     |  2 ++
 processor/bitcoin/src/key_gen.rs | 26 ++++++++++++++++++++++++
 processor/bitcoin/src/main.rs    |  7 +------
 processor/key-gen/src/db.rs      | 34 +++++++++++++++++++-------------
 5 files changed, 51 insertions(+), 20 deletions(-)
 create mode 100644 processor/bitcoin/src/key_gen.rs

diff --git a/Cargo.lock b/Cargo.lock
index 1839cc98..8c0c3dd5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -8128,6 +8128,7 @@ dependencies = [
  "bitcoin-serai",
  "borsh",
  "ciphersuite",
+ "dkg",
  "env_logger",
  "flexible-transcript",
  "log",
@@ -8139,6 +8140,7 @@ dependencies = [
  "serai-db",
  "serai-env",
  "serai-message-queue",
+ "serai-processor-key-gen",
  "serai-processor-messages",
  "serai-processor-primitives",
  "serai-processor-scanner",
diff --git a/processor/bitcoin/Cargo.toml b/processor/bitcoin/Cargo.toml
index 54ace26f..c92e1384 100644
--- a/processor/bitcoin/Cargo.toml
+++ b/processor/bitcoin/Cargo.toml
@@ -25,6 +25,7 @@ borsh = { version = "1", default-features = false, features = ["std", "derive",
 
 transcript = { package = "flexible-transcript", path = "../../crypto/transcript", default-features = false, features = ["std", "recommended"] }
 ciphersuite = { path = "../../crypto/ciphersuite", default-features = false, features = ["std", "secp256k1"] }
+dkg = { path = "../../crypto/dkg", default-features = false, features = ["std", "evrf-secp256k1"] }
 frost = { package = "modular-frost", path = "../../crypto/frost", default-features = false }
 
 secp256k1 = { version = "0.29", default-features = false, features = ["std", "global-context", "rand-std"] }
@@ -41,6 +42,7 @@ serai-env = { path = "../../common/env" }
 serai-client = { path = "../../substrate/client", default-features = false, features = ["bitcoin"] }
 
 messages = { package = "serai-processor-messages", path = "../messages" }
+key-gen = { package = "serai-processor-key-gen", path = "../key-gen" }
 
 primitives = { package = "serai-processor-primitives", path = "../primitives" }
 scheduler = { package = "serai-processor-scheduler-primitives", path = "../scheduler/primitives" }
diff --git a/processor/bitcoin/src/key_gen.rs b/processor/bitcoin/src/key_gen.rs
new file mode 100644
index 00000000..16183231
--- /dev/null
+++ b/processor/bitcoin/src/key_gen.rs
@@ -0,0 +1,26 @@
+use ciphersuite::{group::GroupEncoding, Ciphersuite, Secp256k1};
+use frost::ThresholdKeys;
+
+use key_gen::KeyGenParams;
+
+use crate::scan::scanner;
+
+pub(crate) struct KeyGen;
+impl KeyGenParams for KeyGen {
+  const ID: &'static str = "Bitcoin";
+
+  type ExternalNetworkCurve = Secp256k1;
+
+  fn tweak_keys(keys: &mut ThresholdKeys<Self::ExternalNetworkCurve>) {
+    *keys = bitcoin_serai::wallet::tweak_keys(keys);
+    // Also create a scanner to assert these keys, and all expected paths, are usable
+    scanner(keys.group_key());
+  }
+
+  fn encode_key(key: <Self::ExternalNetworkCurve as Ciphersuite>::G) -> Vec<u8> {
+    let key = key.to_bytes();
+    let key: &[u8] = key.as_ref();
+    // Skip the parity encoding as we know this key is even
+    key[1 ..].to_vec()
+  }
+}
diff --git a/processor/bitcoin/src/main.rs b/processor/bitcoin/src/main.rs
index 2ff072b4..d86a4ba1 100644
--- a/processor/bitcoin/src/main.rs
+++ b/processor/bitcoin/src/main.rs
@@ -13,6 +13,7 @@ pub(crate) use primitives::*;
 mod scan;
 
 // App-logic trait satisfactions
+mod key_gen;
 mod rpc;
 mod scheduler;
 
@@ -224,12 +225,6 @@ impl Network for Bitcoin {
   // aggregation TX
   const COST_TO_AGGREGATE: u64 = 800;
 
-  fn tweak_keys(keys: &mut ThresholdKeys<Self::Curve>) {
-    *keys = tweak_keys(keys);
-    // Also create a scanner to assert these keys, and all expected paths, are usable
-    scanner(keys.group_key());
-  }
-
   #[cfg(test)]
   async fn get_block_number(&self, id: &[u8; 32]) -> usize {
     self.rpc.get_block_number(id).await.unwrap()
diff --git a/processor/key-gen/src/db.rs b/processor/key-gen/src/db.rs
index e82b84a5..676fd2aa 100644
--- a/processor/key-gen/src/db.rs
+++ b/processor/key-gen/src/db.rs
@@ -9,7 +9,7 @@ use dkg::{Participant, ThresholdCore, ThresholdKeys, evrf::EvrfCurve};
 use serai_validator_sets_primitives::Session;
 
 use borsh::{BorshSerialize, BorshDeserialize};
-use serai_db::{Get, DbTxn, create_db};
+use serai_db::{Get, DbTxn};
 
 use crate::KeyGenParams;
 
@@ -35,20 +35,26 @@ pub(crate) struct Participations {
   pub(crate) network_participations: HashMap<Participant, Vec<u8>>,
 }
 
-create_db!(
-  KeyGen {
-    Params: (session: &Session) -> RawParams,
-    Participations: (session: &Session) -> Participations,
-    KeyShares: (session: &Session) -> Vec<u8>,
-  }
-);
+mod _db {
+  use serai_validator_sets_primitives::Session;
+
+  use serai_db::{Get, DbTxn, create_db};
+
+  create_db!(
+    KeyGen {
+      Params: (session: &Session) -> super::RawParams,
+      Participations: (session: &Session) -> super::Participations,
+      KeyShares: (session: &Session) -> Vec<u8>,
+    }
+  );
+}
 
 pub(crate) struct KeyGenDb<P: KeyGenParams>(PhantomData<P>);
 impl<P: KeyGenParams> KeyGenDb<P> {
   pub(crate) fn set_params(txn: &mut impl DbTxn, session: Session, params: Params<P>) {
     assert_eq!(params.substrate_evrf_public_keys.len(), params.network_evrf_public_keys.len());
 
-    Params::set(
+    _db::Params::set(
       txn,
       &session,
       &RawParams {
@@ -68,7 +74,7 @@ impl<P: KeyGenParams> KeyGenDb<P> {
   }
 
   pub(crate) fn params(getter: &impl Get, session: Session) -> Option<Params<P>> {
-    Params::get(getter, &session).map(|params| Params {
+    _db::Params::get(getter, &session).map(|params| Params {
       t: params.t,
       n: params
         .network_evrf_public_keys
@@ -101,10 +107,10 @@ impl<P: KeyGenParams> KeyGenDb<P> {
     session: Session,
     participations: &Participations,
   ) {
-    Participations::set(txn, &session, participations)
+    _db::Participations::set(txn, &session, participations)
   }
   pub(crate) fn participations(getter: &impl Get, session: Session) -> Option<Participations> {
-    Participations::get(getter, &session)
+    _db::Participations::get(getter, &session)
   }
 
   // Set the key shares for a session.
@@ -121,7 +127,7 @@ impl<P: KeyGenParams> KeyGenDb<P> {
       keys.extend(substrate_keys.serialize().as_slice());
       keys.extend(network_keys.serialize().as_slice());
     }
-    KeyShares::set(txn, &session, &keys);
+    _db::KeyShares::set(txn, &session, &keys);
   }
 
   #[allow(clippy::type_complexity)]
@@ -129,7 +135,7 @@ impl<P: KeyGenParams> KeyGenDb<P> {
     getter: &impl Get,
     session: Session,
   ) -> Option<(Vec<ThresholdKeys<Ristretto>>, Vec<ThresholdKeys<P::ExternalNetworkCurve>>)> {
-    let keys = KeyShares::get(getter, &session)?;
+    let keys = _db::KeyShares::get(getter, &session)?;
     let mut keys: &[u8] = keys.as_ref();
 
     let mut substrate_keys = vec![];