mirror of
https://github.com/serai-dex/serai.git
synced 2024-12-23 03:59:22 +00:00
Support no-std builds of bitcoin-serai
Arguably not meaningful, as it adds the scanner yet not the RPC, and no signing code since modular-frost doesn't support no-std yet. It's a step in the right direction though.
This commit is contained in:
parent
a52c86ad81
commit
76a30fd572
10 changed files with 238 additions and 164 deletions
5
.github/workflows/no-std.yml
vendored
5
.github/workflows/no-std.yml
vendored
|
@ -30,5 +30,8 @@ jobs:
|
||||||
with:
|
with:
|
||||||
github-token: ${{ inputs.github-token }}
|
github-token: ${{ inputs.github-token }}
|
||||||
|
|
||||||
|
- name: Install RISC-V Toolchain
|
||||||
|
run: sudo apt install -y gcc-riscv64-unknown-elf gcc-multilib
|
||||||
|
|
||||||
- name: Verify no-std builds
|
- name: Verify no-std builds
|
||||||
run: cd tests/no-std && cargo build --target riscv32imac-unknown-none-elf
|
run: cd tests/no-std && CFLAGS=-I/usr/include cargo build --target riscv32imac-unknown-none-elf
|
||||||
|
|
24
Cargo.lock
generated
24
Cargo.lock
generated
|
@ -462,6 +462,7 @@ dependencies = [
|
||||||
"bech32 0.9.1",
|
"bech32 0.9.1",
|
||||||
"bitcoin-private",
|
"bitcoin-private",
|
||||||
"bitcoin_hashes",
|
"bitcoin_hashes",
|
||||||
|
"core2 0.3.3",
|
||||||
"hex_lit",
|
"hex_lit",
|
||||||
"secp256k1",
|
"secp256k1",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -481,7 +482,6 @@ dependencies = [
|
||||||
"flexible-transcript",
|
"flexible-transcript",
|
||||||
"hex",
|
"hex",
|
||||||
"k256",
|
"k256",
|
||||||
"lazy_static",
|
|
||||||
"modular-frost",
|
"modular-frost",
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
@ -489,6 +489,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2 0.10.7",
|
"sha2 0.10.7",
|
||||||
|
"std-shims",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
|
@ -501,6 +502,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501"
|
checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitcoin-private",
|
"bitcoin-private",
|
||||||
|
"core2 0.3.3",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -884,7 +886,7 @@ version = "0.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd94671561e36e4e7de75f753f577edafb0e7c05d6e4547229fdf7938fbcd2c3"
|
checksum = "fd94671561e36e4e7de75f753f577edafb0e7c05d6e4547229fdf7938fbcd2c3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"core2",
|
"core2 0.4.0",
|
||||||
"multibase",
|
"multibase",
|
||||||
"multihash 0.18.1",
|
"multihash 0.18.1",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -1140,6 +1142,15 @@ version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core2"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "239fa3ae9b63c2dc74bd3fa852d4792b8b305ae64eeede946265b6af62f1fff3"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core2"
|
name = "core2"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -4800,7 +4811,7 @@ dependencies = [
|
||||||
"blake2b_simd",
|
"blake2b_simd",
|
||||||
"blake2s_simd",
|
"blake2s_simd",
|
||||||
"blake3",
|
"blake3",
|
||||||
"core2",
|
"core2 0.4.0",
|
||||||
"digest 0.10.7",
|
"digest 0.10.7",
|
||||||
"multihash-derive 0.8.0",
|
"multihash-derive 0.8.0",
|
||||||
"sha2 0.10.7",
|
"sha2 0.10.7",
|
||||||
|
@ -4814,7 +4825,7 @@ version = "0.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2fd59dcc2bbe70baabeac52cd22ae52c55eefe6c38ff11a9439f16a350a939f2"
|
checksum = "2fd59dcc2bbe70baabeac52cd22ae52c55eefe6c38ff11a9439f16a350a939f2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"core2",
|
"core2 0.4.0",
|
||||||
"unsigned-varint",
|
"unsigned-varint",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -4827,7 +4838,7 @@ dependencies = [
|
||||||
"blake2b_simd",
|
"blake2b_simd",
|
||||||
"blake2s_simd",
|
"blake2s_simd",
|
||||||
"blake3",
|
"blake3",
|
||||||
"core2",
|
"core2 0.4.0",
|
||||||
"digest 0.10.7",
|
"digest 0.10.7",
|
||||||
"multihash-derive 0.9.0",
|
"multihash-derive 0.9.0",
|
||||||
"ripemd",
|
"ripemd",
|
||||||
|
@ -4857,7 +4868,7 @@ version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "890e72cb7396cb99ed98c1246a97b243cc16394470d94e0bc8b0c2c11d84290e"
|
checksum = "890e72cb7396cb99ed98c1246a97b243cc16394470d94e0bc8b0c2c11d84290e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"core2",
|
"core2 0.4.0",
|
||||||
"multihash 0.19.0",
|
"multihash 0.19.0",
|
||||||
"multihash-derive-impl",
|
"multihash-derive-impl",
|
||||||
]
|
]
|
||||||
|
@ -8100,6 +8111,7 @@ dependencies = [
|
||||||
name = "serai-no-std-tests"
|
name = "serai-no-std-tests"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bitcoin-serai",
|
||||||
"ciphersuite",
|
"ciphersuite",
|
||||||
"dalek-ff-group",
|
"dalek-ff-group",
|
||||||
"dkg",
|
"dkg",
|
||||||
|
|
|
@ -8,25 +8,27 @@ authors = ["Luke Parker <lukeparker5132@gmail.com>", "Vrx <vrx00@proton.me>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lazy_static = "1"
|
std-shims = { version = "0.1.1", path = "../../common/std-shims", default-features = false }
|
||||||
thiserror = "1"
|
|
||||||
|
|
||||||
zeroize = "^1.5"
|
thiserror = { version = "1", optional = true }
|
||||||
rand_core = "0.6"
|
|
||||||
|
|
||||||
sha2 = "0.10"
|
zeroize = { version = "^1.5", default-features = false }
|
||||||
|
rand_core = { version = "0.6", default-features = false }
|
||||||
|
|
||||||
secp256k1 = { version = "0.27", features = ["global-context"] }
|
sha2 = { version = "0.10", default-features = false }
|
||||||
bitcoin = { version = "0.30", features = ["serde"] }
|
|
||||||
|
|
||||||
k256 = { version = "^0.13.1", default-features = false, features = ["std", "arithmetic", "bits"] }
|
secp256k1 = { version = "0.27", default-features = false }
|
||||||
transcript = { package = "flexible-transcript", path = "../../crypto/transcript", version = "0.3", features = ["recommended"] }
|
bitcoin = { version = "0.30", default-features = false, features = ["no-std"] }
|
||||||
frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.8", features = ["secp256k1"] }
|
|
||||||
|
|
||||||
hex = "0.4"
|
k256 = { version = "^0.13.1", default-features = false, features = ["arithmetic", "bits"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
|
||||||
serde_json = "1"
|
transcript = { package = "flexible-transcript", path = "../../crypto/transcript", version = "0.3", features = ["recommended"], optional = true }
|
||||||
reqwest = { version = "0.11", features = ["json"] }
|
frost = { package = "modular-frost", path = "../../crypto/frost", version = "0.8", features = ["secp256k1"], optional = true }
|
||||||
|
|
||||||
|
hex = { version = "0.4", optional = true }
|
||||||
|
serde = { version = "1", features = ["derive"], optional = true }
|
||||||
|
serde_json = { version = "1", optional = true }
|
||||||
|
reqwest = { version = "0.11", features = ["json"], optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
frost = { package = "modular-frost", path = "../../crypto/frost", features = ["tests"] }
|
frost = { package = "modular-frost", path = "../../crypto/frost", features = ["tests"] }
|
||||||
|
@ -34,4 +36,29 @@ frost = { package = "modular-frost", path = "../../crypto/frost", features = ["t
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
std = [
|
||||||
|
"std-shims/std",
|
||||||
|
|
||||||
|
"thiserror",
|
||||||
|
|
||||||
|
"zeroize/std",
|
||||||
|
"rand_core/std",
|
||||||
|
|
||||||
|
"sha2/std",
|
||||||
|
|
||||||
|
"secp256k1/std",
|
||||||
|
"bitcoin/std",
|
||||||
|
"bitcoin/serde",
|
||||||
|
|
||||||
|
"k256/std",
|
||||||
|
|
||||||
|
"transcript",
|
||||||
|
"frost",
|
||||||
|
|
||||||
|
"hex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"reqwest"
|
||||||
|
]
|
||||||
hazmat = []
|
hazmat = []
|
||||||
|
default = ["std"]
|
||||||
|
|
|
@ -1,26 +1,6 @@
|
||||||
use core::fmt::Debug;
|
|
||||||
use std::io;
|
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
|
||||||
use zeroize::Zeroizing;
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
|
||||||
|
|
||||||
use sha2::{Digest, Sha256};
|
|
||||||
use transcript::Transcript;
|
|
||||||
|
|
||||||
use secp256k1::schnorr::Signature;
|
|
||||||
use k256::{
|
use k256::{
|
||||||
elliptic_curve::{
|
elliptic_curve::sec1::{Tag, ToEncodedPoint},
|
||||||
ops::Reduce,
|
ProjectivePoint,
|
||||||
sec1::{Tag, ToEncodedPoint},
|
|
||||||
},
|
|
||||||
U256, Scalar, ProjectivePoint,
|
|
||||||
};
|
|
||||||
use frost::{
|
|
||||||
curve::{Ciphersuite, Secp256k1},
|
|
||||||
Participant, ThresholdKeys, ThresholdView, FrostError,
|
|
||||||
algorithm::{Hram as HramTrait, Algorithm, Schnorr as FrostSchnorr},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use bitcoin::key::XOnlyPublicKey;
|
use bitcoin::key::XOnlyPublicKey;
|
||||||
|
@ -40,6 +20,7 @@ pub fn x_only(key: &ProjectivePoint) -> XOnlyPublicKey {
|
||||||
/// Make a point even by adding the generator until it is even.
|
/// Make a point even by adding the generator until it is even.
|
||||||
///
|
///
|
||||||
/// Returns the even point and the amount of additions required.
|
/// Returns the even point and the amount of additions required.
|
||||||
|
#[cfg(any(feature = "std", feature = "hazmat"))]
|
||||||
pub fn make_even(mut key: ProjectivePoint) -> (ProjectivePoint, u64) {
|
pub fn make_even(mut key: ProjectivePoint) -> (ProjectivePoint, u64) {
|
||||||
let mut c = 0;
|
let mut c = 0;
|
||||||
while key.to_encoded_point(true).tag() == Tag::CompressedOddY {
|
while key.to_encoded_point(true).tag() == Tag::CompressedOddY {
|
||||||
|
@ -49,116 +30,143 @@ pub fn make_even(mut key: ProjectivePoint) -> (ProjectivePoint, u64) {
|
||||||
(key, c)
|
(key, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A BIP-340 compatible HRAm for use with the modular-frost Schnorr Algorithm.
|
#[cfg(feature = "std")]
|
||||||
///
|
mod frost_crypto {
|
||||||
/// If passed an odd nonce, it will have the generator added until it is even.
|
use core::fmt::Debug;
|
||||||
///
|
use std_shims::{sync::OnceLock, vec::Vec, io};
|
||||||
/// If the key is odd, this will panic.
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct Hram;
|
|
||||||
|
|
||||||
lazy_static! {
|
use zeroize::Zeroizing;
|
||||||
static ref TAG_HASH: [u8; 32] = Sha256::digest(b"BIP0340/challenge").into();
|
use rand_core::{RngCore, CryptoRng};
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
use sha2::{Digest, Sha256};
|
||||||
impl HramTrait<Secp256k1> for Hram {
|
use transcript::Transcript;
|
||||||
fn hram(R: &ProjectivePoint, A: &ProjectivePoint, m: &[u8]) -> Scalar {
|
|
||||||
// Convert the nonce to be even
|
|
||||||
let (R, _) = make_even(*R);
|
|
||||||
|
|
||||||
let mut data = Sha256::new();
|
use secp256k1::schnorr::Signature;
|
||||||
data.update(*TAG_HASH);
|
use k256::{elliptic_curve::ops::Reduce, U256, Scalar};
|
||||||
data.update(*TAG_HASH);
|
|
||||||
data.update(x(&R));
|
|
||||||
data.update(x(A));
|
|
||||||
data.update(m);
|
|
||||||
|
|
||||||
Scalar::reduce(U256::from_be_slice(&data.finalize()))
|
use frost::{
|
||||||
}
|
curve::{Ciphersuite, Secp256k1},
|
||||||
}
|
Participant, ThresholdKeys, ThresholdView, FrostError,
|
||||||
|
algorithm::{Hram as HramTrait, Algorithm, Schnorr as FrostSchnorr},
|
||||||
/// BIP-340 Schnorr signature algorithm.
|
};
|
||||||
///
|
|
||||||
/// This must be used with a ThresholdKeys whose group key is even. If it is odd, this will panic.
|
use super::*;
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Schnorr<T: Sync + Clone + Debug + Transcript>(FrostSchnorr<Secp256k1, T, Hram>);
|
/// A BIP-340 compatible HRAm for use with the modular-frost Schnorr Algorithm.
|
||||||
impl<T: Sync + Clone + Debug + Transcript> Schnorr<T> {
|
///
|
||||||
/// Construct a Schnorr algorithm continuing the specified transcript.
|
/// If passed an odd nonce, it will have the generator added until it is even.
|
||||||
pub fn new(transcript: T) -> Schnorr<T> {
|
///
|
||||||
Schnorr(FrostSchnorr::new(transcript))
|
/// If the key is odd, this will panic.
|
||||||
}
|
#[derive(Clone, Copy, Debug)]
|
||||||
}
|
pub struct Hram;
|
||||||
|
|
||||||
impl<T: Sync + Clone + Debug + Transcript> Algorithm<Secp256k1> for Schnorr<T> {
|
static TAG_HASH_CELL: OnceLock<[u8; 32]> = OnceLock::new();
|
||||||
type Transcript = T;
|
#[allow(non_snake_case)]
|
||||||
type Addendum = ();
|
fn TAG_HASH() -> [u8; 32] {
|
||||||
type Signature = Signature;
|
*TAG_HASH_CELL.get_or_init(|| Sha256::digest(b"BIP0340/challenge").into())
|
||||||
|
}
|
||||||
fn transcript(&mut self) -> &mut Self::Transcript {
|
|
||||||
self.0.transcript()
|
#[allow(non_snake_case)]
|
||||||
}
|
impl HramTrait<Secp256k1> for Hram {
|
||||||
|
fn hram(R: &ProjectivePoint, A: &ProjectivePoint, m: &[u8]) -> Scalar {
|
||||||
fn nonces(&self) -> Vec<Vec<ProjectivePoint>> {
|
// Convert the nonce to be even
|
||||||
self.0.nonces()
|
let (R, _) = make_even(*R);
|
||||||
}
|
|
||||||
|
let mut data = Sha256::new();
|
||||||
fn preprocess_addendum<R: RngCore + CryptoRng>(
|
data.update(TAG_HASH());
|
||||||
&mut self,
|
data.update(TAG_HASH());
|
||||||
rng: &mut R,
|
data.update(x(&R));
|
||||||
keys: &ThresholdKeys<Secp256k1>,
|
data.update(x(A));
|
||||||
) {
|
data.update(m);
|
||||||
self.0.preprocess_addendum(rng, keys)
|
|
||||||
}
|
Scalar::reduce(U256::from_be_slice(&data.finalize()))
|
||||||
|
}
|
||||||
fn read_addendum<R: io::Read>(&self, reader: &mut R) -> io::Result<Self::Addendum> {
|
}
|
||||||
self.0.read_addendum(reader)
|
|
||||||
}
|
/// BIP-340 Schnorr signature algorithm.
|
||||||
|
///
|
||||||
fn process_addendum(
|
/// This must be used with a ThresholdKeys whose group key is even. If it is odd, this will panic.
|
||||||
&mut self,
|
#[derive(Clone)]
|
||||||
view: &ThresholdView<Secp256k1>,
|
pub struct Schnorr<T: Sync + Clone + Debug + Transcript>(FrostSchnorr<Secp256k1, T, Hram>);
|
||||||
i: Participant,
|
impl<T: Sync + Clone + Debug + Transcript> Schnorr<T> {
|
||||||
addendum: (),
|
/// Construct a Schnorr algorithm continuing the specified transcript.
|
||||||
) -> Result<(), FrostError> {
|
pub fn new(transcript: T) -> Schnorr<T> {
|
||||||
self.0.process_addendum(view, i, addendum)
|
Schnorr(FrostSchnorr::new(transcript))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn sign_share(
|
|
||||||
&mut self,
|
impl<T: Sync + Clone + Debug + Transcript> Algorithm<Secp256k1> for Schnorr<T> {
|
||||||
params: &ThresholdView<Secp256k1>,
|
type Transcript = T;
|
||||||
nonce_sums: &[Vec<<Secp256k1 as Ciphersuite>::G>],
|
type Addendum = ();
|
||||||
nonces: Vec<Zeroizing<<Secp256k1 as Ciphersuite>::F>>,
|
type Signature = Signature;
|
||||||
msg: &[u8],
|
|
||||||
) -> <Secp256k1 as Ciphersuite>::F {
|
fn transcript(&mut self) -> &mut Self::Transcript {
|
||||||
self.0.sign_share(params, nonce_sums, nonces, msg)
|
self.0.transcript()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
fn nonces(&self) -> Vec<Vec<ProjectivePoint>> {
|
||||||
fn verify(
|
self.0.nonces()
|
||||||
&self,
|
}
|
||||||
group_key: ProjectivePoint,
|
|
||||||
nonces: &[Vec<ProjectivePoint>],
|
fn preprocess_addendum<R: RngCore + CryptoRng>(
|
||||||
sum: Scalar,
|
&mut self,
|
||||||
) -> Option<Self::Signature> {
|
rng: &mut R,
|
||||||
self.0.verify(group_key, nonces, sum).map(|mut sig| {
|
keys: &ThresholdKeys<Secp256k1>,
|
||||||
// Make the R of the final signature even
|
) {
|
||||||
let offset;
|
self.0.preprocess_addendum(rng, keys)
|
||||||
(sig.R, offset) = make_even(sig.R);
|
}
|
||||||
// s = r + cx. Since we added to the r, add to s
|
|
||||||
sig.s += Scalar::from(offset);
|
fn read_addendum<R: io::Read>(&self, reader: &mut R) -> io::Result<Self::Addendum> {
|
||||||
// Convert to a secp256k1 signature
|
self.0.read_addendum(reader)
|
||||||
Signature::from_slice(&sig.serialize()[1 ..])
|
}
|
||||||
.expect("couldn't convert SchnorrSignature to Signature")
|
|
||||||
})
|
fn process_addendum(
|
||||||
}
|
&mut self,
|
||||||
|
view: &ThresholdView<Secp256k1>,
|
||||||
fn verify_share(
|
i: Participant,
|
||||||
&self,
|
addendum: (),
|
||||||
verification_share: ProjectivePoint,
|
) -> Result<(), FrostError> {
|
||||||
nonces: &[Vec<ProjectivePoint>],
|
self.0.process_addendum(view, i, addendum)
|
||||||
share: Scalar,
|
}
|
||||||
) -> Result<Vec<(Scalar, ProjectivePoint)>, ()> {
|
|
||||||
self.0.verify_share(verification_share, nonces, share)
|
fn sign_share(
|
||||||
|
&mut self,
|
||||||
|
params: &ThresholdView<Secp256k1>,
|
||||||
|
nonce_sums: &[Vec<<Secp256k1 as Ciphersuite>::G>],
|
||||||
|
nonces: Vec<Zeroizing<<Secp256k1 as Ciphersuite>::F>>,
|
||||||
|
msg: &[u8],
|
||||||
|
) -> <Secp256k1 as Ciphersuite>::F {
|
||||||
|
self.0.sign_share(params, nonce_sums, nonces, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn verify(
|
||||||
|
&self,
|
||||||
|
group_key: ProjectivePoint,
|
||||||
|
nonces: &[Vec<ProjectivePoint>],
|
||||||
|
sum: Scalar,
|
||||||
|
) -> Option<Self::Signature> {
|
||||||
|
self.0.verify(group_key, nonces, sum).map(|mut sig| {
|
||||||
|
// Make the R of the final signature even
|
||||||
|
let offset;
|
||||||
|
(sig.R, offset) = make_even(sig.R);
|
||||||
|
// s = r + cx. Since we added to the r, add to s
|
||||||
|
sig.s += Scalar::from(offset);
|
||||||
|
// Convert to a secp256k1 signature
|
||||||
|
Signature::from_slice(&sig.serialize()[1 ..])
|
||||||
|
.expect("couldn't convert SchnorrSignature to Signature")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_share(
|
||||||
|
&self,
|
||||||
|
verification_share: ProjectivePoint,
|
||||||
|
nonces: &[Vec<ProjectivePoint>],
|
||||||
|
share: Scalar,
|
||||||
|
) -> Result<Vec<(Scalar, ProjectivePoint)>, ()> {
|
||||||
|
self.0.verify_share(verification_share, nonces, share)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub use frost_crypto::*;
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
/// The bitcoin Rust library.
|
/// The bitcoin Rust library.
|
||||||
pub use bitcoin;
|
pub use bitcoin;
|
||||||
|
@ -13,6 +17,7 @@ pub(crate) mod crypto;
|
||||||
/// Wallet functionality to create transactions.
|
/// Wallet functionality to create transactions.
|
||||||
pub mod wallet;
|
pub mod wallet;
|
||||||
/// A minimal asynchronous Bitcoin RPC client.
|
/// A minimal asynchronous Bitcoin RPC client.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
pub mod rpc;
|
pub mod rpc;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -2,7 +2,7 @@ use rand_core::OsRng;
|
||||||
|
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
use secp256k1::{SECP256K1, Message};
|
use secp256k1::{Secp256k1 as BContext, Message};
|
||||||
|
|
||||||
use k256::Scalar;
|
use k256::Scalar;
|
||||||
use transcript::{Transcript, RecommendedTranscript};
|
use transcript::{Transcript, RecommendedTranscript};
|
||||||
|
@ -37,7 +37,7 @@ fn test_algorithm() {
|
||||||
&Sha256::digest(MESSAGE),
|
&Sha256::digest(MESSAGE),
|
||||||
);
|
);
|
||||||
|
|
||||||
SECP256K1
|
BContext::new()
|
||||||
.verify_schnorr(
|
.verify_schnorr(
|
||||||
&sig,
|
&sig,
|
||||||
&Message::from(Hash::hash(MESSAGE)),
|
&Message::from(Hash::hash(MESSAGE)),
|
||||||
|
|
|
@ -1,33 +1,45 @@
|
||||||
use std::{
|
use std_shims::{
|
||||||
io::{self, Read, Write},
|
vec::Vec,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
io::{self, Write},
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std_shims::io::Read;
|
||||||
|
|
||||||
use k256::{
|
use k256::{
|
||||||
elliptic_curve::sec1::{Tag, ToEncodedPoint},
|
elliptic_curve::sec1::{Tag, ToEncodedPoint},
|
||||||
Scalar, ProjectivePoint,
|
Scalar, ProjectivePoint,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
use frost::{
|
use frost::{
|
||||||
curve::{Ciphersuite, Secp256k1},
|
curve::{Ciphersuite, Secp256k1},
|
||||||
ThresholdKeys,
|
ThresholdKeys,
|
||||||
};
|
};
|
||||||
|
|
||||||
use bitcoin::{
|
use bitcoin::{
|
||||||
consensus::encode::{Decodable, serialize},
|
consensus::encode::serialize,
|
||||||
key::TweakedPublicKey,
|
key::TweakedPublicKey,
|
||||||
address::Payload,
|
address::Payload,
|
||||||
OutPoint, ScriptBuf, TxOut, Transaction, Block,
|
OutPoint, ScriptBuf, TxOut, Transaction, Block,
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use bitcoin::consensus::encode::Decodable;
|
||||||
|
|
||||||
use crate::crypto::{x_only, make_even};
|
use crate::crypto::x_only;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use crate::crypto::make_even;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
mod send;
|
mod send;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
pub use send::*;
|
pub use send::*;
|
||||||
|
|
||||||
/// Tweak keys to ensure they're usable with Bitcoin.
|
/// Tweak keys to ensure they're usable with Bitcoin.
|
||||||
///
|
///
|
||||||
/// Taproot keys, which these keys are used as, must be even. This offsets the keys until they're
|
/// Taproot keys, which these keys are used as, must be even. This offsets the keys until they're
|
||||||
/// even.
|
/// even.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
pub fn tweak_keys(keys: &ThresholdKeys<Secp256k1>) -> ThresholdKeys<Secp256k1> {
|
pub fn tweak_keys(keys: &ThresholdKeys<Secp256k1>) -> ThresholdKeys<Secp256k1> {
|
||||||
let (_, offset) = make_even(keys.group_key());
|
let (_, offset) = make_even(keys.group_key());
|
||||||
keys.offset(Scalar::from(offset))
|
keys.offset(Scalar::from(offset))
|
||||||
|
@ -72,6 +84,7 @@ impl ReceivedOutput {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a ReceivedOutput from a generic satisfying Read.
|
/// Read a ReceivedOutput from a generic satisfying Read.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<ReceivedOutput> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<ReceivedOutput> {
|
||||||
Ok(ReceivedOutput {
|
Ok(ReceivedOutput {
|
||||||
offset: Secp256k1::read_F(r)?,
|
offset: Secp256k1::read_F(r)?,
|
||||||
|
@ -89,9 +102,9 @@ impl ReceivedOutput {
|
||||||
w.write_all(&serialize(&self.outpoint))
|
w.write_all(&serialize(&self.outpoint))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize a ReceivedOutput to a Vec<u8>.
|
/// Serialize a ReceivedOutput to a `Vec<u8>`.
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
let mut res = vec![];
|
let mut res = Vec::new();
|
||||||
self.write(&mut res).unwrap();
|
self.write(&mut res).unwrap();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
@ -143,7 +156,7 @@ impl Scanner {
|
||||||
|
|
||||||
/// Scan a transaction.
|
/// Scan a transaction.
|
||||||
pub fn scan_transaction(&self, tx: &Transaction) -> Vec<ReceivedOutput> {
|
pub fn scan_transaction(&self, tx: &Transaction) -> Vec<ReceivedOutput> {
|
||||||
let mut res = vec![];
|
let mut res = Vec::new();
|
||||||
for (vout, output) in tx.output.iter().enumerate() {
|
for (vout, output) in tx.output.iter().enumerate() {
|
||||||
// If the vout index exceeds 2**32, stop scanning outputs
|
// If the vout index exceeds 2**32, stop scanning outputs
|
||||||
let Ok(vout) = u32::try_from(vout) else { break };
|
let Ok(vout) = u32::try_from(vout) else { break };
|
||||||
|
@ -165,7 +178,7 @@ impl Scanner {
|
||||||
/// must be immediately spendable, a post-processing pass is needed to remove those outputs.
|
/// must be immediately spendable, a post-processing pass is needed to remove those outputs.
|
||||||
/// Alternatively, scan_transaction can be called on `block.txdata[1 ..]`.
|
/// Alternatively, scan_transaction can be called on `block.txdata[1 ..]`.
|
||||||
pub fn scan_block(&self, block: &Block) -> Vec<ReceivedOutput> {
|
pub fn scan_block(&self, block: &Block) -> Vec<ReceivedOutput> {
|
||||||
let mut res = vec![];
|
let mut res = Vec::new();
|
||||||
for tx in &block.txdata {
|
for tx in &block.txdata {
|
||||||
res.extend(self.scan_transaction(tx));
|
res.extend(self.scan_transaction(tx));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{
|
use std_shims::{
|
||||||
io::{self, Read},
|
io::{self, Read},
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use bitcoin_serai::rpc::Rpc;
|
use bitcoin_serai::rpc::Rpc;
|
||||||
|
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
static SEQUENTIAL_CELL: OnceLock<Mutex<()>> = OnceLock::new();
|
||||||
pub static ref SEQUENTIAL: Mutex<()> = Mutex::new(());
|
#[allow(non_snake_case)]
|
||||||
|
pub fn SEQUENTIAL() -> &'static Mutex<()> {
|
||||||
|
SEQUENTIAL_CELL.get_or_init(|| Mutex::new(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -30,7 +34,7 @@ macro_rules! async_sequential {
|
||||||
$(
|
$(
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn $name() {
|
async fn $name() {
|
||||||
let guard = runner::SEQUENTIAL.lock().await;
|
let guard = runner::SEQUENTIAL().lock().await;
|
||||||
let local = tokio::task::LocalSet::new();
|
let local = tokio::task::LocalSet::new();
|
||||||
local.run_until(async move {
|
local.run_until(async move {
|
||||||
if let Err(err) = tokio::task::spawn_local(async move { $body }).await {
|
if let Err(err) = tokio::task::spawn_local(async move { $body }).await {
|
||||||
|
|
|
@ -30,5 +30,7 @@ dkg = { path = "../../crypto/dkg", default-features = false }
|
||||||
# modular-frost = { path = "../../crypto/frost", default-features = false }
|
# modular-frost = { path = "../../crypto/frost", default-features = false }
|
||||||
# frost-schnorrkel = { path = "../../crypto/schnorrkel", default-features = false }
|
# frost-schnorrkel = { path = "../../crypto/schnorrkel", default-features = false }
|
||||||
|
|
||||||
|
bitcoin-serai = { path = "../../coins/bitcoin", default-features = false, features = ["hazmat"] }
|
||||||
|
|
||||||
monero-generators = { path = "../../coins/monero/generators", default-features = false }
|
monero-generators = { path = "../../coins/monero/generators", default-features = false }
|
||||||
monero-serai = { path = "../../coins/monero", default-features = false }
|
monero-serai = { path = "../../coins/monero", default-features = false }
|
||||||
|
|
Loading…
Reference in a new issue