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:
Luke Parker 2023-08-21 08:56:37 -04:00
parent a52c86ad81
commit 76a30fd572
No known key found for this signature in database
10 changed files with 238 additions and 164 deletions

View file

@ -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
View file

@ -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",

View file

@ -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"]

View file

@ -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::*;

View file

@ -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)]

View file

@ -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)),

View file

@ -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));
} }

View file

@ -1,4 +1,4 @@
use std::{ use std_shims::{
io::{self, Read}, io::{self, Read},
collections::HashMap, collections::HashMap,
}; };

View file

@ -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 {

View file

@ -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 }