mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 20:29:23 +00:00
Compare commits
6 Commits
a793aa18ef
...
aggressive
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7685cc305f | ||
|
|
3ca76c51e4 | ||
|
|
286e96ccd8 | ||
|
|
f93106af6b | ||
|
|
dd5fb0df47 | ||
|
|
3a626cc51e |
3
.github/workflows/tests.yml
vendored
3
.github/workflows/tests.yml
vendored
@@ -25,7 +25,8 @@ jobs:
|
|||||||
rust-components: clippy
|
rust-components: clippy
|
||||||
|
|
||||||
- name: Run Clippy
|
- name: Run Clippy
|
||||||
run: cargo clippy --all-features --all-targets -- -D warnings -A clippy::items_after_test_module
|
# Allow dbg_macro when run locally, yet not when pushed
|
||||||
|
run: cargo clippy --all-features --all-targets -- -D clippy::dbg_macro $(grep "\S" ../../clippy-config | grep -v "#")
|
||||||
|
|
||||||
deny:
|
deny:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
51
clippy-config
Normal file
51
clippy-config
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# No warnings allowed
|
||||||
|
-D warnings
|
||||||
|
|
||||||
|
# nursery
|
||||||
|
-D clippy::nursery
|
||||||
|
# Erratic and unhelpful
|
||||||
|
-A clippy::missing_const_for_fn
|
||||||
|
# Too many false/irrelevant positives
|
||||||
|
-A clippy::redundant_pub_crate
|
||||||
|
# Flags on any debug_assert using an RNG
|
||||||
|
-A clippy::debug_assert_with_mut_call
|
||||||
|
# Stylistic preference
|
||||||
|
-A clippy::option_if_let_else
|
||||||
|
|
||||||
|
# pedantic
|
||||||
|
-D clippy::unnecessary_wraps
|
||||||
|
-D clippy::unused_async
|
||||||
|
-D clippy::unused_self
|
||||||
|
|
||||||
|
# restrictions
|
||||||
|
|
||||||
|
# Safety
|
||||||
|
-D clippy::as_conversions
|
||||||
|
-D clippy::disallowed_script_idents
|
||||||
|
-D clippy::wildcard_enum_match_arm
|
||||||
|
|
||||||
|
# Clarity
|
||||||
|
-D clippy::assertions_on_result_states
|
||||||
|
-D clippy::deref_by_slicing
|
||||||
|
-D clippy::empty_structs_with_brackets
|
||||||
|
-D clippy::get_unwrap
|
||||||
|
-D clippy::rest_pat_in_fully_bound_structs
|
||||||
|
-D clippy::semicolon_inside_block
|
||||||
|
-D clippy::tests_outside_test_module
|
||||||
|
|
||||||
|
# Quality
|
||||||
|
-D clippy::format_push_string
|
||||||
|
-D clippy::string_to_string
|
||||||
|
|
||||||
|
# These potentially should be enabled in the future
|
||||||
|
# -D clippy::missing_errors_doc
|
||||||
|
# -D clippy::missing_panics_doc
|
||||||
|
# -D clippy::doc_markdown
|
||||||
|
|
||||||
|
# TODO: Enable this
|
||||||
|
# -D clippy::cargo
|
||||||
|
|
||||||
|
# Not in nightly yet
|
||||||
|
# -D clippy::redundant_type_annotations
|
||||||
|
# -D clippy::big_endian_bytes
|
||||||
|
# -D clippy::host_endian_bytes
|
||||||
@@ -28,10 +28,10 @@ fn serialize(generators_string: &mut String, points: &[EdwardsPoint]) {
|
|||||||
fn generators(prefix: &'static str, path: &str) {
|
fn generators(prefix: &'static str, path: &str) {
|
||||||
let generators = bulletproofs_generators(prefix.as_bytes());
|
let generators = bulletproofs_generators(prefix.as_bytes());
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let mut G_str = "".to_string();
|
let mut G_str = String::new();
|
||||||
serialize(&mut G_str, &generators.G);
|
serialize(&mut G_str, &generators.G);
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let mut H_str = "".to_string();
|
let mut H_str = String::new();
|
||||||
serialize(&mut H_str, &generators.H);
|
serialize(&mut H_str, &generators.H);
|
||||||
|
|
||||||
let path = Path::new(&env::var("OUT_DIR").unwrap()).join(path);
|
let path = Path::new(&env::var("OUT_DIR").unwrap()).join(path);
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ use dalek_ff_group::FieldElement;
|
|||||||
use crate::hash;
|
use crate::hash;
|
||||||
|
|
||||||
/// Monero's hash to point function, as named `ge_fromfe_frombytes_vartime`.
|
/// Monero's hash to point function, as named `ge_fromfe_frombytes_vartime`.
|
||||||
|
#[allow(clippy::many_single_char_names)]
|
||||||
pub fn hash_to_point(bytes: [u8; 32]) -> EdwardsPoint {
|
pub fn hash_to_point(bytes: [u8; 32]) -> EdwardsPoint {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case, clippy::unreadable_literal)]
|
||||||
let A = FieldElement::from(486662u64);
|
let A = FieldElement::from(486662u64);
|
||||||
|
|
||||||
let v = FieldElement::from_square(hash(&bytes)).double();
|
let v = FieldElement::from_square(hash(&bytes)).double();
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use std_shims::io::{self, Write};
|
use std_shims::io::{self, Write};
|
||||||
|
|
||||||
const VARINT_CONTINUATION_MASK: u8 = 0b1000_0000;
|
const VARINT_CONTINUATION_MASK: u8 = 0b1000_0000;
|
||||||
|
|
||||||
|
#[allow(clippy::trivially_copy_pass_by_ref)] // &u64 is needed for API consistency
|
||||||
pub(crate) fn write_varint<W: Write>(varint: &u64, w: &mut W) -> io::Result<()> {
|
pub(crate) fn write_varint<W: Write>(varint: &u64, w: &mut W) -> io::Result<()> {
|
||||||
let mut varint = *varint;
|
let mut varint = *varint;
|
||||||
while {
|
while {
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ impl BlockHeader {
|
|||||||
serialized
|
serialized
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<BlockHeader> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
Ok(BlockHeader {
|
Ok(Self {
|
||||||
major_version: read_varint(r)?,
|
major_version: read_varint(r)?,
|
||||||
minor_version: read_varint(r)?,
|
minor_version: read_varint(r)?,
|
||||||
timestamp: read_varint(r)?,
|
timestamp: read_varint(r)?,
|
||||||
@@ -106,8 +106,8 @@ impl Block {
|
|||||||
serialized
|
serialized
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Block> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
Ok(Block {
|
Ok(Self {
|
||||||
header: BlockHeader::read(r)?,
|
header: BlockHeader::read(r)?,
|
||||||
miner_tx: Transaction::read(r)?,
|
miner_tx: Transaction::read(r)?,
|
||||||
txs: (0 .. read_varint(r)?).map(|_| read_bytes(r)).collect::<Result<_, _>>()?,
|
txs: (0 .. read_varint(r)?).map(|_| read_bytes(r)).collect::<Result<_, _>>()?,
|
||||||
|
|||||||
@@ -60,39 +60,39 @@ pub enum Protocol {
|
|||||||
|
|
||||||
impl Protocol {
|
impl Protocol {
|
||||||
/// Amount of ring members under this protocol version.
|
/// Amount of ring members under this protocol version.
|
||||||
pub fn ring_len(&self) -> usize {
|
pub const fn ring_len(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Protocol::v14 => 11,
|
Self::v14 => 11,
|
||||||
Protocol::v16 => 16,
|
Self::v16 => 16,
|
||||||
Protocol::Custom { ring_len, .. } => *ring_len,
|
Self::Custom { ring_len, .. } => *ring_len,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether or not the specified version uses Bulletproofs or Bulletproofs+.
|
/// Whether or not the specified version uses Bulletproofs or Bulletproofs+.
|
||||||
///
|
///
|
||||||
/// This method will likely be reworked when versions not using Bulletproofs at all are added.
|
/// This method will likely be reworked when versions not using Bulletproofs at all are added.
|
||||||
pub fn bp_plus(&self) -> bool {
|
pub const fn bp_plus(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Protocol::v14 => false,
|
Self::v14 => false,
|
||||||
Protocol::v16 => true,
|
Self::v16 => true,
|
||||||
Protocol::Custom { bp_plus, .. } => *bp_plus,
|
Self::Custom { bp_plus, .. } => *bp_plus,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make this an Option when we support pre-RCT protocols
|
// TODO: Make this an Option when we support pre-RCT protocols
|
||||||
pub fn optimal_rct_type(&self) -> RctType {
|
pub const fn optimal_rct_type(&self) -> RctType {
|
||||||
match self {
|
match self {
|
||||||
Protocol::v14 => RctType::Clsag,
|
Self::v14 => RctType::Clsag,
|
||||||
Protocol::v16 => RctType::BulletproofsPlus,
|
Self::v16 => RctType::BulletproofsPlus,
|
||||||
Protocol::Custom { optimal_rct_type, .. } => *optimal_rct_type,
|
Self::Custom { optimal_rct_type, .. } => *optimal_rct_type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn write<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
|
pub(crate) fn write<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Protocol::v14 => w.write_all(&[0, 14]),
|
Self::v14 => w.write_all(&[0, 14]),
|
||||||
Protocol::v16 => w.write_all(&[0, 16]),
|
Self::v16 => w.write_all(&[0, 16]),
|
||||||
Protocol::Custom { ring_len, bp_plus, optimal_rct_type } => {
|
Self::Custom { ring_len, bp_plus, optimal_rct_type } => {
|
||||||
// Custom, version 0
|
// Custom, version 0
|
||||||
w.write_all(&[1, 0])?;
|
w.write_all(&[1, 0])?;
|
||||||
w.write_all(&u16::try_from(*ring_len).unwrap().to_le_bytes())?;
|
w.write_all(&u16::try_from(*ring_len).unwrap().to_le_bytes())?;
|
||||||
@@ -102,17 +102,17 @@ impl Protocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn read<R: io::Read>(r: &mut R) -> io::Result<Protocol> {
|
pub(crate) fn read<R: io::Read>(r: &mut R) -> io::Result<Self> {
|
||||||
Ok(match read_byte(r)? {
|
Ok(match read_byte(r)? {
|
||||||
// Monero protocol
|
// Monero protocol
|
||||||
0 => match read_byte(r)? {
|
0 => match read_byte(r)? {
|
||||||
14 => Protocol::v14,
|
14 => Self::v14,
|
||||||
16 => Protocol::v16,
|
16 => Self::v16,
|
||||||
_ => Err(io::Error::new(io::ErrorKind::Other, "unrecognized monero protocol"))?,
|
_ => Err(io::Error::new(io::ErrorKind::Other, "unrecognized monero protocol"))?,
|
||||||
},
|
},
|
||||||
// Custom
|
// Custom
|
||||||
1 => match read_byte(r)? {
|
1 => match read_byte(r)? {
|
||||||
0 => Protocol::Custom {
|
0 => Self::Custom {
|
||||||
ring_len: read_u16(r)?.into(),
|
ring_len: read_u16(r)?.into(),
|
||||||
bp_plus: match read_byte(r)? {
|
bp_plus: match read_byte(r)? {
|
||||||
0 => false,
|
0 => false,
|
||||||
@@ -140,13 +140,13 @@ pub struct Commitment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Commitment {
|
impl Commitment {
|
||||||
/// The zero commitment, defined as a mask of 1 (as to not be the identity) and a 0 amount.
|
/// A commitment to zero, defined with a mask of 1 (as to not be the identity).
|
||||||
pub fn zero() -> Commitment {
|
pub fn zero() -> Self {
|
||||||
Commitment { mask: Scalar::one(), amount: 0 }
|
Self { mask: Scalar::one(), amount: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(mask: Scalar, amount: u64) -> Commitment {
|
pub fn new(mask: Scalar, amount: u64) -> Self {
|
||||||
Commitment { mask, amount }
|
Self { mask, amount }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate a Pedersen commitment, as a point, from the transparent structure.
|
/// Calculate a Pedersen commitment, as a point, from the transparent structure.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use std_shims::vec::Vec;
|
|||||||
|
|
||||||
use crate::hash;
|
use crate::hash;
|
||||||
|
|
||||||
pub fn merkle_root(root: [u8; 32], leafs: &[[u8; 32]]) -> [u8; 32] {
|
pub(crate) fn merkle_root(root: [u8; 32], leafs: &[[u8; 32]]) -> [u8; 32] {
|
||||||
match leafs.len() {
|
match leafs.len() {
|
||||||
0 => root,
|
0 => root,
|
||||||
1 => hash(&[root, leafs[0]].concat()),
|
1 => hash(&[root, leafs[0]].concat()),
|
||||||
|
|||||||
@@ -26,19 +26,15 @@ pub struct BorromeanSignatures {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BorromeanSignatures {
|
impl BorromeanSignatures {
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<BorromeanSignatures> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
Ok(BorromeanSignatures {
|
Ok(Self { s0: read_array(read_bytes, r)?, s1: read_array(read_bytes, r)?, ee: read_bytes(r)? })
|
||||||
s0: read_array(read_bytes, r)?,
|
|
||||||
s1: read_array(read_bytes, r)?,
|
|
||||||
ee: read_bytes(r)?,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||||
for s0 in self.s0.iter() {
|
for s0 in &self.s0 {
|
||||||
w.write_all(s0)?;
|
w.write_all(s0)?;
|
||||||
}
|
}
|
||||||
for s1 in self.s1.iter() {
|
for s1 in &self.s1 {
|
||||||
w.write_all(s1)?;
|
w.write_all(s1)?;
|
||||||
}
|
}
|
||||||
w.write_all(&self.ee)
|
w.write_all(&self.ee)
|
||||||
@@ -79,11 +75,8 @@ pub struct BorromeanRange {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BorromeanRange {
|
impl BorromeanRange {
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<BorromeanRange> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
Ok(BorromeanRange {
|
Ok(Self { sigs: BorromeanSignatures::read(r)?, bit_commitments: read_array(read_point, r)? })
|
||||||
sigs: BorromeanSignatures::read(r)?,
|
|
||||||
bit_commitments: read_array(read_point, r)?,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||||
self.sigs.write(w)?;
|
self.sigs.write(w)?;
|
||||||
@@ -91,6 +84,7 @@ impl BorromeanRange {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "experimental")]
|
#[cfg(feature = "experimental")]
|
||||||
|
#[must_use]
|
||||||
pub fn verify(&self, commitment: &EdwardsPoint) -> bool {
|
pub fn verify(&self, commitment: &EdwardsPoint) -> bool {
|
||||||
if &self.bit_commitments.iter().sum::<EdwardsPoint>() != commitment {
|
if &self.bit_commitments.iter().sum::<EdwardsPoint>() != commitment {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ pub(crate) fn vector_exponent(
|
|||||||
|
|
||||||
pub(crate) fn hash_cache(cache: &mut Scalar, mash: &[[u8; 32]]) -> Scalar {
|
pub(crate) fn hash_cache(cache: &mut Scalar, mash: &[[u8; 32]]) -> Scalar {
|
||||||
let slice =
|
let slice =
|
||||||
&[cache.to_bytes().as_ref(), mash.iter().cloned().flatten().collect::<Vec<_>>().as_ref()]
|
&[cache.to_bytes().as_ref(), mash.iter().copied().flatten().collect::<Vec<_>>().as_ref()]
|
||||||
.concat();
|
.concat();
|
||||||
*cache = hash_to_scalar(slice);
|
*cache = hash_to_scalar(slice);
|
||||||
*cache
|
*cache
|
||||||
@@ -78,10 +78,8 @@ pub(crate) fn bit_decompose(commitments: &[Commitment]) -> (ScalarVector, Scalar
|
|||||||
|
|
||||||
for j in 0 .. M {
|
for j in 0 .. M {
|
||||||
for i in (0 .. N).rev() {
|
for i in (0 .. N).rev() {
|
||||||
let mut bit = Choice::from(0);
|
let bit =
|
||||||
if j < sv.len() {
|
if j < sv.len() { Choice::from((sv[j][i / 8] >> (i % 8)) & 1) } else { Choice::from(0) };
|
||||||
bit = Choice::from((sv[j][i / 8] >> (i % 8)) & 1);
|
|
||||||
}
|
|
||||||
aL.0[(j * N) + i] = Scalar::conditional_select(&Scalar::ZERO, &Scalar::ONE, bit);
|
aL.0[(j * N) + i] = Scalar::conditional_select(&Scalar::ZERO, &Scalar::ONE, bit);
|
||||||
aR.0[(j * N) + i] = Scalar::conditional_select(&-Scalar::ONE, &Scalar::ZERO, bit);
|
aR.0[(j * N) + i] = Scalar::conditional_select(&-Scalar::ONE, &Scalar::ZERO, bit);
|
||||||
}
|
}
|
||||||
@@ -118,9 +116,9 @@ pub(crate) fn LR_statements(
|
|||||||
let mut res = a
|
let mut res = a
|
||||||
.0
|
.0
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.copied()
|
||||||
.zip(G_i.iter().cloned())
|
.zip(G_i.iter().copied())
|
||||||
.chain(b.0.iter().cloned().zip(H_i.iter().cloned()))
|
.chain(b.0.iter().copied().zip(H_i.iter().copied()))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
res.push((cL, U));
|
res.push((cL, U));
|
||||||
res
|
res
|
||||||
|
|||||||
@@ -62,14 +62,14 @@ impl Bulletproofs {
|
|||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
outputs: &[Commitment],
|
outputs: &[Commitment],
|
||||||
plus: bool,
|
plus: bool,
|
||||||
) -> Result<Bulletproofs, TransactionError> {
|
) -> Result<Self, TransactionError> {
|
||||||
if outputs.len() > MAX_OUTPUTS {
|
if outputs.len() > MAX_OUTPUTS {
|
||||||
return Err(TransactionError::TooManyOutputs)?;
|
return Err(TransactionError::TooManyOutputs)?;
|
||||||
}
|
}
|
||||||
Ok(if !plus {
|
Ok(if !plus {
|
||||||
Bulletproofs::Original(OriginalStruct::prove(rng, outputs))
|
Self::Plus(PlusStruct::prove(rng, outputs))
|
||||||
} else {
|
} else {
|
||||||
Bulletproofs::Plus(PlusStruct::prove(rng, outputs))
|
Self::Original(OriginalStruct::prove(rng, outputs))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,8 +77,8 @@ impl Bulletproofs {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn verify<R: RngCore + CryptoRng>(&self, rng: &mut R, commitments: &[EdwardsPoint]) -> bool {
|
pub fn verify<R: RngCore + CryptoRng>(&self, rng: &mut R, commitments: &[EdwardsPoint]) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Bulletproofs::Original(bp) => bp.verify(rng, commitments),
|
Self::Original(bp) => bp.verify(rng, commitments),
|
||||||
Bulletproofs::Plus(bp) => bp.verify(rng, commitments),
|
Self::Plus(bp) => bp.verify(rng, commitments),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,8 +94,8 @@ impl Bulletproofs {
|
|||||||
commitments: &[EdwardsPoint],
|
commitments: &[EdwardsPoint],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Bulletproofs::Original(bp) => bp.batch_verify(rng, verifier, id, commitments),
|
Self::Original(bp) => bp.batch_verify(rng, verifier, id, commitments),
|
||||||
Bulletproofs::Plus(bp) => bp.batch_verify(rng, verifier, id, commitments),
|
Self::Plus(bp) => bp.batch_verify(rng, verifier, id, commitments),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ impl Bulletproofs {
|
|||||||
specific_write_vec: F,
|
specific_write_vec: F,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Bulletproofs::Original(bp) => {
|
Self::Original(bp) => {
|
||||||
write_point(&bp.A, w)?;
|
write_point(&bp.A, w)?;
|
||||||
write_point(&bp.S, w)?;
|
write_point(&bp.S, w)?;
|
||||||
write_point(&bp.T1, w)?;
|
write_point(&bp.T1, w)?;
|
||||||
@@ -119,7 +119,7 @@ impl Bulletproofs {
|
|||||||
write_scalar(&bp.t, w)
|
write_scalar(&bp.t, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
Bulletproofs::Plus(bp) => {
|
Self::Plus(bp) => {
|
||||||
write_point(&bp.A, w)?;
|
write_point(&bp.A, w)?;
|
||||||
write_point(&bp.A1, w)?;
|
write_point(&bp.A1, w)?;
|
||||||
write_point(&bp.B, w)?;
|
write_point(&bp.B, w)?;
|
||||||
@@ -147,8 +147,8 @@ impl Bulletproofs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Read Bulletproofs.
|
/// Read Bulletproofs.
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Bulletproofs> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
Ok(Bulletproofs::Original(OriginalStruct {
|
Ok(Self::Original(OriginalStruct {
|
||||||
A: read_point(r)?,
|
A: read_point(r)?,
|
||||||
S: read_point(r)?,
|
S: read_point(r)?,
|
||||||
T1: read_point(r)?,
|
T1: read_point(r)?,
|
||||||
@@ -164,8 +164,8 @@ impl Bulletproofs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Read Bulletproofs+.
|
/// Read Bulletproofs+.
|
||||||
pub fn read_plus<R: Read>(r: &mut R) -> io::Result<Bulletproofs> {
|
pub fn read_plus<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
Ok(Bulletproofs::Plus(PlusStruct {
|
Ok(Self::Plus(PlusStruct {
|
||||||
A: read_point(r)?,
|
A: read_point(r)?,
|
||||||
A1: read_point(r)?,
|
A1: read_point(r)?,
|
||||||
B: read_point(r)?,
|
B: read_point(r)?,
|
||||||
|
|||||||
@@ -36,10 +36,8 @@ pub struct OriginalStruct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OriginalStruct {
|
impl OriginalStruct {
|
||||||
pub(crate) fn prove<R: RngCore + CryptoRng>(
|
#[allow(clippy::many_single_char_names)]
|
||||||
rng: &mut R,
|
pub(crate) fn prove<R: RngCore + CryptoRng>(rng: &mut R, commitments: &[Commitment]) -> Self {
|
||||||
commitments: &[Commitment],
|
|
||||||
) -> OriginalStruct {
|
|
||||||
let (logMN, M, MN) = MN(commitments.len());
|
let (logMN, M, MN) = MN(commitments.len());
|
||||||
|
|
||||||
let (aL, aR) = bit_decompose(commitments);
|
let (aL, aR) = bit_decompose(commitments);
|
||||||
@@ -134,8 +132,8 @@ impl OriginalStruct {
|
|||||||
|
|
||||||
let L_i = prove_multiexp(&LR_statements(&aL, G_R, &bR, H_L, cL, U));
|
let L_i = prove_multiexp(&LR_statements(&aL, G_R, &bR, H_L, cL, U));
|
||||||
let R_i = prove_multiexp(&LR_statements(&aR, G_L, &bL, H_R, cR, U));
|
let R_i = prove_multiexp(&LR_statements(&aR, G_L, &bL, H_R, cR, U));
|
||||||
L.push(L_i);
|
L.push(*L_i);
|
||||||
R.push(R_i);
|
R.push(*R_i);
|
||||||
|
|
||||||
let w = hash_cache(&mut cache, &[L_i.compress().to_bytes(), R_i.compress().to_bytes()]);
|
let w = hash_cache(&mut cache, &[L_i.compress().to_bytes(), R_i.compress().to_bytes()]);
|
||||||
let winv = w.invert().unwrap();
|
let winv = w.invert().unwrap();
|
||||||
@@ -149,15 +147,15 @@ impl OriginalStruct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = OriginalStruct {
|
let res = Self {
|
||||||
A: *A,
|
A: *A,
|
||||||
S: *S,
|
S: *S,
|
||||||
T1: *T1,
|
T1: *T1,
|
||||||
T2: *T2,
|
T2: *T2,
|
||||||
taux: *taux,
|
taux: *taux,
|
||||||
mu: *mu,
|
mu: *mu,
|
||||||
L: L.drain(..).map(|L| *L).collect(),
|
L,
|
||||||
R: R.drain(..).map(|R| *R).collect(),
|
R,
|
||||||
a: *a[0],
|
a: *a[0],
|
||||||
b: *b[0],
|
b: *b[0],
|
||||||
t: *t,
|
t: *t,
|
||||||
@@ -166,6 +164,7 @@ impl OriginalStruct {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::many_single_char_names)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn verify_core<ID: Copy + Zeroize, R: RngCore + CryptoRng>(
|
fn verify_core<ID: Copy + Zeroize, R: RngCore + CryptoRng>(
|
||||||
&self,
|
&self,
|
||||||
@@ -190,7 +189,7 @@ impl OriginalStruct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rebuild all challenges
|
// Rebuild all challenges
|
||||||
let (mut cache, commitments) = hash_commitments(commitments.iter().cloned());
|
let (mut cache, commitments) = hash_commitments(commitments.iter().copied());
|
||||||
let y = hash_cache(&mut cache, &[self.A.compress().to_bytes(), self.S.compress().to_bytes()]);
|
let y = hash_cache(&mut cache, &[self.A.compress().to_bytes(), self.S.compress().to_bytes()]);
|
||||||
|
|
||||||
let z = hash_to_scalar(&y.to_bytes());
|
let z = hash_to_scalar(&y.to_bytes());
|
||||||
@@ -223,7 +222,7 @@ impl OriginalStruct {
|
|||||||
let A = normalize(&self.A);
|
let A = normalize(&self.A);
|
||||||
let S = normalize(&self.S);
|
let S = normalize(&self.S);
|
||||||
|
|
||||||
let commitments = commitments.iter().map(|c| c.mul_by_cofactor()).collect::<Vec<_>>();
|
let commitments = commitments.iter().map(EdwardsPoint::mul_by_cofactor).collect::<Vec<_>>();
|
||||||
|
|
||||||
// Verify it
|
// Verify it
|
||||||
let mut proof = Vec::with_capacity(4 + commitments.len());
|
let mut proof = Vec::with_capacity(4 + commitments.len());
|
||||||
|
|||||||
@@ -56,10 +56,8 @@ pub struct PlusStruct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PlusStruct {
|
impl PlusStruct {
|
||||||
pub(crate) fn prove<R: RngCore + CryptoRng>(
|
#[allow(clippy::many_single_char_names)]
|
||||||
rng: &mut R,
|
pub(crate) fn prove<R: RngCore + CryptoRng>(rng: &mut R, commitments: &[Commitment]) -> Self {
|
||||||
commitments: &[Commitment],
|
|
||||||
) -> PlusStruct {
|
|
||||||
let generators = GENERATORS();
|
let generators = GENERATORS();
|
||||||
|
|
||||||
let (logMN, M, MN) = MN(commitments.len());
|
let (logMN, M, MN) = MN(commitments.len());
|
||||||
@@ -113,12 +111,12 @@ impl PlusStruct {
|
|||||||
let mut L_i = LR_statements(&(&aL * yinvpow[aL.len()]), G_R, &bR, H_L, cL, H());
|
let mut L_i = LR_statements(&(&aL * yinvpow[aL.len()]), G_R, &bR, H_L, cL, H());
|
||||||
L_i.push((dL, G));
|
L_i.push((dL, G));
|
||||||
let L_i = prove_multiexp(&L_i);
|
let L_i = prove_multiexp(&L_i);
|
||||||
L.push(L_i);
|
L.push(*L_i);
|
||||||
|
|
||||||
let mut R_i = LR_statements(&(&aR * ypow[aR.len()]), G_L, &bL, H_R, cR, H());
|
let mut R_i = LR_statements(&(&aR * ypow[aR.len()]), G_L, &bL, H_R, cR, H());
|
||||||
R_i.push((dR, G));
|
R_i.push((dR, G));
|
||||||
let R_i = prove_multiexp(&R_i);
|
let R_i = prove_multiexp(&R_i);
|
||||||
R.push(R_i);
|
R.push(*R_i);
|
||||||
|
|
||||||
let w = hash_cache(&mut cache, &[L_i.compress().to_bytes(), R_i.compress().to_bytes()]);
|
let w = hash_cache(&mut cache, &[L_i.compress().to_bytes(), R_i.compress().to_bytes()]);
|
||||||
let winv = w.invert().unwrap();
|
let winv = w.invert().unwrap();
|
||||||
@@ -158,20 +156,12 @@ impl PlusStruct {
|
|||||||
eta.zeroize();
|
eta.zeroize();
|
||||||
alpha1.zeroize();
|
alpha1.zeroize();
|
||||||
|
|
||||||
let res = PlusStruct {
|
let res = Self { A: *A, A1: *A1, B: *B, r1: *r1, s1: *s1, d1: *d1, L, R };
|
||||||
A: *A,
|
|
||||||
A1: *A1,
|
|
||||||
B: *B,
|
|
||||||
r1: *r1,
|
|
||||||
s1: *s1,
|
|
||||||
d1: *d1,
|
|
||||||
L: L.drain(..).map(|L| *L).collect(),
|
|
||||||
R: R.drain(..).map(|R| *R).collect(),
|
|
||||||
};
|
|
||||||
debug_assert!(res.verify(rng, &commitments_points));
|
debug_assert!(res.verify(rng, &commitments_points));
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::many_single_char_names)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn verify_core<ID: Copy + Zeroize, R: RngCore + CryptoRng>(
|
fn verify_core<ID: Copy + Zeroize, R: RngCore + CryptoRng>(
|
||||||
&self,
|
&self,
|
||||||
@@ -196,7 +186,7 @@ impl PlusStruct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rebuild all challenges
|
// Rebuild all challenges
|
||||||
let (mut cache, commitments) = hash_plus(commitments.iter().cloned());
|
let (mut cache, commitments) = hash_plus(commitments.iter().copied());
|
||||||
let y = hash_cache(&mut cache, &[self.A.compress().to_bytes()]);
|
let y = hash_cache(&mut cache, &[self.A.compress().to_bytes()]);
|
||||||
let yinv = y.invert().unwrap();
|
let yinv = y.invert().unwrap();
|
||||||
let z = hash_to_scalar(&y.to_bytes());
|
let z = hash_to_scalar(&y.to_bytes());
|
||||||
@@ -220,8 +210,6 @@ impl PlusStruct {
|
|||||||
let A1 = normalize(&self.A1);
|
let A1 = normalize(&self.A1);
|
||||||
let B = normalize(&self.B);
|
let B = normalize(&self.B);
|
||||||
|
|
||||||
let mut commitments = commitments.iter().map(|c| c.mul_by_cofactor()).collect::<Vec<_>>();
|
|
||||||
|
|
||||||
// Verify it
|
// Verify it
|
||||||
let mut proof = Vec::with_capacity(logMN + 5 + (2 * (MN + logMN)));
|
let mut proof = Vec::with_capacity(logMN + 5 + (2 * (MN + logMN)));
|
||||||
|
|
||||||
@@ -237,7 +225,7 @@ impl PlusStruct {
|
|||||||
let esq = e * e;
|
let esq = e * e;
|
||||||
let minus_esq = -esq;
|
let minus_esq = -esq;
|
||||||
let commitment_weight = minus_esq * yMNy;
|
let commitment_weight = minus_esq * yMNy;
|
||||||
for (i, commitment) in commitments.drain(..).enumerate() {
|
for (i, commitment) in commitments.iter().map(EdwardsPoint::mul_by_cofactor).enumerate() {
|
||||||
proof.push((commitment_weight * zpow[i], commitment));
|
proof.push((commitment_weight * zpow[i], commitment));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ pub(crate) struct ScalarVector(pub(crate) Vec<Scalar>);
|
|||||||
macro_rules! math_op {
|
macro_rules! math_op {
|
||||||
($Op: ident, $op: ident, $f: expr) => {
|
($Op: ident, $op: ident, $f: expr) => {
|
||||||
impl $Op<Scalar> for ScalarVector {
|
impl $Op<Scalar> for ScalarVector {
|
||||||
type Output = ScalarVector;
|
type Output = Self;
|
||||||
fn $op(self, b: Scalar) -> ScalarVector {
|
fn $op(self, b: Scalar) -> Self {
|
||||||
ScalarVector(self.0.iter().map(|a| $f((a, &b))).collect())
|
Self(self.0.iter().map(|a| $f((a, &b))).collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,16 +27,16 @@ macro_rules! math_op {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl $Op<ScalarVector> for ScalarVector {
|
impl $Op<ScalarVector> for ScalarVector {
|
||||||
type Output = ScalarVector;
|
type Output = Self;
|
||||||
fn $op(self, b: ScalarVector) -> ScalarVector {
|
fn $op(self, b: Self) -> Self {
|
||||||
debug_assert_eq!(self.len(), b.len());
|
debug_assert_eq!(self.len(), b.len());
|
||||||
ScalarVector(self.0.iter().zip(b.0.iter()).map($f).collect())
|
Self(self.0.iter().zip(b.0.iter()).map($f).collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl $Op<&ScalarVector> for &ScalarVector {
|
impl $Op<Self> for &ScalarVector {
|
||||||
type Output = ScalarVector;
|
type Output = ScalarVector;
|
||||||
fn $op(self, b: &ScalarVector) -> ScalarVector {
|
fn $op(self, b: Self) -> ScalarVector {
|
||||||
debug_assert_eq!(self.len(), b.len());
|
debug_assert_eq!(self.len(), b.len());
|
||||||
ScalarVector(self.0.iter().zip(b.0.iter()).map($f).collect())
|
ScalarVector(self.0.iter().zip(b.0.iter()).map($f).collect())
|
||||||
}
|
}
|
||||||
@@ -48,11 +48,11 @@ math_op!(Sub, sub, |(a, b): (&Scalar, &Scalar)| *a - *b);
|
|||||||
math_op!(Mul, mul, |(a, b): (&Scalar, &Scalar)| *a * *b);
|
math_op!(Mul, mul, |(a, b): (&Scalar, &Scalar)| *a * *b);
|
||||||
|
|
||||||
impl ScalarVector {
|
impl ScalarVector {
|
||||||
pub(crate) fn new(len: usize) -> ScalarVector {
|
pub(crate) fn new(len: usize) -> Self {
|
||||||
ScalarVector(vec![Scalar::ZERO; len])
|
Self(vec![Scalar::ZERO; len])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn powers(x: Scalar, len: usize) -> ScalarVector {
|
pub(crate) fn powers(x: Scalar, len: usize) -> Self {
|
||||||
debug_assert!(len != 0);
|
debug_assert!(len != 0);
|
||||||
|
|
||||||
let mut res = Vec::with_capacity(len);
|
let mut res = Vec::with_capacity(len);
|
||||||
@@ -60,16 +60,16 @@ impl ScalarVector {
|
|||||||
for i in 1 .. len {
|
for i in 1 .. len {
|
||||||
res.push(res[i - 1] * x);
|
res.push(res[i - 1] * x);
|
||||||
}
|
}
|
||||||
ScalarVector(res)
|
Self(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn even_powers(x: Scalar, pow: usize) -> ScalarVector {
|
pub(crate) fn even_powers(x: Scalar, pow: usize) -> Self {
|
||||||
debug_assert!(pow != 0);
|
debug_assert!(pow != 0);
|
||||||
// Verify pow is a power of two
|
// Verify pow is a power of two
|
||||||
debug_assert_eq!(((pow - 1) & pow), 0);
|
debug_assert_eq!(((pow - 1) & pow), 0);
|
||||||
|
|
||||||
let xsq = x * x;
|
let xsq = x * x;
|
||||||
let mut res = ScalarVector(Vec::with_capacity(pow / 2));
|
let mut res = Self(Vec::with_capacity(pow / 2));
|
||||||
res.0.push(xsq);
|
res.0.push(xsq);
|
||||||
|
|
||||||
let mut prev = 2;
|
let mut prev = 2;
|
||||||
@@ -89,9 +89,9 @@ impl ScalarVector {
|
|||||||
self.0.len()
|
self.0.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn split(self) -> (ScalarVector, ScalarVector) {
|
pub(crate) fn split(self) -> (Self, Self) {
|
||||||
let (l, r) = self.0.split_at(self.0.len() / 2);
|
let (l, r) = self.0.split_at(self.0.len() / 2);
|
||||||
(ScalarVector(l.to_vec()), ScalarVector(r.to_vec()))
|
(Self(l.to_vec()), Self(r.to_vec()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,7 +119,7 @@ impl Mul<&[EdwardsPoint]> for &ScalarVector {
|
|||||||
type Output = EdwardsPoint;
|
type Output = EdwardsPoint;
|
||||||
fn mul(self, b: &[EdwardsPoint]) -> EdwardsPoint {
|
fn mul(self, b: &[EdwardsPoint]) -> EdwardsPoint {
|
||||||
debug_assert_eq!(self.len(), b.len());
|
debug_assert_eq!(self.len(), b.len());
|
||||||
multiexp(&self.0.iter().cloned().zip(b.iter().cloned()).collect::<Vec<_>>())
|
multiexp(&self.0.iter().copied().zip(b.iter().copied()).collect::<Vec<_>>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ use std_shims::{
|
|||||||
io::{self, Read, Write},
|
io::{self, Read, Write},
|
||||||
};
|
};
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
|
||||||
|
|
||||||
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
|
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
|
||||||
use subtle::{ConstantTimeEq, Choice, CtOption};
|
use subtle::{ConstantTimeEq, Choice, CtOption};
|
||||||
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
use curve25519_dalek::{
|
use curve25519_dalek::{
|
||||||
constants::ED25519_BASEPOINT_TABLE,
|
constants::ED25519_BASEPOINT_TABLE,
|
||||||
@@ -62,7 +61,7 @@ pub struct ClsagInput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ClsagInput {
|
impl ClsagInput {
|
||||||
pub fn new(commitment: Commitment, decoys: Decoys) -> Result<ClsagInput, ClsagError> {
|
pub fn new(commitment: Commitment, decoys: Decoys) -> Result<Self, ClsagError> {
|
||||||
let n = decoys.len();
|
let n = decoys.len();
|
||||||
if n > u8::MAX.into() {
|
if n > u8::MAX.into() {
|
||||||
Err(ClsagError::InternalError("max ring size in this library is u8 max"))?;
|
Err(ClsagError::InternalError("max ring size in this library is u8 max"))?;
|
||||||
@@ -77,7 +76,7 @@ impl ClsagInput {
|
|||||||
Err(ClsagError::InvalidCommitment)?;
|
Err(ClsagError::InvalidCommitment)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ClsagInput { commitment, decoys })
|
Ok(Self { commitment, decoys })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,6 +204,7 @@ pub struct Clsag {
|
|||||||
impl Clsag {
|
impl Clsag {
|
||||||
// Sign core is the extension of core as needed for signing, yet is shared between single signer
|
// Sign core is the extension of core as needed for signing, yet is shared between single signer
|
||||||
// and multisig, hence why it's still core
|
// and multisig, hence why it's still core
|
||||||
|
#[allow(clippy::many_single_char_names)]
|
||||||
pub(crate) fn sign_core<R: RngCore + CryptoRng>(
|
pub(crate) fn sign_core<R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
I: &EdwardsPoint,
|
I: &EdwardsPoint,
|
||||||
@@ -213,7 +213,7 @@ impl Clsag {
|
|||||||
msg: &[u8; 32],
|
msg: &[u8; 32],
|
||||||
A: EdwardsPoint,
|
A: EdwardsPoint,
|
||||||
AH: EdwardsPoint,
|
AH: EdwardsPoint,
|
||||||
) -> (Clsag, EdwardsPoint, Scalar, Scalar) {
|
) -> (Self, EdwardsPoint, Scalar, Scalar) {
|
||||||
let r: usize = input.decoys.i.into();
|
let r: usize = input.decoys.i.into();
|
||||||
|
|
||||||
let pseudo_out = Commitment::new(mask, input.commitment.amount).calculate();
|
let pseudo_out = Commitment::new(mask, input.commitment.amount).calculate();
|
||||||
@@ -228,7 +228,7 @@ impl Clsag {
|
|||||||
let ((D, p, c), c1) =
|
let ((D, p, c), c1) =
|
||||||
core(&input.decoys.ring, I, &pseudo_out, msg, &D, &s, Mode::Sign(r, A, AH));
|
core(&input.decoys.ring, I, &pseudo_out, msg, &D, &s, Mode::Sign(r, A, AH));
|
||||||
|
|
||||||
(Clsag { D, s, c1 }, pseudo_out, p, c * z)
|
(Self { D, s, c1 }, pseudo_out, p, c * z)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate CLSAG signatures for the given inputs.
|
/// Generate CLSAG signatures for the given inputs.
|
||||||
@@ -239,19 +239,20 @@ impl Clsag {
|
|||||||
mut inputs: Vec<(Zeroizing<Scalar>, EdwardsPoint, ClsagInput)>,
|
mut inputs: Vec<(Zeroizing<Scalar>, EdwardsPoint, ClsagInput)>,
|
||||||
sum_outputs: Scalar,
|
sum_outputs: Scalar,
|
||||||
msg: [u8; 32],
|
msg: [u8; 32],
|
||||||
) -> Vec<(Clsag, EdwardsPoint)> {
|
) -> Vec<(Self, EdwardsPoint)> {
|
||||||
let mut res = Vec::with_capacity(inputs.len());
|
let mut res = Vec::with_capacity(inputs.len());
|
||||||
let mut sum_pseudo_outs = Scalar::zero();
|
let mut sum_pseudo_outs = Scalar::zero();
|
||||||
for i in 0 .. inputs.len() {
|
for i in 0 .. inputs.len() {
|
||||||
let mut mask = random_scalar(rng);
|
let mask = if i == (inputs.len() - 1) {
|
||||||
if i == (inputs.len() - 1) {
|
sum_outputs - sum_pseudo_outs
|
||||||
mask = sum_outputs - sum_pseudo_outs;
|
|
||||||
} else {
|
} else {
|
||||||
|
let mask = random_scalar(rng);
|
||||||
sum_pseudo_outs += mask;
|
sum_pseudo_outs += mask;
|
||||||
}
|
mask
|
||||||
|
};
|
||||||
|
|
||||||
let mut nonce = Zeroizing::new(random_scalar(rng));
|
let mut nonce = Zeroizing::new(random_scalar(rng));
|
||||||
let (mut clsag, pseudo_out, p, c) = Clsag::sign_core(
|
let (mut clsag, pseudo_out, p, c) = Self::sign_core(
|
||||||
rng,
|
rng,
|
||||||
&inputs[i].1,
|
&inputs[i].1,
|
||||||
&inputs[i].2,
|
&inputs[i].2,
|
||||||
@@ -318,7 +319,7 @@ impl Clsag {
|
|||||||
write_point(&self.D, w)
|
write_point(&self.D, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(decoys: usize, r: &mut R) -> io::Result<Clsag> {
|
pub fn read<R: Read>(decoys: usize, r: &mut R) -> io::Result<Self> {
|
||||||
Ok(Clsag { s: read_raw_vec(read_scalar, decoys, r)?, c1: read_scalar(r)?, D: read_point(r)? })
|
Ok(Self { s: read_raw_vec(read_scalar, decoys, r)?, c1: read_scalar(r)?, D: read_point(r)? })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
use core::{ops::Deref, fmt::Debug};
|
use core::{ops::Deref, fmt::Debug};
|
||||||
use std_shims::io::{self, Read, Write};
|
use std_shims::{
|
||||||
use std::sync::{Arc, RwLock};
|
sync::Arc,
|
||||||
|
io::{self, Read, Write},
|
||||||
|
};
|
||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng, SeedableRng};
|
use rand_core::{RngCore, CryptoRng, SeedableRng};
|
||||||
use rand_chacha::ChaCha20Rng;
|
use rand_chacha::ChaCha20Rng;
|
||||||
@@ -48,7 +51,7 @@ impl ClsagInput {
|
|||||||
// if in use
|
// if in use
|
||||||
transcript.append_message(b"member", [u8::try_from(i).expect("ring size exceeded 255")]);
|
transcript.append_message(b"member", [u8::try_from(i).expect("ring size exceeded 255")]);
|
||||||
transcript.append_message(b"key", pair[0].compress().to_bytes());
|
transcript.append_message(b"key", pair[0].compress().to_bytes());
|
||||||
transcript.append_message(b"commitment", pair[1].compress().to_bytes())
|
transcript.append_message(b"commitment", pair[1].compress().to_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Doesn't include the commitment's parts as the above ring + index includes the commitment
|
// Doesn't include the commitment's parts as the above ring + index includes the commitment
|
||||||
@@ -65,8 +68,8 @@ pub struct ClsagDetails {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ClsagDetails {
|
impl ClsagDetails {
|
||||||
pub fn new(input: ClsagInput, mask: Scalar) -> ClsagDetails {
|
pub fn new(input: ClsagInput, mask: Scalar) -> Self {
|
||||||
ClsagDetails { input, mask }
|
Self { input, mask }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,8 +119,8 @@ impl ClsagMultisig {
|
|||||||
transcript: RecommendedTranscript,
|
transcript: RecommendedTranscript,
|
||||||
output_key: EdwardsPoint,
|
output_key: EdwardsPoint,
|
||||||
details: Arc<RwLock<Option<ClsagDetails>>>,
|
details: Arc<RwLock<Option<ClsagDetails>>>,
|
||||||
) -> ClsagMultisig {
|
) -> Self {
|
||||||
ClsagMultisig {
|
Self {
|
||||||
transcript,
|
transcript,
|
||||||
|
|
||||||
H: hash_to_point(output_key),
|
H: hash_to_point(output_key),
|
||||||
|
|||||||
@@ -19,20 +19,21 @@ pub struct Mlsag {
|
|||||||
|
|
||||||
impl Mlsag {
|
impl Mlsag {
|
||||||
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||||
for ss in self.ss.iter() {
|
for ss in &self.ss {
|
||||||
write_raw_vec(write_scalar, ss, w)?;
|
write_raw_vec(write_scalar, ss, w)?;
|
||||||
}
|
}
|
||||||
write_scalar(&self.cc, w)
|
write_scalar(&self.cc, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(mixins: usize, r: &mut R) -> io::Result<Mlsag> {
|
pub fn read<R: Read>(mixins: usize, r: &mut R) -> io::Result<Self> {
|
||||||
Ok(Mlsag {
|
Ok(Self {
|
||||||
ss: (0 .. mixins).map(|_| read_array(read_scalar, r)).collect::<Result<_, _>>()?,
|
ss: (0 .. mixins).map(|_| read_array(read_scalar, r)).collect::<Result<_, _>>()?,
|
||||||
cc: read_scalar(r)?,
|
cc: read_scalar(r)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "experimental")]
|
#[cfg(feature = "experimental")]
|
||||||
|
#[must_use]
|
||||||
pub fn verify(
|
pub fn verify(
|
||||||
&self,
|
&self,
|
||||||
msg: &[u8; 32],
|
msg: &[u8; 32],
|
||||||
|
|||||||
@@ -38,21 +38,21 @@ pub enum EncryptedAmount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EncryptedAmount {
|
impl EncryptedAmount {
|
||||||
pub fn read<R: Read>(compact: bool, r: &mut R) -> io::Result<EncryptedAmount> {
|
pub fn read<R: Read>(compact: bool, r: &mut R) -> io::Result<Self> {
|
||||||
Ok(if !compact {
|
Ok(if compact {
|
||||||
EncryptedAmount::Original { mask: read_bytes(r)?, amount: read_bytes(r)? }
|
Self::Compact { amount: read_bytes(r)? }
|
||||||
} else {
|
} else {
|
||||||
EncryptedAmount::Compact { amount: read_bytes(r)? }
|
Self::Original { mask: read_bytes(r)?, amount: read_bytes(r)? }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
EncryptedAmount::Original { mask, amount } => {
|
Self::Original { mask, amount } => {
|
||||||
w.write_all(mask)?;
|
w.write_all(mask)?;
|
||||||
w.write_all(amount)
|
w.write_all(amount)
|
||||||
}
|
}
|
||||||
EncryptedAmount::Compact { amount } => w.write_all(amount),
|
Self::Compact { amount } => w.write_all(amount),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,38 +79,33 @@ pub enum RctType {
|
|||||||
impl RctType {
|
impl RctType {
|
||||||
pub fn to_byte(self) -> u8 {
|
pub fn to_byte(self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
RctType::Null => 0,
|
Self::Null => 0,
|
||||||
RctType::MlsagAggregate => 1,
|
Self::MlsagAggregate => 1,
|
||||||
RctType::MlsagIndividual => 2,
|
Self::MlsagIndividual => 2,
|
||||||
RctType::Bulletproofs => 3,
|
Self::Bulletproofs => 3,
|
||||||
RctType::BulletproofsCompactAmount => 4,
|
Self::BulletproofsCompactAmount => 4,
|
||||||
RctType::Clsag => 5,
|
Self::Clsag => 5,
|
||||||
RctType::BulletproofsPlus => 6,
|
Self::BulletproofsPlus => 6,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_byte(byte: u8) -> Option<Self> {
|
pub fn from_byte(byte: u8) -> Option<Self> {
|
||||||
Some(match byte {
|
Some(match byte {
|
||||||
0 => RctType::Null,
|
0 => Self::Null,
|
||||||
1 => RctType::MlsagAggregate,
|
1 => Self::MlsagAggregate,
|
||||||
2 => RctType::MlsagIndividual,
|
2 => Self::MlsagIndividual,
|
||||||
3 => RctType::Bulletproofs,
|
3 => Self::Bulletproofs,
|
||||||
4 => RctType::BulletproofsCompactAmount,
|
4 => Self::BulletproofsCompactAmount,
|
||||||
5 => RctType::Clsag,
|
5 => Self::Clsag,
|
||||||
6 => RctType::BulletproofsPlus,
|
6 => Self::BulletproofsPlus,
|
||||||
_ => None?,
|
_ => None?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compact_encrypted_amounts(&self) -> bool {
|
pub fn compact_encrypted_amounts(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
RctType::Null => false,
|
Self::Null | Self::MlsagAggregate | Self::MlsagIndividual | Self::Bulletproofs => false,
|
||||||
RctType::MlsagAggregate => false,
|
Self::BulletproofsCompactAmount | Self::Clsag | Self::BulletproofsPlus => true,
|
||||||
RctType::MlsagIndividual => false,
|
|
||||||
RctType::Bulletproofs => false,
|
|
||||||
RctType::BulletproofsCompactAmount => true,
|
|
||||||
RctType::Clsag => true,
|
|
||||||
RctType::BulletproofsPlus => true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,7 +127,12 @@ impl RctBase {
|
|||||||
w.write_all(&[rct_type.to_byte()])?;
|
w.write_all(&[rct_type.to_byte()])?;
|
||||||
match rct_type {
|
match rct_type {
|
||||||
RctType::Null => Ok(()),
|
RctType::Null => Ok(()),
|
||||||
_ => {
|
RctType::MlsagAggregate |
|
||||||
|
RctType::MlsagIndividual |
|
||||||
|
RctType::Bulletproofs |
|
||||||
|
RctType::BulletproofsCompactAmount |
|
||||||
|
RctType::Clsag |
|
||||||
|
RctType::BulletproofsPlus => {
|
||||||
write_varint(&self.fee, w)?;
|
write_varint(&self.fee, w)?;
|
||||||
if rct_type == RctType::MlsagIndividual {
|
if rct_type == RctType::MlsagIndividual {
|
||||||
write_raw_vec(write_point, &self.pseudo_outs, w)?;
|
write_raw_vec(write_point, &self.pseudo_outs, w)?;
|
||||||
@@ -145,14 +145,12 @@ impl RctBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(inputs: usize, outputs: usize, r: &mut R) -> io::Result<(RctBase, RctType)> {
|
pub fn read<R: Read>(inputs: usize, outputs: usize, r: &mut R) -> io::Result<(Self, RctType)> {
|
||||||
let rct_type = RctType::from_byte(read_byte(r)?)
|
let rct_type = RctType::from_byte(read_byte(r)?)
|
||||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid RCT type"))?;
|
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid RCT type"))?;
|
||||||
|
|
||||||
match rct_type {
|
match rct_type {
|
||||||
RctType::Null => {}
|
RctType::Null | RctType::MlsagAggregate | RctType::MlsagIndividual => {}
|
||||||
RctType::MlsagAggregate => {}
|
|
||||||
RctType::MlsagIndividual => {}
|
|
||||||
RctType::Bulletproofs |
|
RctType::Bulletproofs |
|
||||||
RctType::BulletproofsCompactAmount |
|
RctType::BulletproofsCompactAmount |
|
||||||
RctType::Clsag |
|
RctType::Clsag |
|
||||||
@@ -170,9 +168,9 @@ impl RctBase {
|
|||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
if rct_type == RctType::Null {
|
if rct_type == RctType::Null {
|
||||||
RctBase { fee: 0, pseudo_outs: vec![], encrypted_amounts: vec![], commitments: vec![] }
|
Self { fee: 0, pseudo_outs: vec![], encrypted_amounts: vec![], commitments: vec![] }
|
||||||
} else {
|
} else {
|
||||||
RctBase {
|
Self {
|
||||||
fee: read_varint(r)?,
|
fee: read_varint(r)?,
|
||||||
pseudo_outs: if rct_type == RctType::MlsagIndividual {
|
pseudo_outs: if rct_type == RctType::MlsagIndividual {
|
||||||
read_raw_vec(read_point, inputs, r)?
|
read_raw_vec(read_point, inputs, r)?
|
||||||
@@ -217,12 +215,12 @@ impl RctPrunable {
|
|||||||
|
|
||||||
pub fn write<W: Write>(&self, w: &mut W, rct_type: RctType) -> io::Result<()> {
|
pub fn write<W: Write>(&self, w: &mut W, rct_type: RctType) -> io::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
RctPrunable::Null => Ok(()),
|
Self::Null => Ok(()),
|
||||||
RctPrunable::MlsagBorromean { borromean, mlsags } => {
|
Self::MlsagBorromean { borromean, mlsags } => {
|
||||||
write_raw_vec(BorromeanRange::write, borromean, w)?;
|
write_raw_vec(BorromeanRange::write, borromean, w)?;
|
||||||
write_raw_vec(Mlsag::write, mlsags, w)
|
write_raw_vec(Mlsag::write, mlsags, w)
|
||||||
}
|
}
|
||||||
RctPrunable::MlsagBulletproofs { bulletproofs, mlsags, pseudo_outs } => {
|
Self::MlsagBulletproofs { bulletproofs, mlsags, pseudo_outs } => {
|
||||||
if rct_type == RctType::Bulletproofs {
|
if rct_type == RctType::Bulletproofs {
|
||||||
w.write_all(&1u32.to_le_bytes())?;
|
w.write_all(&1u32.to_le_bytes())?;
|
||||||
} else {
|
} else {
|
||||||
@@ -233,7 +231,7 @@ impl RctPrunable {
|
|||||||
write_raw_vec(Mlsag::write, mlsags, w)?;
|
write_raw_vec(Mlsag::write, mlsags, w)?;
|
||||||
write_raw_vec(write_point, pseudo_outs, w)
|
write_raw_vec(write_point, pseudo_outs, w)
|
||||||
}
|
}
|
||||||
RctPrunable::Clsag { bulletproofs, clsags, pseudo_outs } => {
|
Self::Clsag { bulletproofs, clsags, pseudo_outs } => {
|
||||||
w.write_all(&[1])?;
|
w.write_all(&[1])?;
|
||||||
bulletproofs.write(w)?;
|
bulletproofs.write(w)?;
|
||||||
|
|
||||||
@@ -254,15 +252,14 @@ impl RctPrunable {
|
|||||||
decoys: &[usize],
|
decoys: &[usize],
|
||||||
outputs: usize,
|
outputs: usize,
|
||||||
r: &mut R,
|
r: &mut R,
|
||||||
) -> io::Result<RctPrunable> {
|
) -> io::Result<Self> {
|
||||||
Ok(match rct_type {
|
Ok(match rct_type {
|
||||||
RctType::Null => RctPrunable::Null,
|
RctType::Null => Self::Null,
|
||||||
RctType::MlsagAggregate | RctType::MlsagIndividual => RctPrunable::MlsagBorromean {
|
RctType::MlsagAggregate | RctType::MlsagIndividual => Self::MlsagBorromean {
|
||||||
borromean: read_raw_vec(BorromeanRange::read, outputs, r)?,
|
borromean: read_raw_vec(BorromeanRange::read, outputs, r)?,
|
||||||
mlsags: decoys.iter().map(|d| Mlsag::read(*d, r)).collect::<Result<_, _>>()?,
|
mlsags: decoys.iter().map(|d| Mlsag::read(*d, r)).collect::<Result<_, _>>()?,
|
||||||
},
|
},
|
||||||
RctType::Bulletproofs | RctType::BulletproofsCompactAmount => {
|
RctType::Bulletproofs | RctType::BulletproofsCompactAmount => Self::MlsagBulletproofs {
|
||||||
RctPrunable::MlsagBulletproofs {
|
|
||||||
bulletproofs: {
|
bulletproofs: {
|
||||||
if (if rct_type == RctType::Bulletproofs {
|
if (if rct_type == RctType::Bulletproofs {
|
||||||
u64::from(read_u32(r)?)
|
u64::from(read_u32(r)?)
|
||||||
@@ -276,9 +273,8 @@ impl RctPrunable {
|
|||||||
},
|
},
|
||||||
mlsags: decoys.iter().map(|d| Mlsag::read(*d, r)).collect::<Result<_, _>>()?,
|
mlsags: decoys.iter().map(|d| Mlsag::read(*d, r)).collect::<Result<_, _>>()?,
|
||||||
pseudo_outs: read_raw_vec(read_point, decoys.len(), r)?,
|
pseudo_outs: read_raw_vec(read_point, decoys.len(), r)?,
|
||||||
}
|
},
|
||||||
}
|
RctType::Clsag | RctType::BulletproofsPlus => Self::Clsag {
|
||||||
RctType::Clsag | RctType::BulletproofsPlus => RctPrunable::Clsag {
|
|
||||||
bulletproofs: {
|
bulletproofs: {
|
||||||
if read_varint(r)? != 1 {
|
if read_varint(r)? != 1 {
|
||||||
Err(io::Error::new(io::ErrorKind::Other, "n bulletproofs instead of one"))?;
|
Err(io::Error::new(io::ErrorKind::Other, "n bulletproofs instead of one"))?;
|
||||||
@@ -295,12 +291,10 @@ impl RctPrunable {
|
|||||||
|
|
||||||
pub(crate) fn signature_write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
pub(crate) fn signature_write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
RctPrunable::Null => panic!("Serializing RctPrunable::Null for a signature"),
|
Self::Null => panic!("Serializing RctPrunable::Null for a signature"),
|
||||||
RctPrunable::MlsagBorromean { borromean, .. } => {
|
Self::MlsagBorromean { borromean, .. } => borromean.iter().try_for_each(|rs| rs.write(w)),
|
||||||
borromean.iter().try_for_each(|rs| rs.write(w))
|
Self::MlsagBulletproofs { bulletproofs, .. } => bulletproofs.signature_write(w),
|
||||||
}
|
Self::Clsag { bulletproofs, .. } => bulletproofs.signature_write(w),
|
||||||
RctPrunable::MlsagBulletproofs { bulletproofs, .. } => bulletproofs.signature_write(w),
|
|
||||||
RctPrunable::Clsag { bulletproofs, .. } => bulletproofs.signature_write(w),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -382,8 +376,8 @@ impl RctSignatures {
|
|||||||
serialized
|
serialized
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(decoys: Vec<usize>, outputs: usize, r: &mut R) -> io::Result<RctSignatures> {
|
pub fn read<R: Read>(decoys: Vec<usize>, outputs: usize, r: &mut R) -> io::Result<Self> {
|
||||||
let base = RctBase::read(decoys.len(), outputs, r)?;
|
let base = RctBase::read(decoys.len(), outputs, r)?;
|
||||||
Ok(RctSignatures { base: base.0, prunable: RctPrunable::read(base.1, &decoys, outputs, r)? })
|
Ok(Self { base: base.0, prunable: RctPrunable::read(base.1, &decoys, outputs, r)? })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ impl HttpRpc {
|
|||||||
///
|
///
|
||||||
/// A daemon requiring authentication can be used via including the username and password in the
|
/// A daemon requiring authentication can be used via including the username and password in the
|
||||||
/// URL.
|
/// URL.
|
||||||
pub fn new(mut url: String) -> Result<Rpc<HttpRpc>, RpcError> {
|
pub fn new(mut url: String) -> Result<Rpc<Self>, RpcError> {
|
||||||
// Parse out the username and password
|
// Parse out the username and password
|
||||||
let userpass = if url.contains('@') {
|
let userpass = if url.contains('@') {
|
||||||
let url_clone = url;
|
let url_clone = url;
|
||||||
@@ -47,7 +47,7 @@ impl HttpRpc {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Rpc(HttpRpc { client: Client::new(), userpass, url }))
|
Ok(Rpc(Self { client: Client::new(), userpass, url }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ mod http;
|
|||||||
pub use http::*;
|
pub use http::*;
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct EmptyResponse {}
|
pub struct EmptyResponse;
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct JsonRpcResponse<T> {
|
pub struct JsonRpcResponse<T> {
|
||||||
result: T,
|
result: T,
|
||||||
@@ -102,7 +102,7 @@ fn read_epee_vi<R: io::Read>(reader: &mut R) -> io::Result<u64> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait RpcConnection: Clone + Debug {
|
pub trait RpcConnection: Send + Sync + Clone + Debug {
|
||||||
/// Perform a POST request to the specified route with the specified body.
|
/// Perform a POST request to the specified route with the specified body.
|
||||||
///
|
///
|
||||||
/// The implementor is left to handle anything such as authentication.
|
/// The implementor is left to handle anything such as authentication.
|
||||||
@@ -117,7 +117,7 @@ impl<R: RpcConnection> Rpc<R> {
|
|||||||
///
|
///
|
||||||
/// This is NOT a JSON-RPC call. They use a route of "json_rpc" and are available via
|
/// This is NOT a JSON-RPC call. They use a route of "json_rpc" and are available via
|
||||||
/// `json_rpc_call`.
|
/// `json_rpc_call`.
|
||||||
pub async fn rpc_call<Params: Serialize + Debug, Response: DeserializeOwned + Debug>(
|
pub async fn rpc_call<Params: Send + Serialize + Debug, Response: DeserializeOwned + Debug>(
|
||||||
&self,
|
&self,
|
||||||
route: &str,
|
route: &str,
|
||||||
params: Option<Params>,
|
params: Option<Params>,
|
||||||
@@ -299,15 +299,15 @@ impl<R: RpcConnection> Rpc<R> {
|
|||||||
match self.get_block(self.get_block_hash(number).await?).await {
|
match self.get_block(self.get_block_hash(number).await?).await {
|
||||||
Ok(block) => {
|
Ok(block) => {
|
||||||
// Make sure this is actually the block for this number
|
// Make sure this is actually the block for this number
|
||||||
match block.miner_tx.prefix.inputs[0] {
|
match block.miner_tx.prefix.inputs.get(0) {
|
||||||
Input::Gen(actual) => {
|
Some(Input::Gen(actual)) => {
|
||||||
if usize::try_from(actual).unwrap() == number {
|
if usize::try_from(*actual).unwrap() == number {
|
||||||
Ok(block)
|
Ok(block)
|
||||||
} else {
|
} else {
|
||||||
Err(RpcError::InvalidNode)
|
Err(RpcError::InvalidNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Err(RpcError::InvalidNode),
|
Some(Input::ToKey { .. }) | None => Err(RpcError::InvalidNode),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e => e,
|
e => e,
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ pub(crate) fn read_point<R: Read>(r: &mut R) -> io::Result<EdwardsPoint> {
|
|||||||
pub(crate) fn read_torsion_free_point<R: Read>(r: &mut R) -> io::Result<EdwardsPoint> {
|
pub(crate) fn read_torsion_free_point<R: Read>(r: &mut R) -> io::Result<EdwardsPoint> {
|
||||||
read_point(r)
|
read_point(r)
|
||||||
.ok()
|
.ok()
|
||||||
.filter(|point| point.is_torsion_free())
|
.filter(EdwardsPoint::is_torsion_free)
|
||||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid point"))
|
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid point"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
#[cfg(feature = "multisig")]
|
#[cfg(feature = "multisig")]
|
||||||
use std::sync::{Arc, RwLock};
|
use std_shims::sync::Arc;
|
||||||
|
#[cfg(feature = "multisig")]
|
||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
use rand_core::{RngCore, OsRng};
|
use rand_core::{RngCore, OsRng};
|
||||||
@@ -45,13 +47,12 @@ fn clsag() {
|
|||||||
for i in 0 .. RING_LEN {
|
for i in 0 .. RING_LEN {
|
||||||
let dest = Zeroizing::new(random_scalar(&mut OsRng));
|
let dest = Zeroizing::new(random_scalar(&mut OsRng));
|
||||||
let mask = random_scalar(&mut OsRng);
|
let mask = random_scalar(&mut OsRng);
|
||||||
let amount;
|
let amount = if i == real {
|
||||||
if i == real {
|
|
||||||
secrets = (dest.clone(), mask);
|
secrets = (dest.clone(), mask);
|
||||||
amount = AMOUNT;
|
AMOUNT
|
||||||
} else {
|
} else {
|
||||||
amount = OsRng.next_u64();
|
OsRng.next_u64()
|
||||||
}
|
};
|
||||||
ring
|
ring
|
||||||
.push([dest.deref() * &ED25519_BASEPOINT_TABLE, Commitment::new(mask, amount).calculate()]);
|
.push([dest.deref() * &ED25519_BASEPOINT_TABLE, Commitment::new(mask, amount).calculate()]);
|
||||||
}
|
}
|
||||||
@@ -90,16 +91,15 @@ fn clsag_multisig() {
|
|||||||
for i in 0 .. RING_LEN {
|
for i in 0 .. RING_LEN {
|
||||||
let dest;
|
let dest;
|
||||||
let mask;
|
let mask;
|
||||||
let amount;
|
let amount = if i == u64::from(RING_INDEX) {
|
||||||
if i != u64::from(RING_INDEX) {
|
|
||||||
dest = &random_scalar(&mut OsRng) * &ED25519_BASEPOINT_TABLE;
|
|
||||||
mask = random_scalar(&mut OsRng);
|
|
||||||
amount = OsRng.next_u64();
|
|
||||||
} else {
|
|
||||||
dest = keys[&Participant::new(1).unwrap()].group_key().0;
|
dest = keys[&Participant::new(1).unwrap()].group_key().0;
|
||||||
mask = randomness;
|
mask = randomness;
|
||||||
amount = AMOUNT;
|
AMOUNT
|
||||||
}
|
} else {
|
||||||
|
dest = &random_scalar(&mut OsRng) * &ED25519_BASEPOINT_TABLE;
|
||||||
|
mask = random_scalar(&mut OsRng);
|
||||||
|
OsRng.next_u64()
|
||||||
|
};
|
||||||
ring.push([dest, Commitment::new(mask, amount).calculate()]);
|
ring.push([dest, Commitment::new(mask, amount).calculate()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,12 +33,12 @@ impl Input {
|
|||||||
|
|
||||||
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Input::Gen(height) => {
|
Self::Gen(height) => {
|
||||||
w.write_all(&[255])?;
|
w.write_all(&[255])?;
|
||||||
write_varint(height, w)
|
write_varint(height, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
Input::ToKey { amount, key_offsets, key_image } => {
|
Self::ToKey { amount, key_offsets, key_image } => {
|
||||||
w.write_all(&[2])?;
|
w.write_all(&[2])?;
|
||||||
write_varint(&amount.unwrap_or(0), w)?;
|
write_varint(&amount.unwrap_or(0), w)?;
|
||||||
write_vec(write_varint, key_offsets, w)?;
|
write_vec(write_varint, key_offsets, w)?;
|
||||||
@@ -53,13 +53,13 @@ impl Input {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(interpret_as_rct: bool, r: &mut R) -> io::Result<Input> {
|
pub fn read<R: Read>(interpret_as_rct: bool, r: &mut R) -> io::Result<Self> {
|
||||||
Ok(match read_byte(r)? {
|
Ok(match read_byte(r)? {
|
||||||
255 => Input::Gen(read_varint(r)?),
|
255 => Self::Gen(read_varint(r)?),
|
||||||
2 => {
|
2 => {
|
||||||
let amount = read_varint(r)?;
|
let amount = read_varint(r)?;
|
||||||
let amount = if (amount == 0) && interpret_as_rct { None } else { Some(amount) };
|
let amount = if (amount == 0) && interpret_as_rct { None } else { Some(amount) };
|
||||||
Input::ToKey {
|
Self::ToKey {
|
||||||
amount,
|
amount,
|
||||||
key_offsets: read_vec(read_varint, r)?,
|
key_offsets: read_vec(read_varint, r)?,
|
||||||
key_image: read_torsion_free_point(r)?,
|
key_image: read_torsion_free_point(r)?,
|
||||||
@@ -101,7 +101,7 @@ impl Output {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(interpret_as_rct: bool, r: &mut R) -> io::Result<Output> {
|
pub fn read<R: Read>(interpret_as_rct: bool, r: &mut R) -> io::Result<Self> {
|
||||||
let amount = read_varint(r)?;
|
let amount = read_varint(r)?;
|
||||||
let amount = if interpret_as_rct {
|
let amount = if interpret_as_rct {
|
||||||
if amount != 0 {
|
if amount != 0 {
|
||||||
@@ -121,7 +121,7 @@ impl Output {
|
|||||||
))?,
|
))?,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Output {
|
Ok(Self {
|
||||||
amount,
|
amount,
|
||||||
key: CompressedEdwardsY(read_bytes(r)?),
|
key: CompressedEdwardsY(read_bytes(r)?),
|
||||||
view_tag: if view_tag { Some(read_byte(r)?) } else { None },
|
view_tag: if view_tag { Some(read_byte(r)?) } else { None },
|
||||||
@@ -137,22 +137,22 @@ pub enum Timelock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Timelock {
|
impl Timelock {
|
||||||
fn from_raw(raw: u64) -> Timelock {
|
fn from_raw(raw: u64) -> Self {
|
||||||
if raw == 0 {
|
if raw == 0 {
|
||||||
Timelock::None
|
Self::None
|
||||||
} else if raw < 500_000_000 {
|
} else if raw < 500_000_000 {
|
||||||
Timelock::Block(usize::try_from(raw).unwrap())
|
Self::Block(usize::try_from(raw).unwrap())
|
||||||
} else {
|
} else {
|
||||||
Timelock::Time(raw)
|
Self::Time(raw)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||||
write_varint(
|
write_varint(
|
||||||
&match self {
|
&match self {
|
||||||
Timelock::None => 0,
|
Self::None => 0,
|
||||||
Timelock::Block(block) => (*block).try_into().unwrap(),
|
Self::Block(block) => (*block).try_into().unwrap(),
|
||||||
Timelock::Time(time) => *time,
|
Self::Time(time) => *time,
|
||||||
},
|
},
|
||||||
w,
|
w,
|
||||||
)
|
)
|
||||||
@@ -162,9 +162,9 @@ impl Timelock {
|
|||||||
impl PartialOrd for Timelock {
|
impl PartialOrd for Timelock {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Timelock::None, _) => Some(Ordering::Less),
|
(Self::None, _) => Some(Ordering::Less),
|
||||||
(Timelock::Block(a), Timelock::Block(b)) => a.partial_cmp(b),
|
(Self::Block(a), Self::Block(b)) => a.partial_cmp(b),
|
||||||
(Timelock::Time(a), Timelock::Time(b)) => a.partial_cmp(b),
|
(Self::Time(a), Self::Time(b)) => a.partial_cmp(b),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,7 +206,7 @@ impl TransactionPrefix {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<TransactionPrefix> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
let version = read_varint(r)?;
|
let version = read_varint(r)?;
|
||||||
// TODO: Create an enum out of version
|
// TODO: Create an enum out of version
|
||||||
if (version == 0) || (version > 2) {
|
if (version == 0) || (version > 2) {
|
||||||
@@ -221,7 +221,7 @@ impl TransactionPrefix {
|
|||||||
}
|
}
|
||||||
let is_miner_tx = matches!(inputs[0], Input::Gen { .. });
|
let is_miner_tx = matches!(inputs[0], Input::Gen { .. });
|
||||||
|
|
||||||
let mut prefix = TransactionPrefix {
|
let mut prefix = Self {
|
||||||
version,
|
version,
|
||||||
timelock,
|
timelock,
|
||||||
inputs,
|
inputs,
|
||||||
@@ -279,7 +279,7 @@ impl Transaction {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Transaction> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
let prefix = TransactionPrefix::read(r)?;
|
let prefix = TransactionPrefix::read(r)?;
|
||||||
let mut signatures = vec![];
|
let mut signatures = vec![];
|
||||||
let mut rct_signatures = RctSignatures {
|
let mut rct_signatures = RctSignatures {
|
||||||
@@ -328,7 +328,7 @@ impl Transaction {
|
|||||||
Err(io::Error::new(io::ErrorKind::Other, "Tried to deserialize unknown version"))?;
|
Err(io::Error::new(io::ErrorKind::Other, "Tried to deserialize unknown version"))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Transaction { prefix, signatures, rct_signatures })
|
Ok(Self { prefix, signatures, rct_signatures })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash(&self) -> [u8; 32] {
|
pub fn hash(&self) -> [u8; 32] {
|
||||||
@@ -345,14 +345,15 @@ impl Transaction {
|
|||||||
hashes.extend(hash(&buf));
|
hashes.extend(hash(&buf));
|
||||||
buf.clear();
|
buf.clear();
|
||||||
|
|
||||||
match self.rct_signatures.prunable {
|
hashes.extend(&match self.rct_signatures.prunable {
|
||||||
RctPrunable::Null => buf.resize(32, 0),
|
RctPrunable::Null => [0; 32],
|
||||||
_ => {
|
RctPrunable::MlsagBorromean { .. } |
|
||||||
|
RctPrunable::MlsagBulletproofs { .. } |
|
||||||
|
RctPrunable::Clsag { .. } => {
|
||||||
self.rct_signatures.prunable.write(&mut buf, self.rct_signatures.rct_type()).unwrap();
|
self.rct_signatures.prunable.write(&mut buf, self.rct_signatures.rct_type()).unwrap();
|
||||||
buf = hash(&buf).to_vec();
|
hash(&buf)
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
hashes.extend(&buf);
|
|
||||||
|
|
||||||
hash(&hashes)
|
hash(&hashes)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,18 +32,18 @@ pub struct SubaddressIndex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SubaddressIndex {
|
impl SubaddressIndex {
|
||||||
pub const fn new(account: u32, address: u32) -> Option<SubaddressIndex> {
|
pub const fn new(account: u32, address: u32) -> Option<Self> {
|
||||||
if (account == 0) && (address == 0) {
|
if (account == 0) && (address == 0) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(SubaddressIndex { account, address })
|
Some(Self { account, address })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn account(&self) -> u32 {
|
pub const fn account(&self) -> u32 {
|
||||||
self.account
|
self.account
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn address(&self) -> u32 {
|
pub const fn address(&self) -> u32 {
|
||||||
self.address
|
self.address
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,23 +58,22 @@ pub enum AddressSpec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AddressType {
|
impl AddressType {
|
||||||
pub fn is_subaddress(&self) -> bool {
|
pub const fn is_subaddress(&self) -> bool {
|
||||||
matches!(self, AddressType::Subaddress) ||
|
matches!(self, Self::Subaddress) || matches!(self, Self::Featured { subaddress: true, .. })
|
||||||
matches!(self, AddressType::Featured { subaddress: true, .. })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn payment_id(&self) -> Option<[u8; 8]> {
|
pub const fn payment_id(&self) -> Option<[u8; 8]> {
|
||||||
if let AddressType::Integrated(id) = self {
|
if let Self::Integrated(id) = self {
|
||||||
Some(*id)
|
Some(*id)
|
||||||
} else if let AddressType::Featured { payment_id, .. } = self {
|
} else if let Self::Featured { payment_id, .. } = self {
|
||||||
*payment_id
|
*payment_id
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_guaranteed(&self) -> bool {
|
pub const fn is_guaranteed(&self) -> bool {
|
||||||
matches!(self, AddressType::Featured { guaranteed: true, .. })
|
matches!(self, Self::Featured { guaranteed: true, .. })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,8 +141,8 @@ impl<B: AddressBytes> AddressMeta<B> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create an address's metadata.
|
/// Create an address's metadata.
|
||||||
pub fn new(network: Network, kind: AddressType) -> Self {
|
pub const fn new(network: Network, kind: AddressType) -> Self {
|
||||||
AddressMeta { _bytes: PhantomData, network, kind }
|
Self { _bytes: PhantomData, network, kind }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns an incomplete instantiation in the case of Integrated/Featured addresses
|
// Returns an incomplete instantiation in the case of Integrated/Featured addresses
|
||||||
@@ -160,7 +159,7 @@ impl<B: AddressBytes> AddressMeta<B> {
|
|||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
} {
|
} {
|
||||||
meta = Some(AddressMeta::new(network, kind));
|
meta = Some(Self::new(network, kind));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,15 +167,15 @@ impl<B: AddressBytes> AddressMeta<B> {
|
|||||||
meta.ok_or(AddressError::InvalidByte)
|
meta.ok_or(AddressError::InvalidByte)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_subaddress(&self) -> bool {
|
pub const fn is_subaddress(&self) -> bool {
|
||||||
self.kind.is_subaddress()
|
self.kind.is_subaddress()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn payment_id(&self) -> Option<[u8; 8]> {
|
pub const fn payment_id(&self) -> Option<[u8; 8]> {
|
||||||
self.kind.payment_id()
|
self.kind.payment_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_guaranteed(&self) -> bool {
|
pub const fn is_guaranteed(&self) -> bool {
|
||||||
self.kind.is_guaranteed()
|
self.kind.is_guaranteed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,8 +215,8 @@ impl<B: AddressBytes> ToString for Address<B> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<B: AddressBytes> Address<B> {
|
impl<B: AddressBytes> Address<B> {
|
||||||
pub fn new(meta: AddressMeta<B>, spend: EdwardsPoint, view: EdwardsPoint) -> Self {
|
pub const fn new(meta: AddressMeta<B>, spend: EdwardsPoint, view: EdwardsPoint) -> Self {
|
||||||
Address { meta, spend, view }
|
Self { meta, spend, view }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_str_raw(s: &str) -> Result<Self, AddressError> {
|
pub fn from_str_raw(s: &str) -> Result<Self, AddressError> {
|
||||||
@@ -267,7 +266,7 @@ impl<B: AddressBytes> Address<B> {
|
|||||||
id.copy_from_slice(&raw[(read - 8) .. read]);
|
id.copy_from_slice(&raw[(read - 8) .. read]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Address { meta, spend, view })
|
Ok(Self { meta, spend, view })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_str(network: Network, s: &str) -> Result<Self, AddressError> {
|
pub fn from_str(network: Network, s: &str) -> Result<Self, AddressError> {
|
||||||
@@ -280,30 +279,30 @@ impl<B: AddressBytes> Address<B> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn network(&self) -> Network {
|
pub const fn network(&self) -> Network {
|
||||||
self.meta.network
|
self.meta.network
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_subaddress(&self) -> bool {
|
pub const fn is_subaddress(&self) -> bool {
|
||||||
self.meta.is_subaddress()
|
self.meta.is_subaddress()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn payment_id(&self) -> Option<[u8; 8]> {
|
pub const fn payment_id(&self) -> Option<[u8; 8]> {
|
||||||
self.meta.payment_id()
|
self.meta.payment_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_guaranteed(&self) -> bool {
|
pub const fn is_guaranteed(&self) -> bool {
|
||||||
self.meta.is_guaranteed()
|
self.meta.is_guaranteed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Instantiation of the Address type with Monero's network bytes.
|
/// Instantiation of the Address type with Monero's network bytes.
|
||||||
pub type MoneroAddress = Address<MoneroAddressBytes>;
|
pub type MoneroAddress = Address<MoneroAddressBytes>;
|
||||||
// Allow re-interpreting of an arbitrary address as a monero address so it can be used with the
|
// Allow re-interpreting of an arbitrary address as a Monero address so it can be used with the
|
||||||
// rest of this library. Doesn't use From as it was conflicting with From<T> for T.
|
// rest of this library. Doesn't use From as it was conflicting with From<T> for T.
|
||||||
impl MoneroAddress {
|
impl MoneroAddress {
|
||||||
pub fn from<B: AddressBytes>(address: Address<B>) -> MoneroAddress {
|
pub const fn from<B: AddressBytes>(address: Address<B>) -> Self {
|
||||||
MoneroAddress::new(
|
Self::new(
|
||||||
AddressMeta::new(address.meta.network, address.meta.kind),
|
AddressMeta::new(address.meta.network, address.meta.kind),
|
||||||
address.spend,
|
address.spend,
|
||||||
address.view,
|
address.view,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ const MATURITY: u64 = 60;
|
|||||||
const RECENT_WINDOW: usize = 15;
|
const RECENT_WINDOW: usize = 15;
|
||||||
const BLOCK_TIME: usize = 120;
|
const BLOCK_TIME: usize = 120;
|
||||||
const BLOCKS_PER_YEAR: usize = 365 * 24 * 60 * 60 / BLOCK_TIME;
|
const BLOCKS_PER_YEAR: usize = 365 * 24 * 60 * 60 / BLOCK_TIME;
|
||||||
|
#[allow(clippy::as_conversions)]
|
||||||
const TIP_APPLICATION: f64 = (LOCK_WINDOW * BLOCK_TIME) as f64;
|
const TIP_APPLICATION: f64 = (LOCK_WINDOW * BLOCK_TIME) as f64;
|
||||||
|
|
||||||
// TODO: Expose an API to reset this in case a reorg occurs/the RPC fails/returns garbage
|
// TODO: Expose an API to reset this in case a reorg occurs/the RPC fails/returns garbage
|
||||||
@@ -31,11 +32,11 @@ const TIP_APPLICATION: f64 = (LOCK_WINDOW * BLOCK_TIME) as f64;
|
|||||||
static DISTRIBUTION_CELL: OnceLock<Mutex<Vec<u64>>> = OnceLock::new();
|
static DISTRIBUTION_CELL: OnceLock<Mutex<Vec<u64>>> = OnceLock::new();
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn DISTRIBUTION() -> &'static Mutex<Vec<u64>> {
|
fn DISTRIBUTION() -> &'static Mutex<Vec<u64>> {
|
||||||
DISTRIBUTION_CELL.get_or_init(|| Mutex::new(Vec::with_capacity(3000000)))
|
DISTRIBUTION_CELL.get_or_init(|| Mutex::new(Vec::with_capacity(3_000_000)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments, clippy::as_conversions)]
|
||||||
async fn select_n<'a, R: RngCore + CryptoRng, RPC: RpcConnection>(
|
async fn select_n<'a, R: Send + RngCore + CryptoRng, RPC: RpcConnection>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
rpc: &Rpc<RPC>,
|
rpc: &Rpc<RPC>,
|
||||||
distribution: &[u64],
|
distribution: &[u64],
|
||||||
@@ -146,13 +147,14 @@ impl Decoys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Select decoys using the same distribution as Monero.
|
/// Select decoys using the same distribution as Monero.
|
||||||
pub async fn select<R: RngCore + CryptoRng, RPC: RpcConnection>(
|
#[allow(clippy::as_conversions)]
|
||||||
|
pub async fn select<R: Send + RngCore + CryptoRng, RPC: RpcConnection>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
rpc: &Rpc<RPC>,
|
rpc: &Rpc<RPC>,
|
||||||
ring_len: usize,
|
ring_len: usize,
|
||||||
height: usize,
|
height: usize,
|
||||||
inputs: &[SpendableOutput],
|
inputs: &[SpendableOutput],
|
||||||
) -> Result<Vec<Decoys>, RpcError> {
|
) -> Result<Vec<Self>, RpcError> {
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
let mut distribution = DISTRIBUTION().lock();
|
let mut distribution = DISTRIBUTION().lock();
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@@ -230,6 +232,7 @@ impl Decoys {
|
|||||||
let target_median = high * 3 / 5;
|
let target_median = high * 3 / 5;
|
||||||
while ring[ring_len / 2].0 < target_median {
|
while ring[ring_len / 2].0 < target_median {
|
||||||
// If it's not, update the bottom half with new values to ensure the median only moves up
|
// If it's not, update the bottom half with new values to ensure the median only moves up
|
||||||
|
#[allow(clippy::needless_collect)] // Needed for ownership reasons
|
||||||
for removed in ring.drain(0 .. (ring_len / 2)).collect::<Vec<_>>() {
|
for removed in ring.drain(0 .. (ring_len / 2)).collect::<Vec<_>>() {
|
||||||
// If we removed the real spend, add it back
|
// If we removed the real spend, add it back
|
||||||
if removed.0 == o.0 {
|
if removed.0 == o.0 {
|
||||||
@@ -265,7 +268,7 @@ impl Decoys {
|
|||||||
// members
|
// members
|
||||||
}
|
}
|
||||||
|
|
||||||
res.push(Decoys {
|
res.push(Self {
|
||||||
// Binary searches for the real spend since we don't know where it sorted to
|
// Binary searches for the real spend since we don't know where it sorted to
|
||||||
i: u8::try_from(ring.partition_point(|x| x.0 < o.0)).unwrap(),
|
i: u8::try_from(ring.partition_point(|x| x.0 < o.0)).unwrap(),
|
||||||
offsets: offset(&ring.iter().map(|output| output.0).collect::<Vec<_>>()),
|
offsets: offset(&ring.iter().map(|output| output.0).collect::<Vec<_>>()),
|
||||||
|
|||||||
@@ -30,14 +30,14 @@ pub enum PaymentId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BitXor<[u8; 8]> for PaymentId {
|
impl BitXor<[u8; 8]> for PaymentId {
|
||||||
type Output = PaymentId;
|
type Output = Self;
|
||||||
|
|
||||||
fn bitxor(self, bytes: [u8; 8]) -> PaymentId {
|
fn bitxor(self, bytes: [u8; 8]) -> Self {
|
||||||
match self {
|
match self {
|
||||||
// Don't perform the xor since this isn't intended to be encrypted with xor
|
// Don't perform the xor since this isn't intended to be encrypted with xor
|
||||||
PaymentId::Unencrypted(_) => self,
|
Self::Unencrypted(_) => self,
|
||||||
PaymentId::Encrypted(id) => {
|
Self::Encrypted(id) => {
|
||||||
PaymentId::Encrypted((u64::from_le_bytes(id) ^ u64::from_le_bytes(bytes)).to_le_bytes())
|
Self::Encrypted((u64::from_le_bytes(id) ^ u64::from_le_bytes(bytes)).to_le_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,11 +46,11 @@ impl BitXor<[u8; 8]> for PaymentId {
|
|||||||
impl PaymentId {
|
impl PaymentId {
|
||||||
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
PaymentId::Unencrypted(id) => {
|
Self::Unencrypted(id) => {
|
||||||
w.write_all(&[PAYMENT_ID_MARKER])?;
|
w.write_all(&[PAYMENT_ID_MARKER])?;
|
||||||
w.write_all(id)?;
|
w.write_all(id)?;
|
||||||
}
|
}
|
||||||
PaymentId::Encrypted(id) => {
|
Self::Encrypted(id) => {
|
||||||
w.write_all(&[ENCRYPTED_PAYMENT_ID_MARKER])?;
|
w.write_all(&[ENCRYPTED_PAYMENT_ID_MARKER])?;
|
||||||
w.write_all(id)?;
|
w.write_all(id)?;
|
||||||
}
|
}
|
||||||
@@ -58,10 +58,10 @@ impl PaymentId {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<PaymentId> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
Ok(match read_byte(r)? {
|
Ok(match read_byte(r)? {
|
||||||
0 => PaymentId::Unencrypted(read_bytes(r)?),
|
0 => Self::Unencrypted(read_bytes(r)?),
|
||||||
1 => PaymentId::Encrypted(read_bytes(r)?),
|
1 => Self::Encrypted(read_bytes(r)?),
|
||||||
_ => Err(io::Error::new(io::ErrorKind::Other, "unknown payment ID type"))?,
|
_ => Err(io::Error::new(io::ErrorKind::Other, "unknown payment ID type"))?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -79,20 +79,20 @@ pub enum ExtraField {
|
|||||||
impl ExtraField {
|
impl ExtraField {
|
||||||
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
ExtraField::PublicKey(key) => {
|
Self::PublicKey(key) => {
|
||||||
w.write_all(&[1])?;
|
w.write_all(&[1])?;
|
||||||
w.write_all(&key.compress().to_bytes())?;
|
w.write_all(&key.compress().to_bytes())?;
|
||||||
}
|
}
|
||||||
ExtraField::Nonce(data) => {
|
Self::Nonce(data) => {
|
||||||
w.write_all(&[2])?;
|
w.write_all(&[2])?;
|
||||||
write_vec(write_byte, data, w)?;
|
write_vec(write_byte, data, w)?;
|
||||||
}
|
}
|
||||||
ExtraField::MergeMining(height, merkle) => {
|
Self::MergeMining(height, merkle) => {
|
||||||
w.write_all(&[3])?;
|
w.write_all(&[3])?;
|
||||||
write_varint(&u64::try_from(*height).unwrap(), w)?;
|
write_varint(&u64::try_from(*height).unwrap(), w)?;
|
||||||
w.write_all(merkle)?;
|
w.write_all(merkle)?;
|
||||||
}
|
}
|
||||||
ExtraField::PublicKeys(keys) => {
|
Self::PublicKeys(keys) => {
|
||||||
w.write_all(&[4])?;
|
w.write_all(&[4])?;
|
||||||
write_vec(write_point, keys, w)?;
|
write_vec(write_point, keys, w)?;
|
||||||
}
|
}
|
||||||
@@ -100,22 +100,22 @@ impl ExtraField {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<ExtraField> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
Ok(match read_byte(r)? {
|
Ok(match read_byte(r)? {
|
||||||
1 => ExtraField::PublicKey(read_point(r)?),
|
1 => Self::PublicKey(read_point(r)?),
|
||||||
2 => ExtraField::Nonce({
|
2 => Self::Nonce({
|
||||||
let nonce = read_vec(read_byte, r)?;
|
let nonce = read_vec(read_byte, r)?;
|
||||||
if nonce.len() > MAX_TX_EXTRA_NONCE_SIZE {
|
if nonce.len() > MAX_TX_EXTRA_NONCE_SIZE {
|
||||||
Err(io::Error::new(io::ErrorKind::Other, "too long nonce"))?;
|
Err(io::Error::new(io::ErrorKind::Other, "too long nonce"))?;
|
||||||
}
|
}
|
||||||
nonce
|
nonce
|
||||||
}),
|
}),
|
||||||
3 => ExtraField::MergeMining(
|
3 => Self::MergeMining(
|
||||||
usize::try_from(read_varint(r)?)
|
usize::try_from(read_varint(r)?)
|
||||||
.map_err(|_| io::Error::new(io::ErrorKind::Other, "varint for height exceeds usize"))?,
|
.map_err(|_| io::Error::new(io::ErrorKind::Other, "varint for height exceeds usize"))?,
|
||||||
read_bytes(r)?,
|
read_bytes(r)?,
|
||||||
),
|
),
|
||||||
4 => ExtraField::PublicKeys(read_vec(read_point, r)?),
|
4 => Self::PublicKeys(read_vec(read_point, r)?),
|
||||||
_ => Err(io::Error::new(io::ErrorKind::Other, "unknown extra field"))?,
|
_ => Err(io::Error::new(io::ErrorKind::Other, "unknown extra field"))?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -131,9 +131,9 @@ impl Extra {
|
|||||||
match field.clone() {
|
match field.clone() {
|
||||||
ExtraField::PublicKey(this_key) => key = key.or(Some(this_key)),
|
ExtraField::PublicKey(this_key) => key = key.or(Some(this_key)),
|
||||||
ExtraField::PublicKeys(these_additional) => {
|
ExtraField::PublicKeys(these_additional) => {
|
||||||
additional = additional.or(Some(these_additional))
|
additional = additional.or(Some(these_additional));
|
||||||
}
|
}
|
||||||
_ => (),
|
ExtraField::Nonce(_) | ExtraField::MergeMining(..) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Don't return any keys if this was non-standard and didn't include the primary key
|
// Don't return any keys if this was non-standard and didn't include the primary key
|
||||||
@@ -161,8 +161,8 @@ impl Extra {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn new(key: EdwardsPoint, additional: Vec<EdwardsPoint>) -> Extra {
|
pub(crate) fn new(key: EdwardsPoint, additional: Vec<EdwardsPoint>) -> Self {
|
||||||
let mut res = Extra(Vec::with_capacity(3));
|
let mut res = Self(Vec::with_capacity(3));
|
||||||
res.push(ExtraField::PublicKey(key));
|
res.push(ExtraField::PublicKey(key));
|
||||||
if !additional.is_empty() {
|
if !additional.is_empty() {
|
||||||
res.push(ExtraField::PublicKeys(additional));
|
res.push(ExtraField::PublicKeys(additional));
|
||||||
@@ -204,8 +204,8 @@ impl Extra {
|
|||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Extra> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
let mut res = Extra(vec![]);
|
let mut res = Self(vec![]);
|
||||||
let mut field;
|
let mut field;
|
||||||
while {
|
while {
|
||||||
field = ExtraField::read(r);
|
field = ExtraField::read(r);
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ pub(crate) fn shared_key(
|
|||||||
|
|
||||||
// uniqueness ||
|
// uniqueness ||
|
||||||
let shared_key = if let Some(uniqueness) = uniqueness {
|
let shared_key = if let Some(uniqueness) = uniqueness {
|
||||||
[uniqueness.as_ref(), &output_derivation].concat().to_vec()
|
[uniqueness.as_ref(), &output_derivation].concat()
|
||||||
} else {
|
} else {
|
||||||
output_derivation
|
output_derivation
|
||||||
};
|
};
|
||||||
@@ -141,11 +141,11 @@ pub struct ViewPair {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ViewPair {
|
impl ViewPair {
|
||||||
pub fn new(spend: EdwardsPoint, view: Zeroizing<Scalar>) -> ViewPair {
|
pub const fn new(spend: EdwardsPoint, view: Zeroizing<Scalar>) -> Self {
|
||||||
ViewPair { spend, view }
|
Self { spend, view }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spend(&self) -> EdwardsPoint {
|
pub const fn spend(&self) -> EdwardsPoint {
|
||||||
self.spend
|
self.spend
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,15 +241,19 @@ impl ZeroizeOnDrop for Scanner {}
|
|||||||
|
|
||||||
impl Scanner {
|
impl Scanner {
|
||||||
/// Create a Scanner from a ViewPair.
|
/// Create a Scanner from a ViewPair.
|
||||||
|
///
|
||||||
/// burning_bug is a HashSet of used keys, intended to prevent key reuse which would burn funds.
|
/// burning_bug is a HashSet of used keys, intended to prevent key reuse which would burn funds.
|
||||||
|
///
|
||||||
/// When an output is successfully scanned, the output key MUST be saved to disk.
|
/// When an output is successfully scanned, the output key MUST be saved to disk.
|
||||||
|
///
|
||||||
/// When a new scanner is created, ALL saved output keys must be passed in to be secure.
|
/// When a new scanner is created, ALL saved output keys must be passed in to be secure.
|
||||||
|
///
|
||||||
/// If None is passed, a modified shared key derivation is used which is immune to the burning
|
/// If None is passed, a modified shared key derivation is used which is immune to the burning
|
||||||
/// bug (specifically the Guaranteed feature from Featured Addresses).
|
/// bug (specifically the Guaranteed feature from Featured Addresses).
|
||||||
pub fn from_view(pair: ViewPair, burning_bug: Option<HashSet<CompressedEdwardsY>>) -> Scanner {
|
pub fn from_view(pair: ViewPair, burning_bug: Option<HashSet<CompressedEdwardsY>>) -> Self {
|
||||||
let mut subaddresses = HashMap::new();
|
let mut subaddresses = HashMap::new();
|
||||||
subaddresses.insert(pair.spend.compress(), None);
|
subaddresses.insert(pair.spend.compress(), None);
|
||||||
Scanner { pair, subaddresses, burning_bug }
|
Self { pair, subaddresses, burning_bug }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register a subaddress.
|
/// Register a subaddress.
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ impl AbsoluteId {
|
|||||||
serialized
|
serialized
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<AbsoluteId> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
Ok(AbsoluteId { tx: read_bytes(r)?, o: read_byte(r)? })
|
Ok(Self { tx: read_bytes(r)?, o: read_byte(r)? })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,8 +66,8 @@ impl OutputData {
|
|||||||
serialized
|
serialized
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<OutputData> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
Ok(OutputData {
|
Ok(Self {
|
||||||
key: read_point(r)?,
|
key: read_point(r)?,
|
||||||
key_offset: read_scalar(r)?,
|
key_offset: read_scalar(r)?,
|
||||||
commitment: Commitment::new(read_scalar(r)?, read_u64(r)?),
|
commitment: Commitment::new(read_scalar(r)?, read_u64(r)?),
|
||||||
@@ -114,7 +114,8 @@ impl Metadata {
|
|||||||
serialized
|
serialized
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Metadata> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
|
#[allow(clippy::if_then_some_else_none)] // The Result usage makes this invalid
|
||||||
let subaddress = if read_byte(r)? == 1 {
|
let subaddress = if read_byte(r)? == 1 {
|
||||||
Some(
|
Some(
|
||||||
SubaddressIndex::new(read_u32(r)?, read_u32(r)?)
|
SubaddressIndex::new(read_u32(r)?, read_u32(r)?)
|
||||||
@@ -124,7 +125,7 @@ impl Metadata {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Metadata {
|
Ok(Self {
|
||||||
subaddress,
|
subaddress,
|
||||||
payment_id: read_bytes(r)?,
|
payment_id: read_bytes(r)?,
|
||||||
arbitrary_data: {
|
arbitrary_data: {
|
||||||
@@ -176,8 +177,8 @@ impl ReceivedOutput {
|
|||||||
serialized
|
serialized
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<ReceivedOutput> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
Ok(ReceivedOutput {
|
Ok(Self {
|
||||||
absolute: AbsoluteId::read(r)?,
|
absolute: AbsoluteId::read(r)?,
|
||||||
data: OutputData::read(r)?,
|
data: OutputData::read(r)?,
|
||||||
metadata: Metadata::read(r)?,
|
metadata: Metadata::read(r)?,
|
||||||
@@ -209,8 +210,8 @@ impl SpendableOutput {
|
|||||||
pub async fn from<RPC: RpcConnection>(
|
pub async fn from<RPC: RpcConnection>(
|
||||||
rpc: &Rpc<RPC>,
|
rpc: &Rpc<RPC>,
|
||||||
output: ReceivedOutput,
|
output: ReceivedOutput,
|
||||||
) -> Result<SpendableOutput, RpcError> {
|
) -> Result<Self, RpcError> {
|
||||||
let mut output = SpendableOutput { output, global_index: 0 };
|
let mut output = Self { output, global_index: 0 };
|
||||||
output.refresh_global_index(rpc).await?;
|
output.refresh_global_index(rpc).await?;
|
||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
@@ -242,8 +243,8 @@ impl SpendableOutput {
|
|||||||
serialized
|
serialized
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<SpendableOutput> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
Ok(SpendableOutput { output: ReceivedOutput::read(r)?, global_index: read_u64(r)? })
|
Ok(Self { output: ReceivedOutput::read(r)?, global_index: read_u64(r)? })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,6 +264,7 @@ impl<O: Clone + Zeroize> Timelocked<O> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the outputs if they're not timelocked, or an empty vector if they are.
|
/// Return the outputs if they're not timelocked, or an empty vector if they are.
|
||||||
|
#[must_use]
|
||||||
pub fn not_locked(&self) -> Vec<O> {
|
pub fn not_locked(&self) -> Vec<O> {
|
||||||
if self.0 == Timelock::None {
|
if self.0 == Timelock::None {
|
||||||
return self.1.clone();
|
return self.1.clone();
|
||||||
@@ -271,6 +273,7 @@ impl<O: Clone + Zeroize> Timelocked<O> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns None if the Timelocks aren't comparable. Returns Some(vec![]) if none are unlocked.
|
/// Returns None if the Timelocks aren't comparable. Returns Some(vec![]) if none are unlocked.
|
||||||
|
#[must_use]
|
||||||
pub fn unlocked(&self, timelock: Timelock) -> Option<Vec<O>> {
|
pub fn unlocked(&self, timelock: Timelock) -> Option<Vec<O>> {
|
||||||
// If the Timelocks are comparable, return the outputs if they're now unlocked
|
// If the Timelocks are comparable, return the outputs if they're now unlocked
|
||||||
if self.0 <= timelock {
|
if self.0 <= timelock {
|
||||||
@@ -280,6 +283,7 @@ impl<O: Clone + Zeroize> Timelocked<O> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn ignore_timelock(&self) -> Vec<O> {
|
pub fn ignore_timelock(&self) -> Vec<O> {
|
||||||
self.1.clone()
|
self.1.clone()
|
||||||
}
|
}
|
||||||
@@ -293,16 +297,11 @@ impl Scanner {
|
|||||||
return Timelocked(tx.prefix.timelock, vec![]);
|
return Timelocked(tx.prefix.timelock, vec![]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let extra = Extra::read::<&[u8]>(&mut tx.prefix.extra.as_ref());
|
let Ok(extra) = Extra::read::<&[u8]>(&mut tx.prefix.extra.as_ref()) else {
|
||||||
let extra = if let Ok(extra) = extra {
|
|
||||||
extra
|
|
||||||
} else {
|
|
||||||
return Timelocked(tx.prefix.timelock, vec![]);
|
return Timelocked(tx.prefix.timelock, vec![]);
|
||||||
};
|
};
|
||||||
|
|
||||||
let (tx_key, additional) = if let Some((tx_key, additional)) = extra.keys() {
|
let Some((tx_key, additional)) = extra.keys() else {
|
||||||
(tx_key, additional)
|
|
||||||
} else {
|
|
||||||
return Timelocked(tx.prefix.timelock, vec![]);
|
return Timelocked(tx.prefix.timelock, vec![]);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -324,9 +323,8 @@ impl Scanner {
|
|||||||
let output_key = output_key.unwrap();
|
let output_key = output_key.unwrap();
|
||||||
|
|
||||||
for key in [Some(Some(&tx_key)), additional.as_ref().map(|additional| additional.get(o))] {
|
for key in [Some(Some(&tx_key)), additional.as_ref().map(|additional| additional.get(o))] {
|
||||||
let key = if let Some(Some(key)) = key {
|
let Some(Some(key)) = key else {
|
||||||
key
|
if key == Some(None) {
|
||||||
} else if let Some(None) = key {
|
|
||||||
// This is non-standard. There were additional keys, yet not one for this output
|
// This is non-standard. There were additional keys, yet not one for this output
|
||||||
// https://github.com/monero-project/monero/
|
// https://github.com/monero-project/monero/
|
||||||
// blob/04a1e2875d6e35e27bb21497988a6c822d319c28/
|
// blob/04a1e2875d6e35e27bb21497988a6c822d319c28/
|
||||||
@@ -335,6 +333,7 @@ impl Scanner {
|
|||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let (view_tag, shared_key, payment_id_xor) = shared_key(
|
let (view_tag, shared_key, payment_id_xor) = shared_key(
|
||||||
if self.burning_bug.is_none() { Some(uniqueness(&tx.prefix.inputs)) } else { None },
|
if self.burning_bug.is_none() { Some(uniqueness(&tx.prefix.inputs)) } else { None },
|
||||||
@@ -453,7 +452,7 @@ impl Scanner {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut res = vec![];
|
let mut res = vec![];
|
||||||
for tx in txs.drain(..) {
|
for tx in txs {
|
||||||
if let Some(timelock) = map(self.scan_transaction(&tx), index) {
|
if let Some(timelock) = map(self.scan_transaction(&tx), index) {
|
||||||
res.push(timelock);
|
res.push(timelock);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ struct WordList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl WordList {
|
impl WordList {
|
||||||
fn new(word_list: Vec<&'static str>, prefix_length: usize) -> WordList {
|
fn new(word_list: Vec<&'static str>, prefix_length: usize) -> Self {
|
||||||
let mut lang = WordList {
|
let mut lang = Self {
|
||||||
word_list,
|
word_list,
|
||||||
word_map: HashMap::new(),
|
word_map: HashMap::new(),
|
||||||
trimmed_word_map: HashMap::new(),
|
trimmed_word_map: HashMap::new(),
|
||||||
@@ -74,10 +74,10 @@ fn LANGUAGES() -> &'static HashMap<Language, WordList> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) fn trim_by_lang(word: &str, lang: Language) -> String {
|
pub(crate) fn trim_by_lang(word: &str, lang: Language) -> String {
|
||||||
if lang != Language::EnglishOld {
|
if lang == Language::EnglishOld {
|
||||||
word.chars().take(LANGUAGES()[&lang].unique_prefix_length).collect()
|
|
||||||
} else {
|
|
||||||
word.to_string()
|
word.to_string()
|
||||||
|
} else {
|
||||||
|
word.chars().take(LANGUAGES()[&lang].unique_prefix_length).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,11 +239,11 @@ pub(crate) fn seed_to_bytes(words: &str) -> Result<(Language, Zeroizing<[u8; 32]
|
|||||||
#[derive(Clone, PartialEq, Eq, Zeroize)]
|
#[derive(Clone, PartialEq, Eq, Zeroize)]
|
||||||
pub struct ClassicSeed(Zeroizing<String>);
|
pub struct ClassicSeed(Zeroizing<String>);
|
||||||
impl ClassicSeed {
|
impl ClassicSeed {
|
||||||
pub(crate) fn new<R: RngCore + CryptoRng>(rng: &mut R, lang: Language) -> ClassicSeed {
|
pub(crate) fn new<R: RngCore + CryptoRng>(rng: &mut R, lang: Language) -> Self {
|
||||||
key_to_seed(lang, Zeroizing::new(random_scalar(rng)))
|
key_to_seed(lang, Zeroizing::new(random_scalar(rng)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_string(words: Zeroizing<String>) -> Result<ClassicSeed, SeedError> {
|
pub fn from_string(words: Zeroizing<String>) -> Result<Self, SeedError> {
|
||||||
let (lang, entropy) = seed_to_bytes(&words)?;
|
let (lang, entropy) = seed_to_bytes(&words)?;
|
||||||
|
|
||||||
// Make sure this is a valid scalar
|
// Make sure this is a valid scalar
|
||||||
@@ -257,7 +257,7 @@ impl ClassicSeed {
|
|||||||
Ok(Self::from_entropy(lang, entropy).unwrap())
|
Ok(Self::from_entropy(lang, entropy).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_entropy(lang: Language, entropy: Zeroizing<[u8; 32]>) -> Option<ClassicSeed> {
|
pub fn from_entropy(lang: Language, entropy: Zeroizing<[u8; 32]>) -> Option<Self> {
|
||||||
Scalar::from_canonical_bytes(*entropy).map(|scalar| key_to_seed(lang, Zeroizing::new(scalar)))
|
Scalar::from_canonical_bytes(*entropy).map(|scalar| key_to_seed(lang, Zeroizing::new(scalar)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,43 +50,43 @@ pub enum Seed {
|
|||||||
impl fmt::Debug for Seed {
|
impl fmt::Debug for Seed {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Seed::Classic(_) => f.debug_struct("Seed::Classic").finish_non_exhaustive(),
|
Self::Classic(_) => f.debug_struct("Seed::Classic").finish_non_exhaustive(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Seed {
|
impl Seed {
|
||||||
/// Create a new seed.
|
/// Create a new seed.
|
||||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R, lang: Language) -> Seed {
|
pub fn new<R: RngCore + CryptoRng>(rng: &mut R, lang: Language) -> Self {
|
||||||
Seed::Classic(ClassicSeed::new(rng, lang))
|
Self::Classic(ClassicSeed::new(rng, lang))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a seed from a String.
|
/// Parse a seed from a String.
|
||||||
pub fn from_string(words: Zeroizing<String>) -> Result<Seed, SeedError> {
|
pub fn from_string(words: Zeroizing<String>) -> Result<Self, SeedError> {
|
||||||
match words.split_whitespace().count() {
|
match words.split_whitespace().count() {
|
||||||
CLASSIC_SEED_LENGTH | CLASSIC_SEED_LENGTH_WITH_CHECKSUM => {
|
CLASSIC_SEED_LENGTH | CLASSIC_SEED_LENGTH_WITH_CHECKSUM => {
|
||||||
ClassicSeed::from_string(words).map(Seed::Classic)
|
ClassicSeed::from_string(words).map(Self::Classic)
|
||||||
}
|
}
|
||||||
_ => Err(SeedError::InvalidSeedLength)?,
|
_ => Err(SeedError::InvalidSeedLength)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a Seed from entropy.
|
/// Create a Seed from entropy.
|
||||||
pub fn from_entropy(lang: Language, entropy: Zeroizing<[u8; 32]>) -> Option<Seed> {
|
pub fn from_entropy(lang: Language, entropy: Zeroizing<[u8; 32]>) -> Option<Self> {
|
||||||
ClassicSeed::from_entropy(lang, entropy).map(Seed::Classic)
|
ClassicSeed::from_entropy(lang, entropy).map(Self::Classic)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a seed to a String.
|
/// Convert a seed to a String.
|
||||||
pub fn to_string(&self) -> Zeroizing<String> {
|
pub fn to_string(&self) -> Zeroizing<String> {
|
||||||
match self {
|
match self {
|
||||||
Seed::Classic(seed) => seed.to_string(),
|
Self::Classic(seed) => seed.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the entropy for this seed.
|
/// Return the entropy for this seed.
|
||||||
pub fn entropy(&self) -> Zeroizing<[u8; 32]> {
|
pub fn entropy(&self) -> Zeroizing<[u8; 32]> {
|
||||||
match self {
|
match self {
|
||||||
Seed::Classic(seed) => seed.entropy(),
|
Self::Classic(seed) => seed.entropy(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,10 +81,11 @@ impl Eq for SignableTransactionBuilder {}
|
|||||||
|
|
||||||
impl Zeroize for SignableTransactionBuilder {
|
impl Zeroize for SignableTransactionBuilder {
|
||||||
fn zeroize(&mut self) {
|
fn zeroize(&mut self) {
|
||||||
self.0.write().unwrap().zeroize()
|
self.0.write().unwrap().zeroize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::return_self_not_must_use)]
|
||||||
impl SignableTransactionBuilder {
|
impl SignableTransactionBuilder {
|
||||||
fn shallow_copy(&self) -> Self {
|
fn shallow_copy(&self) -> Self {
|
||||||
Self(self.0.clone())
|
Self(self.0.clone())
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ impl SendOutput {
|
|||||||
output: (usize, (MoneroAddress, u64)),
|
output: (usize, (MoneroAddress, u64)),
|
||||||
ecdh: EdwardsPoint,
|
ecdh: EdwardsPoint,
|
||||||
R: EdwardsPoint,
|
R: EdwardsPoint,
|
||||||
) -> (SendOutput, Option<[u8; 8]>) {
|
) -> (Self, Option<[u8; 8]>) {
|
||||||
let o = output.0;
|
let o = output.0;
|
||||||
let output = output.1;
|
let output = output.1;
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ impl SendOutput {
|
|||||||
shared_key(Some(unique).filter(|_| output.0.is_guaranteed()), ecdh, o);
|
shared_key(Some(unique).filter(|_| output.0.is_guaranteed()), ecdh, o);
|
||||||
|
|
||||||
(
|
(
|
||||||
SendOutput {
|
Self {
|
||||||
R,
|
R,
|
||||||
view_tag,
|
view_tag,
|
||||||
dest: ((&shared_key * &ED25519_BASEPOINT_TABLE) + output.0.spend),
|
dest: ((&shared_key * &ED25519_BASEPOINT_TABLE) + output.0.spend),
|
||||||
@@ -98,16 +98,16 @@ impl SendOutput {
|
|||||||
r: &Zeroizing<Scalar>,
|
r: &Zeroizing<Scalar>,
|
||||||
unique: [u8; 32],
|
unique: [u8; 32],
|
||||||
output: (usize, (MoneroAddress, u64)),
|
output: (usize, (MoneroAddress, u64)),
|
||||||
) -> (SendOutput, Option<[u8; 8]>) {
|
) -> (Self, Option<[u8; 8]>) {
|
||||||
let address = output.1 .0;
|
let address = output.1 .0;
|
||||||
SendOutput::internal(
|
Self::internal(
|
||||||
unique,
|
unique,
|
||||||
output,
|
output,
|
||||||
r.deref() * address.view,
|
r.deref() * address.view,
|
||||||
if !address.is_subaddress() {
|
if address.is_subaddress() {
|
||||||
r.deref() * &ED25519_BASEPOINT_TABLE
|
|
||||||
} else {
|
|
||||||
r.deref() * address.spend
|
r.deref() * address.spend
|
||||||
|
} else {
|
||||||
|
r.deref() * &ED25519_BASEPOINT_TABLE
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -116,8 +116,8 @@ impl SendOutput {
|
|||||||
ecdh: EdwardsPoint,
|
ecdh: EdwardsPoint,
|
||||||
unique: [u8; 32],
|
unique: [u8; 32],
|
||||||
output: (usize, (MoneroAddress, u64)),
|
output: (usize, (MoneroAddress, u64)),
|
||||||
) -> (SendOutput, Option<[u8; 8]>) {
|
) -> (Self, Option<[u8; 8]>) {
|
||||||
SendOutput::internal(unique, output, ecdh, ED25519_BASEPOINT_POINT)
|
Self::internal(unique, output, ecdh, ED25519_BASEPOINT_POINT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ pub enum TransactionError {
|
|||||||
FrostError(FrostError),
|
FrostError(FrostError),
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn prepare_inputs<R: RngCore + CryptoRng, RPC: RpcConnection>(
|
async fn prepare_inputs<R: Send + RngCore + CryptoRng, RPC: RpcConnection>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
rpc: &Rpc<RPC>,
|
rpc: &Rpc<RPC>,
|
||||||
ring_len: usize,
|
ring_len: usize,
|
||||||
@@ -261,8 +261,8 @@ impl fmt::Debug for Change {
|
|||||||
|
|
||||||
impl Change {
|
impl Change {
|
||||||
/// Create a change output specification from a ViewPair, as needed to maintain privacy.
|
/// Create a change output specification from a ViewPair, as needed to maintain privacy.
|
||||||
pub fn new(view: &ViewPair, guaranteed: bool) -> Change {
|
pub fn new(view: &ViewPair, guaranteed: bool) -> Self {
|
||||||
Change {
|
Self {
|
||||||
address: view.address(
|
address: view.address(
|
||||||
Network::Mainnet,
|
Network::Mainnet,
|
||||||
if !guaranteed {
|
if !guaranteed {
|
||||||
@@ -275,10 +275,11 @@ impl Change {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a fingerprintable change output specification which will harm privacy. Only use this
|
/// Create a fingerprintable change output specification which will harm privacy.
|
||||||
/// if you know what you're doing.
|
///
|
||||||
pub fn fingerprintable(address: MoneroAddress) -> Change {
|
/// Only use this if you know what you're doing.
|
||||||
Change { address, view: None }
|
pub const fn fingerprintable(address: MoneroAddress) -> Self {
|
||||||
|
Self { address, view: None }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,17 +297,17 @@ impl SignableTransaction {
|
|||||||
protocol: Protocol,
|
protocol: Protocol,
|
||||||
r_seed: Option<Zeroizing<[u8; 32]>>,
|
r_seed: Option<Zeroizing<[u8; 32]>>,
|
||||||
inputs: Vec<SpendableOutput>,
|
inputs: Vec<SpendableOutput>,
|
||||||
mut payments: Vec<(MoneroAddress, u64)>,
|
payments: Vec<(MoneroAddress, u64)>,
|
||||||
change_address: Option<Change>,
|
change_address: Option<Change>,
|
||||||
data: Vec<Vec<u8>>,
|
data: Vec<Vec<u8>>,
|
||||||
fee_rate: Fee,
|
fee_rate: Fee,
|
||||||
) -> Result<SignableTransaction, TransactionError> {
|
) -> Result<Self, TransactionError> {
|
||||||
// Make sure there's only one payment ID
|
// Make sure there's only one payment ID
|
||||||
let mut has_payment_id = {
|
let mut has_payment_id = {
|
||||||
let mut payment_ids = 0;
|
let mut payment_ids = 0;
|
||||||
let mut count = |addr: MoneroAddress| {
|
let mut count = |addr: MoneroAddress| {
|
||||||
if addr.payment_id().is_some() {
|
if addr.payment_id().is_some() {
|
||||||
payment_ids += 1
|
payment_ids += 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
for payment in &payments {
|
for payment in &payments {
|
||||||
@@ -382,12 +383,12 @@ impl SignableTransaction {
|
|||||||
Err(TransactionError::TooManyOutputs)?;
|
Err(TransactionError::TooManyOutputs)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut payments = payments.drain(..).map(InternalPayment::Payment).collect::<Vec<_>>();
|
let mut payments = payments.into_iter().map(InternalPayment::Payment).collect::<Vec<_>>();
|
||||||
if let Some(change) = change_address {
|
if let Some(change) = change_address {
|
||||||
payments.push(InternalPayment::Change(change, in_amount - out_amount));
|
payments.push(InternalPayment::Change(change, in_amount - out_amount));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(SignableTransaction { protocol, r_seed, inputs, payments, data, fee })
|
Ok(Self { protocol, r_seed, inputs, payments, data, fee })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fee(&self) -> u64 {
|
pub fn fee(&self) -> u64 {
|
||||||
@@ -444,11 +445,8 @@ impl SignableTransaction {
|
|||||||
0;
|
0;
|
||||||
|
|
||||||
// We need additional keys if we have any subaddresses
|
// We need additional keys if we have any subaddresses
|
||||||
let mut additional = subaddresses;
|
// UNLESS there's only two payments and we have the view-key for the change output
|
||||||
// Unless the above change view key path is taken
|
let additional = if (payments.len() == 2) && has_change_view { false } else { subaddresses };
|
||||||
if (payments.len() == 2) && has_change_view {
|
|
||||||
additional = false;
|
|
||||||
}
|
|
||||||
let modified_change_ecdh = subaddresses && (!additional);
|
let modified_change_ecdh = subaddresses && (!additional);
|
||||||
|
|
||||||
// If we're using the aR rewrite, update tx_public_key from rG to rB
|
// If we're using the aR rewrite, update tx_public_key from rG to rB
|
||||||
@@ -562,11 +560,12 @@ impl SignableTransaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the eventuality of this transaction.
|
/// Returns the eventuality of this transaction.
|
||||||
|
///
|
||||||
/// The eventuality is defined as the TX extra/outputs this transaction will create, if signed
|
/// The eventuality is defined as the TX extra/outputs this transaction will create, if signed
|
||||||
/// with the specified seed. This eventuality can be compared to on-chain transactions to see
|
/// with the specified seed. This eventuality can be compared to on-chain transactions to see
|
||||||
/// if the transaction has already been signed and published.
|
/// if the transaction has already been signed and published.
|
||||||
pub fn eventuality(&self) -> Option<Eventuality> {
|
pub fn eventuality(&self) -> Option<Eventuality> {
|
||||||
let inputs = self.inputs.iter().map(|input| input.key()).collect::<Vec<_>>();
|
let inputs = self.inputs.iter().map(SpendableOutput::key).collect::<Vec<_>>();
|
||||||
let (tx_key, additional, outputs, id) = Self::prepare_payments(
|
let (tx_key, additional, outputs, id) = Self::prepare_payments(
|
||||||
self.r_seed.as_ref()?,
|
self.r_seed.as_ref()?,
|
||||||
&inputs,
|
&inputs,
|
||||||
@@ -606,7 +605,7 @@ impl SignableTransaction {
|
|||||||
|
|
||||||
let (tx_key, additional, outputs, id) = Self::prepare_payments(
|
let (tx_key, additional, outputs, id) = Self::prepare_payments(
|
||||||
&r_seed,
|
&r_seed,
|
||||||
&self.inputs.iter().map(|input| input.key()).collect::<Vec<_>>(),
|
&self.inputs.iter().map(SpendableOutput::key).collect::<Vec<_>>(),
|
||||||
&mut self.payments,
|
&mut self.payments,
|
||||||
uniqueness,
|
uniqueness,
|
||||||
);
|
);
|
||||||
@@ -656,7 +655,7 @@ impl SignableTransaction {
|
|||||||
fee,
|
fee,
|
||||||
encrypted_amounts,
|
encrypted_amounts,
|
||||||
pseudo_outs: vec![],
|
pseudo_outs: vec![],
|
||||||
commitments: commitments.iter().map(|commitment| commitment.calculate()).collect(),
|
commitments: commitments.iter().map(Commitment::calculate).collect(),
|
||||||
},
|
},
|
||||||
prunable: RctPrunable::Clsag { bulletproofs: bp, clsags: vec![], pseudo_outs: vec![] },
|
prunable: RctPrunable::Clsag { bulletproofs: bp, clsags: vec![], pseudo_outs: vec![] },
|
||||||
},
|
},
|
||||||
@@ -666,7 +665,7 @@ impl SignableTransaction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sign this transaction.
|
/// Sign this transaction.
|
||||||
pub async fn sign<R: RngCore + CryptoRng, RPC: RpcConnection>(
|
pub async fn sign<R: Send + RngCore + CryptoRng, RPC: RpcConnection>(
|
||||||
mut self,
|
mut self,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
rpc: &Rpc<RPC>,
|
rpc: &Rpc<RPC>,
|
||||||
@@ -704,7 +703,9 @@ impl SignableTransaction {
|
|||||||
clsags.append(&mut clsag_pairs.iter().map(|clsag| clsag.0.clone()).collect::<Vec<_>>());
|
clsags.append(&mut clsag_pairs.iter().map(|clsag| clsag.0.clone()).collect::<Vec<_>>());
|
||||||
pseudo_outs.append(&mut clsag_pairs.iter().map(|clsag| clsag.1).collect::<Vec<_>>());
|
pseudo_outs.append(&mut clsag_pairs.iter().map(|clsag| clsag.1).collect::<Vec<_>>());
|
||||||
}
|
}
|
||||||
_ => unreachable!("attempted to sign a TX which wasn't CLSAG"),
|
RctPrunable::MlsagBorromean { .. } | RctPrunable::MlsagBulletproofs { .. } => {
|
||||||
|
unreachable!("attempted to sign a TX which wasn't CLSAG")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(tx)
|
Ok(tx)
|
||||||
}
|
}
|
||||||
@@ -713,13 +714,18 @@ impl SignableTransaction {
|
|||||||
impl Eventuality {
|
impl Eventuality {
|
||||||
/// Enables building a HashMap of Extra -> Eventuality for efficiently checking if an on-chain
|
/// Enables building a HashMap of Extra -> Eventuality for efficiently checking if an on-chain
|
||||||
/// transaction may match this eventuality.
|
/// transaction may match this eventuality.
|
||||||
|
///
|
||||||
/// This extra is cryptographically bound to:
|
/// This extra is cryptographically bound to:
|
||||||
/// 1) A specific set of inputs (via their output key)
|
/// 1) A specific set of inputs (via their output key)
|
||||||
/// 2) A specific seed for the ephemeral keys
|
/// 2) A specific seed for the ephemeral keys
|
||||||
|
///
|
||||||
|
/// This extra may be used with a transaction with a distinct set of inputs, yet no honest
|
||||||
|
/// transaction which doesn't satisfy this Eventuality will contain it.
|
||||||
pub fn extra(&self) -> &[u8] {
|
pub fn extra(&self) -> &[u8] {
|
||||||
&self.extra
|
&self.extra
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub fn matches(&self, tx: &Transaction) -> bool {
|
pub fn matches(&self, tx: &Transaction) -> bool {
|
||||||
if self.payments.len() != tx.prefix.outputs.len() {
|
if self.payments.len() != tx.prefix.outputs.len() {
|
||||||
return false;
|
return false;
|
||||||
@@ -752,9 +758,10 @@ impl Eventuality {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Remove this when the following for loop is updated
|
// TODO: Remove this when the following for loop is updated
|
||||||
if !rct_type.compact_encrypted_amounts() {
|
assert!(
|
||||||
panic!("created an Eventuality for a very old RctType we don't support proving for");
|
rct_type.compact_encrypted_amounts(),
|
||||||
}
|
"created an Eventuality for a very old RctType we don't support proving for"
|
||||||
|
);
|
||||||
|
|
||||||
for (o, (expected, actual)) in outputs.iter().zip(tx.prefix.outputs.iter()).enumerate() {
|
for (o, (expected, actual)) in outputs.iter().zip(tx.prefix.outputs.iter()).enumerate() {
|
||||||
// Verify the output, commitment, and encrypted amount.
|
// Verify the output, commitment, and encrypted amount.
|
||||||
@@ -810,12 +817,12 @@ impl Eventuality {
|
|||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: io::Read>(r: &mut R) -> io::Result<Eventuality> {
|
pub fn read<R: io::Read>(r: &mut R) -> io::Result<Self> {
|
||||||
fn read_address<R: io::Read>(r: &mut R) -> io::Result<MoneroAddress> {
|
fn read_address<R: io::Read>(r: &mut R) -> io::Result<MoneroAddress> {
|
||||||
String::from_utf8(read_vec(read_byte, r)?)
|
String::from_utf8(read_vec(read_byte, r)?)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|str| MoneroAddress::from_str_raw(&str).ok())
|
.and_then(|str| MoneroAddress::from_str_raw(&str).ok())
|
||||||
.ok_or(io::Error::new(io::ErrorKind::Other, "invalid address"))
|
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid address"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_payment<R: io::Read>(r: &mut R) -> io::Result<InternalPayment> {
|
fn read_payment<R: io::Read>(r: &mut R) -> io::Result<InternalPayment> {
|
||||||
@@ -836,7 +843,7 @@ impl Eventuality {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Eventuality {
|
Ok(Self {
|
||||||
protocol: Protocol::read(r)?,
|
protocol: Protocol::read(r)?,
|
||||||
r_seed: Zeroizing::new(read_bytes::<_, 32>(r)?),
|
r_seed: Zeroizing::new(read_bytes::<_, 32>(r)?),
|
||||||
inputs: read_vec(read_point, r)?,
|
inputs: read_vec(read_point, r)?,
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
use std_shims::{
|
use std_shims::{
|
||||||
|
sync::Arc,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
io::{self, Read},
|
io::{self, Read},
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
};
|
};
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::RwLock;
|
||||||
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
@@ -267,14 +268,15 @@ impl SignMachine<Transaction> for TransactionSignMachine {
|
|||||||
mut commitments: HashMap<Participant, Self::Preprocess>,
|
mut commitments: HashMap<Participant, Self::Preprocess>,
|
||||||
msg: &[u8],
|
msg: &[u8],
|
||||||
) -> Result<(TransactionSignatureMachine, Self::SignatureShare), FrostError> {
|
) -> Result<(TransactionSignatureMachine, Self::SignatureShare), FrostError> {
|
||||||
if !msg.is_empty() {
|
assert!(
|
||||||
panic!("message was passed to the TransactionMachine when it generates its own");
|
msg.is_empty(),
|
||||||
}
|
"message was passed to the TransactionMachine when it generates its own"
|
||||||
|
);
|
||||||
|
|
||||||
// Find out who's included
|
// Find out who's included
|
||||||
// This may not be a valid set of signers yet the algorithm machine will error if it's not
|
// This may not be a valid set of signers yet the algorithm machine will error if it's not
|
||||||
commitments.remove(&self.i); // Remove, if it was included for some reason
|
commitments.remove(&self.i); // Remove, if it was included for some reason
|
||||||
let mut included = commitments.keys().cloned().collect::<Vec<_>>();
|
let mut included = commitments.keys().copied().collect::<Vec<_>>();
|
||||||
included.push(self.i);
|
included.push(self.i);
|
||||||
included.sort_unstable();
|
included.sort_unstable();
|
||||||
|
|
||||||
@@ -325,7 +327,7 @@ impl SignMachine<Transaction> for TransactionSignMachine {
|
|||||||
|
|
||||||
// Remove our preprocess which shouldn't be here. It was just the easiest way to implement the
|
// Remove our preprocess which shouldn't be here. It was just the easiest way to implement the
|
||||||
// above
|
// above
|
||||||
for map in commitments.iter_mut() {
|
for map in &mut commitments {
|
||||||
map.remove(&self.i);
|
map.remove(&self.i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,12 +367,13 @@ impl SignMachine<Transaction> for TransactionSignMachine {
|
|||||||
while !sorted.is_empty() {
|
while !sorted.is_empty() {
|
||||||
let value = sorted.remove(0);
|
let value = sorted.remove(0);
|
||||||
|
|
||||||
let mut mask = random_scalar(&mut rng);
|
let mask = if sorted.is_empty() {
|
||||||
if sorted.is_empty() {
|
output_masks - sum_pseudo_outs
|
||||||
mask = output_masks - sum_pseudo_outs;
|
|
||||||
} else {
|
} else {
|
||||||
|
let mask = random_scalar(&mut rng);
|
||||||
sum_pseudo_outs += mask;
|
sum_pseudo_outs += mask;
|
||||||
}
|
mask
|
||||||
|
};
|
||||||
|
|
||||||
tx.prefix.inputs.push(Input::ToKey {
|
tx.prefix.inputs.push(Input::ToKey {
|
||||||
amount: None,
|
amount: None,
|
||||||
@@ -430,7 +433,9 @@ impl SignatureMachine<Transaction> for TransactionSignatureMachine {
|
|||||||
pseudo_outs.push(pseudo_out);
|
pseudo_outs.push(pseudo_out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => unreachable!("attempted to sign a multisig TX which wasn't CLSAG"),
|
RctPrunable::MlsagBorromean { .. } | RctPrunable::MlsagBulletproofs { .. } => {
|
||||||
|
unreachable!("attempted to sign a multisig TX which wasn't CLSAG")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(tx)
|
Ok(tx)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ test!(
|
|||||||
let arbitrary_data = vec![b'\0'; MAX_ARBITRARY_DATA_SIZE - 1];
|
let arbitrary_data = vec![b'\0'; MAX_ARBITRARY_DATA_SIZE - 1];
|
||||||
|
|
||||||
// make sure we can add to tx
|
// make sure we can add to tx
|
||||||
let result = builder.add_data(arbitrary_data.clone());
|
builder.add_data(arbitrary_data.clone()).unwrap();
|
||||||
assert!(result.is_ok());
|
|
||||||
|
|
||||||
builder.add_payment(addr, 5);
|
builder.add_payment(addr, 5);
|
||||||
(builder.build().unwrap(), (arbitrary_data,))
|
(builder.build().unwrap(), (arbitrary_data,))
|
||||||
@@ -37,8 +36,7 @@ test!(
|
|||||||
|
|
||||||
// Add data multiple times
|
// Add data multiple times
|
||||||
for data in &data {
|
for data in &data {
|
||||||
let result = builder.add_data(data.clone());
|
builder.add_data(data.clone()).unwrap();
|
||||||
assert!(result.is_ok());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.add_payment(addr, 5);
|
builder.add_payment(addr, 5);
|
||||||
@@ -65,7 +63,7 @@ test!(
|
|||||||
// Reduce data size and retry. The data will now be 255 bytes long (including the added
|
// Reduce data size and retry. The data will now be 255 bytes long (including the added
|
||||||
// marker), exactly
|
// marker), exactly
|
||||||
data.pop();
|
data.pop();
|
||||||
assert!(builder.add_data(data.clone()).is_ok());
|
builder.add_data(data.clone()).unwrap();
|
||||||
|
|
||||||
builder.add_payment(addr, 5);
|
builder.add_payment(addr, 5);
|
||||||
(builder.build().unwrap(), data)
|
(builder.build().unwrap(), data)
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ pub static SEQUENTIAL: OnceLock<Mutex<()>> = OnceLock::new();
|
|||||||
macro_rules! async_sequential {
|
macro_rules! async_sequential {
|
||||||
($(async fn $name: ident() $body: block)*) => {
|
($(async fn $name: ident() $body: block)*) => {
|
||||||
$(
|
$(
|
||||||
|
#[allow(clippy::tests_outside_test_module)]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn $name() {
|
async fn $name() {
|
||||||
let guard = runner::SEQUENTIAL.get_or_init(|| tokio::sync::Mutex::new(())).lock().await;
|
let guard = runner::SEQUENTIAL.get_or_init(|| tokio::sync::Mutex::new(())).lock().await;
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ test!(
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|rpc, mut builder: Builder, addr, mut outputs: Vec<ReceivedOutput>| async move {
|
|rpc, mut builder: Builder, addr, outputs: Vec<ReceivedOutput>| async move {
|
||||||
for output in outputs.drain(..) {
|
for output in outputs {
|
||||||
builder.add_input(SpendableOutput::from(&rpc, output).await.unwrap());
|
builder.add_input(SpendableOutput::from(&rpc, output).await.unwrap());
|
||||||
}
|
}
|
||||||
builder.add_payment(addr, 6);
|
builder.add_payment(addr, 6);
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ async fn from_wallet_rpc_to_self(spec: AddressSpec) {
|
|||||||
|
|
||||||
// make an addr
|
// make an addr
|
||||||
let (_, view_pair, _) = runner::random_address();
|
let (_, view_pair, _) = runner::random_address();
|
||||||
let addr = Address::from_str(&view_pair.address(Network::Mainnet, spec).to_string()[..]).unwrap();
|
let addr = Address::from_str(&view_pair.address(Network::Mainnet, spec).to_string()).unwrap();
|
||||||
|
|
||||||
// refresh & make a tx
|
// refresh & make a tx
|
||||||
wallet_rpc.refresh(None).await.unwrap();
|
wallet_rpc.refresh(None).await.unwrap();
|
||||||
@@ -103,7 +103,9 @@ async fn from_wallet_rpc_to_self(spec: AddressSpec) {
|
|||||||
assert_eq!(output.metadata.payment_id, payment_id);
|
assert_eq!(output.metadata.payment_id, payment_id);
|
||||||
assert_eq!(output.metadata.subaddress, None);
|
assert_eq!(output.metadata.subaddress, None);
|
||||||
}
|
}
|
||||||
_ => assert_eq!(output.metadata.subaddress, None),
|
AddressSpec::Standard | AddressSpec::Featured { .. } => {
|
||||||
|
assert_eq!(output.metadata.subaddress, None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
assert_eq!(output.commitment().amount, 1000000000000);
|
assert_eq!(output.commitment().amount, 1000000000000);
|
||||||
}
|
}
|
||||||
@@ -228,7 +230,7 @@ test!(
|
|||||||
for _ in 0 .. 2 {
|
for _ in 0 .. 2 {
|
||||||
// Subtract 1 since we prefix data with 127
|
// Subtract 1 since we prefix data with 127
|
||||||
let data = vec![b'a'; MAX_TX_EXTRA_NONCE_SIZE - 1];
|
let data = vec![b'a'; MAX_TX_EXTRA_NONCE_SIZE - 1];
|
||||||
assert!(builder.add_data(data).is_ok());
|
builder.add_data(data).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
(builder.build().unwrap(), (wallet_rpc,))
|
(builder.build().unwrap(), (wallet_rpc,))
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
extern crate alloc;
|
||||||
|
use alloc::sync::Arc;
|
||||||
use std::{
|
use std::{
|
||||||
sync::{Arc, RwLock},
|
sync::RwLock,
|
||||||
collections::{HashSet, HashMap},
|
collections::{HashSet, HashMap},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -23,7 +25,7 @@ pub trait Db: 'static + Send + Sync + Clone + Debug + Get {
|
|||||||
fn key(db_dst: &'static [u8], item_dst: &'static [u8], key: impl AsRef<[u8]>) -> Vec<u8> {
|
fn key(db_dst: &'static [u8], item_dst: &'static [u8], key: impl AsRef<[u8]>) -> Vec<u8> {
|
||||||
let db_len = u8::try_from(db_dst.len()).unwrap();
|
let db_len = u8::try_from(db_dst.len()).unwrap();
|
||||||
let dst_len = u8::try_from(item_dst.len()).unwrap();
|
let dst_len = u8::try_from(item_dst.len()).unwrap();
|
||||||
[[db_len].as_ref(), db_dst, [dst_len].as_ref(), item_dst, key.as_ref()].concat().to_vec()
|
[[db_len].as_ref(), db_dst, [dst_len].as_ref(), item_dst, key.as_ref()].concat()
|
||||||
}
|
}
|
||||||
fn txn(&mut self) -> Self::Transaction<'_>;
|
fn txn(&mut self) -> Self::Transaction<'_>;
|
||||||
}
|
}
|
||||||
@@ -38,7 +40,7 @@ impl<'a> Get for MemDbTxn<'a> {
|
|||||||
if self.2.contains(key.as_ref()) {
|
if self.2.contains(key.as_ref()) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
self.1.get(key.as_ref()).cloned().or(self.0 .0.read().unwrap().get(key.as_ref()).cloned())
|
self.1.get(key.as_ref()).cloned().or_else(|| self.0 .0.read().unwrap().get(key.as_ref()).cloned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a> DbTxn for MemDbTxn<'a> {
|
impl<'a> DbTxn for MemDbTxn<'a> {
|
||||||
@@ -66,22 +68,22 @@ impl<'a> DbTxn for MemDbTxn<'a> {
|
|||||||
pub struct MemDb(Arc<RwLock<HashMap<Vec<u8>, Vec<u8>>>>);
|
pub struct MemDb(Arc<RwLock<HashMap<Vec<u8>, Vec<u8>>>>);
|
||||||
|
|
||||||
impl PartialEq for MemDb {
|
impl PartialEq for MemDb {
|
||||||
fn eq(&self, other: &MemDb) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
*self.0.read().unwrap() == *other.0.read().unwrap()
|
*self.0.read().unwrap() == *other.0.read().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Eq for MemDb {}
|
impl Eq for MemDb {}
|
||||||
|
|
||||||
impl Default for MemDb {
|
impl Default for MemDb {
|
||||||
fn default() -> MemDb {
|
fn default() -> Self {
|
||||||
MemDb(Arc::new(RwLock::new(HashMap::new())))
|
Self(Arc::new(RwLock::new(HashMap::new())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MemDb {
|
impl MemDb {
|
||||||
/// Create a new in-memory database.
|
/// Create a new in-memory database.
|
||||||
pub fn new() -> MemDb {
|
pub fn new() -> Self {
|
||||||
MemDb::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ mod shims {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
pub fn new<E: 'static + Send + Sync>(kind: ErrorKind, error: E) -> Error {
|
pub fn new<E: 'static + Send + Sync>(kind: ErrorKind, error: E) -> Self {
|
||||||
Error { kind, error: Box::new(error) }
|
Self { kind, error: Box::new(error) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kind(&self) -> ErrorKind {
|
pub const fn kind(&self) -> ErrorKind {
|
||||||
self.kind
|
self.kind
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,10 +53,7 @@ mod shims {
|
|||||||
|
|
||||||
impl Read for &[u8] {
|
impl Read for &[u8] {
|
||||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||||
let mut read = buf.len();
|
let read = self.len().min(buf.len());
|
||||||
if self.len() < buf.len() {
|
|
||||||
read = self.len();
|
|
||||||
}
|
|
||||||
buf[.. read].copy_from_slice(&self[.. read]);
|
buf[.. read].copy_from_slice(&self[.. read]);
|
||||||
*self = &self[read ..];
|
*self = &self[read ..];
|
||||||
Ok(read)
|
Ok(read)
|
||||||
|
|||||||
@@ -2,33 +2,20 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
extern crate alloc;
|
||||||
#[allow(unused_imports)]
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[macro_use]
|
|
||||||
pub extern crate alloc;
|
|
||||||
|
|
||||||
pub mod sync;
|
pub mod sync;
|
||||||
pub mod collections;
|
pub mod collections;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
|
|
||||||
pub mod vec {
|
pub mod vec {
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
pub use alloc::vec::*;
|
pub use alloc::vec::*;
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub use std::vec::*;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod str {
|
pub mod str {
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
pub use alloc::str::*;
|
pub use alloc::str::*;
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub use std::str::*;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod string {
|
pub mod string {
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
pub use alloc::string::*;
|
pub use alloc::string::*;
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub use std::string::*;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
pub use core::sync::*;
|
pub use core::sync::*;
|
||||||
|
pub use alloc::sync::*;
|
||||||
|
|
||||||
mod mutex_shim {
|
mod mutex_shim {
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@@ -32,24 +33,24 @@ mod oncelock_shim {
|
|||||||
|
|
||||||
pub struct OnceLock<T>(Mutex<bool>, Option<T>);
|
pub struct OnceLock<T>(Mutex<bool>, Option<T>);
|
||||||
impl<T> OnceLock<T> {
|
impl<T> OnceLock<T> {
|
||||||
pub const fn new() -> OnceLock<T> {
|
pub const fn new() -> Self {
|
||||||
OnceLock(Mutex::new(false), None)
|
Self(Mutex::new(false), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
// These return a distinct Option in case of None so another caller using get_or_init doesn't
|
// These return a distinct Option in case of None so another caller using get_or_init doesn't
|
||||||
// transform it from None to Some
|
// transform it from None to Some
|
||||||
pub fn get(&self) -> Option<&T> {
|
pub fn get(&self) -> Option<&T> {
|
||||||
if !*self.0.lock() {
|
if *self.0.lock() {
|
||||||
None
|
|
||||||
} else {
|
|
||||||
self.1.as_ref()
|
self.1.as_ref()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_mut(&mut self) -> Option<&mut T> {
|
pub fn get_mut(&mut self) -> Option<&mut T> {
|
||||||
if !*self.0.lock() {
|
if *self.0.lock() {
|
||||||
None
|
|
||||||
} else {
|
|
||||||
self.1.as_mut()
|
self.1.as_mut()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +58,7 @@ mod oncelock_shim {
|
|||||||
let mut lock = self.0.lock();
|
let mut lock = self.0.lock();
|
||||||
if !*lock {
|
if !*lock {
|
||||||
unsafe {
|
unsafe {
|
||||||
(core::ptr::addr_of!(self.1) as *mut Option<_>).write_unaligned(Some(f()));
|
core::ptr::addr_of!(self.1).cast_mut().write_unaligned(Some(f()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*lock = true;
|
*lock = true;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
use digest::{
|
use digest::{
|
||||||
typenum::U114, core_api::BlockSizeUser, Update, Output, OutputSizeUser, FixedOutput,
|
typenum::U114, generic_array::GenericArray, core_api::BlockSizeUser, Update, Output,
|
||||||
ExtendableOutput, XofReader, HashMarker, Digest,
|
OutputSizeUser, FixedOutput, ExtendableOutput, XofReader, HashMarker, Digest,
|
||||||
};
|
};
|
||||||
use sha3::Shake256;
|
use sha3::Shake256;
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ impl Update for Shake256_114 {
|
|||||||
}
|
}
|
||||||
impl FixedOutput for Shake256_114 {
|
impl FixedOutput for Shake256_114 {
|
||||||
fn finalize_fixed(self) -> Output<Self> {
|
fn finalize_fixed(self) -> Output<Self> {
|
||||||
let mut res = Default::default();
|
let mut res = GenericArray::default();
|
||||||
FixedOutput::finalize_into(self, &mut res);
|
FixedOutput::finalize_into(self, &mut res);
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ fn test_secp256k1() {
|
|||||||
)
|
)
|
||||||
.to_repr()
|
.to_repr()
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.copied()
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
hex::decode("acc83278035223c1ba464e2d11bfacfc872b2b23e1041cf5f6130da21e4d8068").unwrap()
|
hex::decode("acc83278035223c1ba464e2d11bfacfc872b2b23e1041cf5f6130da21e4d8068").unwrap()
|
||||||
);
|
);
|
||||||
@@ -167,7 +167,7 @@ f4e8cf80aec3f888d997900ac7e3e349944b5a6b47649fc32186d2f1238103c6\
|
|||||||
)
|
)
|
||||||
.to_repr()
|
.to_repr()
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.copied()
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
hex::decode("f871dfcf6bcd199342651adc361b92c941cb6a0d8c8c1a3b91d79e2c1bf3722d").unwrap()
|
hex::decode("f871dfcf6bcd199342651adc361b92c941cb6a0d8c8c1a3b91d79e2c1bf3722d").unwrap()
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#![allow(clippy::tests_outside_test_module)]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
#![doc = include_str!("lib.md")]
|
#![doc = include_str!("lib.md")]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use core::{
|
use core::{
|
||||||
ops::{DerefMut, Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign},
|
ops::{Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign},
|
||||||
iter::{Sum, Product},
|
iter::{Sum, Product},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ math!(
|
|||||||
macro_rules! from_wrapper {
|
macro_rules! from_wrapper {
|
||||||
($uint: ident) => {
|
($uint: ident) => {
|
||||||
impl From<$uint> for FieldElement {
|
impl From<$uint> for FieldElement {
|
||||||
fn from(a: $uint) -> FieldElement {
|
fn from(a: $uint) -> Self {
|
||||||
Self(ResidueType::new(&U256::from(a)))
|
Self(ResidueType::new(&U256::from(a)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,19 +106,19 @@ impl Field for FieldElement {
|
|||||||
fn random(mut rng: impl RngCore) -> Self {
|
fn random(mut rng: impl RngCore) -> Self {
|
||||||
let mut bytes = [0; 64];
|
let mut bytes = [0; 64];
|
||||||
rng.fill_bytes(&mut bytes);
|
rng.fill_bytes(&mut bytes);
|
||||||
FieldElement(reduce(U512::from_le_bytes(bytes)))
|
Self(reduce(U512::from_le_bytes(bytes)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn square(&self) -> Self {
|
fn square(&self) -> Self {
|
||||||
FieldElement(self.0.square())
|
Self(self.0.square())
|
||||||
}
|
}
|
||||||
fn double(&self) -> Self {
|
fn double(&self) -> Self {
|
||||||
FieldElement(self.0.add(&self.0))
|
Self(self.0.add(&self.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invert(&self) -> CtOption<Self> {
|
fn invert(&self) -> CtOption<Self> {
|
||||||
const NEG_2: FieldElement =
|
#[allow(clippy::use_self)]
|
||||||
FieldElement(ResidueType::new(&MODULUS.saturating_sub(&U256::from_u8(2))));
|
const NEG_2: FieldElement = Self(ResidueType::new(&MODULUS.saturating_sub(&U256::from_u8(2))));
|
||||||
CtOption::new(self.pow(NEG_2), !self.is_zero())
|
CtOption::new(self.pow(NEG_2), !self.is_zero())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ impl Field for FieldElement {
|
|||||||
CtOption::new(candidate, candidate.square().ct_eq(self))
|
CtOption::new(candidate, candidate.square().ct_eq(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sqrt_ratio(u: &FieldElement, v: &FieldElement) -> (Choice, FieldElement) {
|
fn sqrt_ratio(u: &Self, v: &Self) -> (Choice, Self) {
|
||||||
let i = SQRT_M1;
|
let i = SQRT_M1;
|
||||||
|
|
||||||
let u = *u;
|
let u = *u;
|
||||||
@@ -163,7 +163,7 @@ impl PrimeField for FieldElement {
|
|||||||
const NUM_BITS: u32 = 255;
|
const NUM_BITS: u32 = 255;
|
||||||
const CAPACITY: u32 = 254;
|
const CAPACITY: u32 = 254;
|
||||||
|
|
||||||
const TWO_INV: Self = FieldElement(ResidueType::new(&U256::from_u8(2)).invert().0);
|
const TWO_INV: Self = Self(ResidueType::new(&U256::from_u8(2)).invert().0);
|
||||||
|
|
||||||
// This was calculated with the method from the ff crate docs
|
// This was calculated with the method from the ff crate docs
|
||||||
// SageMath GF(modulus).primitive_element()
|
// SageMath GF(modulus).primitive_element()
|
||||||
@@ -174,15 +174,15 @@ impl PrimeField for FieldElement {
|
|||||||
|
|
||||||
// This was calculated via the formula from the ff crate docs
|
// This was calculated via the formula from the ff crate docs
|
||||||
// Self::MULTIPLICATIVE_GENERATOR ** ((modulus - 1) >> Self::S)
|
// Self::MULTIPLICATIVE_GENERATOR ** ((modulus - 1) >> Self::S)
|
||||||
const ROOT_OF_UNITY: Self = FieldElement(ResidueType::new(&U256::from_be_hex(
|
const ROOT_OF_UNITY: Self = Self(ResidueType::new(&U256::from_be_hex(
|
||||||
"2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0",
|
"2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0",
|
||||||
)));
|
)));
|
||||||
// Self::ROOT_OF_UNITY.invert()
|
// Self::ROOT_OF_UNITY.invert()
|
||||||
const ROOT_OF_UNITY_INV: Self = FieldElement(Self::ROOT_OF_UNITY.0.invert().0);
|
const ROOT_OF_UNITY_INV: Self = Self(Self::ROOT_OF_UNITY.0.invert().0);
|
||||||
|
|
||||||
// This was calculated via the formula from the ff crate docs
|
// This was calculated via the formula from the ff crate docs
|
||||||
// Self::MULTIPLICATIVE_GENERATOR ** (2 ** Self::S)
|
// Self::MULTIPLICATIVE_GENERATOR ** (2 ** Self::S)
|
||||||
const DELTA: Self = FieldElement(ResidueType::new(&U256::from_be_hex(
|
const DELTA: Self = Self(ResidueType::new(&U256::from_be_hex(
|
||||||
"0000000000000000000000000000000000000000000000000000000000000010",
|
"0000000000000000000000000000000000000000000000000000000000000010",
|
||||||
)));
|
)));
|
||||||
|
|
||||||
@@ -217,24 +217,25 @@ impl PrimeFieldBits for FieldElement {
|
|||||||
|
|
||||||
impl FieldElement {
|
impl FieldElement {
|
||||||
/// Interpret the value as a little-endian integer, square it, and reduce it into a FieldElement.
|
/// Interpret the value as a little-endian integer, square it, and reduce it into a FieldElement.
|
||||||
pub fn from_square(value: [u8; 32]) -> FieldElement {
|
pub fn from_square(value: [u8; 32]) -> Self {
|
||||||
let value = U256::from_le_bytes(value);
|
let value = U256::from_le_bytes(value);
|
||||||
FieldElement(reduce(U512::from(value.mul_wide(&value))))
|
Self(reduce(U512::from(value.mul_wide(&value))))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform an exponentation.
|
/// Perform an exponentation.
|
||||||
pub fn pow(&self, other: FieldElement) -> FieldElement {
|
#[must_use]
|
||||||
let mut table = [FieldElement::ONE; 16];
|
pub fn pow(&self, other: Self) -> Self {
|
||||||
|
let mut table = [Self::ONE; 16];
|
||||||
table[1] = *self;
|
table[1] = *self;
|
||||||
for i in 2 .. 16 {
|
for i in 2 .. 16 {
|
||||||
table[i] = table[i - 1] * self;
|
table[i] = table[i - 1] * self;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut res = FieldElement::ONE;
|
let mut res = Self::ONE;
|
||||||
let mut bits = 0;
|
let mut bits = 0;
|
||||||
for (i, mut bit) in other.to_le_bits().iter_mut().rev().enumerate() {
|
for (i, mut bit) in other.to_le_bits().iter_mut().rev().enumerate() {
|
||||||
bits <<= 1;
|
bits <<= 1;
|
||||||
let mut bit = u8_from_bool(bit.deref_mut());
|
let mut bit = u8_from_bool(&mut bit);
|
||||||
bits |= bit;
|
bits |= bit;
|
||||||
bit.zeroize();
|
bit.zeroize();
|
||||||
|
|
||||||
@@ -257,7 +258,7 @@ impl FieldElement {
|
|||||||
/// The result is only a valid square root if the Choice is true.
|
/// The result is only a valid square root if the Choice is true.
|
||||||
/// RFC 8032 simply fails if there isn't a square root, leaving any return value undefined.
|
/// RFC 8032 simply fails if there isn't a square root, leaving any return value undefined.
|
||||||
/// Ristretto explicitly returns 0 or sqrt((SQRT_M1 * u) / v).
|
/// Ristretto explicitly returns 0 or sqrt((SQRT_M1 * u) / v).
|
||||||
pub fn sqrt_ratio_i(u: FieldElement, v: FieldElement) -> (Choice, FieldElement) {
|
pub fn sqrt_ratio_i(u: Self, v: Self) -> (Choice, Self) {
|
||||||
let i = SQRT_M1;
|
let i = SQRT_M1;
|
||||||
|
|
||||||
let v3 = v.square() * v;
|
let v3 = v.square() * v;
|
||||||
@@ -288,9 +289,9 @@ impl FieldElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sum<FieldElement> for FieldElement {
|
impl Sum<Self> for FieldElement {
|
||||||
fn sum<I: Iterator<Item = FieldElement>>(iter: I) -> FieldElement {
|
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||||
let mut res = FieldElement::ZERO;
|
let mut res = Self::ZERO;
|
||||||
for item in iter {
|
for item in iter {
|
||||||
res += item;
|
res += item;
|
||||||
}
|
}
|
||||||
@@ -298,15 +299,15 @@ impl Sum<FieldElement> for FieldElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Sum<&'a FieldElement> for FieldElement {
|
impl<'a> Sum<&'a Self> for FieldElement {
|
||||||
fn sum<I: Iterator<Item = &'a FieldElement>>(iter: I) -> FieldElement {
|
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
|
||||||
iter.cloned().sum()
|
iter.copied().sum()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Product<FieldElement> for FieldElement {
|
impl Product<Self> for FieldElement {
|
||||||
fn product<I: Iterator<Item = FieldElement>>(iter: I) -> FieldElement {
|
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||||
let mut res = FieldElement::ONE;
|
let mut res = Self::ONE;
|
||||||
for item in iter {
|
for item in iter {
|
||||||
res *= item;
|
res *= item;
|
||||||
}
|
}
|
||||||
@@ -314,9 +315,9 @@ impl Product<FieldElement> for FieldElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Product<&'a FieldElement> for FieldElement {
|
impl<'a> Product<&'a Self> for FieldElement {
|
||||||
fn product<I: Iterator<Item = &'a FieldElement>>(iter: I) -> FieldElement {
|
fn product<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
|
||||||
iter.cloned().product()
|
iter.copied().product()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
|
#![allow(clippy::tests_outside_test_module)]
|
||||||
|
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
#![no_std] // Prevents writing new code, in what should be a simple wrapper, which requires std
|
#![no_std] // Prevents writing new code, in what should be a simple wrapper, which requires std
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
use core::{
|
use core::{
|
||||||
borrow::Borrow,
|
borrow::Borrow,
|
||||||
ops::{Deref, DerefMut, Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign},
|
ops::{Deref, Add, AddAssign, Sub, SubAssign, Neg, Mul, MulAssign},
|
||||||
iter::{Iterator, Sum, Product},
|
iter::{Iterator, Sum, Product},
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
};
|
};
|
||||||
@@ -50,6 +52,7 @@ fn u8_from_bool(bit_ref: &mut bool) -> u8 {
|
|||||||
let bit_ref = black_box(bit_ref);
|
let bit_ref = black_box(bit_ref);
|
||||||
|
|
||||||
let mut bit = black_box(*bit_ref);
|
let mut bit = black_box(*bit_ref);
|
||||||
|
#[allow(clippy::as_conversions, clippy::cast_lossless)]
|
||||||
let res = black_box(bit as u8);
|
let res = black_box(bit as u8);
|
||||||
bit.zeroize();
|
bit.zeroize();
|
||||||
debug_assert!((res | 1) == 1);
|
debug_assert!((res | 1) == 1);
|
||||||
@@ -172,8 +175,8 @@ math_neg!(Scalar, Scalar, DScalar::add, DScalar::sub, DScalar::mul);
|
|||||||
macro_rules! from_wrapper {
|
macro_rules! from_wrapper {
|
||||||
($uint: ident) => {
|
($uint: ident) => {
|
||||||
impl From<$uint> for Scalar {
|
impl From<$uint> for Scalar {
|
||||||
fn from(a: $uint) -> Scalar {
|
fn from(a: $uint) -> Self {
|
||||||
Scalar(DScalar::from(a))
|
Self(DScalar::from(a))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -190,18 +193,19 @@ const MODULUS: U256 =
|
|||||||
U256::from_be_hex("1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed");
|
U256::from_be_hex("1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed");
|
||||||
|
|
||||||
impl Scalar {
|
impl Scalar {
|
||||||
pub fn pow(&self, other: Scalar) -> Scalar {
|
#[must_use]
|
||||||
let mut table = [Scalar::ONE; 16];
|
pub fn pow(&self, other: Self) -> Self {
|
||||||
|
let mut table = [Self::ONE; 16];
|
||||||
table[1] = *self;
|
table[1] = *self;
|
||||||
for i in 2 .. 16 {
|
for i in 2 .. 16 {
|
||||||
table[i] = table[i - 1] * self;
|
table[i] = table[i - 1] * self;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut res = Scalar::ONE;
|
let mut res = Self::ONE;
|
||||||
let mut bits = 0;
|
let mut bits = 0;
|
||||||
for (i, mut bit) in other.to_le_bits().iter_mut().rev().enumerate() {
|
for (i, mut bit) in other.to_le_bits().iter_mut().rev().enumerate() {
|
||||||
bits <<= 1;
|
bits <<= 1;
|
||||||
let mut bit = u8_from_bool(bit.deref_mut());
|
let mut bit = u8_from_bool(&mut bit);
|
||||||
bits |= bit;
|
bits |= bit;
|
||||||
bit.zeroize();
|
bit.zeroize();
|
||||||
|
|
||||||
@@ -219,23 +223,23 @@ impl Scalar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Perform wide reduction on a 64-byte array to create a Scalar without bias.
|
/// Perform wide reduction on a 64-byte array to create a Scalar without bias.
|
||||||
pub fn from_bytes_mod_order_wide(bytes: &[u8; 64]) -> Scalar {
|
pub fn from_bytes_mod_order_wide(bytes: &[u8; 64]) -> Self {
|
||||||
Self(DScalar::from_bytes_mod_order_wide(bytes))
|
Self(DScalar::from_bytes_mod_order_wide(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Derive a Scalar without bias from a digest via wide reduction.
|
/// Derive a Scalar without bias from a digest via wide reduction.
|
||||||
pub fn from_hash<D: Digest<OutputSize = U64> + HashMarker>(hash: D) -> Scalar {
|
pub fn from_hash<D: Digest<OutputSize = U64> + HashMarker>(hash: D) -> Self {
|
||||||
let mut output = [0u8; 64];
|
let mut output = [0u8; 64];
|
||||||
output.copy_from_slice(&hash.finalize());
|
output.copy_from_slice(&hash.finalize());
|
||||||
let res = Scalar(DScalar::from_bytes_mod_order_wide(&output));
|
let res = Self(DScalar::from_bytes_mod_order_wide(&output));
|
||||||
output.zeroize();
|
output.zeroize();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Field for Scalar {
|
impl Field for Scalar {
|
||||||
const ZERO: Scalar = Scalar(DScalar::from_bits([0; 32]));
|
const ZERO: Self = Self(DScalar::from_bits([0; 32]));
|
||||||
const ONE: Scalar = Scalar(DScalar::from_bits({
|
const ONE: Self = Self(DScalar::from_bits({
|
||||||
let mut bytes = [0; 32];
|
let mut bytes = [0; 32];
|
||||||
bytes[0] = 1;
|
bytes[0] = 1;
|
||||||
bytes
|
bytes
|
||||||
@@ -259,10 +263,10 @@ impl Field for Scalar {
|
|||||||
|
|
||||||
fn sqrt(&self) -> CtOption<Self> {
|
fn sqrt(&self) -> CtOption<Self> {
|
||||||
let mod_3_8 = MODULUS.saturating_add(&U256::from_u8(3)).wrapping_div(&U256::from_u8(8));
|
let mod_3_8 = MODULUS.saturating_add(&U256::from_u8(3)).wrapping_div(&U256::from_u8(8));
|
||||||
let mod_3_8 = Scalar::from_repr(mod_3_8.to_le_bytes()).unwrap();
|
let mod_3_8 = Self::from_repr(mod_3_8.to_le_bytes()).unwrap();
|
||||||
|
|
||||||
let sqrt_m1 = MODULUS.saturating_sub(&U256::from_u8(1)).wrapping_div(&U256::from_u8(4));
|
let sqrt_m1 = MODULUS.saturating_sub(&U256::from_u8(1)).wrapping_div(&U256::from_u8(4));
|
||||||
let sqrt_m1 = Scalar::from(2u8).pow(Scalar::from_repr(sqrt_m1.to_le_bytes()).unwrap());
|
let sqrt_m1 = Self::from(2u8).pow(Self::from_repr(sqrt_m1.to_le_bytes()).unwrap());
|
||||||
|
|
||||||
let tv1 = self.pow(mod_3_8);
|
let tv1 = self.pow(mod_3_8);
|
||||||
let tv2 = tv1 * sqrt_m1;
|
let tv2 = tv1 * sqrt_m1;
|
||||||
@@ -284,14 +288,14 @@ impl PrimeField for Scalar {
|
|||||||
const CAPACITY: u32 = 252;
|
const CAPACITY: u32 = 252;
|
||||||
|
|
||||||
// 2.invert()
|
// 2.invert()
|
||||||
const TWO_INV: Scalar = Scalar(DScalar::from_bits([
|
const TWO_INV: Self = Self(DScalar::from_bits([
|
||||||
247, 233, 122, 46, 141, 49, 9, 44, 107, 206, 123, 81, 239, 124, 111, 10, 0, 0, 0, 0, 0, 0, 0,
|
247, 233, 122, 46, 141, 49, 9, 44, 107, 206, 123, 81, 239, 124, 111, 10, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 8,
|
0, 0, 0, 0, 0, 0, 0, 0, 8,
|
||||||
]));
|
]));
|
||||||
|
|
||||||
// This was calculated with the method from the ff crate docs
|
// This was calculated with the method from the ff crate docs
|
||||||
// SageMath GF(modulus).primitive_element()
|
// SageMath GF(modulus).primitive_element()
|
||||||
const MULTIPLICATIVE_GENERATOR: Scalar = Scalar(DScalar::from_bits({
|
const MULTIPLICATIVE_GENERATOR: Self = Self(DScalar::from_bits({
|
||||||
let mut bytes = [0; 32];
|
let mut bytes = [0; 32];
|
||||||
bytes[0] = 2;
|
bytes[0] = 2;
|
||||||
bytes
|
bytes
|
||||||
@@ -302,26 +306,26 @@ impl PrimeField for Scalar {
|
|||||||
|
|
||||||
// This was calculated via the formula from the ff crate docs
|
// This was calculated via the formula from the ff crate docs
|
||||||
// Self::MULTIPLICATIVE_GENERATOR ** ((modulus - 1) >> Self::S)
|
// Self::MULTIPLICATIVE_GENERATOR ** ((modulus - 1) >> Self::S)
|
||||||
const ROOT_OF_UNITY: Scalar = Scalar(DScalar::from_bits([
|
const ROOT_OF_UNITY: Self = Self(DScalar::from_bits([
|
||||||
212, 7, 190, 235, 223, 117, 135, 190, 254, 131, 206, 66, 83, 86, 240, 14, 122, 194, 193, 171,
|
212, 7, 190, 235, 223, 117, 135, 190, 254, 131, 206, 66, 83, 86, 240, 14, 122, 194, 193, 171,
|
||||||
96, 109, 61, 125, 231, 129, 121, 224, 16, 115, 74, 9,
|
96, 109, 61, 125, 231, 129, 121, 224, 16, 115, 74, 9,
|
||||||
]));
|
]));
|
||||||
// Self::ROOT_OF_UNITY.invert()
|
// Self::ROOT_OF_UNITY.invert()
|
||||||
const ROOT_OF_UNITY_INV: Scalar = Scalar(DScalar::from_bits([
|
const ROOT_OF_UNITY_INV: Self = Self(DScalar::from_bits([
|
||||||
25, 204, 55, 113, 58, 237, 138, 153, 215, 24, 41, 96, 139, 163, 238, 5, 134, 61, 62, 84, 159,
|
25, 204, 55, 113, 58, 237, 138, 153, 215, 24, 41, 96, 139, 163, 238, 5, 134, 61, 62, 84, 159,
|
||||||
146, 194, 130, 24, 126, 134, 31, 239, 140, 181, 6,
|
146, 194, 130, 24, 126, 134, 31, 239, 140, 181, 6,
|
||||||
]));
|
]));
|
||||||
|
|
||||||
// This was calculated via the formula from the ff crate docs
|
// This was calculated via the formula from the ff crate docs
|
||||||
// Self::MULTIPLICATIVE_GENERATOR ** (2 ** Self::S)
|
// Self::MULTIPLICATIVE_GENERATOR ** (2 ** Self::S)
|
||||||
const DELTA: Scalar = Scalar(DScalar::from_bits([
|
const DELTA: Self = Self(DScalar::from_bits([
|
||||||
16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
]));
|
]));
|
||||||
|
|
||||||
fn from_repr(bytes: [u8; 32]) -> CtOption<Self> {
|
fn from_repr(bytes: [u8; 32]) -> CtOption<Self> {
|
||||||
let scalar = DScalar::from_canonical_bytes(bytes);
|
let scalar = DScalar::from_canonical_bytes(bytes);
|
||||||
// TODO: This unwrap_or_else isn't constant time, yet we don't exactly have an alternative...
|
// TODO: This unwrap_or_else isn't constant time, yet we don't exactly have an alternative...
|
||||||
CtOption::new(Scalar(scalar.unwrap_or_else(DScalar::zero)), choice(black_box(scalar).is_some()))
|
CtOption::new(Self(scalar.unwrap_or_else(DScalar::zero)), choice(black_box(scalar).is_some()))
|
||||||
}
|
}
|
||||||
fn to_repr(&self) -> [u8; 32] {
|
fn to_repr(&self) -> [u8; 32] {
|
||||||
self.0.to_bytes()
|
self.0.to_bytes()
|
||||||
@@ -337,7 +341,7 @@ impl PrimeField for Scalar {
|
|||||||
// methods does not
|
// methods does not
|
||||||
// We do not use one of its methods to ensure we write via zeroize
|
// We do not use one of its methods to ensure we write via zeroize
|
||||||
for mut bit in bits.iter_mut() {
|
for mut bit in bits.iter_mut() {
|
||||||
bit.deref_mut().zeroize();
|
bit.zeroize();
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@@ -355,33 +359,33 @@ impl PrimeFieldBits for Scalar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn char_le_bits() -> FieldBits<Self::ReprBits> {
|
fn char_le_bits() -> FieldBits<Self::ReprBits> {
|
||||||
let mut bytes = (Scalar::ZERO - Scalar::ONE).to_repr();
|
let mut bytes = (Self::ZERO - Self::ONE).to_repr();
|
||||||
bytes[0] += 1;
|
bytes[0] += 1;
|
||||||
debug_assert_eq!(DScalar::from_bytes_mod_order(bytes), DScalar::zero());
|
debug_assert_eq!(DScalar::from_bytes_mod_order(bytes), DScalar::zero());
|
||||||
bytes.into()
|
bytes.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sum<Scalar> for Scalar {
|
impl Sum<Self> for Scalar {
|
||||||
fn sum<I: Iterator<Item = Scalar>>(iter: I) -> Scalar {
|
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||||
Self(DScalar::sum(iter))
|
Self(DScalar::sum(iter))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Sum<&'a Scalar> for Scalar {
|
impl<'a> Sum<&'a Self> for Scalar {
|
||||||
fn sum<I: Iterator<Item = &'a Scalar>>(iter: I) -> Scalar {
|
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
|
||||||
Self(DScalar::sum(iter))
|
Self(DScalar::sum(iter))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Product<Scalar> for Scalar {
|
impl Product<Self> for Scalar {
|
||||||
fn product<I: Iterator<Item = Scalar>>(iter: I) -> Scalar {
|
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||||
Self(DScalar::product(iter))
|
Self(DScalar::product(iter))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Product<&'a Scalar> for Scalar {
|
impl<'a> Product<&'a Self> for Scalar {
|
||||||
fn product<I: Iterator<Item = &'a Scalar>>(iter: I) -> Scalar {
|
fn product<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
|
||||||
Self(DScalar::product(iter))
|
Self(DScalar::product(iter))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -502,8 +506,9 @@ dalek_group!(
|
|||||||
);
|
);
|
||||||
|
|
||||||
impl EdwardsPoint {
|
impl EdwardsPoint {
|
||||||
pub fn mul_by_cofactor(&self) -> EdwardsPoint {
|
#[must_use]
|
||||||
EdwardsPoint(self.0.mul_by_cofactor())
|
pub fn mul_by_cofactor(&self) -> Self {
|
||||||
|
Self(self.0.mul_by_cofactor())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ fn cipher<C: Ciphersuite>(context: &str, ecdh: &Zeroizing<C::G>) -> ChaCha20 {
|
|||||||
transcript.append_message(b"shared_key", ecdh.as_ref());
|
transcript.append_message(b"shared_key", ecdh.as_ref());
|
||||||
ecdh.as_mut().zeroize();
|
ecdh.as_mut().zeroize();
|
||||||
|
|
||||||
|
#[allow(clippy::redundant_closure_for_method_calls)] // Not redundant due to typing
|
||||||
let zeroize = |buf: &mut [u8]| buf.zeroize();
|
let zeroize = |buf: &mut [u8]| buf.zeroize();
|
||||||
|
|
||||||
let mut key = Cc20Key::default();
|
let mut key = Cc20Key::default();
|
||||||
@@ -390,9 +391,10 @@ impl<C: Ciphersuite> Encryption<C> {
|
|||||||
participant: Participant,
|
participant: Participant,
|
||||||
msg: EncryptionKeyMessage<C, M>,
|
msg: EncryptionKeyMessage<C, M>,
|
||||||
) -> M {
|
) -> M {
|
||||||
if self.enc_keys.contains_key(&participant) {
|
assert!(
|
||||||
panic!("Re-registering encryption key for a participant");
|
!self.enc_keys.contains_key(&participant),
|
||||||
}
|
"Re-registering encryption key for a participant"
|
||||||
|
);
|
||||||
self.enc_keys.insert(participant, msg.enc_key);
|
self.enc_keys.insert(participant, msg.enc_key);
|
||||||
msg.msg
|
msg.msg
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ impl<C: Ciphersuite> ReadWrite for Commitments<C> {
|
|||||||
commitments.push(read_G()?);
|
commitments.push(read_G()?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Commitments { commitments, cached_msg, sig: SchnorrSignature::read(reader)? })
|
Ok(Self { commitments, cached_msg, sig: SchnorrSignature::read(reader)? })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||||
@@ -87,14 +87,15 @@ impl<C: Ciphersuite> ReadWrite for Commitments<C> {
|
|||||||
pub struct KeyGenMachine<C: Ciphersuite> {
|
pub struct KeyGenMachine<C: Ciphersuite> {
|
||||||
params: ThresholdParams,
|
params: ThresholdParams,
|
||||||
context: String,
|
context: String,
|
||||||
_curve: PhantomData<C>,
|
curve: PhantomData<C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Ciphersuite> KeyGenMachine<C> {
|
impl<C: Ciphersuite> KeyGenMachine<C> {
|
||||||
/// Create a new machine to generate a key.
|
/// Create a new machine to generate a key.
|
||||||
// The context string should be unique among multisigs.
|
///
|
||||||
pub fn new(params: ThresholdParams, context: String) -> KeyGenMachine<C> {
|
/// The context string should be unique among multisigs.
|
||||||
KeyGenMachine { params, context, _curve: PhantomData }
|
pub fn new(params: ThresholdParams, context: String) -> Self {
|
||||||
|
Self { params, context, curve: PhantomData }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start generating a key according to the FROST DKG spec.
|
/// Start generating a key according to the FROST DKG spec.
|
||||||
@@ -171,7 +172,6 @@ fn polynomial<F: PrimeField + Zeroize>(
|
|||||||
/// channel.
|
/// channel.
|
||||||
///
|
///
|
||||||
/// If any participant sends multiple secret shares to another participant, they are faulty.
|
/// If any participant sends multiple secret shares to another participant, they are faulty.
|
||||||
|
|
||||||
// This should presumably be written as SecretShare(Zeroizing<F::Repr>).
|
// This should presumably be written as SecretShare(Zeroizing<F::Repr>).
|
||||||
// It's unfortunately not possible as F::Repr doesn't have Zeroize as a bound.
|
// It's unfortunately not possible as F::Repr doesn't have Zeroize as a bound.
|
||||||
// The encryption system also explicitly uses Zeroizing<M> so it can ensure anything being
|
// The encryption system also explicitly uses Zeroizing<M> so it can ensure anything being
|
||||||
@@ -195,7 +195,7 @@ impl<F: PrimeField> fmt::Debug for SecretShare<F> {
|
|||||||
}
|
}
|
||||||
impl<F: PrimeField> Zeroize for SecretShare<F> {
|
impl<F: PrimeField> Zeroize for SecretShare<F> {
|
||||||
fn zeroize(&mut self) {
|
fn zeroize(&mut self) {
|
||||||
self.0.as_mut().zeroize()
|
self.0.as_mut().zeroize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Still manually implement ZeroizeOnDrop to ensure these don't stick around.
|
// Still manually implement ZeroizeOnDrop to ensure these don't stick around.
|
||||||
@@ -213,7 +213,7 @@ impl<F: PrimeField> ReadWrite for SecretShare<F> {
|
|||||||
fn read<R: Read>(reader: &mut R, _: ThresholdParams) -> io::Result<Self> {
|
fn read<R: Read>(reader: &mut R, _: ThresholdParams) -> io::Result<Self> {
|
||||||
let mut repr = F::Repr::default();
|
let mut repr = F::Repr::default();
|
||||||
reader.read_exact(repr.as_mut())?;
|
reader.read_exact(repr.as_mut())?;
|
||||||
Ok(SecretShare(repr))
|
Ok(Self(repr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||||
@@ -353,7 +353,7 @@ impl<C: Ciphersuite> Zeroize for KeyMachine<C> {
|
|||||||
fn zeroize(&mut self) {
|
fn zeroize(&mut self) {
|
||||||
self.params.zeroize();
|
self.params.zeroize();
|
||||||
self.secret.zeroize();
|
self.secret.zeroize();
|
||||||
for (_, commitments) in self.commitments.iter_mut() {
|
for commitments in self.commitments.values_mut() {
|
||||||
commitments.zeroize();
|
commitments.zeroize();
|
||||||
}
|
}
|
||||||
self.encryption.zeroize();
|
self.encryption.zeroize();
|
||||||
@@ -466,7 +466,7 @@ impl<C: Ciphersuite> KeyMachine<C> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let KeyMachine { commitments, encryption, params, secret } = self;
|
let Self { commitments, encryption, params, secret } = self;
|
||||||
Ok(BlameMachine {
|
Ok(BlameMachine {
|
||||||
commitments,
|
commitments,
|
||||||
encryption,
|
encryption,
|
||||||
@@ -499,7 +499,7 @@ impl<C: Ciphersuite> fmt::Debug for BlameMachine<C> {
|
|||||||
|
|
||||||
impl<C: Ciphersuite> Zeroize for BlameMachine<C> {
|
impl<C: Ciphersuite> Zeroize for BlameMachine<C> {
|
||||||
fn zeroize(&mut self) {
|
fn zeroize(&mut self) {
|
||||||
for (_, commitments) in self.commitments.iter_mut() {
|
for commitments in self.commitments.values_mut() {
|
||||||
commitments.zeroize();
|
commitments.zeroize();
|
||||||
}
|
}
|
||||||
self.encryption.zeroize();
|
self.encryption.zeroize();
|
||||||
@@ -517,6 +517,7 @@ impl<C: Ciphersuite> BlameMachine<C> {
|
|||||||
/// territory of consensus protocols. This library does not handle that nor does it provide any
|
/// territory of consensus protocols. This library does not handle that nor does it provide any
|
||||||
/// tooling to do so. This function is solely intended to force users to acknowledge they're
|
/// tooling to do so. This function is solely intended to force users to acknowledge they're
|
||||||
/// completing the protocol, not processing any blame.
|
/// completing the protocol, not processing any blame.
|
||||||
|
#[allow(clippy::missing_const_for_fn)] // False positive
|
||||||
pub fn complete(self) -> ThresholdCore<C> {
|
pub fn complete(self) -> ThresholdCore<C> {
|
||||||
self.result
|
self.result
|
||||||
}
|
}
|
||||||
@@ -536,10 +537,9 @@ impl<C: Ciphersuite> BlameMachine<C> {
|
|||||||
Err(DecryptionError::InvalidProof) => return recipient,
|
Err(DecryptionError::InvalidProof) => return recipient,
|
||||||
};
|
};
|
||||||
|
|
||||||
let share = match Option::<C::F>::from(C::F::from_repr(share_bytes.0)) {
|
let Some(share) = Option::<C::F>::from(C::F::from_repr(share_bytes.0)) else {
|
||||||
Some(share) => share,
|
|
||||||
// If this isn't a valid scalar, the sender is faulty
|
// If this isn't a valid scalar, the sender is faulty
|
||||||
None => return sender,
|
return sender;
|
||||||
};
|
};
|
||||||
|
|
||||||
// If this isn't a valid share, the sender is faulty
|
// If this isn't a valid share, the sender is faulty
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
use core::fmt::{self, Debug};
|
use core::fmt::{self, Debug};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
@@ -35,23 +34,23 @@ pub mod tests;
|
|||||||
pub struct Participant(pub(crate) u16);
|
pub struct Participant(pub(crate) u16);
|
||||||
impl Participant {
|
impl Participant {
|
||||||
/// Create a new Participant identifier from a u16.
|
/// Create a new Participant identifier from a u16.
|
||||||
pub fn new(i: u16) -> Option<Participant> {
|
pub const fn new(i: u16) -> Option<Self> {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(Participant(i))
|
Some(Self(i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a Participant identifier to bytes.
|
/// Convert a Participant identifier to bytes.
|
||||||
#[allow(clippy::wrong_self_convention)]
|
#[allow(clippy::wrong_self_convention)]
|
||||||
pub fn to_bytes(&self) -> [u8; 2] {
|
pub const fn to_bytes(&self) -> [u8; 2] {
|
||||||
self.0.to_le_bytes()
|
self.0.to_le_bytes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Participant> for u16 {
|
impl From<Participant> for u16 {
|
||||||
fn from(participant: Participant) -> u16 {
|
fn from(participant: Participant) -> Self {
|
||||||
participant.0
|
participant.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,7 +104,7 @@ mod lib {
|
|||||||
pub use super::*;
|
pub use super::*;
|
||||||
|
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
use std::{io, sync::Arc, collections::HashMap};
|
use std::{sync::Arc, io, collections::HashMap};
|
||||||
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
@@ -158,7 +157,7 @@ mod lib {
|
|||||||
|
|
||||||
impl ThresholdParams {
|
impl ThresholdParams {
|
||||||
/// Create a new set of parameters.
|
/// Create a new set of parameters.
|
||||||
pub fn new(t: u16, n: u16, i: Participant) -> Result<ThresholdParams, DkgError<()>> {
|
pub fn new(t: u16, n: u16, i: Participant) -> Result<Self, DkgError<()>> {
|
||||||
if (t == 0) || (n == 0) {
|
if (t == 0) || (n == 0) {
|
||||||
Err(DkgError::ZeroParameter(t, n))?;
|
Err(DkgError::ZeroParameter(t, n))?;
|
||||||
}
|
}
|
||||||
@@ -170,19 +169,19 @@ mod lib {
|
|||||||
Err(DkgError::InvalidParticipant(n, i))?;
|
Err(DkgError::InvalidParticipant(n, i))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ThresholdParams { t, n, i })
|
Ok(Self { t, n, i })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the threshold for a multisig with these parameters.
|
/// Return the threshold for a multisig with these parameters.
|
||||||
pub fn t(&self) -> u16 {
|
pub const fn t(&self) -> u16 {
|
||||||
self.t
|
self.t
|
||||||
}
|
}
|
||||||
/// Return the amount of participants for a multisig with these parameters.
|
/// Return the amount of participants for a multisig with these parameters.
|
||||||
pub fn n(&self) -> u16 {
|
pub const fn n(&self) -> u16 {
|
||||||
self.n
|
self.n
|
||||||
}
|
}
|
||||||
/// Return the participant index of the share with these parameters.
|
/// Return the participant index of the share with these parameters.
|
||||||
pub fn i(&self) -> Participant {
|
pub const fn i(&self) -> Participant {
|
||||||
self.i
|
self.i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -239,7 +238,7 @@ mod lib {
|
|||||||
self.params.zeroize();
|
self.params.zeroize();
|
||||||
self.secret_share.zeroize();
|
self.secret_share.zeroize();
|
||||||
self.group_key.zeroize();
|
self.group_key.zeroize();
|
||||||
for (_, share) in self.verification_shares.iter_mut() {
|
for share in self.verification_shares.values_mut() {
|
||||||
share.zeroize();
|
share.zeroize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -250,9 +249,9 @@ mod lib {
|
|||||||
params: ThresholdParams,
|
params: ThresholdParams,
|
||||||
secret_share: Zeroizing<C::F>,
|
secret_share: Zeroizing<C::F>,
|
||||||
verification_shares: HashMap<Participant, C::G>,
|
verification_shares: HashMap<Participant, C::G>,
|
||||||
) -> ThresholdCore<C> {
|
) -> Self {
|
||||||
let t = (1 ..= params.t()).map(Participant).collect::<Vec<_>>();
|
let t = (1 ..= params.t()).map(Participant).collect::<Vec<_>>();
|
||||||
ThresholdCore {
|
Self {
|
||||||
params,
|
params,
|
||||||
secret_share,
|
secret_share,
|
||||||
group_key: t.iter().map(|i| verification_shares[i] * lagrange::<C::F>(*i, &t)).sum(),
|
group_key: t.iter().map(|i| verification_shares[i] * lagrange::<C::F>(*i, &t)).sum(),
|
||||||
@@ -304,7 +303,7 @@ mod lib {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Read keys from a type satisfying std::io::Read.
|
/// Read keys from a type satisfying std::io::Read.
|
||||||
pub fn read<R: io::Read>(reader: &mut R) -> io::Result<ThresholdCore<C>> {
|
pub fn read<R: io::Read>(reader: &mut R) -> io::Result<Self> {
|
||||||
{
|
{
|
||||||
let different =
|
let different =
|
||||||
|| io::Error::new(io::ErrorKind::Other, "deserializing ThresholdCore for another curve");
|
|| io::Error::new(io::ErrorKind::Other, "deserializing ThresholdCore for another curve");
|
||||||
@@ -332,7 +331,7 @@ mod lib {
|
|||||||
read_u16()?,
|
read_u16()?,
|
||||||
read_u16()?,
|
read_u16()?,
|
||||||
Participant::new(read_u16()?)
|
Participant::new(read_u16()?)
|
||||||
.ok_or(io::Error::new(io::ErrorKind::Other, "invalid participant index"))?,
|
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid participant index"))?,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -343,7 +342,7 @@ mod lib {
|
|||||||
verification_shares.insert(l, <C as Ciphersuite>::read_G(reader)?);
|
verification_shares.insert(l, <C as Ciphersuite>::read_G(reader)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ThresholdCore::new(
|
Ok(Self::new(
|
||||||
ThresholdParams::new(t, n, i)
|
ThresholdParams::new(t, n, i)
|
||||||
.map_err(|_| io::Error::new(io::ErrorKind::Other, "invalid parameters"))?,
|
.map_err(|_| io::Error::new(io::ErrorKind::Other, "invalid parameters"))?,
|
||||||
secret_share,
|
secret_share,
|
||||||
@@ -395,10 +394,10 @@ mod lib {
|
|||||||
self.group_key.zeroize();
|
self.group_key.zeroize();
|
||||||
self.included.zeroize();
|
self.included.zeroize();
|
||||||
self.secret_share.zeroize();
|
self.secret_share.zeroize();
|
||||||
for (_, share) in self.original_verification_shares.iter_mut() {
|
for share in self.original_verification_shares.values_mut() {
|
||||||
share.zeroize();
|
share.zeroize();
|
||||||
}
|
}
|
||||||
for (_, share) in self.verification_shares.iter_mut() {
|
for share in self.verification_shares.values_mut() {
|
||||||
share.zeroize();
|
share.zeroize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -406,8 +405,8 @@ mod lib {
|
|||||||
|
|
||||||
impl<C: Ciphersuite> ThresholdKeys<C> {
|
impl<C: Ciphersuite> ThresholdKeys<C> {
|
||||||
/// Create a new set of ThresholdKeys from a ThresholdCore.
|
/// Create a new set of ThresholdKeys from a ThresholdCore.
|
||||||
pub fn new(core: ThresholdCore<C>) -> ThresholdKeys<C> {
|
pub fn new(core: ThresholdCore<C>) -> Self {
|
||||||
ThresholdKeys { core: Arc::new(core), offset: None }
|
Self { core: Arc::new(core), offset: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Offset the keys by a given scalar to allow for various account and privacy schemes.
|
/// Offset the keys by a given scalar to allow for various account and privacy schemes.
|
||||||
@@ -415,7 +414,7 @@ mod lib {
|
|||||||
/// This offset is ephemeral and will not be included when these keys are serialized. It also
|
/// This offset is ephemeral and will not be included when these keys are serialized. It also
|
||||||
/// accumulates, so calling offset multiple times will produce a offset of the offsets' sum.
|
/// accumulates, so calling offset multiple times will produce a offset of the offsets' sum.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn offset(&self, offset: C::F) -> ThresholdKeys<C> {
|
pub fn offset(&self, offset: C::F) -> Self {
|
||||||
let mut res = self.clone();
|
let mut res = self.clone();
|
||||||
// Carry any existing offset
|
// Carry any existing offset
|
||||||
// Enables schemes like Monero's subaddresses which have a per-subaddress offset and then a
|
// Enables schemes like Monero's subaddresses which have a per-subaddress offset and then a
|
||||||
@@ -469,7 +468,7 @@ mod lib {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let mut verification_shares = self.verification_shares();
|
let mut verification_shares = self.verification_shares();
|
||||||
for (i, share) in verification_shares.iter_mut() {
|
for (i, share) in &mut verification_shares {
|
||||||
*share *= lagrange::<C::F>(*i, &included);
|
*share *= lagrange::<C::F>(*i, &included);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,8 +491,8 @@ mod lib {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Ciphersuite> From<ThresholdCore<C>> for ThresholdKeys<C> {
|
impl<C: Ciphersuite> From<ThresholdCore<C>> for ThresholdKeys<C> {
|
||||||
fn from(keys: ThresholdCore<C>) -> ThresholdKeys<C> {
|
fn from(keys: ThresholdCore<C>) -> Self {
|
||||||
ThresholdKeys::new(keys)
|
Self::new(keys)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use core::{marker::PhantomData, ops::Deref};
|
use core::{marker::PhantomData, ops::Deref};
|
||||||
use std::{
|
use std::{
|
||||||
io::{self, Read, Write},
|
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
io::{self, Read, Write},
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -45,11 +45,8 @@ impl<C: Ciphersuite> GeneratorProof<C> {
|
|||||||
self.proof.write(writer)
|
self.proof.write(writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<R: Read>(reader: &mut R) -> io::Result<GeneratorProof<C>> {
|
pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
|
||||||
Ok(GeneratorProof {
|
Ok(Self { share: <C as Ciphersuite>::read_G(reader)?, proof: DLEqProof::read(reader)? })
|
||||||
share: <C as Ciphersuite>::read_G(reader)?,
|
|
||||||
proof: DLEqProof::read(reader)?,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
@@ -70,16 +67,13 @@ pub struct GeneratorPromotion<C1: Ciphersuite, C2: Ciphersuite> {
|
|||||||
_c2: PhantomData<C2>,
|
_c2: PhantomData<C2>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C1: Ciphersuite, C2: Ciphersuite> GeneratorPromotion<C1, C2>
|
impl<C1: Ciphersuite, C2: Ciphersuite<F = C1::F, G = C1::G>> GeneratorPromotion<C1, C2> {
|
||||||
where
|
|
||||||
C2: Ciphersuite<F = C1::F, G = C1::G>,
|
|
||||||
{
|
|
||||||
/// Begin promoting keys from one generator to another. Returns a proof this share was properly
|
/// Begin promoting keys from one generator to another. Returns a proof this share was properly
|
||||||
/// promoted.
|
/// promoted.
|
||||||
pub fn promote<R: RngCore + CryptoRng>(
|
pub fn promote<R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
base: ThresholdKeys<C1>,
|
base: ThresholdKeys<C1>,
|
||||||
) -> (GeneratorPromotion<C1, C2>, GeneratorProof<C1>) {
|
) -> (Self, GeneratorProof<C1>) {
|
||||||
// Do a DLEqProof for the new generator
|
// Do a DLEqProof for the new generator
|
||||||
let proof = GeneratorProof {
|
let proof = GeneratorProof {
|
||||||
share: C2::generator() * base.secret_share().deref(),
|
share: C2::generator() * base.secret_share().deref(),
|
||||||
@@ -91,7 +85,7 @@ where
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
(GeneratorPromotion { base, proof, _c2: PhantomData::<C2> }, proof)
|
(Self { base, proof, _c2: PhantomData::<C2> }, proof)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete promotion by taking in the proofs from all other participants.
|
/// Complete promotion by taking in the proofs from all other participants.
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ pub const PARTICIPANTS: u16 = 5;
|
|||||||
pub const THRESHOLD: u16 = ((PARTICIPANTS * 2) / 3) + 1;
|
pub const THRESHOLD: u16 = ((PARTICIPANTS * 2) / 3) + 1;
|
||||||
|
|
||||||
/// Clone a map without a specific value.
|
/// Clone a map without a specific value.
|
||||||
pub fn clone_without<K: Clone + std::cmp::Eq + std::hash::Hash, V: Clone>(
|
pub fn clone_without<K: Clone + core::cmp::Eq + core::hash::Hash, V: Clone>(
|
||||||
map: &HashMap<K, V>,
|
map: &HashMap<K, V>,
|
||||||
without: &K,
|
without: &K,
|
||||||
) -> HashMap<K, V> {
|
) -> HashMap<K, V> {
|
||||||
@@ -40,7 +40,7 @@ pub fn clone_without<K: Clone + std::cmp::Eq + std::hash::Hash, V: Clone>(
|
|||||||
pub fn recover_key<C: Ciphersuite>(keys: &HashMap<Participant, ThresholdKeys<C>>) -> C::F {
|
pub fn recover_key<C: Ciphersuite>(keys: &HashMap<Participant, ThresholdKeys<C>>) -> C::F {
|
||||||
let first = keys.values().next().expect("no keys provided");
|
let first = keys.values().next().expect("no keys provided");
|
||||||
assert!(keys.len() >= first.params().t().into(), "not enough keys provided");
|
assert!(keys.len() >= first.params().t().into(), "not enough keys provided");
|
||||||
let included = keys.keys().cloned().collect::<Vec<_>>();
|
let included = keys.keys().copied().collect::<Vec<_>>();
|
||||||
|
|
||||||
let group_private = keys.iter().fold(C::F::ZERO, |accum, (i, keys)| {
|
let group_private = keys.iter().fold(C::F::ZERO, |accum, (i, keys)| {
|
||||||
accum + (lagrange::<C::F>(*i, &included) * keys.secret_share().deref())
|
accum + (lagrange::<C::F>(*i, &included) * keys.secret_share().deref())
|
||||||
@@ -95,6 +95,7 @@ pub fn test_ciphersuite<R: RngCore + CryptoRng, C: Ciphersuite>(rng: &mut R) {
|
|||||||
test_generator_promotion::<_, C>(rng);
|
test_generator_promotion::<_, C>(rng);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::tests_outside_test_module)]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_with_ristretto() {
|
fn test_with_ristretto() {
|
||||||
test_ciphersuite::<_, ciphersuite::Ristretto>(&mut rand_core::OsRng);
|
test_ciphersuite::<_, ciphersuite::Ristretto>(&mut rand_core::OsRng);
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ pub fn test_musig<R: RngCore + CryptoRng, C: Ciphersuite>(rng: &mut R) {
|
|||||||
const CONTEXT: &[u8] = b"MuSig Test";
|
const CONTEXT: &[u8] = b"MuSig Test";
|
||||||
|
|
||||||
// Empty signing set
|
// Empty signing set
|
||||||
assert!(musig::<C>(CONTEXT, &Zeroizing::new(C::F::ZERO), &[]).is_err());
|
musig::<C>(CONTEXT, &Zeroizing::new(C::F::ZERO), &[]).unwrap_err();
|
||||||
// Signing set we're not part of
|
// Signing set we're not part of
|
||||||
assert!(musig::<C>(CONTEXT, &Zeroizing::new(C::F::ZERO), &[C::generator()]).is_err());
|
musig::<C>(CONTEXT, &Zeroizing::new(C::F::ZERO), &[C::generator()]).unwrap_err();
|
||||||
|
|
||||||
// Test with n keys
|
// Test with n keys
|
||||||
{
|
{
|
||||||
@@ -55,7 +55,8 @@ pub fn test_musig<R: RngCore + CryptoRng, C: Ciphersuite>(rng: &mut R) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::tests_outside_test_module)]
|
||||||
#[test]
|
#[test]
|
||||||
fn musig_literal() {
|
fn musig_literal() {
|
||||||
test_musig::<_, ciphersuite::Ristretto>(&mut rand_core::OsRng)
|
test_musig::<_, ciphersuite::Ristretto>(&mut rand_core::OsRng);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use crate::{
|
|||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
|
||||||
struct AltGenerator<C: Ciphersuite> {
|
struct AltGenerator<C: Ciphersuite> {
|
||||||
_curve: PhantomData<C>,
|
curve: PhantomData<C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Ciphersuite> Ciphersuite for AltGenerator<C> {
|
impl<C: Ciphersuite> Ciphersuite for AltGenerator<C> {
|
||||||
|
|||||||
@@ -37,12 +37,12 @@ pub(crate) enum Re<G0: PrimeGroup, G1: PrimeGroup> {
|
|||||||
|
|
||||||
impl<G0: PrimeGroup, G1: PrimeGroup> Re<G0, G1> {
|
impl<G0: PrimeGroup, G1: PrimeGroup> Re<G0, G1> {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub(crate) fn R_default() -> Re<G0, G1> {
|
pub(crate) fn R_default() -> Self {
|
||||||
Re::R(G0::identity(), G1::identity())
|
Self::R(G0::identity(), G1::identity())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn e_default() -> Re<G0, G1> {
|
pub(crate) fn e_default() -> Self {
|
||||||
Re::e(G0::Scalar::ZERO)
|
Self::e(G0::Scalar::ZERO)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,13 +122,13 @@ where
|
|||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let mut R = original_R;
|
let mut R = original_R;
|
||||||
|
|
||||||
for i in ((actual + 1) .. (actual + RING_LEN + 1)).map(|i| i % RING_LEN) {
|
for i in ((actual + 1) ..= (actual + RING_LEN)).map(|i| i % RING_LEN) {
|
||||||
let e = Self::nonces(transcript.clone(), R);
|
let e = Self::nonces(transcript.clone(), R);
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
match Re_0 {
|
match Re_0 {
|
||||||
Re::R(ref mut R0_0, ref mut R1_0) => {
|
Re::R(ref mut R0_0, ref mut R1_0) => {
|
||||||
*R0_0 = R.0;
|
*R0_0 = R.0;
|
||||||
*R1_0 = R.1
|
*R1_0 = R.1;
|
||||||
}
|
}
|
||||||
Re::e(ref mut e_0) => *e_0 = e.0,
|
Re::e(ref mut e_0) => *e_0 = e.0,
|
||||||
}
|
}
|
||||||
@@ -144,15 +144,15 @@ where
|
|||||||
r.0.zeroize();
|
r.0.zeroize();
|
||||||
r.1.zeroize();
|
r.1.zeroize();
|
||||||
break;
|
break;
|
||||||
// Generate a decoy response
|
|
||||||
} else {
|
|
||||||
s[i] = (G0::Scalar::random(&mut *rng), G1::Scalar::random(&mut *rng));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate a decoy response
|
||||||
|
s[i] = (G0::Scalar::random(&mut *rng), G1::Scalar::random(&mut *rng));
|
||||||
|
|
||||||
R = Self::R(generators, s[i], ring[i], e);
|
R = Self::R(generators, s[i], ring[i], e);
|
||||||
}
|
}
|
||||||
|
|
||||||
Aos { Re_0, s }
|
Self { Re_0, s }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assumes the ring has already been transcripted in some form. Critically insecure if it hasn't
|
// Assumes the ring has already been transcripted in some form. Critically insecure if it hasn't
|
||||||
@@ -234,7 +234,7 @@ where
|
|||||||
match Re_0 {
|
match Re_0 {
|
||||||
Re::R(ref mut R0, ref mut R1) => {
|
Re::R(ref mut R0, ref mut R1) => {
|
||||||
*R0 = read_point(r)?;
|
*R0 = read_point(r)?;
|
||||||
*R1 = read_point(r)?
|
*R1 = read_point(r)?;
|
||||||
}
|
}
|
||||||
Re::e(ref mut e) => *e = read_scalar(r)?,
|
Re::e(ref mut e) => *e = read_scalar(r)?,
|
||||||
}
|
}
|
||||||
@@ -244,6 +244,6 @@ where
|
|||||||
*s = (read_scalar(r)?, read_scalar(r)?);
|
*s = (read_scalar(r)?, read_scalar(r)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Aos { Re_0, s })
|
Ok(Self { Re_0, s })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,44 +26,41 @@ pub(crate) enum BitSignature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BitSignature {
|
impl BitSignature {
|
||||||
pub(crate) const fn to_u8(&self) -> u8 {
|
pub(crate) fn to_u8(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
BitSignature::ClassicLinear => 0,
|
Self::ClassicLinear => 0,
|
||||||
BitSignature::ConciseLinear => 1,
|
Self::ConciseLinear => 1,
|
||||||
BitSignature::EfficientLinear => 2,
|
Self::EfficientLinear => 2,
|
||||||
BitSignature::CompromiseLinear => 3,
|
Self::CompromiseLinear => 3,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) const fn from(algorithm: u8) -> BitSignature {
|
pub(crate) fn from(algorithm: u8) -> Self {
|
||||||
match algorithm {
|
match algorithm {
|
||||||
0 => BitSignature::ClassicLinear,
|
0 => Self::ClassicLinear,
|
||||||
1 => BitSignature::ConciseLinear,
|
1 => Self::ConciseLinear,
|
||||||
2 => BitSignature::EfficientLinear,
|
2 => Self::EfficientLinear,
|
||||||
3 => BitSignature::CompromiseLinear,
|
3 => Self::CompromiseLinear,
|
||||||
_ => panic!("Unknown algorithm"),
|
_ => panic!("Unknown algorithm"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) const fn bits(&self) -> usize {
|
pub(crate) fn bits(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
BitSignature::ClassicLinear => 1,
|
Self::ClassicLinear | Self::EfficientLinear => 1,
|
||||||
BitSignature::ConciseLinear => 2,
|
Self::ConciseLinear | Self::CompromiseLinear => 2,
|
||||||
BitSignature::EfficientLinear => 1,
|
|
||||||
BitSignature::CompromiseLinear => 2,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) const fn ring_len(&self) -> usize {
|
pub(crate) fn ring_len(&self) -> usize {
|
||||||
|
#[allow(clippy::as_conversions, clippy::cast_possible_truncation)] // Needed for const
|
||||||
2_usize.pow(self.bits() as u32)
|
2_usize.pow(self.bits() as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn aos_form<G0: PrimeGroup, G1: PrimeGroup>(&self) -> Re<G0, G1> {
|
fn aos_form<G0: PrimeGroup, G1: PrimeGroup>(&self) -> Re<G0, G1> {
|
||||||
match self {
|
match self {
|
||||||
BitSignature::ClassicLinear => Re::e_default(),
|
Self::ClassicLinear | Self::ConciseLinear => Re::e_default(),
|
||||||
BitSignature::ConciseLinear => Re::e_default(),
|
Self::EfficientLinear | Self::CompromiseLinear => Re::R_default(),
|
||||||
BitSignature::EfficientLinear => Re::R_default(),
|
|
||||||
BitSignature::CompromiseLinear => Re::R_default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,7 +136,7 @@ where
|
|||||||
bits.zeroize();
|
bits.zeroize();
|
||||||
|
|
||||||
Self::shift(pow_2);
|
Self::shift(pow_2);
|
||||||
Bits { commitments, signature }
|
Self { commitments, signature }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn verify<R: RngCore + CryptoRng, T: Clone + Transcript>(
|
pub(crate) fn verify<R: RngCore + CryptoRng, T: Clone + Transcript>(
|
||||||
@@ -174,7 +171,7 @@ where
|
|||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
pub(crate) fn read<R: Read>(r: &mut R) -> std::io::Result<Self> {
|
pub(crate) fn read<R: Read>(r: &mut R) -> std::io::Result<Self> {
|
||||||
Ok(Bits {
|
Ok(Self {
|
||||||
commitments: (read_point(r)?, read_point(r)?),
|
commitments: (read_point(r)?, read_point(r)?),
|
||||||
signature: Aos::read(r, BitSignature::from(SIGNATURE).aos_form())?,
|
signature: Aos::read(r, BitSignature::from(SIGNATURE).aos_form())?,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
use std::io::{Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use rand_core::{RngCore, CryptoRng};
|
|
||||||
|
|
||||||
use zeroize::{Zeroize, Zeroizing};
|
use zeroize::{Zeroize, Zeroizing};
|
||||||
|
use rand_core::{RngCore, CryptoRng};
|
||||||
|
|
||||||
use digest::{Digest, HashMarker};
|
use digest::{Digest, HashMarker};
|
||||||
|
|
||||||
@@ -42,6 +41,7 @@ fn u8_from_bool(bit_ref: &mut bool) -> u8 {
|
|||||||
let bit_ref = black_box(bit_ref);
|
let bit_ref = black_box(bit_ref);
|
||||||
|
|
||||||
let mut bit = black_box(*bit_ref);
|
let mut bit = black_box(*bit_ref);
|
||||||
|
#[allow(clippy::as_conversions, clippy::cast_lossless)]
|
||||||
let res = black_box(bit as u8);
|
let res = black_box(bit as u8);
|
||||||
bit.zeroize();
|
bit.zeroize();
|
||||||
debug_assert!((res | 1) == 1);
|
debug_assert!((res | 1) == 1);
|
||||||
@@ -51,15 +51,15 @@ fn u8_from_bool(bit_ref: &mut bool) -> u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
pub(crate) fn read_point<R: Read, G: PrimeGroup>(r: &mut R) -> std::io::Result<G> {
|
pub(crate) fn read_point<R: Read, G: PrimeGroup>(r: &mut R) -> io::Result<G> {
|
||||||
let mut repr = G::Repr::default();
|
let mut repr = G::Repr::default();
|
||||||
r.read_exact(repr.as_mut())?;
|
r.read_exact(repr.as_mut())?;
|
||||||
let point = G::from_bytes(&repr);
|
let point = G::from_bytes(&repr);
|
||||||
let Some(point) = Option::<G>::from(point) else {
|
let Some(point) = Option::<G>::from(point) else {
|
||||||
Err(std::io::Error::new(std::io::ErrorKind::Other, "invalid point"))?
|
Err(io::Error::new(io::ErrorKind::Other, "invalid point"))?
|
||||||
};
|
};
|
||||||
if point.to_bytes().as_ref() != repr.as_ref() {
|
if point.to_bytes().as_ref() != repr.as_ref() {
|
||||||
Err(std::io::Error::new(std::io::ErrorKind::Other, "non-canonical point"))?;
|
Err(io::Error::new(io::ErrorKind::Other, "non-canonical point"))?;
|
||||||
}
|
}
|
||||||
Ok(point)
|
Ok(point)
|
||||||
}
|
}
|
||||||
@@ -78,11 +78,11 @@ pub struct Generators<G: PrimeGroup> {
|
|||||||
|
|
||||||
impl<G: PrimeGroup> Generators<G> {
|
impl<G: PrimeGroup> Generators<G> {
|
||||||
/// Create a new set of generators.
|
/// Create a new set of generators.
|
||||||
pub fn new(primary: G, alt: G) -> Option<Generators<G>> {
|
pub fn new(primary: G, alt: G) -> Option<Self> {
|
||||||
if primary == alt {
|
if primary == alt {
|
||||||
None?;
|
None?;
|
||||||
}
|
}
|
||||||
Some(Generators { primary, alt })
|
Some(Self { primary, alt })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transcript<T: Transcript>(&self, transcript: &mut T) {
|
fn transcript<T: Transcript>(&self, transcript: &mut T) {
|
||||||
@@ -335,7 +335,7 @@ where
|
|||||||
|
|
||||||
these_bits.zeroize();
|
these_bits.zeroize();
|
||||||
|
|
||||||
let proof = __DLEqProof { bits, remainder, poks };
|
let proof = Self { bits, remainder, poks };
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
proof.reconstruct_keys(),
|
proof.reconstruct_keys(),
|
||||||
(generators.0.primary * f.0.deref(), generators.1.primary * f.1.deref())
|
(generators.0.primary * f.0.deref(), generators.1.primary * f.1.deref())
|
||||||
@@ -412,10 +412,8 @@ where
|
|||||||
Self::transcript(transcript, generators, keys);
|
Self::transcript(transcript, generators, keys);
|
||||||
|
|
||||||
let batch_capacity = match BitSignature::from(SIGNATURE) {
|
let batch_capacity = match BitSignature::from(SIGNATURE) {
|
||||||
BitSignature::ClassicLinear => 3,
|
BitSignature::ClassicLinear | BitSignature::ConciseLinear => 3,
|
||||||
BitSignature::ConciseLinear => 3,
|
BitSignature::EfficientLinear | BitSignature::CompromiseLinear => (self.bits.len() + 1) * 3,
|
||||||
BitSignature::EfficientLinear => (self.bits.len() + 1) * 3,
|
|
||||||
BitSignature::CompromiseLinear => (self.bits.len() + 1) * 3,
|
|
||||||
};
|
};
|
||||||
let mut batch = (BatchVerifier::new(batch_capacity), BatchVerifier::new(batch_capacity));
|
let mut batch = (BatchVerifier::new(batch_capacity), BatchVerifier::new(batch_capacity));
|
||||||
|
|
||||||
@@ -439,7 +437,7 @@ where
|
|||||||
|
|
||||||
/// Write a Cross-Group Discrete Log Equality proof to a type satisfying std::io::Write.
|
/// Write a Cross-Group Discrete Log Equality proof to a type satisfying std::io::Write.
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
pub fn write<W: Write>(&self, w: &mut W) -> std::io::Result<()> {
|
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||||
for bit in &self.bits {
|
for bit in &self.bits {
|
||||||
bit.write(w)?;
|
bit.write(w)?;
|
||||||
}
|
}
|
||||||
@@ -452,7 +450,7 @@ where
|
|||||||
|
|
||||||
/// Read a Cross-Group Discrete Log Equality proof from a type satisfying std::io::Read.
|
/// Read a Cross-Group Discrete Log Equality proof from a type satisfying std::io::Read.
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
pub fn read<R: Read>(r: &mut R) -> std::io::Result<Self> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
let capacity = usize::try_from(G0::Scalar::CAPACITY.min(G1::Scalar::CAPACITY)).unwrap();
|
let capacity = usize::try_from(G0::Scalar::CAPACITY.min(G1::Scalar::CAPACITY)).unwrap();
|
||||||
let bits_per_group = BitSignature::from(SIGNATURE).bits();
|
let bits_per_group = BitSignature::from(SIGNATURE).bits();
|
||||||
|
|
||||||
@@ -466,6 +464,6 @@ where
|
|||||||
remainder = Some(Bits::read(r)?);
|
remainder = Some(Bits::read(r)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(__DLEqProof { bits, remainder, poks: (SchnorrPoK::read(r)?, SchnorrPoK::read(r)?) })
|
Ok(Self { bits, remainder, poks: (SchnorrPoK::read(r)?, SchnorrPoK::read(r)?) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use zeroize::Zeroize;
|
|||||||
use crate::cross_group::u8_from_bool;
|
use crate::cross_group::u8_from_bool;
|
||||||
|
|
||||||
/// Convert a uniform scalar into one usable on both fields, clearing the top bits as needed.
|
/// Convert a uniform scalar into one usable on both fields, clearing the top bits as needed.
|
||||||
|
#[must_use]
|
||||||
pub fn scalar_normalize<F0: PrimeFieldBits + Zeroize, F1: PrimeFieldBits>(
|
pub fn scalar_normalize<F0: PrimeFieldBits + Zeroize, F1: PrimeFieldBits>(
|
||||||
mut scalar: F0,
|
mut scalar: F0,
|
||||||
) -> (F0, F1) {
|
) -> (F0, F1) {
|
||||||
@@ -49,6 +50,7 @@ pub fn scalar_normalize<F0: PrimeFieldBits + Zeroize, F1: PrimeFieldBits>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to convert a scalar between fields. Returns None if the scalar isn't mutually valid.
|
/// Helper to convert a scalar between fields. Returns None if the scalar isn't mutually valid.
|
||||||
|
#[must_use]
|
||||||
pub fn scalar_convert<F0: PrimeFieldBits + Zeroize, F1: PrimeFieldBits>(
|
pub fn scalar_convert<F0: PrimeFieldBits + Zeroize, F1: PrimeFieldBits>(
|
||||||
mut scalar: F0,
|
mut scalar: F0,
|
||||||
) -> Option<F1> {
|
) -> Option<F1> {
|
||||||
@@ -60,6 +62,7 @@ pub fn scalar_convert<F0: PrimeFieldBits + Zeroize, F1: PrimeFieldBits>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a mutually valid scalar from bytes via bit truncation to not introduce bias.
|
/// Create a mutually valid scalar from bytes via bit truncation to not introduce bias.
|
||||||
|
#[must_use]
|
||||||
pub fn mutual_scalar_from_bytes<F0: PrimeFieldBits + Zeroize, F1: PrimeFieldBits>(
|
pub fn mutual_scalar_from_bytes<F0: PrimeFieldBits + Zeroize, F1: PrimeFieldBits>(
|
||||||
bytes: &[u8],
|
bytes: &[u8],
|
||||||
) -> (F0, F1) {
|
) -> (F0, F1) {
|
||||||
|
|||||||
@@ -47,13 +47,13 @@ where
|
|||||||
transcript: &mut T,
|
transcript: &mut T,
|
||||||
generator: G,
|
generator: G,
|
||||||
private_key: &Zeroizing<G::Scalar>,
|
private_key: &Zeroizing<G::Scalar>,
|
||||||
) -> SchnorrPoK<G> {
|
) -> Self {
|
||||||
let nonce = Zeroizing::new(G::Scalar::random(rng));
|
let nonce = Zeroizing::new(G::Scalar::random(rng));
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let R = generator * nonce.deref();
|
let R = generator * nonce.deref();
|
||||||
SchnorrPoK {
|
Self {
|
||||||
R,
|
R,
|
||||||
s: (SchnorrPoK::hra(transcript, generator, R, generator * private_key.deref()) *
|
s: (Self::hra(transcript, generator, R, generator * private_key.deref()) *
|
||||||
private_key.deref()) +
|
private_key.deref()) +
|
||||||
nonce.deref(),
|
nonce.deref(),
|
||||||
}
|
}
|
||||||
@@ -85,7 +85,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
pub fn read<R: Read>(r: &mut R) -> std::io::Result<SchnorrPoK<G>> {
|
pub fn read<R: Read>(r: &mut R) -> std::io::Result<Self> {
|
||||||
Ok(SchnorrPoK { R: read_point(r)?, s: read_scalar(r)? })
|
Ok(Self { R: read_point(r)?, s: read_scalar(r)? })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ where
|
|||||||
transcript: &mut T,
|
transcript: &mut T,
|
||||||
generators: &[G],
|
generators: &[G],
|
||||||
scalar: &Zeroizing<G::Scalar>,
|
scalar: &Zeroizing<G::Scalar>,
|
||||||
) -> DLEqProof<G> {
|
) -> Self {
|
||||||
let r = Zeroizing::new(G::Scalar::random(rng));
|
let r = Zeroizing::new(G::Scalar::random(rng));
|
||||||
|
|
||||||
transcript.domain_separate(b"dleq");
|
transcript.domain_separate(b"dleq");
|
||||||
@@ -144,7 +144,7 @@ where
|
|||||||
// r + ca
|
// r + ca
|
||||||
let s = (c * scalar.deref()) + r.deref();
|
let s = (c * scalar.deref()) + r.deref();
|
||||||
|
|
||||||
DLEqProof { c, s }
|
Self { c, s }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transcript a specific generator/nonce/point (G/R/A), as used when verifying a proof.
|
// Transcript a specific generator/nonce/point (G/R/A), as used when verifying a proof.
|
||||||
@@ -194,8 +194,8 @@ where
|
|||||||
|
|
||||||
/// Read a DLEq proof from something implementing Read.
|
/// Read a DLEq proof from something implementing Read.
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
pub fn read<R: Read>(r: &mut R) -> io::Result<DLEqProof<G>> {
|
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||||
Ok(DLEqProof { c: read_scalar(r)?, s: read_scalar(r)? })
|
Ok(Self { c: read_scalar(r)?, s: read_scalar(r)? })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize a DLEq proof to a `Vec<u8>`.
|
/// Serialize a DLEq proof to a `Vec<u8>`.
|
||||||
@@ -235,7 +235,7 @@ where
|
|||||||
transcript: &mut T,
|
transcript: &mut T,
|
||||||
generators: &[Vec<G>],
|
generators: &[Vec<G>],
|
||||||
scalars: &[Zeroizing<G::Scalar>],
|
scalars: &[Zeroizing<G::Scalar>],
|
||||||
) -> MultiDLEqProof<G> {
|
) -> Self {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
generators.len(),
|
generators.len(),
|
||||||
scalars.len(),
|
scalars.len(),
|
||||||
@@ -268,7 +268,7 @@ where
|
|||||||
s.push((c * scalar.deref()) + nonce.deref());
|
s.push((c * scalar.deref()) + nonce.deref());
|
||||||
}
|
}
|
||||||
|
|
||||||
MultiDLEqProof { c, s }
|
Self { c, s }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify each series of points share a discrete logarithm against their matching series of
|
/// Verify each series of points share a discrete logarithm against their matching series of
|
||||||
@@ -317,13 +317,13 @@ where
|
|||||||
|
|
||||||
/// Read a multi-DLEq proof from something implementing Read.
|
/// Read a multi-DLEq proof from something implementing Read.
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
pub fn read<R: Read>(r: &mut R, discrete_logs: usize) -> io::Result<MultiDLEqProof<G>> {
|
pub fn read<R: Read>(r: &mut R, discrete_logs: usize) -> io::Result<Self> {
|
||||||
let c = read_scalar(r)?;
|
let c = read_scalar(r)?;
|
||||||
let mut s = vec![];
|
let mut s = vec![];
|
||||||
for _ in 0 .. discrete_logs {
|
for _ in 0 .. discrete_logs {
|
||||||
s.push(read_scalar(r)?);
|
s.push(read_scalar(r)?);
|
||||||
}
|
}
|
||||||
Ok(MultiDLEqProof { c, s })
|
Ok(Self { c, s })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize a multi-DLEq proof to a `Vec<u8>`.
|
/// Serialize a multi-DLEq proof to a `Vec<u8>`.
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ fn test_scalar() {
|
|||||||
// The initial scalar should equal the new scalar with Ed25519's capacity
|
// The initial scalar should equal the new scalar with Ed25519's capacity
|
||||||
let mut initial_bytes = initial.to_repr().to_vec();
|
let mut initial_bytes = initial.to_repr().to_vec();
|
||||||
// Drop the first 4 bits to hit 252
|
// Drop the first 4 bits to hit 252
|
||||||
initial_bytes[0] &= 0b00001111;
|
initial_bytes[0] &= 0b0000_1111;
|
||||||
let k_bytes = k.to_repr().to_vec();
|
let k_bytes = k.to_repr().to_vec();
|
||||||
assert_eq!(initial_bytes, k_bytes);
|
assert_eq!(initial_bytes, k_bytes);
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ fn test_dleq() {
|
|||||||
assert!(proof
|
assert!(proof
|
||||||
.verify(
|
.verify(
|
||||||
&mut transcript(),
|
&mut transcript(),
|
||||||
generators[.. i].iter().cloned().rev().collect::<Vec<_>>().as_ref(),
|
generators[.. i].iter().copied().rev().collect::<Vec<_>>().as_ref(),
|
||||||
&keys[.. i]
|
&keys[.. i]
|
||||||
)
|
)
|
||||||
.is_err());
|
.is_err());
|
||||||
@@ -86,7 +86,7 @@ fn test_dleq() {
|
|||||||
.verify(
|
.verify(
|
||||||
&mut transcript(),
|
&mut transcript(),
|
||||||
&generators[.. i],
|
&generators[.. i],
|
||||||
keys[.. i].iter().cloned().rev().collect::<Vec<_>>().as_ref()
|
keys[.. i].iter().copied().rev().collect::<Vec<_>>().as_ref()
|
||||||
)
|
)
|
||||||
.is_err());
|
.is_err());
|
||||||
}
|
}
|
||||||
@@ -117,7 +117,7 @@ fn test_multi_dleq() {
|
|||||||
// 0: 0
|
// 0: 0
|
||||||
// 1: 1, 2
|
// 1: 1, 2
|
||||||
// 2: 2, 3, 4
|
// 2: 2, 3, 4
|
||||||
let key_generators = generators[i .. (i + i + 1)].to_vec();
|
let key_generators = generators[i ..= i + i].to_vec();
|
||||||
let mut these_pub_keys = vec![];
|
let mut these_pub_keys = vec![];
|
||||||
for generator in &key_generators {
|
for generator in &key_generators {
|
||||||
these_pub_keys.push(generator * key.deref());
|
these_pub_keys.push(generator * key.deref());
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ pub(crate) fn u8_from_bool(bit_ref: &mut bool) -> u8 {
|
|||||||
let bit_ref = black_box(bit_ref);
|
let bit_ref = black_box(bit_ref);
|
||||||
|
|
||||||
let mut bit = black_box(*bit_ref);
|
let mut bit = black_box(*bit_ref);
|
||||||
|
#[allow(clippy::as_conversions, clippy::cast_lossless)]
|
||||||
let res = black_box(bit as u8);
|
let res = black_box(bit as u8);
|
||||||
bit.zeroize();
|
bit.zeroize();
|
||||||
debug_assert!((res | 1) == 1);
|
debug_assert!((res | 1) == 1);
|
||||||
@@ -80,7 +81,7 @@ macro_rules! field {
|
|||||||
$DELTA: expr,
|
$DELTA: expr,
|
||||||
) => {
|
) => {
|
||||||
use core::{
|
use core::{
|
||||||
ops::{DerefMut, Add, AddAssign, Neg, Sub, SubAssign, Mul, MulAssign},
|
ops::{Add, AddAssign, Neg, Sub, SubAssign, Mul, MulAssign},
|
||||||
iter::{Sum, Product},
|
iter::{Sum, Product},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -139,6 +140,7 @@ macro_rules! field {
|
|||||||
|
|
||||||
impl $FieldName {
|
impl $FieldName {
|
||||||
/// Perform an exponentation.
|
/// Perform an exponentation.
|
||||||
|
#[must_use]
|
||||||
pub fn pow(&self, other: $FieldName) -> $FieldName {
|
pub fn pow(&self, other: $FieldName) -> $FieldName {
|
||||||
let mut table = [Self(Residue::ONE); 16];
|
let mut table = [Self(Residue::ONE); 16];
|
||||||
table[1] = *self;
|
table[1] = *self;
|
||||||
@@ -150,7 +152,7 @@ macro_rules! field {
|
|||||||
let mut bits = 0;
|
let mut bits = 0;
|
||||||
for (i, mut bit) in other.to_le_bits().iter_mut().rev().enumerate() {
|
for (i, mut bit) in other.to_le_bits().iter_mut().rev().enumerate() {
|
||||||
bits <<= 1;
|
bits <<= 1;
|
||||||
let mut bit = u8_from_bool(bit.deref_mut());
|
let mut bit = u8_from_bool(&mut bit);
|
||||||
bits |= bit;
|
bits |= bit;
|
||||||
bit.zeroize();
|
bit.zeroize();
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ pub struct FieldElement(pub(crate) ResidueType);
|
|||||||
impl DefaultIsZeroes for FieldElement {}
|
impl DefaultIsZeroes for FieldElement {}
|
||||||
|
|
||||||
// 2**448 - 2**224 - 1
|
// 2**448 - 2**224 - 1
|
||||||
pub(crate) const MODULUS: U448 = U448::from_be_hex(MODULUS_STR);
|
const MODULUS: U448 = U448::from_be_hex(MODULUS_STR);
|
||||||
|
|
||||||
const WIDE_MODULUS: U896 = U896::from_be_hex(concat!(
|
const WIDE_MODULUS: U896 = U896::from_be_hex(concat!(
|
||||||
"00000000000000000000000000000000000000000000000000000000",
|
"00000000000000000000000000000000000000000000000000000000",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#![allow(clippy::tests_outside_test_module)]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use core::{
|
use core::{
|
||||||
ops::{DerefMut, Add, AddAssign, Neg, Sub, SubAssign, Mul, MulAssign},
|
ops::{Add, AddAssign, Neg, Sub, SubAssign, Mul, MulAssign},
|
||||||
iter::Sum,
|
iter::Sum,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ impl ConstantTimeEq for Point {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Point {
|
impl PartialEq for Point {
|
||||||
fn eq(&self, other: &Point) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.ct_eq(other).into()
|
self.ct_eq(other).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ impl Eq for Point {}
|
|||||||
|
|
||||||
impl ConditionallySelectable for Point {
|
impl ConditionallySelectable for Point {
|
||||||
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
|
||||||
Point {
|
Self {
|
||||||
x: FieldElement::conditional_select(&a.x, &b.x, choice),
|
x: FieldElement::conditional_select(&a.x, &b.x, choice),
|
||||||
y: FieldElement::conditional_select(&a.y, &b.y, choice),
|
y: FieldElement::conditional_select(&a.y, &b.y, choice),
|
||||||
z: FieldElement::conditional_select(&a.z, &b.z, choice),
|
z: FieldElement::conditional_select(&a.z, &b.z, choice),
|
||||||
@@ -90,8 +90,9 @@ impl ConditionallySelectable for Point {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Add for Point {
|
impl Add for Point {
|
||||||
type Output = Point;
|
type Output = Self;
|
||||||
fn add(self, other: Self) -> Self {
|
fn add(self, other: Self) -> Self {
|
||||||
|
// add-2008-bbjlp
|
||||||
// 12 muls, 7 additions, 4 negations
|
// 12 muls, 7 additions, 4 negations
|
||||||
let xcp = self.x * other.x;
|
let xcp = self.x * other.x;
|
||||||
let ycp = self.y * other.y;
|
let ycp = self.y * other.y;
|
||||||
@@ -105,7 +106,7 @@ impl Add for Point {
|
|||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let G_ = B + E;
|
let G_ = B + E;
|
||||||
|
|
||||||
Point {
|
Self {
|
||||||
x: zcp * F * ((self.x + self.y) * (other.x + other.y) - xcp - ycp),
|
x: zcp * F * ((self.x + self.y) * (other.x + other.y) - xcp - ycp),
|
||||||
y: zcp * G_ * (ycp - xcp),
|
y: zcp * G_ * (ycp - xcp),
|
||||||
z: F * G_,
|
z: F * G_,
|
||||||
@@ -114,33 +115,33 @@ impl Add for Point {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AddAssign for Point {
|
impl AddAssign for Point {
|
||||||
fn add_assign(&mut self, other: Point) {
|
fn add_assign(&mut self, other: Self) {
|
||||||
*self = *self + other;
|
*self = *self + other;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Add<&Point> for Point {
|
impl Add<&Self> for Point {
|
||||||
type Output = Point;
|
type Output = Self;
|
||||||
fn add(self, other: &Point) -> Point {
|
fn add(self, other: &Self) -> Self {
|
||||||
self + *other
|
self + *other
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AddAssign<&Point> for Point {
|
impl AddAssign<&Self> for Point {
|
||||||
fn add_assign(&mut self, other: &Point) {
|
fn add_assign(&mut self, other: &Self) {
|
||||||
*self += *other;
|
*self += *other;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Neg for Point {
|
impl Neg for Point {
|
||||||
type Output = Point;
|
type Output = Self;
|
||||||
fn neg(self) -> Self {
|
fn neg(self) -> Self {
|
||||||
Point { x: -self.x, y: self.y, z: self.z }
|
Self { x: -self.x, y: self.y, z: self.z }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sub for Point {
|
impl Sub for Point {
|
||||||
type Output = Point;
|
type Output = Self;
|
||||||
#[allow(clippy::suspicious_arithmetic_impl)]
|
#[allow(clippy::suspicious_arithmetic_impl)]
|
||||||
fn sub(self, other: Self) -> Self {
|
fn sub(self, other: Self) -> Self {
|
||||||
self + other.neg()
|
self + other.neg()
|
||||||
@@ -148,20 +149,20 @@ impl Sub for Point {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SubAssign for Point {
|
impl SubAssign for Point {
|
||||||
fn sub_assign(&mut self, other: Point) {
|
fn sub_assign(&mut self, other: Self) {
|
||||||
*self = *self - other;
|
*self = *self - other;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sub<&Point> for Point {
|
impl Sub<&Self> for Point {
|
||||||
type Output = Point;
|
type Output = Self;
|
||||||
fn sub(self, other: &Point) -> Point {
|
fn sub(self, other: &Self) -> Self {
|
||||||
self - *other
|
self - *other
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SubAssign<&Point> for Point {
|
impl SubAssign<&Self> for Point {
|
||||||
fn sub_assign(&mut self, other: &Point) {
|
fn sub_assign(&mut self, other: &Self) {
|
||||||
*self -= *other;
|
*self -= *other;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,7 +181,7 @@ impl Group for Point {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn identity() -> Self {
|
fn identity() -> Self {
|
||||||
Point { x: FieldElement::ZERO, y: FieldElement::ONE, z: FieldElement::ONE }
|
Self { x: FieldElement::ZERO, y: FieldElement::ONE, z: FieldElement::ONE }
|
||||||
}
|
}
|
||||||
fn generator() -> Self {
|
fn generator() -> Self {
|
||||||
G
|
G
|
||||||
@@ -198,12 +199,12 @@ impl Group for Point {
|
|||||||
let F = xsq + ysq;
|
let F = xsq + ysq;
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let J = F - zsq.double();
|
let J = F - zsq.double();
|
||||||
Point { x: J * (xy.square() - xsq - ysq), y: F * (xsq - ysq), z: F * J }
|
Self { x: J * (xy.square() - xsq - ysq), y: F * (xsq - ysq), z: F * J }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sum<Point> for Point {
|
impl Sum<Self> for Point {
|
||||||
fn sum<I: Iterator<Item = Point>>(iter: I) -> Point {
|
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
|
||||||
let mut res = Self::identity();
|
let mut res = Self::identity();
|
||||||
for i in iter {
|
for i in iter {
|
||||||
res += i;
|
res += i;
|
||||||
@@ -212,17 +213,17 @@ impl Sum<Point> for Point {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Sum<&'a Point> for Point {
|
impl<'a> Sum<&'a Self> for Point {
|
||||||
fn sum<I: Iterator<Item = &'a Point>>(iter: I) -> Point {
|
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
|
||||||
Point::sum(iter.cloned())
|
Self::sum(iter.copied())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mul<Scalar> for Point {
|
impl Mul<Scalar> for Point {
|
||||||
type Output = Point;
|
type Output = Self;
|
||||||
fn mul(self, mut other: Scalar) -> Point {
|
fn mul(self, mut other: Scalar) -> Self {
|
||||||
// Precompute the optimal amount that's a multiple of 2
|
// Precompute the optimal amount that's a multiple of 2
|
||||||
let mut table = [Point::identity(); 16];
|
let mut table = [Self::identity(); 16];
|
||||||
table[1] = self;
|
table[1] = self;
|
||||||
for i in 2 .. 16 {
|
for i in 2 .. 16 {
|
||||||
table[i] = table[i - 1] + self;
|
table[i] = table[i - 1] + self;
|
||||||
@@ -232,7 +233,7 @@ impl Mul<Scalar> for Point {
|
|||||||
let mut bits = 0;
|
let mut bits = 0;
|
||||||
for (i, mut bit) in other.to_le_bits().iter_mut().rev().enumerate() {
|
for (i, mut bit) in other.to_le_bits().iter_mut().rev().enumerate() {
|
||||||
bits <<= 1;
|
bits <<= 1;
|
||||||
let mut bit = u8_from_bool(bit.deref_mut());
|
let mut bit = u8_from_bool(&mut bit);
|
||||||
bits |= bit;
|
bits |= bit;
|
||||||
bit.zeroize();
|
bit.zeroize();
|
||||||
|
|
||||||
@@ -258,8 +259,8 @@ impl MulAssign<Scalar> for Point {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Mul<&Scalar> for Point {
|
impl Mul<&Scalar> for Point {
|
||||||
type Output = Point;
|
type Output = Self;
|
||||||
fn mul(self, other: &Scalar) -> Point {
|
fn mul(self, other: &Scalar) -> Self {
|
||||||
self * *other
|
self * *other
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -291,14 +292,14 @@ impl GroupEncoding for Point {
|
|||||||
recover_x(y).and_then(|mut x| {
|
recover_x(y).and_then(|mut x| {
|
||||||
x.conditional_negate(x.is_odd().ct_eq(&!sign));
|
x.conditional_negate(x.is_odd().ct_eq(&!sign));
|
||||||
let not_negative_zero = !(x.is_zero() & sign);
|
let not_negative_zero = !(x.is_zero() & sign);
|
||||||
let point = Point { x, y, z: FieldElement::ONE };
|
let point = Self { x, y, z: FieldElement::ONE };
|
||||||
CtOption::new(point, not_negative_zero & point.is_torsion_free())
|
CtOption::new(point, not_negative_zero & point.is_torsion_free())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
|
fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
|
||||||
Point::from_bytes(bytes)
|
Self::from_bytes(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_bytes(&self) -> Self::Repr {
|
fn to_bytes(&self) -> Self::Repr {
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ type ResidueType = Residue<ScalarModulus, { ScalarModulus::LIMBS }>;
|
|||||||
|
|
||||||
/// Ed448 Scalar field element.
|
/// Ed448 Scalar field element.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
|
||||||
pub struct Scalar(pub(crate) ResidueType);
|
pub struct Scalar(ResidueType);
|
||||||
|
|
||||||
impl DefaultIsZeroes for Scalar {}
|
impl DefaultIsZeroes for Scalar {}
|
||||||
|
|
||||||
// 2**446 - 13818066809895115352007386748515426880336692474882178609894547503885
|
// 2**446 - 13818066809895115352007386748515426880336692474882178609894547503885
|
||||||
pub(crate) const MODULUS: U448 = U448::from_be_hex(MODULUS_STR);
|
const MODULUS: U448 = U448::from_be_hex(MODULUS_STR);
|
||||||
|
|
||||||
const WIDE_MODULUS: U896 = U896::from_be_hex(concat!(
|
const WIDE_MODULUS: U896 = U896::from_be_hex(concat!(
|
||||||
"00000000000000000000000000000000000000000000000000000000",
|
"00000000000000000000000000000000000000000000000000000000",
|
||||||
@@ -53,9 +53,9 @@ field!(
|
|||||||
|
|
||||||
impl Scalar {
|
impl Scalar {
|
||||||
/// Perform a wide reduction to obtain a non-biased Scalar.
|
/// Perform a wide reduction to obtain a non-biased Scalar.
|
||||||
pub fn wide_reduce(bytes: [u8; 114]) -> Scalar {
|
pub fn wide_reduce(bytes: [u8; 114]) -> Self {
|
||||||
let wide = U1024::from_le_slice(&[bytes.as_ref(), &[0; 14]].concat());
|
let wide = U1024::from_le_slice(&[bytes.as_ref(), &[0; 14]].concat());
|
||||||
Scalar(Residue::new(&U448::from_le_slice(
|
Self(Residue::new(&U448::from_le_slice(
|
||||||
&wide.rem(&WIDE_REDUCTION_MODULUS).to_le_bytes()[.. 56],
|
&wide.rem(&WIDE_REDUCTION_MODULUS).to_le_bytes()[.. 56],
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,9 +39,10 @@ pub fn test_add<F: Field>() {
|
|||||||
|
|
||||||
/// Perform basic tests on sum.
|
/// Perform basic tests on sum.
|
||||||
pub fn test_sum<F: Field>() {
|
pub fn test_sum<F: Field>() {
|
||||||
assert_eq!((&[] as &[F]).iter().sum::<F>(), F::ZERO, "[].sum() != 0");
|
let empty_slice: &[F] = &[];
|
||||||
assert_eq!([F::ZERO].iter().sum::<F>(), F::ZERO, "[0].sum() != 0");
|
assert_eq!(empty_slice.iter().sum::<F>(), F::ZERO, "[].sum() != 0");
|
||||||
assert_eq!([F::ONE].iter().sum::<F>(), F::ONE, "[1].sum() != 1");
|
assert_eq!(core::iter::once(F::ZERO).sum::<F>(), F::ZERO, "[0].sum() != 0");
|
||||||
|
assert_eq!(core::iter::once(F::ONE).sum::<F>(), F::ONE, "[1].sum() != 1");
|
||||||
|
|
||||||
let two = F::ONE + F::ONE;
|
let two = F::ONE + F::ONE;
|
||||||
assert_eq!([F::ONE, F::ONE].iter().sum::<F>(), two, "[1, 1].sum() != 2");
|
assert_eq!([F::ONE, F::ONE].iter().sum::<F>(), two, "[1, 1].sum() != 2");
|
||||||
@@ -79,9 +80,10 @@ pub fn test_mul<F: Field>() {
|
|||||||
|
|
||||||
/// Perform basic tests on product.
|
/// Perform basic tests on product.
|
||||||
pub fn test_product<F: Field>() {
|
pub fn test_product<F: Field>() {
|
||||||
assert_eq!((&[] as &[F]).iter().product::<F>(), F::ONE, "[].product() != 1");
|
let empty_slice: &[F] = &[];
|
||||||
assert_eq!([F::ZERO].iter().product::<F>(), F::ZERO, "[0].product() != 0");
|
assert_eq!(empty_slice.iter().product::<F>(), F::ONE, "[].product() != 1");
|
||||||
assert_eq!([F::ONE].iter().product::<F>(), F::ONE, "[1].product() != 1");
|
assert_eq!(core::iter::once(F::ZERO).product::<F>(), F::ZERO, "[0].product() != 0");
|
||||||
|
assert_eq!(core::iter::once(F::ONE).product::<F>(), F::ONE, "[1].product() != 1");
|
||||||
|
|
||||||
assert_eq!([F::ONE, F::ONE].iter().product::<F>(), F::ONE, "[1, 1].product() != 2");
|
assert_eq!([F::ONE, F::ONE].iter().product::<F>(), F::ONE, "[1, 1].product() != 2");
|
||||||
let two = F::ONE + F::ONE;
|
let two = F::ONE + F::ONE;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#![allow(clippy::tests_outside_test_module)]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
|
|||||||
@@ -97,8 +97,8 @@ mod sealed {
|
|||||||
impl Transcript for IetfTranscript {
|
impl Transcript for IetfTranscript {
|
||||||
type Challenge = Vec<u8>;
|
type Challenge = Vec<u8>;
|
||||||
|
|
||||||
fn new(_: &'static [u8]) -> IetfTranscript {
|
fn new(_: &'static [u8]) -> Self {
|
||||||
IetfTranscript(vec![])
|
Self(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn domain_separate(&mut self, _: &[u8]) {}
|
fn domain_separate(&mut self, _: &[u8]) {}
|
||||||
@@ -147,8 +147,8 @@ pub type IetfSchnorr<C, H> = Schnorr<C, IetfTranscript, H>;
|
|||||||
|
|
||||||
impl<C: Curve, T: Sync + Clone + Debug + Transcript, H: Hram<C>> Schnorr<C, T, H> {
|
impl<C: Curve, T: Sync + Clone + Debug + Transcript, H: Hram<C>> Schnorr<C, T, H> {
|
||||||
/// Construct a Schnorr algorithm continuing the specified transcript.
|
/// Construct a Schnorr algorithm continuing the specified transcript.
|
||||||
pub fn new(transcript: T) -> Schnorr<C, T, H> {
|
pub fn new(transcript: T) -> Self {
|
||||||
Schnorr { transcript, c: None, _hram: PhantomData }
|
Self { transcript, c: None, _hram: PhantomData }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,8 +156,8 @@ impl<C: Curve, H: Hram<C>> IetfSchnorr<C, H> {
|
|||||||
/// Construct a IETF-compatible Schnorr algorithm.
|
/// Construct a IETF-compatible Schnorr algorithm.
|
||||||
///
|
///
|
||||||
/// Please see the `IetfSchnorr` documentation for the full details of this.
|
/// Please see the `IetfSchnorr` documentation for the full details of this.
|
||||||
pub fn ietf() -> IetfSchnorr<C, H> {
|
pub fn ietf() -> Self {
|
||||||
Schnorr::new(IetfTranscript(vec![]))
|
Self::new(IetfTranscript(vec![]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ pub(crate) struct Nonce<C: Curve>(pub(crate) [Zeroizing<C::F>; 2]);
|
|||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
pub(crate) struct GeneratorCommitments<C: Curve>(pub(crate) [C::G; 2]);
|
pub(crate) struct GeneratorCommitments<C: Curve>(pub(crate) [C::G; 2]);
|
||||||
impl<C: Curve> GeneratorCommitments<C> {
|
impl<C: Curve> GeneratorCommitments<C> {
|
||||||
fn read<R: Read>(reader: &mut R) -> io::Result<GeneratorCommitments<C>> {
|
fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
|
||||||
Ok(GeneratorCommitments([<C as Curve>::read_G(reader)?, <C as Curve>::read_G(reader)?]))
|
Ok(Self([<C as Curve>::read_G(reader)?, <C as Curve>::read_G(reader)?]))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||||
@@ -82,7 +82,7 @@ impl<C: Curve> NonceCommitments<C> {
|
|||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
secret_share: &Zeroizing<C::F>,
|
secret_share: &Zeroizing<C::F>,
|
||||||
generators: &[C::G],
|
generators: &[C::G],
|
||||||
) -> (Nonce<C>, NonceCommitments<C>) {
|
) -> (Nonce<C>, Self) {
|
||||||
let nonce = Nonce::<C>([
|
let nonce = Nonce::<C>([
|
||||||
C::random_nonce(secret_share, &mut *rng),
|
C::random_nonce(secret_share, &mut *rng),
|
||||||
C::random_nonce(secret_share, &mut *rng),
|
C::random_nonce(secret_share, &mut *rng),
|
||||||
@@ -96,11 +96,11 @@ impl<C: Curve> NonceCommitments<C> {
|
|||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
(nonce, NonceCommitments { generators: commitments })
|
(nonce, Self { generators: commitments })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read<R: Read>(reader: &mut R, generators: &[C::G]) -> io::Result<NonceCommitments<C>> {
|
fn read<R: Read>(reader: &mut R, generators: &[C::G]) -> io::Result<Self> {
|
||||||
Ok(NonceCommitments {
|
Ok(Self {
|
||||||
generators: (0 .. generators.len())
|
generators: (0 .. generators.len())
|
||||||
.map(|_| GeneratorCommitments::read(reader))
|
.map(|_| GeneratorCommitments::read(reader))
|
||||||
.collect::<Result<_, _>>()?,
|
.collect::<Result<_, _>>()?,
|
||||||
@@ -146,7 +146,7 @@ impl<C: Curve> Commitments<C> {
|
|||||||
secret_share: &Zeroizing<C::F>,
|
secret_share: &Zeroizing<C::F>,
|
||||||
planned_nonces: &[Vec<C::G>],
|
planned_nonces: &[Vec<C::G>],
|
||||||
context: &[u8],
|
context: &[u8],
|
||||||
) -> (Vec<Nonce<C>>, Commitments<C>) {
|
) -> (Vec<Nonce<C>>, Self) {
|
||||||
let mut nonces = vec![];
|
let mut nonces = vec![];
|
||||||
let mut commitments = vec![];
|
let mut commitments = vec![];
|
||||||
|
|
||||||
@@ -168,18 +168,18 @@ impl<C: Curve> Commitments<C> {
|
|||||||
commitments.push(these_commitments);
|
commitments.push(these_commitments);
|
||||||
}
|
}
|
||||||
|
|
||||||
let dleq = if !dleq_generators.is_empty() {
|
let dleq = if dleq_generators.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
Some(MultiDLEqProof::prove(
|
Some(MultiDLEqProof::prove(
|
||||||
rng,
|
rng,
|
||||||
&mut dleq_transcript::<T>(context),
|
&mut dleq_transcript::<T>(context),
|
||||||
&dleq_generators,
|
&dleq_generators,
|
||||||
&dleq_nonces,
|
&dleq_nonces,
|
||||||
))
|
))
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
(nonces, Commitments { nonces: commitments, dleq })
|
(nonces, Self { nonces: commitments, dleq })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn transcript<T: Transcript>(&self, t: &mut T) {
|
pub(crate) fn transcript<T: Transcript>(&self, t: &mut T) {
|
||||||
@@ -219,17 +219,17 @@ impl<C: Curve> Commitments<C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let dleq = if !dleq_generators.is_empty() {
|
let dleq = if dleq_generators.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
let dleq = MultiDLEqProof::read(reader, dleq_generators.len())?;
|
let dleq = MultiDLEqProof::read(reader, dleq_generators.len())?;
|
||||||
dleq
|
dleq
|
||||||
.verify(&mut dleq_transcript::<T>(context), &dleq_generators, &dleq_nonces)
|
.verify(&mut dleq_transcript::<T>(context), &dleq_generators, &dleq_nonces)
|
||||||
.map_err(|_| io::Error::new(io::ErrorKind::Other, "invalid DLEq proof"))?;
|
.map_err(|_| io::Error::new(io::ErrorKind::Other, "invalid DLEq proof"))?;
|
||||||
Some(dleq)
|
Some(dleq)
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Commitments { nonces, dleq })
|
Ok(Self { nonces, dleq })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
pub(crate) fn write<W: Write>(&self, writer: &mut W) -> io::Result<()> {
|
||||||
@@ -256,7 +256,7 @@ impl<C: Curve> BindingFactor<C> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn calculate_binding_factors<T: Clone + Transcript>(&mut self, transcript: &mut T) {
|
pub(crate) fn calculate_binding_factors<T: Clone + Transcript>(&mut self, transcript: &mut T) {
|
||||||
for (l, binding) in self.0.iter_mut() {
|
for (l, binding) in &mut self.0 {
|
||||||
let mut transcript = transcript.clone();
|
let mut transcript = transcript.clone();
|
||||||
transcript.append_message(b"participant", C::F::from(u64::from(u16::from(*l))).to_repr());
|
transcript.append_message(b"participant", C::F::from(u64::from(u16::from(*l))).to_repr());
|
||||||
// It *should* be perfectly fine to reuse a binding factor for multiple nonces
|
// It *should* be perfectly fine to reuse a binding factor for multiple nonces
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ struct Params<C: Curve, A: Algorithm<C>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Curve, A: Algorithm<C>> Params<C, A> {
|
impl<C: Curve, A: Algorithm<C>> Params<C, A> {
|
||||||
fn new(algorithm: A, keys: ThresholdKeys<C>) -> Params<C, A> {
|
fn new(algorithm: A, keys: ThresholdKeys<C>) -> Self {
|
||||||
Params { algorithm, keys }
|
Self { algorithm, keys }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn multisig_params(&self) -> ThresholdParams {
|
fn multisig_params(&self) -> ThresholdParams {
|
||||||
@@ -111,8 +111,8 @@ pub struct AlgorithmMachine<C: Curve, A: Algorithm<C>> {
|
|||||||
|
|
||||||
impl<C: Curve, A: Algorithm<C>> AlgorithmMachine<C, A> {
|
impl<C: Curve, A: Algorithm<C>> AlgorithmMachine<C, A> {
|
||||||
/// Creates a new machine to generate a signature with the specified keys.
|
/// Creates a new machine to generate a signature with the specified keys.
|
||||||
pub fn new(algorithm: A, keys: ThresholdKeys<C>) -> AlgorithmMachine<C, A> {
|
pub fn new(algorithm: A, keys: ThresholdKeys<C>) -> Self {
|
||||||
AlgorithmMachine { params: Params::new(algorithm, keys) }
|
Self { params: Params::new(algorithm, keys) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn seeded_preprocess(
|
fn seeded_preprocess(
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ pub const PARTICIPANTS: u16 = 5;
|
|||||||
pub const THRESHOLD: u16 = ((PARTICIPANTS * 2) / 3) + 1;
|
pub const THRESHOLD: u16 = ((PARTICIPANTS * 2) / 3) + 1;
|
||||||
|
|
||||||
/// Clone a map without a specific value.
|
/// Clone a map without a specific value.
|
||||||
pub fn clone_without<K: Clone + std::cmp::Eq + std::hash::Hash, V: Clone>(
|
pub fn clone_without<K: Clone + core::cmp::Eq + core::hash::Hash, V: Clone>(
|
||||||
map: &HashMap<K, V>,
|
map: &HashMap<K, V>,
|
||||||
without: &K,
|
without: &K,
|
||||||
) -> HashMap<K, V> {
|
) -> HashMap<K, V> {
|
||||||
@@ -57,11 +57,7 @@ pub fn algorithm_machines<R: RngCore, C: Curve, A: Algorithm<C>>(
|
|||||||
keys
|
keys
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(i, keys)| {
|
.filter_map(|(i, keys)| {
|
||||||
if included.contains(i) {
|
included.contains(i).then(|| (*i, AlgorithmMachine::new(algorithm.clone(), keys.clone())))
|
||||||
Some((*i, AlgorithmMachine::new(algorithm.clone(), keys.clone())))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@@ -177,8 +173,8 @@ pub fn sign<R: RngCore + CryptoRng, M: PreprocessMachine>(
|
|||||||
machines,
|
machines,
|
||||||
|rng, machines| {
|
|rng, machines| {
|
||||||
// Cache and rebuild half of the machines
|
// Cache and rebuild half of the machines
|
||||||
let mut included = machines.keys().cloned().collect::<Vec<_>>();
|
let included = machines.keys().copied().collect::<Vec<_>>();
|
||||||
for i in included.drain(..) {
|
for i in included {
|
||||||
if (rng.next_u64() % 2) == 0 {
|
if (rng.next_u64() % 2) == 0 {
|
||||||
let cache = machines.remove(&i).unwrap().cache();
|
let cache = machines.remove(&i).unwrap().cache();
|
||||||
machines.insert(
|
machines.insert(
|
||||||
@@ -208,13 +204,13 @@ pub fn test_schnorr_with_keys<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
|
|||||||
/// Test a basic Schnorr signature.
|
/// Test a basic Schnorr signature.
|
||||||
pub fn test_schnorr<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(rng: &mut R) {
|
pub fn test_schnorr<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(rng: &mut R) {
|
||||||
let keys = key_gen(&mut *rng);
|
let keys = key_gen(&mut *rng);
|
||||||
test_schnorr_with_keys::<_, _, H>(&mut *rng, keys)
|
test_schnorr_with_keys::<_, _, H>(&mut *rng, keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test a basic Schnorr signature, yet with MuSig.
|
/// Test a basic Schnorr signature, yet with MuSig.
|
||||||
pub fn test_musig_schnorr<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(rng: &mut R) {
|
pub fn test_musig_schnorr<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(rng: &mut R) {
|
||||||
let keys = musig_key_gen(&mut *rng);
|
let keys = musig_key_gen(&mut *rng);
|
||||||
test_schnorr_with_keys::<_, _, H>(&mut *rng, keys)
|
test_schnorr_with_keys::<_, _, H>(&mut *rng, keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test an offset Schnorr signature.
|
/// Test an offset Schnorr signature.
|
||||||
@@ -226,7 +222,7 @@ pub fn test_offset_schnorr<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(rng: &m
|
|||||||
|
|
||||||
let offset = C::F::from(5);
|
let offset = C::F::from(5);
|
||||||
let offset_key = group_key + (C::generator() * offset);
|
let offset_key = group_key + (C::generator() * offset);
|
||||||
for (_, keys) in keys.iter_mut() {
|
for keys in keys.values_mut() {
|
||||||
*keys = keys.offset(offset);
|
*keys = keys.offset(offset);
|
||||||
assert_eq!(keys.group_key(), offset_key);
|
assert_eq!(keys.group_key(), offset_key);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ struct MultiNonce<C: Curve> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Curve> MultiNonce<C> {
|
impl<C: Curve> MultiNonce<C> {
|
||||||
fn new() -> MultiNonce<C> {
|
fn new() -> Self {
|
||||||
MultiNonce {
|
Self {
|
||||||
transcript: RecommendedTranscript::new(b"FROST MultiNonce Algorithm Test"),
|
transcript: RecommendedTranscript::new(b"FROST MultiNonce Algorithm Test"),
|
||||||
nonces: None,
|
nonces: None,
|
||||||
}
|
}
|
||||||
@@ -173,16 +173,10 @@ pub fn test_invalid_commitment<R: RngCore + CryptoRng, C: Curve>(rng: &mut R) {
|
|||||||
let mut preprocess = preprocesses.remove(&faulty).unwrap();
|
let mut preprocess = preprocesses.remove(&faulty).unwrap();
|
||||||
|
|
||||||
// Mutate one of the commitments
|
// Mutate one of the commitments
|
||||||
let nonce =
|
let nonce = &mut preprocess.commitments.nonces[usize::try_from(rng.next_u64()).unwrap() % 2];
|
||||||
preprocess.commitments.nonces.get_mut(usize::try_from(rng.next_u64()).unwrap() % 2).unwrap();
|
|
||||||
let generators_len = nonce.generators.len();
|
let generators_len = nonce.generators.len();
|
||||||
*nonce
|
nonce.generators[usize::try_from(rng.next_u64()).unwrap() % generators_len].0
|
||||||
.generators
|
[usize::try_from(rng.next_u64()).unwrap() % 2] = C::G::random(&mut *rng);
|
||||||
.get_mut(usize::try_from(rng.next_u64()).unwrap() % generators_len)
|
|
||||||
.unwrap()
|
|
||||||
.0
|
|
||||||
.get_mut(usize::try_from(rng.next_u64()).unwrap() % 2)
|
|
||||||
.unwrap() = C::G::random(&mut *rng);
|
|
||||||
|
|
||||||
// The commitments are validated at time of deserialization (read_preprocess)
|
// The commitments are validated at time of deserialization (read_preprocess)
|
||||||
// Accordingly, serialize it and read it again to make sure that errors
|
// Accordingly, serialize it and read it again to make sure that errors
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use std::str::FromStr;
|
use core::str::FromStr;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
@@ -45,11 +45,12 @@ pub struct Vectors {
|
|||||||
// Vectors are expected to be formatted per the IETF proof of concept
|
// Vectors are expected to be formatted per the IETF proof of concept
|
||||||
// The included vectors are direcly from
|
// The included vectors are direcly from
|
||||||
// https://github.com/cfrg/draft-irtf-cfrg-frost/tree/draft-irtf-cfrg-frost-11/poc
|
// https://github.com/cfrg/draft-irtf-cfrg-frost/tree/draft-irtf-cfrg-frost-11/poc
|
||||||
|
#[allow(clippy::fallible_impl_from)]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
impl From<serde_json::Value> for Vectors {
|
impl From<serde_json::Value> for Vectors {
|
||||||
fn from(value: serde_json::Value) -> Vectors {
|
fn from(value: serde_json::Value) -> Self {
|
||||||
let to_str = |value: &serde_json::Value| value.as_str().unwrap().to_string();
|
let to_str = |value: &serde_json::Value| value.as_str().unwrap().to_string();
|
||||||
Vectors {
|
Self {
|
||||||
threshold: u16::from_str(value["config"]["NUM_PARTICIPANTS"].as_str().unwrap()).unwrap(),
|
threshold: u16::from_str(value["config"]["NUM_PARTICIPANTS"].as_str().unwrap()).unwrap(),
|
||||||
|
|
||||||
group_secret: to_str(&value["inputs"]["group_secret_key"]),
|
group_secret: to_str(&value["inputs"]["group_secret_key"]),
|
||||||
@@ -166,8 +167,9 @@ pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut commitments = HashMap::new();
|
let mut commitments = HashMap::new();
|
||||||
let mut machines = machines
|
#[allow(clippy::needless_collect)] // Fails to compile without it due to borrow checking
|
||||||
.drain(..)
|
let machines = machines
|
||||||
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(c, (i, machine))| {
|
.map(|(c, (i, machine))| {
|
||||||
let nonce = |i| {
|
let nonce = |i| {
|
||||||
@@ -224,8 +226,8 @@ pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let mut shares = HashMap::new();
|
let mut shares = HashMap::new();
|
||||||
let mut machines = machines
|
let machines = machines
|
||||||
.drain(..)
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(c, (i, machine))| {
|
.map(|(c, (i, machine))| {
|
||||||
let (machine, share) = machine
|
let (machine, share) = machine
|
||||||
@@ -244,7 +246,7 @@ pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
|
|||||||
})
|
})
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
|
|
||||||
for (i, machine) in machines.drain() {
|
for (i, machine) in machines {
|
||||||
let sig = machine.complete(clone_without(&shares, i)).unwrap();
|
let sig = machine.complete(clone_without(&shares, i)).unwrap();
|
||||||
let mut serialized = sig.R.to_bytes().as_ref().to_vec();
|
let mut serialized = sig.R.to_bytes().as_ref().to_vec();
|
||||||
serialized.extend(sig.s.to_repr().as_ref());
|
serialized.extend(sig.s.to_repr().as_ref());
|
||||||
@@ -265,7 +267,7 @@ pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||||
dest.copy_from_slice(&self.0.remove(0))
|
dest.copy_from_slice(&self.0.remove(0));
|
||||||
}
|
}
|
||||||
fn try_fill_bytes(&mut self, _: &mut [u8]) -> Result<(), rand_core::Error> {
|
fn try_fill_bytes(&mut self, _: &mut [u8]) -> Result<(), rand_core::Error> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
@@ -347,7 +349,7 @@ pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
|
|||||||
machines.push((i, AlgorithmMachine::new(IetfSchnorr::<C, H>::ietf(), keys[i].clone())));
|
machines.push((i, AlgorithmMachine::new(IetfSchnorr::<C, H>::ietf(), keys[i].clone())));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i, machine) in machines.drain(..) {
|
for (i, machine) in machines {
|
||||||
let (_, preprocess) = machine.preprocess(&mut frosts.clone());
|
let (_, preprocess) = machine.preprocess(&mut frosts.clone());
|
||||||
|
|
||||||
// Calculate the expected nonces
|
// Calculate the expected nonces
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ fn flat<Id: Copy + Zeroize, G: Group + Zeroize>(
|
|||||||
where
|
where
|
||||||
<G as Group>::Scalar: PrimeFieldBits + Zeroize,
|
<G as Group>::Scalar: PrimeFieldBits + Zeroize,
|
||||||
{
|
{
|
||||||
Zeroizing::new(slice.iter().flat_map(|pairs| pairs.1.iter()).cloned().collect::<Vec<_>>())
|
Zeroizing::new(slice.iter().flat_map(|pairs| pairs.1.iter()).copied().collect::<Vec<_>>())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A batch verifier intended to verify a series of statements are each equivalent to zero.
|
/// A batch verifier intended to verify a series of statements are each equivalent to zero.
|
||||||
@@ -35,9 +35,10 @@ where
|
|||||||
<G as Group>::Scalar: PrimeFieldBits + Zeroize,
|
<G as Group>::Scalar: PrimeFieldBits + Zeroize,
|
||||||
{
|
{
|
||||||
/// Create a new batch verifier, expected to verify the following amount of statements.
|
/// Create a new batch verifier, expected to verify the following amount of statements.
|
||||||
/// This is a size hint and is not required to be accurate.
|
///
|
||||||
pub fn new(capacity: usize) -> BatchVerifier<Id, G> {
|
/// `capacity` is a size hint and is not required to be accurate.
|
||||||
BatchVerifier(Zeroizing::new(Vec::with_capacity(capacity)))
|
pub fn new(capacity: usize) -> Self {
|
||||||
|
Self(Zeroizing::new(Vec::with_capacity(capacity)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Queue a statement for batch verification.
|
/// Queue a statement for batch verification.
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
use core::ops::DerefMut;
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
@@ -39,6 +38,7 @@ fn u8_from_bool(bit_ref: &mut bool) -> u8 {
|
|||||||
let bit_ref = black_box(bit_ref);
|
let bit_ref = black_box(bit_ref);
|
||||||
|
|
||||||
let mut bit = black_box(*bit_ref);
|
let mut bit = black_box(*bit_ref);
|
||||||
|
#[allow(clippy::as_conversions, clippy::cast_lossless)]
|
||||||
let res = black_box(bit as u8);
|
let res = black_box(bit as u8);
|
||||||
bit.zeroize();
|
bit.zeroize();
|
||||||
debug_assert!((res | 1) == 1);
|
debug_assert!((res | 1) == 1);
|
||||||
@@ -62,7 +62,7 @@ where
|
|||||||
groupings.push(vec![0; (bits.len() + (w_usize - 1)) / w_usize]);
|
groupings.push(vec![0; (bits.len() + (w_usize - 1)) / w_usize]);
|
||||||
|
|
||||||
for (i, mut bit) in bits.iter_mut().enumerate() {
|
for (i, mut bit) in bits.iter_mut().enumerate() {
|
||||||
let mut bit = u8_from_bool(bit.deref_mut());
|
let mut bit = u8_from_bool(&mut bit);
|
||||||
groupings[p][i / w_usize] |= bit << (i % w_usize);
|
groupings[p][i / w_usize] |= bit << (i % w_usize);
|
||||||
bit.zeroize();
|
bit.zeroize();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ impl<C: Ciphersuite> SchnorrAggregate<C> {
|
|||||||
Rs.push(C::read_G(reader)?);
|
Rs.push(C::read_G(reader)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(SchnorrAggregate { Rs, s: C::read_F(reader)? })
|
Ok(Self { Rs, s: C::read_F(reader)? })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write a SchnorrAggregate to something implementing Write.
|
/// Write a SchnorrAggregate to something implementing Write.
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ pub struct SchnorrSignature<C: Ciphersuite> {
|
|||||||
impl<C: Ciphersuite> SchnorrSignature<C> {
|
impl<C: Ciphersuite> SchnorrSignature<C> {
|
||||||
/// Read a SchnorrSignature from something implementing Read.
|
/// Read a SchnorrSignature from something implementing Read.
|
||||||
pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
|
pub fn read<R: Read>(reader: &mut R) -> io::Result<Self> {
|
||||||
Ok(SchnorrSignature { R: C::read_G(reader)?, s: C::read_F(reader)? })
|
Ok(Self { R: C::read_G(reader)?, s: C::read_F(reader)? })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write a SchnorrSignature to something implementing Read.
|
/// Write a SchnorrSignature to something implementing Read.
|
||||||
@@ -69,12 +69,8 @@ impl<C: Ciphersuite> SchnorrSignature<C> {
|
|||||||
/// This challenge must be properly crafted, which means being binding to the public key, nonce,
|
/// This challenge must be properly crafted, which means being binding to the public key, nonce,
|
||||||
/// and any message. Failure to do so will let a malicious adversary to forge signatures for
|
/// and any message. Failure to do so will let a malicious adversary to forge signatures for
|
||||||
/// different keys/messages.
|
/// different keys/messages.
|
||||||
pub fn sign(
|
pub fn sign(private_key: &Zeroizing<C::F>, nonce: Zeroizing<C::F>, challenge: C::F) -> Self {
|
||||||
private_key: &Zeroizing<C::F>,
|
Self {
|
||||||
nonce: Zeroizing<C::F>,
|
|
||||||
challenge: C::F,
|
|
||||||
) -> SchnorrSignature<C> {
|
|
||||||
SchnorrSignature {
|
|
||||||
// Uses deref instead of * as * returns C::F yet deref returns &C::F, preventing a copy
|
// Uses deref instead of * as * returns C::F yet deref returns &C::F, preventing a copy
|
||||||
R: C::generator() * nonce.deref(),
|
R: C::generator() * nonce.deref(),
|
||||||
s: (challenge * private_key.deref()) + nonce.deref(),
|
s: (challenge * private_key.deref()) + nonce.deref(),
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ pub(crate) fn aggregate<C: Ciphersuite>() {
|
|||||||
keys
|
keys
|
||||||
.iter()
|
.iter()
|
||||||
.map(|key| C::generator() * key.deref())
|
.map(|key| C::generator() * key.deref())
|
||||||
.zip(challenges.iter().cloned())
|
.zip(challenges.iter().copied())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.as_ref(),
|
.as_ref(),
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -62,12 +62,8 @@ impl Schnorrkel {
|
|||||||
/// Create a new algorithm with the specified context.
|
/// Create a new algorithm with the specified context.
|
||||||
///
|
///
|
||||||
/// If the context is greater than or equal to 4 GB in size, this will panic.
|
/// If the context is greater than or equal to 4 GB in size, this will panic.
|
||||||
pub fn new(context: &'static [u8]) -> Schnorrkel {
|
pub fn new(context: &'static [u8]) -> Self {
|
||||||
Schnorrkel {
|
Self { context, schnorr: Schnorr::new(MerlinTranscript::new(b"FROST Schnorrkel")), msg: None }
|
||||||
context,
|
|
||||||
schnorr: Schnorr::new(MerlinTranscript::new(b"FROST Schnorrkel")),
|
|
||||||
msg: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,5 +21,5 @@ fn test() {
|
|||||||
let signature = sign(&mut OsRng, Schnorrkel::new(CONTEXT), keys, machines, MSG);
|
let signature = sign(&mut OsRng, Schnorrkel::new(CONTEXT), keys, machines, MSG);
|
||||||
|
|
||||||
let key = PublicKey::from_bytes(key.to_bytes().as_ref()).unwrap();
|
let key = PublicKey::from_bytes(key.to_bytes().as_ref()).unwrap();
|
||||||
key.verify(&mut SigningContext::new(CONTEXT).bytes(MSG), &signature).unwrap()
|
key.verify(&mut SigningContext::new(CONTEXT).bytes(MSG), &signature).unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,13 +63,13 @@ enum DigestTranscriptMember {
|
|||||||
impl DigestTranscriptMember {
|
impl DigestTranscriptMember {
|
||||||
fn as_u8(&self) -> u8 {
|
fn as_u8(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
DigestTranscriptMember::Name => 0,
|
Self::Name => 0,
|
||||||
DigestTranscriptMember::Domain => 1,
|
Self::Domain => 1,
|
||||||
DigestTranscriptMember::Label => 2,
|
Self::Label => 2,
|
||||||
DigestTranscriptMember::Value => 3,
|
Self::Value => 3,
|
||||||
DigestTranscriptMember::Challenge => 4,
|
Self::Challenge => 4,
|
||||||
DigestTranscriptMember::Continued => 5,
|
Self::Continued => 5,
|
||||||
DigestTranscriptMember::Challenged => 6,
|
Self::Challenged => 6,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@ impl<D: Send + Clone + SecureDigest> Transcript for DigestTranscript<D> {
|
|||||||
type Challenge = Output<D>;
|
type Challenge = Output<D>;
|
||||||
|
|
||||||
fn new(name: &'static [u8]) -> Self {
|
fn new(name: &'static [u8]) -> Self {
|
||||||
let mut res = DigestTranscript(D::new());
|
let mut res = Self(D::new());
|
||||||
res.append(DigestTranscriptMember::Name, name);
|
res.append(DigestTranscriptMember::Name, name);
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@@ -139,10 +139,7 @@ impl<D: Send + Clone + SecureDigest> Transcript for DigestTranscript<D> {
|
|||||||
// Digest doesn't implement Zeroize
|
// Digest doesn't implement Zeroize
|
||||||
// Implement Zeroize for DigestTranscript by writing twice the block size to the digest in an
|
// Implement Zeroize for DigestTranscript by writing twice the block size to the digest in an
|
||||||
// attempt to overwrite the internal hash state/any leftover bytes
|
// attempt to overwrite the internal hash state/any leftover bytes
|
||||||
impl<D: Send + Clone + SecureDigest> Zeroize for DigestTranscript<D>
|
impl<D: Send + Clone + SecureDigest + BlockSizeUser> Zeroize for DigestTranscript<D> {
|
||||||
where
|
|
||||||
D: BlockSizeUser,
|
|
||||||
{
|
|
||||||
fn zeroize(&mut self) {
|
fn zeroize(&mut self) {
|
||||||
// Update in 4-byte chunks to reduce call quantity and enable word-level update optimizations
|
// Update in 4-byte chunks to reduce call quantity and enable word-level update optimizations
|
||||||
const WORD_SIZE: usize = 4;
|
const WORD_SIZE: usize = 4;
|
||||||
@@ -187,7 +184,7 @@ where
|
|||||||
choice.zeroize();
|
choice.zeroize();
|
||||||
}
|
}
|
||||||
|
|
||||||
mark_read(self)
|
mark_read(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ impl Transcript for MerlinTranscript {
|
|||||||
type Challenge = [u8; 64];
|
type Challenge = [u8; 64];
|
||||||
|
|
||||||
fn new(name: &'static [u8]) -> Self {
|
fn new(name: &'static [u8]) -> Self {
|
||||||
MerlinTranscript(merlin::Transcript::new(name))
|
Self(merlin::Transcript::new(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn domain_separate(&mut self, label: &'static [u8]) {
|
fn domain_separate(&mut self, label: &'static [u8]) {
|
||||||
@@ -37,10 +37,7 @@ impl Transcript for MerlinTranscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn append_message<M: AsRef<[u8]>>(&mut self, label: &'static [u8], message: M) {
|
fn append_message<M: AsRef<[u8]>>(&mut self, label: &'static [u8], message: M) {
|
||||||
assert!(
|
assert!(label != b"dom-sep", "\"dom-sep\" is reserved for the domain_separate function",);
|
||||||
label != "dom-sep".as_bytes(),
|
|
||||||
"\"dom-sep\" is reserved for the domain_separate function",
|
|
||||||
);
|
|
||||||
self.0.append_message(label, message.as_ref());
|
self.0.append_message(label, message.as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,11 @@ where
|
|||||||
assert!(t().rng_seed(b"a") != t().rng_seed(b"b"));
|
assert!(t().rng_seed(b"a") != t().rng_seed(b"b"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::module_inception)]
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_digest() {
|
fn test_digest() {
|
||||||
test_transcript::<crate::DigestTranscript<sha2::Sha256>>();
|
test_transcript::<crate::DigestTranscript<sha2::Sha256>>();
|
||||||
@@ -101,3 +106,4 @@ fn test_recommended() {
|
|||||||
fn test_merlin() {
|
fn test_merlin() {
|
||||||
test_transcript::<crate::MerlinTranscript>();
|
test_transcript::<crate::MerlinTranscript>();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user