mirror of
https://github.com/serai-dex/serai.git
synced 2024-11-16 17:07:35 +00:00
Use the IETF transacript in bitcoin-serai, not RecommendedTranscript
This is more likely to be interoperable in the long term.
This commit is contained in:
parent
dfb5a053ae
commit
3af430d8de
7 changed files with 21 additions and 76 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1054,7 +1054,6 @@ name = "bitcoin-serai"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitcoin",
|
"bitcoin",
|
||||||
"flexible-transcript",
|
|
||||||
"hex",
|
"hex",
|
||||||
"k256",
|
"k256",
|
||||||
"modular-frost",
|
"modular-frost",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "bitcoin-serai"
|
name = "bitcoin-serai"
|
||||||
version = "0.4.0"
|
version = "0.3.0"
|
||||||
description = "A Bitcoin library for FROST-signing transactions"
|
description = "A Bitcoin library for FROST-signing transactions"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/serai-dex/serai/tree/develop/networks/bitcoin"
|
repository = "https://github.com/serai-dex/serai/tree/develop/networks/bitcoin"
|
||||||
|
@ -26,8 +26,6 @@ rand_core = { version = "0.6", default-features = false }
|
||||||
bitcoin = { version = "0.32", default-features = false }
|
bitcoin = { version = "0.32", default-features = false }
|
||||||
|
|
||||||
k256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits"] }
|
k256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits"] }
|
||||||
|
|
||||||
transcript = { package = "flexible-transcript", path = "../../crypto/transcript", version = "0.3", default-features = false, features = ["recommended"], optional = true }
|
|
||||||
frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.8", default-features = false, features = ["secp256k1"], optional = true }
|
frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.8", default-features = false, features = ["secp256k1"], optional = true }
|
||||||
|
|
||||||
hex = { version = "0.4", default-features = false, optional = true }
|
hex = { version = "0.4", default-features = false, optional = true }
|
||||||
|
@ -55,8 +53,6 @@ std = [
|
||||||
"bitcoin/serde",
|
"bitcoin/serde",
|
||||||
|
|
||||||
"k256/std",
|
"k256/std",
|
||||||
|
|
||||||
"transcript/std",
|
|
||||||
"frost",
|
"frost",
|
||||||
|
|
||||||
"hex/std",
|
"hex/std",
|
||||||
|
|
|
@ -40,14 +40,12 @@ mod frost_crypto {
|
||||||
|
|
||||||
use bitcoin::hashes::{HashEngine, Hash, sha256::Hash as Sha256};
|
use bitcoin::hashes::{HashEngine, Hash, sha256::Hash as Sha256};
|
||||||
|
|
||||||
use transcript::Transcript;
|
|
||||||
|
|
||||||
use k256::{elliptic_curve::ops::Reduce, U256, Scalar};
|
use k256::{elliptic_curve::ops::Reduce, U256, Scalar};
|
||||||
|
|
||||||
use frost::{
|
use frost::{
|
||||||
curve::{Ciphersuite, Secp256k1},
|
curve::{Ciphersuite, Secp256k1},
|
||||||
Participant, ThresholdKeys, ThresholdView, FrostError,
|
Participant, ThresholdKeys, ThresholdView, FrostError,
|
||||||
algorithm::{Hram as HramTrait, Algorithm, Schnorr as FrostSchnorr},
|
algorithm::{Hram as HramTrait, Algorithm, IetfSchnorr as FrostSchnorr},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -82,16 +80,17 @@ mod frost_crypto {
|
||||||
///
|
///
|
||||||
/// This must be used with a ThresholdKeys whose group key is even. If it is odd, this will panic.
|
/// This must be used with a ThresholdKeys whose group key is even. If it is odd, this will panic.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Schnorr<T: Sync + Clone + Debug + Transcript>(FrostSchnorr<Secp256k1, T, Hram>);
|
pub struct Schnorr(FrostSchnorr<Secp256k1, Hram>);
|
||||||
impl<T: Sync + Clone + Debug + Transcript> Schnorr<T> {
|
impl Schnorr {
|
||||||
/// Construct a Schnorr algorithm continuing the specified transcript.
|
/// Construct a Schnorr algorithm continuing the specified transcript.
|
||||||
pub fn new(transcript: T) -> Schnorr<T> {
|
#[allow(clippy::new_without_default)]
|
||||||
Schnorr(FrostSchnorr::new(transcript))
|
pub fn new() -> Schnorr {
|
||||||
|
Schnorr(FrostSchnorr::ietf())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Sync + Clone + Debug + Transcript> Algorithm<Secp256k1> for Schnorr<T> {
|
impl Algorithm<Secp256k1> for Schnorr {
|
||||||
type Transcript = T;
|
type Transcript = <FrostSchnorr<Secp256k1, Hram> as Algorithm<Secp256k1>>::Transcript;
|
||||||
type Addendum = ();
|
type Addendum = ();
|
||||||
type Signature = [u8; 64];
|
type Signature = [u8; 64];
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ use rand_core::OsRng;
|
||||||
use secp256k1::{Secp256k1 as BContext, Message, schnorr::Signature};
|
use secp256k1::{Secp256k1 as BContext, Message, schnorr::Signature};
|
||||||
|
|
||||||
use k256::Scalar;
|
use k256::Scalar;
|
||||||
use transcript::{Transcript, RecommendedTranscript};
|
|
||||||
use frost::{
|
use frost::{
|
||||||
curve::Secp256k1,
|
curve::Secp256k1,
|
||||||
Participant,
|
Participant,
|
||||||
|
@ -25,8 +24,7 @@ fn test_algorithm() {
|
||||||
*keys = keys.offset(Scalar::from(offset));
|
*keys = keys.offset(Scalar::from(offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
let algo =
|
let algo = Schnorr::new();
|
||||||
Schnorr::<RecommendedTranscript>::new(RecommendedTranscript::new(b"bitcoin-serai sign test"));
|
|
||||||
let sig = sign(
|
let sig = sign(
|
||||||
&mut OsRng,
|
&mut OsRng,
|
||||||
&algo,
|
&algo,
|
||||||
|
|
|
@ -7,9 +7,7 @@ use thiserror::Error;
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
use transcript::{Transcript, RecommendedTranscript};
|
use k256::Scalar;
|
||||||
|
|
||||||
use k256::{elliptic_curve::sec1::ToEncodedPoint, Scalar};
|
|
||||||
use frost::{curve::Secp256k1, Participant, ThresholdKeys, FrostError, sign::*};
|
use frost::{curve::Secp256k1, Participant, ThresholdKeys, FrostError, sign::*};
|
||||||
|
|
||||||
use bitcoin::{
|
use bitcoin::{
|
||||||
|
@ -268,41 +266,15 @@ impl SignableTransaction {
|
||||||
/// Create a multisig machine for this transaction.
|
/// Create a multisig machine for this transaction.
|
||||||
///
|
///
|
||||||
/// Returns None if the wrong keys are used.
|
/// Returns None if the wrong keys are used.
|
||||||
pub fn multisig(
|
pub fn multisig(self, keys: &ThresholdKeys<Secp256k1>) -> Option<TransactionMachine> {
|
||||||
self,
|
|
||||||
keys: &ThresholdKeys<Secp256k1>,
|
|
||||||
mut transcript: RecommendedTranscript,
|
|
||||||
) -> Option<TransactionMachine> {
|
|
||||||
transcript.domain_separate(b"bitcoin_transaction");
|
|
||||||
transcript.append_message(b"root_key", keys.group_key().to_encoded_point(true).as_bytes());
|
|
||||||
|
|
||||||
// Transcript the inputs and outputs
|
|
||||||
let tx = &self.tx;
|
|
||||||
for input in &tx.input {
|
|
||||||
transcript.append_message(b"input_hash", input.previous_output.txid);
|
|
||||||
transcript.append_message(b"input_output_index", input.previous_output.vout.to_le_bytes());
|
|
||||||
}
|
|
||||||
for payment in &tx.output {
|
|
||||||
transcript.append_message(b"output_script", payment.script_pubkey.as_bytes());
|
|
||||||
transcript.append_message(b"output_amount", payment.value.to_sat().to_le_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut sigs = vec![];
|
let mut sigs = vec![];
|
||||||
for i in 0 .. tx.input.len() {
|
for i in 0 .. self.tx.input.len() {
|
||||||
let mut transcript = transcript.clone();
|
|
||||||
// This unwrap is safe since any transaction with this many inputs violates the maximum
|
|
||||||
// size allowed under standards, which this lib will error on creation of
|
|
||||||
transcript.append_message(b"signing_input", u32::try_from(i).unwrap().to_le_bytes());
|
|
||||||
|
|
||||||
let offset = keys.clone().offset(self.offsets[i]);
|
let offset = keys.clone().offset(self.offsets[i]);
|
||||||
if p2tr_script_buf(offset.group_key())? != self.prevouts[i].script_pubkey {
|
if p2tr_script_buf(offset.group_key())? != self.prevouts[i].script_pubkey {
|
||||||
None?;
|
None?;
|
||||||
}
|
}
|
||||||
|
|
||||||
sigs.push(AlgorithmMachine::new(
|
sigs.push(AlgorithmMachine::new(Schnorr::new(), keys.clone().offset(self.offsets[i])));
|
||||||
Schnorr::new(transcript),
|
|
||||||
keys.clone().offset(self.offsets[i]),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(TransactionMachine { tx: self, sigs })
|
Some(TransactionMachine { tx: self, sigs })
|
||||||
|
@ -315,7 +287,7 @@ impl SignableTransaction {
|
||||||
/// This will panic if either `cache` is called or the message isn't empty.
|
/// This will panic if either `cache` is called or the message isn't empty.
|
||||||
pub struct TransactionMachine {
|
pub struct TransactionMachine {
|
||||||
tx: SignableTransaction,
|
tx: SignableTransaction,
|
||||||
sigs: Vec<AlgorithmMachine<Secp256k1, Schnorr<RecommendedTranscript>>>,
|
sigs: Vec<AlgorithmMachine<Secp256k1, Schnorr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PreprocessMachine for TransactionMachine {
|
impl PreprocessMachine for TransactionMachine {
|
||||||
|
@ -344,7 +316,7 @@ impl PreprocessMachine for TransactionMachine {
|
||||||
|
|
||||||
pub struct TransactionSignMachine {
|
pub struct TransactionSignMachine {
|
||||||
tx: SignableTransaction,
|
tx: SignableTransaction,
|
||||||
sigs: Vec<AlgorithmSignMachine<Secp256k1, Schnorr<RecommendedTranscript>>>,
|
sigs: Vec<AlgorithmSignMachine<Secp256k1, Schnorr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SignMachine<Transaction> for TransactionSignMachine {
|
impl SignMachine<Transaction> for TransactionSignMachine {
|
||||||
|
@ -424,7 +396,7 @@ impl SignMachine<Transaction> for TransactionSignMachine {
|
||||||
|
|
||||||
pub struct TransactionSignatureMachine {
|
pub struct TransactionSignatureMachine {
|
||||||
tx: Transaction,
|
tx: Transaction,
|
||||||
sigs: Vec<AlgorithmSignatureMachine<Secp256k1, Schnorr<RecommendedTranscript>>>,
|
sigs: Vec<AlgorithmSignatureMachine<Secp256k1, Schnorr>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SignatureMachine<Transaction> for TransactionSignatureMachine {
|
impl SignatureMachine<Transaction> for TransactionSignatureMachine {
|
||||||
|
|
|
@ -2,8 +2,6 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use rand_core::{RngCore, OsRng};
|
use rand_core::{RngCore, OsRng};
|
||||||
|
|
||||||
use transcript::{Transcript, RecommendedTranscript};
|
|
||||||
|
|
||||||
use k256::{
|
use k256::{
|
||||||
elliptic_curve::{
|
elliptic_curve::{
|
||||||
group::{ff::Field, Group},
|
group::{ff::Field, Group},
|
||||||
|
@ -94,12 +92,7 @@ fn sign(
|
||||||
) -> Transaction {
|
) -> Transaction {
|
||||||
let mut machines = HashMap::new();
|
let mut machines = HashMap::new();
|
||||||
for i in (1 ..= THRESHOLD).map(|i| Participant::new(i).unwrap()) {
|
for i in (1 ..= THRESHOLD).map(|i| Participant::new(i).unwrap()) {
|
||||||
machines.insert(
|
machines.insert(i, tx.clone().multisig(&keys[&i].clone()).unwrap());
|
||||||
i,
|
|
||||||
tx.clone()
|
|
||||||
.multisig(&keys[&i].clone(), RecommendedTranscript::new(b"bitcoin-serai Test Transaction"))
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
sign_without_caching(&mut OsRng, machines, &[])
|
sign_without_caching(&mut OsRng, machines, &[])
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ use async_trait::async_trait;
|
||||||
|
|
||||||
use scale::{Encode, Decode};
|
use scale::{Encode, Decode};
|
||||||
|
|
||||||
use transcript::{Transcript, RecommendedTranscript};
|
|
||||||
use ciphersuite::group::ff::PrimeField;
|
use ciphersuite::group::ff::PrimeField;
|
||||||
use k256::{ProjectivePoint, Scalar};
|
use k256::{ProjectivePoint, Scalar};
|
||||||
use frost::{
|
use frost::{
|
||||||
|
@ -249,7 +248,6 @@ impl EventualityTrait for Eventuality {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SignableTransaction {
|
pub struct SignableTransaction {
|
||||||
transcript: RecommendedTranscript,
|
|
||||||
actual: BSignableTransaction,
|
actual: BSignableTransaction,
|
||||||
}
|
}
|
||||||
impl PartialEq for SignableTransaction {
|
impl PartialEq for SignableTransaction {
|
||||||
|
@ -820,7 +818,7 @@ impl Network for Bitcoin {
|
||||||
async fn signable_transaction(
|
async fn signable_transaction(
|
||||||
&self,
|
&self,
|
||||||
block_number: usize,
|
block_number: usize,
|
||||||
plan_id: &[u8; 32],
|
_plan_id: &[u8; 32],
|
||||||
_key: ProjectivePoint,
|
_key: ProjectivePoint,
|
||||||
inputs: &[Output],
|
inputs: &[Output],
|
||||||
payments: &[Payment<Self>],
|
payments: &[Payment<Self>],
|
||||||
|
@ -829,12 +827,8 @@ impl Network for Bitcoin {
|
||||||
) -> Result<Option<(Self::SignableTransaction, Self::Eventuality)>, NetworkError> {
|
) -> Result<Option<(Self::SignableTransaction, Self::Eventuality)>, NetworkError> {
|
||||||
Ok(self.make_signable_transaction(block_number, inputs, payments, change, false).await?.map(
|
Ok(self.make_signable_transaction(block_number, inputs, payments, change, false).await?.map(
|
||||||
|signable| {
|
|signable| {
|
||||||
let mut transcript =
|
|
||||||
RecommendedTranscript::new(b"Serai Processor Bitcoin Transaction Transcript");
|
|
||||||
transcript.append_message(b"plan", plan_id);
|
|
||||||
|
|
||||||
let eventuality = Eventuality(signable.txid());
|
let eventuality = Eventuality(signable.txid());
|
||||||
(SignableTransaction { transcript, actual: signable }, eventuality)
|
(SignableTransaction { actual: signable }, eventuality)
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -844,13 +838,7 @@ impl Network for Bitcoin {
|
||||||
keys: ThresholdKeys<Self::Curve>,
|
keys: ThresholdKeys<Self::Curve>,
|
||||||
transaction: Self::SignableTransaction,
|
transaction: Self::SignableTransaction,
|
||||||
) -> Result<Self::TransactionMachine, NetworkError> {
|
) -> Result<Self::TransactionMachine, NetworkError> {
|
||||||
Ok(
|
Ok(transaction.actual.clone().multisig(&keys).expect("used the wrong keys"))
|
||||||
transaction
|
|
||||||
.actual
|
|
||||||
.clone()
|
|
||||||
.multisig(&keys, transaction.transcript)
|
|
||||||
.expect("used the wrong keys"),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn publish_completion(&self, tx: &Transaction) -> Result<(), NetworkError> {
|
async fn publish_completion(&self, tx: &Transaction) -> Result<(), NetworkError> {
|
||||||
|
|
Loading…
Reference in a new issue