Skip to content

Commit

Permalink
Save operator winternitz
Browse files Browse the repository at this point in the history
  • Loading branch information
ekrembal committed Dec 23, 2024
1 parent 8bf7a57 commit 553f935
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 30 deletions.
9 changes: 9 additions & 0 deletions core/src/actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub enum TxType {
TimeTx,
KickoffTx,
BitVM,
OperatorLongestChain,
WatchtowerChallenge,
}

/// Derivation path specification for Winternitz one time public key generation.
Expand All @@ -31,6 +33,7 @@ pub struct WinternitzDerivationPath {
pub operator_idx: Option<u32>,
pub watchtower_idx: Option<u32>,
pub time_tx_idx: Option<u32>,
pub intermediate_step_idx: Option<u32>,
}
impl WinternitzDerivationPath {
fn to_vec(self) -> Vec<u8> {
Expand All @@ -50,6 +53,10 @@ impl WinternitzDerivationPath {
None => 0,
Some(i) => i + 1,
};
let intermediate_step_idx = match self.intermediate_step_idx {
None => 0,
Some(i) => i + 1,
};

[
vec![self.tx_type as u8],
Expand All @@ -58,6 +65,7 @@ impl WinternitzDerivationPath {
operator_idx.to_be_bytes(),
watchtower_idx.to_be_bytes(),
time_tx_idx.to_be_bytes(),
intermediate_step_idx.to_be_bytes(),
]
.concat(),
]
Expand All @@ -74,6 +82,7 @@ impl Default for WinternitzDerivationPath {
operator_idx: Default::default(),
watchtower_idx: Default::default(),
time_tx_idx: Default::default(),
intermediate_step_idx: Default::default(),
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions core/src/builder/sighash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub async fn dummy(
{
// get the watchtower winternitz pubkeys for this operator
let watchtower_challenge_wots: Vec<_> = (0..config.num_watchtowers)
.map(|i| db.get_winternitz_public_keys(None, i as u32, operator_idx as u32))
.map(|i| db.get_watchtower_winternitz_public_keys(None, i as u32, operator_idx as u32))
.collect();
let watchtower_challenge_wots =
futures::future::try_join_all(watchtower_challenge_wots).await?;
Expand Down Expand Up @@ -172,7 +172,7 @@ pub fn create_nofn_sighash_stream(
{
// get the watchtower winternitz pubkeys for this operator
let watchtower_challenge_wots: Vec<_> = (0..config.num_watchtowers)
.map(|i| db.get_winternitz_public_keys(None, i as u32, operator_idx as u32))
.map(|i| db.get_watchtower_winternitz_public_keys(None, i as u32, operator_idx as u32))
.collect();
let watchtower_challenge_wots =
futures::future::try_join_all(watchtower_challenge_wots).await?;
Expand Down
2 changes: 2 additions & 0 deletions core/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ pub const MAX_BITVM_CHALLENGE_RESPONSE_BLOCKS: u32 = 5;
pub type VerifierChallenge = (BlockHash, U256, u8);

// pub const TEST_MODE: bool = true;

pub const NUM_INTERMEDIATE_STEPS: usize = 100;
77 changes: 67 additions & 10 deletions core/src/database/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -974,7 +974,7 @@ impl Database {

/// Sets Winternitz public keys for an operator.
#[tracing::instrument(skip(self, tx, winternitz_public_key), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))]
pub async fn save_winternitz_public_key(
pub async fn save_watchtower_winternitz_pk(
&self,
tx: Option<&mut sqlx::Transaction<'_, Postgres>>,
watchtower_id: u32,
Expand All @@ -988,7 +988,7 @@ impl Database {
let wpk = borsh::to_vec(&wpks).map_err(BridgeError::BorschError)?;

let query = sqlx::query(
"INSERT INTO winternitz_public_keys (watchtower_id, operator_id, winternitz_public_keys) VALUES ($1, $2, $3);",
"INSERT INTO watchtower_winternitz_public_keys (watchtower_id, operator_id, winternitz_public_keys) VALUES ($1, $2, $3);",
)
.bind(watchtower_id as i64)
.bind(operator_id as i64)
Expand All @@ -1004,14 +1004,14 @@ impl Database {

/// Gets Winternitz public keys for every time_tx of an operator and a watchtower.
#[tracing::instrument(skip(self, tx), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))]
pub async fn get_winternitz_public_keys(
pub async fn get_watchtower_winternitz_public_keys(
&self,
tx: Option<&mut sqlx::Transaction<'_, Postgres>>,
watchtower_id: u32,
operator_id: u32,
) -> Result<Vec<winternitz::PublicKey>, BridgeError> {
let query = sqlx::query_as(
"SELECT winternitz_public_keys FROM winternitz_public_keys WHERE operator_id = $1 AND watchtower_id = $2;",
"SELECT winternitz_public_keys FROM watchtower_winternitz_public_keys WHERE operator_id = $1 AND watchtower_id = $2;",
)
.bind(operator_id as i64)
.bind(watchtower_id as i64);
Expand All @@ -1021,10 +1021,62 @@ impl Database {
None => query.fetch_one(&self.connection).await,
}?;

let winternitz_public_keys: Vec<winternitz::PublicKey> =
let watchtower_winternitz_public_keys: Vec<winternitz::PublicKey> =
borsh::from_slice(&wpks.0).map_err(BridgeError::BorschError)?;

Ok(winternitz_public_keys)
Ok(watchtower_winternitz_public_keys)
}

/// Sets Winternitz public keys for an operator.
#[tracing::instrument(skip(self, tx, winternitz_public_key), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))]
pub async fn save_operator_winternitz_pk(
&self,
tx: Option<&mut sqlx::Transaction<'_, Postgres>>,
operator_id: u32,
winternitz_public_key: Vec<WinternitzPublicKey>,
) -> Result<(), BridgeError> {
let wpks = winternitz_public_key
.into_iter()
.map(|wpk| wpk.public_key)
.collect::<Vec<_>>();
let wpk = borsh::to_vec(&wpks).map_err(BridgeError::BorschError)?;

let query = sqlx::query(
"INSERT INTO operator_winternitz_public_keys (operator_id, winternitz_public_keys) VALUES ($1, $2);",
)
.bind(operator_id as i64)
.bind(wpk);

match tx {
Some(tx) => query.execute(&mut **tx).await,
None => query.execute(&self.connection).await,
}?;

Ok(())
}

/// Gets Winternitz public keys for every time_tx of an operator and a watchtower.
#[tracing::instrument(skip(self, tx), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))]
pub async fn get_operator_winternitz_public_keys(
&self,
tx: Option<&mut sqlx::Transaction<'_, Postgres>>,
watchtower_id: u32,
operator_id: u32,
) -> Result<Vec<winternitz::PublicKey>, BridgeError> {
let query = sqlx::query_as(
"SELECT winternitz_public_keys FROM operator_winternitz_public_keys WHERE operator_id = $1;",
)
.bind(operator_id as i64);

let wpks: (Vec<u8>,) = match tx {
Some(tx) => query.fetch_one(&mut **tx).await,
None => query.fetch_one(&self.connection).await,
}?;

let watchtower_winternitz_public_keys: Vec<winternitz::PublicKey> =
borsh::from_slice(&wpks.0).map_err(BridgeError::BorschError)?;

Ok(watchtower_winternitz_public_keys)
}
}

Expand Down Expand Up @@ -1812,7 +1864,7 @@ mod tests {
// Assuming there are 2 time_txs.
let wpk0: winternitz::PublicKey = vec![[0x45; 20], [0x1F; 20]];
let wpk1: winternitz::PublicKey = vec![[0x12; 20], [0x34; 20]];
let winternitz_public_keys = vec![
let watchtower_winternitz_public_keys = vec![
WinternitzPublicKey {
public_key: wpk0.clone(),
parameters: Parameters::new(0, 4),
Expand All @@ -1824,16 +1876,21 @@ mod tests {
];

database
.save_winternitz_public_key(None, 0x45, 0x1F, winternitz_public_keys.clone())
.save_watchtower_winternitz_pk(
None,
0x45,
0x1F,
watchtower_winternitz_public_keys.clone(),
)
.await
.unwrap();

let read_wpks = database
.get_winternitz_public_keys(None, 0x45, 0x1F)
.get_watchtower_winternitz_public_keys(None, 0x45, 0x1F)
.await
.unwrap();

assert_eq!(winternitz_public_keys.len(), read_wpks.len());
assert_eq!(watchtower_winternitz_public_keys.len(), read_wpks.len());
assert_eq!(wpk0, read_wpks[0]);
assert_eq!(wpk1, read_wpks[1]);
}
Expand Down
21 changes: 19 additions & 2 deletions core/src/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::actor::{Actor, WinternitzDerivationPath};
use crate::builder::transaction::KICKOFF_UTXO_AMOUNT_SATS;
use crate::builder::{self};
use crate::config::BridgeConfig;
use crate::constants::NUM_INTERMEDIATE_STEPS;
use crate::database::Database;
use crate::errors::BridgeError;
use crate::extended_rpc::ExtendedRpc;
Expand Down Expand Up @@ -709,16 +710,32 @@ impl Operator {

for time_tx in 0..self.config.num_time_txs as u32 {
let path = WinternitzDerivationPath {
message_length: 480,
message_length: 128,
log_d: 4,
tx_type: crate::actor::TxType::BitVM,
tx_type: crate::actor::TxType::OperatorLongestChain,
index: Some(self.idx as u32),
operator_idx: None,
watchtower_idx: None,
time_tx_idx: Some(time_tx),
intermediate_step_idx: None,
};

winternitz_pubkeys.push(self.signer.derive_winternitz_pk(path)?);

for intermediate_step in 0..NUM_INTERMEDIATE_STEPS as u32 {
let path = WinternitzDerivationPath {
message_length: 40,
log_d: 4,
tx_type: crate::actor::TxType::BitVM,
index: Some(self.idx as u32),
operator_idx: None,
watchtower_idx: None,
time_tx_idx: Some(time_tx),
intermediate_step_idx: Some(intermediate_step),
};

winternitz_pubkeys.push(self.signer.derive_winternitz_pk(path)?);
}
}

Ok(winternitz_pubkeys)
Expand Down
9 changes: 6 additions & 3 deletions core/src/rpc/aggregator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ mod tests {

#[tokio::test]
#[serial_test::serial]
async fn aggregator_setup_winternitz_public_keys() {
async fn aggregator_setup_watchtower_winternitz_public_keys() {
let mut config = create_test_config_with_thread_name!(None);

let (_verifiers, _operators, aggregator, _watchtowers) = create_actors!(config.clone());
Expand All @@ -466,7 +466,10 @@ mod tests {
.unwrap();

let watchtower = Watchtower::new(config.clone()).await.unwrap();
let watchtower_wpks = watchtower.get_winternitz_public_keys().await.unwrap();
let watchtower_wpks = watchtower
.get_watchtower_winternitz_public_keys()
.await
.unwrap();

let rpc = ExtendedRpc::new(
config.bitcoin_rpc_url.clone(),
Expand All @@ -479,7 +482,7 @@ mod tests {
let verifier = Verifier::new(rpc, config.clone()).await.unwrap();
let verifier_wpks = verifier
.db
.get_winternitz_public_keys(None, 0, 0)
.get_watchtower_winternitz_public_keys(None, 0, 0)
.await
.unwrap();

Expand Down
33 changes: 27 additions & 6 deletions core/src/rpc/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,26 @@ impl ClementineVerifier for Verifier {
.save_timeout_tx_sigs(None, operator_config.operator_idx, timeout_tx_sigs)
.await?;

// Convert RPC type into BitVM type.
let operator_winternitz_public_keys = operator_params
.winternitz_pubkeys
.into_iter()
.map(|wpk| {
Ok(WinternitzPublicKey {
public_key: wpk.to_bitvm(),
parameters: winternitz::Parameters::new(0, 4), // TODO: Fix this.
})
})
.collect::<Result<Vec<_>, BridgeError>>()?;

self.db
.save_operator_winternitz_pk(
None,
operator_config.operator_idx,
operator_winternitz_public_keys,
)
.await?;

Ok(Response::new(Empty {}))
}

Expand All @@ -158,34 +178,35 @@ impl ClementineVerifier for Verifier {
let watchtower_params = request.into_inner();

// Convert RPC type into BitVM type.
let winternitz_public_keys = watchtower_params
let watchtower_winternitz_public_keys = watchtower_params
.winternitz_pubkeys
.into_iter()
.map(|wpk| {
Ok(WinternitzPublicKey {
public_key: wpk.to_bitvm(),
parameters: winternitz::Parameters::new(0, 4),
parameters: winternitz::Parameters::new(0, 4), // TODO: Fix this.
})
})
.collect::<Result<Vec<_>, BridgeError>>()?;

let required_number_of_pubkeys = self.config.num_operators * self.config.num_time_txs;
if winternitz_public_keys.len() != required_number_of_pubkeys {
if watchtower_winternitz_public_keys.len() != required_number_of_pubkeys {
return Err(Status::invalid_argument(format!(
"Request has {} Winternitz public keys but it needs to be {}!",
winternitz_public_keys.len(),
watchtower_winternitz_public_keys.len(),
required_number_of_pubkeys
)));
}

for operator_idx in 0..self.config.num_operators {
let index = operator_idx * self.config.num_time_txs;
self.db
.save_winternitz_public_key(
.save_watchtower_winternitz_pk(
None,
watchtower_params.watchtower_id,
operator_idx as u32,
winternitz_public_keys[index..index + self.config.num_time_txs].to_vec(),
watchtower_winternitz_public_keys[index..index + self.config.num_time_txs]
.to_vec(),
)
.await?;
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/rpc/watchtower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ impl ClementineWatchtower for Watchtower {
_request: Request<Empty>,
) -> Result<Response<WatchtowerParams>, Status> {
let winternitz_pubkeys = self
.get_winternitz_public_keys()
.get_watchtower_winternitz_public_keys()
.await?
.into_iter()
.map(WinternitzPubkey::from_bitvm)
Expand Down
Loading

0 comments on commit 553f935

Please sign in to comment.