mirror of
https://github.com/serai-dex/serai.git
synced 2025-01-30 22:46:06 +00:00
Mostly lint Monero
This commit is contained in:
parent
dd5fb0df47
commit
f93106af6b
35 changed files with 553 additions and 459 deletions
|
@ -15,7 +15,11 @@
|
|||
-A clippy::redundant_pub_crate
|
||||
-A clippy::similar_names
|
||||
|
||||
# Flags on any debug_assert using an RNG
|
||||
-A clippy::debug_assert_with_mut_call
|
||||
|
||||
# Frequently used
|
||||
-A clippy::large_types_passed_by_value
|
||||
-A clippy::wildcard_imports
|
||||
-A clippy::too_many_lines
|
||||
|
||||
|
|
|
@ -28,10 +28,10 @@ fn serialize(generators_string: &mut String, points: &[EdwardsPoint]) {
|
|||
fn generators(prefix: &'static str, path: &str) {
|
||||
let generators = bulletproofs_generators(prefix.as_bytes());
|
||||
#[allow(non_snake_case)]
|
||||
let mut G_str = "".to_string();
|
||||
let mut G_str = String::new();
|
||||
serialize(&mut G_str, &generators.G);
|
||||
#[allow(non_snake_case)]
|
||||
let mut H_str = "".to_string();
|
||||
let mut H_str = String::new();
|
||||
serialize(&mut H_str, &generators.H);
|
||||
|
||||
let path = Path::new(&env::var("OUT_DIR").unwrap()).join(path);
|
||||
|
|
|
@ -8,8 +8,10 @@ use dalek_ff_group::FieldElement;
|
|||
use crate::hash;
|
||||
|
||||
/// Monero's hash to point function, as named `ge_fromfe_frombytes_vartime`.
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
#[must_use]
|
||||
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 v = FieldElement::from_square(hash(&bytes)).double();
|
||||
|
|
|
@ -61,6 +61,7 @@ pub struct Generators {
|
|||
}
|
||||
|
||||
/// Generate generators as needed for Bulletproofs(+), as Monero does.
|
||||
#[must_use]
|
||||
pub fn bulletproofs_generators(dst: &'static [u8]) -> Generators {
|
||||
let mut res =
|
||||
Generators { G: [EdwardsPoint::identity(); MAX_MN], H: [EdwardsPoint::identity(); MAX_MN] };
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use std_shims::io::{self, Write};
|
||||
|
||||
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<()> {
|
||||
let mut varint = *varint;
|
||||
while {
|
||||
|
|
|
@ -33,14 +33,15 @@ impl BlockHeader {
|
|||
w.write_all(&self.nonce.to_le_bytes())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut serialized = vec![];
|
||||
self.write(&mut serialized).unwrap();
|
||||
serialized
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<BlockHeader> {
|
||||
Ok(BlockHeader {
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||
Ok(Self {
|
||||
major_version: read_varint(r)?,
|
||||
minor_version: read_varint(r)?,
|
||||
timestamp: read_varint(r)?,
|
||||
|
@ -58,6 +59,7 @@ pub struct Block {
|
|||
}
|
||||
|
||||
impl Block {
|
||||
#[must_use]
|
||||
pub fn number(&self) -> usize {
|
||||
match self.miner_tx.prefix.inputs.get(0) {
|
||||
Some(Input::Gen(number)) => (*number).try_into().unwrap(),
|
||||
|
@ -91,6 +93,7 @@ impl Block {
|
|||
out
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn hash(&self) -> [u8; 32] {
|
||||
let hash = hash(&self.serialize_hashable());
|
||||
if hash == CORRECT_BLOCK_HASH_202612 {
|
||||
|
@ -100,14 +103,15 @@ impl Block {
|
|||
hash
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut serialized = vec![];
|
||||
self.write(&mut serialized).unwrap();
|
||||
serialized
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Block> {
|
||||
Ok(Block {
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||
Ok(Self {
|
||||
header: BlockHeader::read(r)?,
|
||||
miner_tx: Transaction::read(r)?,
|
||||
txs: (0 .. read_varint(r)?).map(|_| read_bytes(r)).collect::<Result<_, _>>()?,
|
||||
|
|
|
@ -60,39 +60,42 @@ pub enum Protocol {
|
|||
|
||||
impl Protocol {
|
||||
/// Amount of ring members under this protocol version.
|
||||
pub fn ring_len(&self) -> usize {
|
||||
#[must_use]
|
||||
pub const fn ring_len(&self) -> usize {
|
||||
match self {
|
||||
Protocol::v14 => 11,
|
||||
Protocol::v16 => 16,
|
||||
Protocol::Custom { ring_len, .. } => *ring_len,
|
||||
Self::v14 => 11,
|
||||
Self::v16 => 16,
|
||||
Self::Custom { ring_len, .. } => *ring_len,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn bp_plus(&self) -> bool {
|
||||
#[must_use]
|
||||
pub const fn bp_plus(&self) -> bool {
|
||||
match self {
|
||||
Protocol::v14 => false,
|
||||
Protocol::v16 => true,
|
||||
Protocol::Custom { bp_plus, .. } => *bp_plus,
|
||||
Self::v14 => false,
|
||||
Self::v16 => true,
|
||||
Self::Custom { bp_plus, .. } => *bp_plus,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make this an Option when we support pre-RCT protocols
|
||||
pub fn optimal_rct_type(&self) -> RctType {
|
||||
#[must_use]
|
||||
pub const fn optimal_rct_type(&self) -> RctType {
|
||||
match self {
|
||||
Protocol::v14 => RctType::Clsag,
|
||||
Protocol::v16 => RctType::BulletproofsPlus,
|
||||
Protocol::Custom { optimal_rct_type, .. } => *optimal_rct_type,
|
||||
Self::v14 => RctType::Clsag,
|
||||
Self::v16 => RctType::BulletproofsPlus,
|
||||
Self::Custom { optimal_rct_type, .. } => *optimal_rct_type,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn write<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
|
||||
match self {
|
||||
Protocol::v14 => w.write_all(&[0, 14]),
|
||||
Protocol::v16 => w.write_all(&[0, 16]),
|
||||
Protocol::Custom { ring_len, bp_plus, optimal_rct_type } => {
|
||||
Self::v14 => w.write_all(&[0, 14]),
|
||||
Self::v16 => w.write_all(&[0, 16]),
|
||||
Self::Custom { ring_len, bp_plus, optimal_rct_type } => {
|
||||
// Custom, version 0
|
||||
w.write_all(&[1, 0])?;
|
||||
w.write_all(&u16::try_from(*ring_len).unwrap().to_le_bytes())?;
|
||||
|
@ -102,17 +105,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)? {
|
||||
// Monero protocol
|
||||
0 => match read_byte(r)? {
|
||||
14 => Protocol::v14,
|
||||
16 => Protocol::v16,
|
||||
14 => Self::v14,
|
||||
16 => Self::v16,
|
||||
_ => Err(io::Error::new(io::ErrorKind::Other, "unrecognized monero protocol"))?,
|
||||
},
|
||||
// Custom
|
||||
1 => match read_byte(r)? {
|
||||
0 => Protocol::Custom {
|
||||
0 => Self::Custom {
|
||||
ring_len: read_u16(r)?.into(),
|
||||
bp_plus: match read_byte(r)? {
|
||||
0 => false,
|
||||
|
@ -140,22 +143,26 @@ pub struct Commitment {
|
|||
}
|
||||
|
||||
impl Commitment {
|
||||
/// The zero commitment, defined as a mask of 1 (as to not be the identity) and a 0 amount.
|
||||
pub fn zero() -> Commitment {
|
||||
Commitment { mask: Scalar::one(), amount: 0 }
|
||||
/// A commitment to zero, defined with a mask of 1 (as to not be the identity).
|
||||
#[must_use]
|
||||
pub fn zero() -> Self {
|
||||
Self { mask: Scalar::one(), amount: 0 }
|
||||
}
|
||||
|
||||
pub fn new(mask: Scalar, amount: u64) -> Commitment {
|
||||
Commitment { mask, amount }
|
||||
#[must_use]
|
||||
pub const fn new(mask: Scalar, amount: u64) -> Self {
|
||||
Self { mask, amount }
|
||||
}
|
||||
|
||||
/// Calculate a Pedersen commitment, as a point, from the transparent structure.
|
||||
#[must_use]
|
||||
pub fn calculate(&self) -> EdwardsPoint {
|
||||
(&self.mask * &ED25519_BASEPOINT_TABLE) + (Scalar::from(self.amount) * H())
|
||||
}
|
||||
}
|
||||
|
||||
/// Support generating a random scalar using a modern rand, as dalek's is notoriously dated.
|
||||
#[must_use]
|
||||
pub fn random_scalar<R: RngCore + CryptoRng>(rng: &mut R) -> Scalar {
|
||||
let mut r = [0; 64];
|
||||
rng.fill_bytes(&mut r);
|
||||
|
@ -167,6 +174,7 @@ pub(crate) fn hash(data: &[u8]) -> [u8; 32] {
|
|||
}
|
||||
|
||||
/// Hash the provided data to a scalar via keccak256(data) % l.
|
||||
#[must_use]
|
||||
pub fn hash_to_scalar(data: &[u8]) -> Scalar {
|
||||
let scalar = Scalar::from_bytes_mod_order(hash(data));
|
||||
// Monero will explicitly error in this case
|
||||
|
|
|
@ -2,7 +2,8 @@ use std_shims::vec::Vec;
|
|||
|
||||
use crate::hash;
|
||||
|
||||
pub fn merkle_root(root: [u8; 32], leafs: &[[u8; 32]]) -> [u8; 32] {
|
||||
#[must_use]
|
||||
pub(crate) fn merkle_root(root: [u8; 32], leafs: &[[u8; 32]]) -> [u8; 32] {
|
||||
match leafs.len() {
|
||||
0 => root,
|
||||
1 => hash(&[root, leafs[0]].concat()),
|
||||
|
|
|
@ -26,19 +26,15 @@ pub struct BorromeanSignatures {
|
|||
}
|
||||
|
||||
impl BorromeanSignatures {
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<BorromeanSignatures> {
|
||||
Ok(BorromeanSignatures {
|
||||
s0: read_array(read_bytes, r)?,
|
||||
s1: read_array(read_bytes, r)?,
|
||||
ee: read_bytes(r)?,
|
||||
})
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||
Ok(Self { 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<()> {
|
||||
for s0 in self.s0.iter() {
|
||||
for s0 in &self.s0 {
|
||||
w.write_all(s0)?;
|
||||
}
|
||||
for s1 in self.s1.iter() {
|
||||
for s1 in &self.s1 {
|
||||
w.write_all(s1)?;
|
||||
}
|
||||
w.write_all(&self.ee)
|
||||
|
@ -79,11 +75,8 @@ pub struct BorromeanRange {
|
|||
}
|
||||
|
||||
impl BorromeanRange {
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<BorromeanRange> {
|
||||
Ok(BorromeanRange {
|
||||
sigs: BorromeanSignatures::read(r)?,
|
||||
bit_commitments: read_array(read_point, r)?,
|
||||
})
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||
Ok(Self { sigs: BorromeanSignatures::read(r)?, bit_commitments: read_array(read_point, r)? })
|
||||
}
|
||||
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||
self.sigs.write(w)?;
|
||||
|
@ -91,6 +84,7 @@ impl BorromeanRange {
|
|||
}
|
||||
|
||||
#[cfg(feature = "experimental")]
|
||||
#[must_use]
|
||||
pub fn verify(&self, commitment: &EdwardsPoint) -> bool {
|
||||
if &self.bit_commitments.iter().sum::<EdwardsPoint>() != commitment {
|
||||
return false;
|
||||
|
|
|
@ -50,13 +50,13 @@ pub(crate) fn vector_exponent(
|
|||
|
||||
pub(crate) fn hash_cache(cache: &mut Scalar, mash: &[[u8; 32]]) -> Scalar {
|
||||
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();
|
||||
*cache = hash_to_scalar(slice);
|
||||
*cache
|
||||
}
|
||||
|
||||
pub(crate) fn MN(outputs: usize) -> (usize, usize, usize) {
|
||||
pub(crate) const fn MN(outputs: usize) -> (usize, usize, usize) {
|
||||
let mut logM = 0;
|
||||
let mut M;
|
||||
while {
|
||||
|
@ -78,10 +78,8 @@ pub(crate) fn bit_decompose(commitments: &[Commitment]) -> (ScalarVector, Scalar
|
|||
|
||||
for j in 0 .. M {
|
||||
for i in (0 .. N).rev() {
|
||||
let mut bit = Choice::from(0);
|
||||
if j < sv.len() {
|
||||
bit = Choice::from((sv[j][i / 8] >> (i % 8)) & 1);
|
||||
}
|
||||
let bit =
|
||||
if j < sv.len() { Choice::from((sv[j][i / 8] >> (i % 8)) & 1) } else { Choice::from(0) };
|
||||
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);
|
||||
}
|
||||
|
@ -118,9 +116,9 @@ pub(crate) fn LR_statements(
|
|||
let mut res = a
|
||||
.0
|
||||
.iter()
|
||||
.cloned()
|
||||
.zip(G_i.iter().cloned())
|
||||
.chain(b.0.iter().cloned().zip(H_i.iter().cloned()))
|
||||
.copied()
|
||||
.zip(G_i.iter().copied())
|
||||
.chain(b.0.iter().copied().zip(H_i.iter().copied()))
|
||||
.collect::<Vec<_>>();
|
||||
res.push((cL, U));
|
||||
res
|
||||
|
|
|
@ -62,14 +62,14 @@ impl Bulletproofs {
|
|||
rng: &mut R,
|
||||
outputs: &[Commitment],
|
||||
plus: bool,
|
||||
) -> Result<Bulletproofs, TransactionError> {
|
||||
) -> Result<Self, TransactionError> {
|
||||
if outputs.len() > MAX_OUTPUTS {
|
||||
return Err(TransactionError::TooManyOutputs)?;
|
||||
}
|
||||
Ok(if !plus {
|
||||
Bulletproofs::Original(OriginalStruct::prove(rng, outputs))
|
||||
Self::Plus(PlusStruct::prove(rng, outputs))
|
||||
} else {
|
||||
Bulletproofs::Plus(PlusStruct::prove(rng, outputs))
|
||||
Self::Original(OriginalStruct::prove(rng, outputs))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -77,8 +77,8 @@ impl Bulletproofs {
|
|||
#[must_use]
|
||||
pub fn verify<R: RngCore + CryptoRng>(&self, rng: &mut R, commitments: &[EdwardsPoint]) -> bool {
|
||||
match self {
|
||||
Bulletproofs::Original(bp) => bp.verify(rng, commitments),
|
||||
Bulletproofs::Plus(bp) => bp.verify(rng, commitments),
|
||||
Self::Original(bp) => bp.verify(rng, commitments),
|
||||
Self::Plus(bp) => bp.verify(rng, commitments),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,8 +94,8 @@ impl Bulletproofs {
|
|||
commitments: &[EdwardsPoint],
|
||||
) -> bool {
|
||||
match self {
|
||||
Bulletproofs::Original(bp) => bp.batch_verify(rng, verifier, id, commitments),
|
||||
Bulletproofs::Plus(bp) => bp.batch_verify(rng, verifier, id, commitments),
|
||||
Self::Original(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,
|
||||
) -> io::Result<()> {
|
||||
match self {
|
||||
Bulletproofs::Original(bp) => {
|
||||
Self::Original(bp) => {
|
||||
write_point(&bp.A, w)?;
|
||||
write_point(&bp.S, w)?;
|
||||
write_point(&bp.T1, w)?;
|
||||
|
@ -119,7 +119,7 @@ impl Bulletproofs {
|
|||
write_scalar(&bp.t, w)
|
||||
}
|
||||
|
||||
Bulletproofs::Plus(bp) => {
|
||||
Self::Plus(bp) => {
|
||||
write_point(&bp.A, w)?;
|
||||
write_point(&bp.A1, w)?;
|
||||
write_point(&bp.B, w)?;
|
||||
|
@ -140,6 +140,7 @@ impl Bulletproofs {
|
|||
self.write_core(w, |points, w| write_vec(write_point, points, w))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut serialized = vec![];
|
||||
self.write(&mut serialized).unwrap();
|
||||
|
@ -147,8 +148,8 @@ impl Bulletproofs {
|
|||
}
|
||||
|
||||
/// Read Bulletproofs.
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Bulletproofs> {
|
||||
Ok(Bulletproofs::Original(OriginalStruct {
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||
Ok(Self::Original(OriginalStruct {
|
||||
A: read_point(r)?,
|
||||
S: read_point(r)?,
|
||||
T1: read_point(r)?,
|
||||
|
@ -164,8 +165,8 @@ impl Bulletproofs {
|
|||
}
|
||||
|
||||
/// Read Bulletproofs+.
|
||||
pub fn read_plus<R: Read>(r: &mut R) -> io::Result<Bulletproofs> {
|
||||
Ok(Bulletproofs::Plus(PlusStruct {
|
||||
pub fn read_plus<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||
Ok(Self::Plus(PlusStruct {
|
||||
A: read_point(r)?,
|
||||
A1: read_point(r)?,
|
||||
B: read_point(r)?,
|
||||
|
|
|
@ -36,10 +36,8 @@ pub struct OriginalStruct {
|
|||
}
|
||||
|
||||
impl OriginalStruct {
|
||||
pub(crate) fn prove<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
commitments: &[Commitment],
|
||||
) -> OriginalStruct {
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub(crate) fn prove<R: RngCore + CryptoRng>(rng: &mut R, commitments: &[Commitment]) -> Self {
|
||||
let (logMN, M, MN) = MN(commitments.len());
|
||||
|
||||
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 R_i = prove_multiexp(&LR_statements(&aR, G_L, &bL, H_R, cR, U));
|
||||
L.push(L_i);
|
||||
R.push(R_i);
|
||||
L.push(*L_i);
|
||||
R.push(*R_i);
|
||||
|
||||
let w = hash_cache(&mut cache, &[L_i.compress().to_bytes(), R_i.compress().to_bytes()]);
|
||||
let winv = w.invert().unwrap();
|
||||
|
@ -149,15 +147,15 @@ impl OriginalStruct {
|
|||
}
|
||||
}
|
||||
|
||||
let res = OriginalStruct {
|
||||
let res = Self {
|
||||
A: *A,
|
||||
S: *S,
|
||||
T1: *T1,
|
||||
T2: *T2,
|
||||
taux: *taux,
|
||||
mu: *mu,
|
||||
L: L.drain(..).map(|L| *L).collect(),
|
||||
R: R.drain(..).map(|R| *R).collect(),
|
||||
L,
|
||||
R,
|
||||
a: *a[0],
|
||||
b: *b[0],
|
||||
t: *t,
|
||||
|
@ -166,6 +164,7 @@ impl OriginalStruct {
|
|||
res
|
||||
}
|
||||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
#[must_use]
|
||||
fn verify_core<ID: Copy + Zeroize, R: RngCore + CryptoRng>(
|
||||
&self,
|
||||
|
@ -190,7 +189,7 @@ impl OriginalStruct {
|
|||
}
|
||||
|
||||
// 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 z = hash_to_scalar(&y.to_bytes());
|
||||
|
@ -223,7 +222,7 @@ impl OriginalStruct {
|
|||
let A = normalize(&self.A);
|
||||
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
|
||||
let mut proof = Vec::with_capacity(4 + commitments.len());
|
||||
|
|
|
@ -56,10 +56,8 @@ pub struct PlusStruct {
|
|||
}
|
||||
|
||||
impl PlusStruct {
|
||||
pub(crate) fn prove<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
commitments: &[Commitment],
|
||||
) -> PlusStruct {
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub(crate) fn prove<R: RngCore + CryptoRng>(rng: &mut R, commitments: &[Commitment]) -> Self {
|
||||
let generators = GENERATORS();
|
||||
|
||||
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());
|
||||
L_i.push((dL, G));
|
||||
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());
|
||||
R_i.push((dR, G));
|
||||
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 winv = w.invert().unwrap();
|
||||
|
@ -158,20 +156,12 @@ impl PlusStruct {
|
|||
eta.zeroize();
|
||||
alpha1.zeroize();
|
||||
|
||||
let res = PlusStruct {
|
||||
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(),
|
||||
};
|
||||
let res = Self { A: *A, A1: *A1, B: *B, r1: *r1, s1: *s1, d1: *d1, L, R };
|
||||
debug_assert!(res.verify(rng, &commitments_points));
|
||||
res
|
||||
}
|
||||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
#[must_use]
|
||||
fn verify_core<ID: Copy + Zeroize, R: RngCore + CryptoRng>(
|
||||
&self,
|
||||
|
@ -196,7 +186,7 @@ impl PlusStruct {
|
|||
}
|
||||
|
||||
// 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 yinv = y.invert().unwrap();
|
||||
let z = hash_to_scalar(&y.to_bytes());
|
||||
|
@ -220,8 +210,6 @@ impl PlusStruct {
|
|||
let A1 = normalize(&self.A1);
|
||||
let B = normalize(&self.B);
|
||||
|
||||
let mut commitments = commitments.iter().map(|c| c.mul_by_cofactor()).collect::<Vec<_>>();
|
||||
|
||||
// Verify it
|
||||
let mut proof = Vec::with_capacity(logMN + 5 + (2 * (MN + logMN)));
|
||||
|
||||
|
@ -237,7 +225,7 @@ impl PlusStruct {
|
|||
let esq = e * e;
|
||||
let minus_esq = -esq;
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
|
@ -13,9 +13,9 @@ pub(crate) struct ScalarVector(pub(crate) Vec<Scalar>);
|
|||
macro_rules! math_op {
|
||||
($Op: ident, $op: ident, $f: expr) => {
|
||||
impl $Op<Scalar> for ScalarVector {
|
||||
type Output = ScalarVector;
|
||||
fn $op(self, b: Scalar) -> ScalarVector {
|
||||
ScalarVector(self.0.iter().map(|a| $f((a, &b))).collect())
|
||||
type Output = Self;
|
||||
fn $op(self, b: Scalar) -> Self {
|
||||
Self(self.0.iter().map(|a| $f((a, &b))).collect())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,16 +27,16 @@ macro_rules! math_op {
|
|||
}
|
||||
|
||||
impl $Op<ScalarVector> for ScalarVector {
|
||||
type Output = ScalarVector;
|
||||
fn $op(self, b: ScalarVector) -> ScalarVector {
|
||||
type Output = Self;
|
||||
fn $op(self, b: Self) -> Self {
|
||||
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;
|
||||
fn $op(self, b: &ScalarVector) -> ScalarVector {
|
||||
fn $op(self, b: Self) -> ScalarVector {
|
||||
debug_assert_eq!(self.len(), b.len());
|
||||
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);
|
||||
|
||||
impl ScalarVector {
|
||||
pub(crate) fn new(len: usize) -> ScalarVector {
|
||||
ScalarVector(vec![Scalar::ZERO; len])
|
||||
pub(crate) fn new(len: usize) -> Self {
|
||||
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);
|
||||
|
||||
let mut res = Vec::with_capacity(len);
|
||||
|
@ -60,16 +60,16 @@ impl ScalarVector {
|
|||
for i in 1 .. len {
|
||||
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);
|
||||
// Verify pow is a power of two
|
||||
debug_assert_eq!(((pow - 1) & pow), 0);
|
||||
|
||||
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);
|
||||
|
||||
let mut prev = 2;
|
||||
|
@ -89,9 +89,9 @@ impl ScalarVector {
|
|||
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);
|
||||
(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;
|
||||
fn mul(self, b: &[EdwardsPoint]) -> EdwardsPoint {
|
||||
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<_>>())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,27 +30,31 @@ pub use multisig::{ClsagDetails, ClsagAddendum, ClsagMultisig};
|
|||
#[cfg(feature = "multisig")]
|
||||
pub(crate) use multisig::add_key_image_share;
|
||||
|
||||
/// Errors returned when CLSAG signing fails.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "std", derive(thiserror::Error))]
|
||||
pub enum ClsagError {
|
||||
#[cfg_attr(feature = "std", error("internal error ({0})"))]
|
||||
InternalError(&'static str),
|
||||
#[cfg_attr(feature = "std", error("invalid ring"))]
|
||||
InvalidRing,
|
||||
#[cfg_attr(feature = "std", error("invalid ring member (member {0}, ring size {1})"))]
|
||||
InvalidRingMember(u8, u8),
|
||||
#[cfg_attr(feature = "std", error("invalid commitment"))]
|
||||
InvalidCommitment,
|
||||
#[cfg_attr(feature = "std", error("invalid key image"))]
|
||||
InvalidImage,
|
||||
#[cfg_attr(feature = "std", error("invalid D"))]
|
||||
InvalidD,
|
||||
#[cfg_attr(feature = "std", error("invalid s"))]
|
||||
InvalidS,
|
||||
#[cfg_attr(feature = "std", error("invalid c1"))]
|
||||
InvalidC1,
|
||||
#[allow(clippy::std_instead_of_core)]
|
||||
mod clsag_error {
|
||||
/// Errors returned when CLSAG signing fails.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "std", derive(thiserror::Error))]
|
||||
pub enum ClsagError {
|
||||
#[cfg_attr(feature = "std", error("internal error ({0})"))]
|
||||
InternalError(&'static str),
|
||||
#[cfg_attr(feature = "std", error("invalid ring"))]
|
||||
InvalidRing,
|
||||
#[cfg_attr(feature = "std", error("invalid ring member (member {0}, ring size {1})"))]
|
||||
InvalidRingMember(u8, u8),
|
||||
#[cfg_attr(feature = "std", error("invalid commitment"))]
|
||||
InvalidCommitment,
|
||||
#[cfg_attr(feature = "std", error("invalid key image"))]
|
||||
InvalidImage,
|
||||
#[cfg_attr(feature = "std", error("invalid D"))]
|
||||
InvalidD,
|
||||
#[cfg_attr(feature = "std", error("invalid s"))]
|
||||
InvalidS,
|
||||
#[cfg_attr(feature = "std", error("invalid c1"))]
|
||||
InvalidC1,
|
||||
}
|
||||
}
|
||||
pub use clsag_error::ClsagError;
|
||||
|
||||
/// Input being signed for.
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize, ZeroizeOnDrop)]
|
||||
|
@ -62,7 +66,7 @@ pub struct 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();
|
||||
if n > u8::MAX.into() {
|
||||
Err(ClsagError::InternalError("max ring size in this library is u8 max"))?;
|
||||
|
@ -77,7 +81,7 @@ impl ClsagInput {
|
|||
Err(ClsagError::InvalidCommitment)?;
|
||||
}
|
||||
|
||||
Ok(ClsagInput { commitment, decoys })
|
||||
Ok(Self { commitment, decoys })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,6 +209,7 @@ pub struct Clsag {
|
|||
impl Clsag {
|
||||
// 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
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub(crate) fn sign_core<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
I: &EdwardsPoint,
|
||||
|
@ -213,7 +218,7 @@ impl Clsag {
|
|||
msg: &[u8; 32],
|
||||
A: EdwardsPoint,
|
||||
AH: EdwardsPoint,
|
||||
) -> (Clsag, EdwardsPoint, Scalar, Scalar) {
|
||||
) -> (Self, EdwardsPoint, Scalar, Scalar) {
|
||||
let r: usize = input.decoys.i.into();
|
||||
|
||||
let pseudo_out = Commitment::new(mask, input.commitment.amount).calculate();
|
||||
|
@ -228,18 +233,19 @@ impl Clsag {
|
|||
let ((D, p, c), c1) =
|
||||
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.
|
||||
/// inputs is of the form (private key, key image, input).
|
||||
/// sum_outputs is for the sum of the outputs' commitment masks.
|
||||
#[must_use]
|
||||
pub fn sign<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
mut inputs: Vec<(Zeroizing<Scalar>, EdwardsPoint, ClsagInput)>,
|
||||
sum_outputs: Scalar,
|
||||
msg: [u8; 32],
|
||||
) -> Vec<(Clsag, EdwardsPoint)> {
|
||||
) -> Vec<(Self, EdwardsPoint)> {
|
||||
let mut res = Vec::with_capacity(inputs.len());
|
||||
let mut sum_pseudo_outs = Scalar::zero();
|
||||
for i in 0 .. inputs.len() {
|
||||
|
@ -251,7 +257,7 @@ impl Clsag {
|
|||
}
|
||||
|
||||
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,
|
||||
&inputs[i].1,
|
||||
&inputs[i].2,
|
||||
|
@ -308,7 +314,7 @@ impl Clsag {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn fee_weight(ring_len: usize) -> usize {
|
||||
pub(crate) const fn fee_weight(ring_len: usize) -> usize {
|
||||
(ring_len * 32) + 32 + 32
|
||||
}
|
||||
|
||||
|
@ -318,7 +324,7 @@ impl Clsag {
|
|||
write_point(&self.D, w)
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(decoys: usize, r: &mut R) -> io::Result<Clsag> {
|
||||
Ok(Clsag { s: read_raw_vec(read_scalar, decoys, r)?, c1: read_scalar(r)?, D: read_point(r)? })
|
||||
pub fn read<R: Read>(decoys: usize, r: &mut R) -> io::Result<Self> {
|
||||
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 std_shims::io::{self, Read, Write};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std_shims::{
|
||||
sync::Arc,
|
||||
io::{self, Read, Write},
|
||||
};
|
||||
use std::sync::RwLock;
|
||||
|
||||
use rand_core::{RngCore, CryptoRng, SeedableRng};
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
|
@ -48,7 +51,7 @@ impl ClsagInput {
|
|||
// if in use
|
||||
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"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
|
||||
|
@ -65,8 +68,9 @@ pub struct ClsagDetails {
|
|||
}
|
||||
|
||||
impl ClsagDetails {
|
||||
pub fn new(input: ClsagInput, mask: Scalar) -> ClsagDetails {
|
||||
ClsagDetails { input, mask }
|
||||
#[must_use]
|
||||
pub fn new(input: ClsagInput, mask: Scalar) -> Self {
|
||||
Self { input, mask }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,12 +116,13 @@ pub struct ClsagMultisig {
|
|||
}
|
||||
|
||||
impl ClsagMultisig {
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
transcript: RecommendedTranscript,
|
||||
output_key: EdwardsPoint,
|
||||
details: Arc<RwLock<Option<ClsagDetails>>>,
|
||||
) -> ClsagMultisig {
|
||||
ClsagMultisig {
|
||||
) -> Self {
|
||||
Self {
|
||||
transcript,
|
||||
|
||||
H: hash_to_point(output_key),
|
||||
|
|
|
@ -3,6 +3,7 @@ use curve25519_dalek::edwards::EdwardsPoint;
|
|||
pub use monero_generators::{hash_to_point as raw_hash_to_point};
|
||||
|
||||
/// Monero's hash to point function, as named `ge_fromfe_frombytes_vartime`.
|
||||
#[must_use]
|
||||
pub fn hash_to_point(key: EdwardsPoint) -> EdwardsPoint {
|
||||
raw_hash_to_point(key.compress().to_bytes())
|
||||
}
|
||||
|
|
|
@ -19,20 +19,21 @@ pub struct Mlsag {
|
|||
|
||||
impl Mlsag {
|
||||
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_scalar(&self.cc, w)
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(mixins: usize, r: &mut R) -> io::Result<Mlsag> {
|
||||
Ok(Mlsag {
|
||||
pub fn read<R: Read>(mixins: usize, r: &mut R) -> io::Result<Self> {
|
||||
Ok(Self {
|
||||
ss: (0 .. mixins).map(|_| read_array(read_scalar, r)).collect::<Result<_, _>>()?,
|
||||
cc: read_scalar(r)?,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental")]
|
||||
#[must_use]
|
||||
pub fn verify(
|
||||
&self,
|
||||
msg: &[u8; 32],
|
||||
|
|
|
@ -27,6 +27,7 @@ use crate::{
|
|||
};
|
||||
|
||||
/// Generate a key image for a given key. Defined as `x * hash_to_point(xG)`.
|
||||
#[must_use]
|
||||
pub fn generate_key_image(secret: &Zeroizing<Scalar>) -> EdwardsPoint {
|
||||
hash_to_point(&ED25519_BASEPOINT_TABLE * secret.deref()) * secret.deref()
|
||||
}
|
||||
|
@ -38,21 +39,21 @@ pub enum EncryptedAmount {
|
|||
}
|
||||
|
||||
impl EncryptedAmount {
|
||||
pub fn read<R: Read>(compact: bool, r: &mut R) -> io::Result<EncryptedAmount> {
|
||||
Ok(if !compact {
|
||||
EncryptedAmount::Original { mask: read_bytes(r)?, amount: read_bytes(r)? }
|
||||
pub fn read<R: Read>(compact: bool, r: &mut R) -> io::Result<Self> {
|
||||
Ok(if compact {
|
||||
Self::Compact { amount: read_bytes(r)? }
|
||||
} 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<()> {
|
||||
match self {
|
||||
EncryptedAmount::Original { mask, amount } => {
|
||||
Self::Original { mask, amount } => {
|
||||
w.write_all(mask)?;
|
||||
w.write_all(amount)
|
||||
}
|
||||
EncryptedAmount::Compact { amount } => w.write_all(amount),
|
||||
Self::Compact { amount } => w.write_all(amount),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,40 +78,38 @@ pub enum RctType {
|
|||
}
|
||||
|
||||
impl RctType {
|
||||
pub fn to_byte(self) -> u8 {
|
||||
#[must_use]
|
||||
pub const fn to_byte(self) -> u8 {
|
||||
match self {
|
||||
RctType::Null => 0,
|
||||
RctType::MlsagAggregate => 1,
|
||||
RctType::MlsagIndividual => 2,
|
||||
RctType::Bulletproofs => 3,
|
||||
RctType::BulletproofsCompactAmount => 4,
|
||||
RctType::Clsag => 5,
|
||||
RctType::BulletproofsPlus => 6,
|
||||
Self::Null => 0,
|
||||
Self::MlsagAggregate => 1,
|
||||
Self::MlsagIndividual => 2,
|
||||
Self::Bulletproofs => 3,
|
||||
Self::BulletproofsCompactAmount => 4,
|
||||
Self::Clsag => 5,
|
||||
Self::BulletproofsPlus => 6,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn from_byte(byte: u8) -> Option<Self> {
|
||||
Some(match byte {
|
||||
0 => RctType::Null,
|
||||
1 => RctType::MlsagAggregate,
|
||||
2 => RctType::MlsagIndividual,
|
||||
3 => RctType::Bulletproofs,
|
||||
4 => RctType::BulletproofsCompactAmount,
|
||||
5 => RctType::Clsag,
|
||||
6 => RctType::BulletproofsPlus,
|
||||
0 => Self::Null,
|
||||
1 => Self::MlsagAggregate,
|
||||
2 => Self::MlsagIndividual,
|
||||
3 => Self::Bulletproofs,
|
||||
4 => Self::BulletproofsCompactAmount,
|
||||
5 => Self::Clsag,
|
||||
6 => Self::BulletproofsPlus,
|
||||
_ => None?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn compact_encrypted_amounts(&self) -> bool {
|
||||
#[must_use]
|
||||
pub const fn compact_encrypted_amounts(&self) -> bool {
|
||||
match self {
|
||||
RctType::Null => false,
|
||||
RctType::MlsagAggregate => false,
|
||||
RctType::MlsagIndividual => false,
|
||||
RctType::Bulletproofs => false,
|
||||
RctType::BulletproofsCompactAmount => true,
|
||||
RctType::Clsag => true,
|
||||
RctType::BulletproofsPlus => true,
|
||||
Self::Null | Self::MlsagAggregate | Self::MlsagIndividual | Self::Bulletproofs => false,
|
||||
Self::BulletproofsCompactAmount | Self::Clsag | Self::BulletproofsPlus => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -124,7 +123,7 @@ pub struct RctBase {
|
|||
}
|
||||
|
||||
impl RctBase {
|
||||
pub(crate) fn fee_weight(outputs: usize) -> usize {
|
||||
pub(crate) const fn fee_weight(outputs: usize) -> usize {
|
||||
1 + 8 + (outputs * (8 + 32))
|
||||
}
|
||||
|
||||
|
@ -132,7 +131,12 @@ impl RctBase {
|
|||
w.write_all(&[rct_type.to_byte()])?;
|
||||
match rct_type {
|
||||
RctType::Null => Ok(()),
|
||||
_ => {
|
||||
RctType::MlsagAggregate |
|
||||
RctType::MlsagIndividual |
|
||||
RctType::Bulletproofs |
|
||||
RctType::BulletproofsCompactAmount |
|
||||
RctType::Clsag |
|
||||
RctType::BulletproofsPlus => {
|
||||
write_varint(&self.fee, w)?;
|
||||
if rct_type == RctType::MlsagIndividual {
|
||||
write_raw_vec(write_point, &self.pseudo_outs, w)?;
|
||||
|
@ -145,14 +149,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)?)
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid RCT type"))?;
|
||||
|
||||
match rct_type {
|
||||
RctType::Null => {}
|
||||
RctType::MlsagAggregate => {}
|
||||
RctType::MlsagIndividual => {}
|
||||
RctType::Null | RctType::MlsagAggregate | RctType::MlsagIndividual => {}
|
||||
RctType::Bulletproofs |
|
||||
RctType::BulletproofsCompactAmount |
|
||||
RctType::Clsag |
|
||||
|
@ -170,9 +172,9 @@ impl RctBase {
|
|||
|
||||
Ok((
|
||||
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 {
|
||||
RctBase {
|
||||
Self {
|
||||
fee: read_varint(r)?,
|
||||
pseudo_outs: if rct_type == RctType::MlsagIndividual {
|
||||
read_raw_vec(read_point, inputs, r)?
|
||||
|
@ -217,12 +219,12 @@ impl RctPrunable {
|
|||
|
||||
pub fn write<W: Write>(&self, w: &mut W, rct_type: RctType) -> io::Result<()> {
|
||||
match self {
|
||||
RctPrunable::Null => Ok(()),
|
||||
RctPrunable::MlsagBorromean { borromean, mlsags } => {
|
||||
Self::Null => Ok(()),
|
||||
Self::MlsagBorromean { borromean, mlsags } => {
|
||||
write_raw_vec(BorromeanRange::write, borromean, 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 {
|
||||
w.write_all(&1u32.to_le_bytes())?;
|
||||
} else {
|
||||
|
@ -233,7 +235,7 @@ impl RctPrunable {
|
|||
write_raw_vec(Mlsag::write, mlsags, 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])?;
|
||||
bulletproofs.write(w)?;
|
||||
|
||||
|
@ -243,6 +245,7 @@ impl RctPrunable {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn serialize(&self, rct_type: RctType) -> Vec<u8> {
|
||||
let mut serialized = vec![];
|
||||
self.write(&mut serialized, rct_type).unwrap();
|
||||
|
@ -254,31 +257,29 @@ impl RctPrunable {
|
|||
decoys: &[usize],
|
||||
outputs: usize,
|
||||
r: &mut R,
|
||||
) -> io::Result<RctPrunable> {
|
||||
) -> io::Result<Self> {
|
||||
Ok(match rct_type {
|
||||
RctType::Null => RctPrunable::Null,
|
||||
RctType::MlsagAggregate | RctType::MlsagIndividual => RctPrunable::MlsagBorromean {
|
||||
RctType::Null => Self::Null,
|
||||
RctType::MlsagAggregate | RctType::MlsagIndividual => Self::MlsagBorromean {
|
||||
borromean: read_raw_vec(BorromeanRange::read, outputs, r)?,
|
||||
mlsags: decoys.iter().map(|d| Mlsag::read(*d, r)).collect::<Result<_, _>>()?,
|
||||
},
|
||||
RctType::Bulletproofs | RctType::BulletproofsCompactAmount => {
|
||||
RctPrunable::MlsagBulletproofs {
|
||||
bulletproofs: {
|
||||
if (if rct_type == RctType::Bulletproofs {
|
||||
u64::from(read_u32(r)?)
|
||||
} else {
|
||||
read_varint(r)?
|
||||
}) != 1
|
||||
{
|
||||
Err(io::Error::new(io::ErrorKind::Other, "n bulletproofs instead of one"))?;
|
||||
}
|
||||
Bulletproofs::read(r)?
|
||||
},
|
||||
mlsags: decoys.iter().map(|d| Mlsag::read(*d, r)).collect::<Result<_, _>>()?,
|
||||
pseudo_outs: read_raw_vec(read_point, decoys.len(), r)?,
|
||||
}
|
||||
}
|
||||
RctType::Clsag | RctType::BulletproofsPlus => RctPrunable::Clsag {
|
||||
RctType::Bulletproofs | RctType::BulletproofsCompactAmount => Self::MlsagBulletproofs {
|
||||
bulletproofs: {
|
||||
if (if rct_type == RctType::Bulletproofs {
|
||||
u64::from(read_u32(r)?)
|
||||
} else {
|
||||
read_varint(r)?
|
||||
}) != 1
|
||||
{
|
||||
Err(io::Error::new(io::ErrorKind::Other, "n bulletproofs instead of one"))?;
|
||||
}
|
||||
Bulletproofs::read(r)?
|
||||
},
|
||||
mlsags: decoys.iter().map(|d| Mlsag::read(*d, r)).collect::<Result<_, _>>()?,
|
||||
pseudo_outs: read_raw_vec(read_point, decoys.len(), r)?,
|
||||
},
|
||||
RctType::Clsag | RctType::BulletproofsPlus => Self::Clsag {
|
||||
bulletproofs: {
|
||||
if read_varint(r)? != 1 {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "n bulletproofs instead of one"))?;
|
||||
|
@ -295,12 +296,10 @@ impl RctPrunable {
|
|||
|
||||
pub(crate) fn signature_write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||
match self {
|
||||
RctPrunable::Null => panic!("Serializing RctPrunable::Null for a signature"),
|
||||
RctPrunable::MlsagBorromean { borromean, .. } => {
|
||||
borromean.iter().try_for_each(|rs| rs.write(w))
|
||||
}
|
||||
RctPrunable::MlsagBulletproofs { bulletproofs, .. } => bulletproofs.signature_write(w),
|
||||
RctPrunable::Clsag { bulletproofs, .. } => bulletproofs.signature_write(w),
|
||||
Self::Null => panic!("Serializing RctPrunable::Null for a signature"),
|
||||
Self::MlsagBorromean { borromean, .. } => borromean.iter().try_for_each(|rs| rs.write(w)),
|
||||
Self::MlsagBulletproofs { bulletproofs, .. } => bulletproofs.signature_write(w),
|
||||
Self::Clsag { bulletproofs, .. } => bulletproofs.signature_write(w),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -313,6 +312,7 @@ pub struct RctSignatures {
|
|||
|
||||
impl RctSignatures {
|
||||
/// RctType for a given RctSignatures struct.
|
||||
#[must_use]
|
||||
pub fn rct_type(&self) -> RctType {
|
||||
match &self.prunable {
|
||||
RctPrunable::Null => RctType::Null,
|
||||
|
@ -376,14 +376,15 @@ impl RctSignatures {
|
|||
self.prunable.write(w, rct_type)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut serialized = vec![];
|
||||
self.write(&mut serialized).unwrap();
|
||||
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)?;
|
||||
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
|
||||
/// 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
|
||||
let userpass = if url.contains('@') {
|
||||
let url_clone = url;
|
||||
|
@ -47,7 +47,7 @@ impl HttpRpc {
|
|||
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::*;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct EmptyResponse {}
|
||||
pub struct EmptyResponse;
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct JsonRpcResponse<T> {
|
||||
result: T,
|
||||
|
@ -47,26 +47,31 @@ struct TransactionsResponse {
|
|||
txs: Vec<TransactionResponse>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "std", derive(thiserror::Error))]
|
||||
pub enum RpcError {
|
||||
#[cfg_attr(feature = "std", error("internal error ({0})"))]
|
||||
InternalError(&'static str),
|
||||
#[cfg_attr(feature = "std", error("connection error"))]
|
||||
ConnectionError,
|
||||
#[cfg_attr(feature = "std", error("invalid node"))]
|
||||
InvalidNode,
|
||||
#[cfg_attr(feature = "std", error("unsupported protocol version ({0})"))]
|
||||
UnsupportedProtocol(usize),
|
||||
#[cfg_attr(feature = "std", error("transactions not found"))]
|
||||
TransactionsNotFound(Vec<[u8; 32]>),
|
||||
#[cfg_attr(feature = "std", error("invalid point ({0})"))]
|
||||
InvalidPoint(String),
|
||||
#[cfg_attr(feature = "std", error("pruned transaction"))]
|
||||
PrunedTransaction,
|
||||
#[cfg_attr(feature = "std", error("invalid transaction ({0:?})"))]
|
||||
InvalidTransaction([u8; 32]),
|
||||
#[allow(clippy::std_instead_of_core)]
|
||||
mod rpc_error {
|
||||
use std_shims::{vec::Vec, string::String};
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "std", derive(thiserror::Error))]
|
||||
pub enum RpcError {
|
||||
#[cfg_attr(feature = "std", error("internal error ({0})"))]
|
||||
InternalError(&'static str),
|
||||
#[cfg_attr(feature = "std", error("connection error"))]
|
||||
ConnectionError,
|
||||
#[cfg_attr(feature = "std", error("invalid node"))]
|
||||
InvalidNode,
|
||||
#[cfg_attr(feature = "std", error("unsupported protocol version ({0})"))]
|
||||
UnsupportedProtocol(usize),
|
||||
#[cfg_attr(feature = "std", error("transactions not found"))]
|
||||
TransactionsNotFound(Vec<[u8; 32]>),
|
||||
#[cfg_attr(feature = "std", error("invalid point ({0})"))]
|
||||
InvalidPoint(String),
|
||||
#[cfg_attr(feature = "std", error("pruned transaction"))]
|
||||
PrunedTransaction,
|
||||
#[cfg_attr(feature = "std", error("invalid transaction ({0:?})"))]
|
||||
InvalidTransaction([u8; 32]),
|
||||
}
|
||||
}
|
||||
pub use rpc_error::RpcError;
|
||||
|
||||
fn rpc_hex(value: &str) -> Result<Vec<u8>, RpcError> {
|
||||
hex::decode(value).map_err(|_| RpcError::InvalidNode)
|
||||
|
@ -299,15 +304,15 @@ impl<R: RpcConnection> Rpc<R> {
|
|||
match self.get_block(self.get_block_hash(number).await?).await {
|
||||
Ok(block) => {
|
||||
// Make sure this is actually the block for this number
|
||||
match block.miner_tx.prefix.inputs[0] {
|
||||
Input::Gen(actual) => {
|
||||
if usize::try_from(actual).unwrap() == number {
|
||||
match block.miner_tx.prefix.inputs.get(0) {
|
||||
Some(Input::Gen(actual)) => {
|
||||
if usize::try_from(*actual).unwrap() == number {
|
||||
Ok(block)
|
||||
} else {
|
||||
Err(RpcError::InvalidNode)
|
||||
}
|
||||
}
|
||||
_ => Err(RpcError::InvalidNode),
|
||||
Some(Input::ToKey { .. }) | None => Err(RpcError::InvalidNode),
|
||||
}
|
||||
}
|
||||
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> {
|
||||
read_point(r)
|
||||
.ok()
|
||||
.filter(|point| point.is_torsion_free())
|
||||
.filter(EdwardsPoint::is_torsion_free)
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "invalid point"))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use core::ops::Deref;
|
||||
#[cfg(feature = "multisig")]
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std_shims::sync::Arc;
|
||||
#[cfg(feature = "multisig")]
|
||||
use std::sync::RwLock;
|
||||
|
||||
use zeroize::Zeroizing;
|
||||
use rand_core::{RngCore, OsRng};
|
||||
|
@ -45,13 +47,12 @@ fn clsag() {
|
|||
for i in 0 .. RING_LEN {
|
||||
let dest = Zeroizing::new(random_scalar(&mut OsRng));
|
||||
let mask = random_scalar(&mut OsRng);
|
||||
let amount;
|
||||
if i == real {
|
||||
let amount = if i == real {
|
||||
secrets = (dest.clone(), mask);
|
||||
amount = AMOUNT;
|
||||
AMOUNT
|
||||
} else {
|
||||
amount = OsRng.next_u64();
|
||||
}
|
||||
OsRng.next_u64()
|
||||
};
|
||||
ring
|
||||
.push([dest.deref() * &ED25519_BASEPOINT_TABLE, Commitment::new(mask, amount).calculate()]);
|
||||
}
|
||||
|
@ -90,16 +91,15 @@ fn clsag_multisig() {
|
|||
for i in 0 .. RING_LEN {
|
||||
let dest;
|
||||
let mask;
|
||||
let amount;
|
||||
if i != u64::from(RING_INDEX) {
|
||||
dest = &random_scalar(&mut OsRng) * &ED25519_BASEPOINT_TABLE;
|
||||
mask = random_scalar(&mut OsRng);
|
||||
amount = OsRng.next_u64();
|
||||
} else {
|
||||
let amount = if i == u64::from(RING_INDEX) {
|
||||
dest = keys[&Participant::new(1).unwrap()].group_key().0;
|
||||
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()]);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ pub enum Input {
|
|||
|
||||
impl Input {
|
||||
// Worst-case predictive len
|
||||
pub(crate) fn fee_weight(ring_len: usize) -> usize {
|
||||
pub(crate) const fn fee_weight(ring_len: usize) -> usize {
|
||||
// Uses 1 byte for the VarInt amount due to amount being 0
|
||||
// Uses 1 byte for the VarInt encoding of the length of the ring as well
|
||||
1 + 1 + 1 + (8 * ring_len) + 32
|
||||
|
@ -33,12 +33,12 @@ impl Input {
|
|||
|
||||
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||
match self {
|
||||
Input::Gen(height) => {
|
||||
Self::Gen(height) => {
|
||||
w.write_all(&[255])?;
|
||||
write_varint(height, w)
|
||||
}
|
||||
|
||||
Input::ToKey { amount, key_offsets, key_image } => {
|
||||
Self::ToKey { amount, key_offsets, key_image } => {
|
||||
w.write_all(&[2])?;
|
||||
write_varint(&amount.unwrap_or(0), w)?;
|
||||
write_vec(write_varint, key_offsets, w)?;
|
||||
|
@ -47,19 +47,20 @@ impl Input {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut res = vec![];
|
||||
self.write(&mut res).unwrap();
|
||||
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)? {
|
||||
255 => Input::Gen(read_varint(r)?),
|
||||
255 => Self::Gen(read_varint(r)?),
|
||||
2 => {
|
||||
let amount = read_varint(r)?;
|
||||
let amount = if (amount == 0) && interpret_as_rct { None } else { Some(amount) };
|
||||
Input::ToKey {
|
||||
Self::ToKey {
|
||||
amount,
|
||||
key_offsets: read_vec(read_varint, r)?,
|
||||
key_image: read_torsion_free_point(r)?,
|
||||
|
@ -81,7 +82,7 @@ pub struct Output {
|
|||
}
|
||||
|
||||
impl Output {
|
||||
pub(crate) fn fee_weight() -> usize {
|
||||
pub(crate) const fn fee_weight() -> usize {
|
||||
1 + 1 + 32 + 1
|
||||
}
|
||||
|
||||
|
@ -95,13 +96,14 @@ impl Output {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut res = Vec::with_capacity(8 + 1 + 32);
|
||||
self.write(&mut res).unwrap();
|
||||
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 = if interpret_as_rct {
|
||||
if amount != 0 {
|
||||
|
@ -121,7 +123,7 @@ impl Output {
|
|||
))?,
|
||||
};
|
||||
|
||||
Ok(Output {
|
||||
Ok(Self {
|
||||
amount,
|
||||
key: CompressedEdwardsY(read_bytes(r)?),
|
||||
view_tag: if view_tag { Some(read_byte(r)?) } else { None },
|
||||
|
@ -137,22 +139,22 @@ pub enum Timelock {
|
|||
}
|
||||
|
||||
impl Timelock {
|
||||
fn from_raw(raw: u64) -> Timelock {
|
||||
fn from_raw(raw: u64) -> Self {
|
||||
if raw == 0 {
|
||||
Timelock::None
|
||||
Self::None
|
||||
} else if raw < 500_000_000 {
|
||||
Timelock::Block(usize::try_from(raw).unwrap())
|
||||
Self::Block(usize::try_from(raw).unwrap())
|
||||
} else {
|
||||
Timelock::Time(raw)
|
||||
Self::Time(raw)
|
||||
}
|
||||
}
|
||||
|
||||
fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||
write_varint(
|
||||
&match self {
|
||||
Timelock::None => 0,
|
||||
Timelock::Block(block) => (*block).try_into().unwrap(),
|
||||
Timelock::Time(time) => *time,
|
||||
Self::None => 0,
|
||||
Self::Block(block) => (*block).try_into().unwrap(),
|
||||
Self::Time(time) => *time,
|
||||
},
|
||||
w,
|
||||
)
|
||||
|
@ -162,9 +164,9 @@ impl Timelock {
|
|||
impl PartialOrd for Timelock {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
match (self, other) {
|
||||
(Timelock::None, _) => Some(Ordering::Less),
|
||||
(Timelock::Block(a), Timelock::Block(b)) => a.partial_cmp(b),
|
||||
(Timelock::Time(a), Timelock::Time(b)) => a.partial_cmp(b),
|
||||
(Self::None, _) => Some(Ordering::Less),
|
||||
(Self::Block(a), Self::Block(b)) => a.partial_cmp(b),
|
||||
(Self::Time(a), Self::Time(b)) => a.partial_cmp(b),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -200,13 +202,14 @@ impl TransactionPrefix {
|
|||
w.write_all(&self.extra)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut res = vec![];
|
||||
self.write(&mut res).unwrap();
|
||||
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)?;
|
||||
// TODO: Create an enum out of version
|
||||
if (version == 0) || (version > 2) {
|
||||
|
@ -221,7 +224,7 @@ impl TransactionPrefix {
|
|||
}
|
||||
let is_miner_tx = matches!(inputs[0], Input::Gen { .. });
|
||||
|
||||
let mut prefix = TransactionPrefix {
|
||||
let mut prefix = Self {
|
||||
version,
|
||||
timelock,
|
||||
inputs,
|
||||
|
@ -232,6 +235,7 @@ impl TransactionPrefix {
|
|||
Ok(prefix)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn hash(&self) -> [u8; 32] {
|
||||
hash(&self.serialize())
|
||||
}
|
||||
|
@ -273,13 +277,14 @@ impl Transaction {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut res = Vec::with_capacity(2048);
|
||||
self.write(&mut res).unwrap();
|
||||
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 mut signatures = vec![];
|
||||
let mut rct_signatures = RctSignatures {
|
||||
|
@ -328,9 +333,10 @@ impl Transaction {
|
|||
Err(io::Error::new(io::ErrorKind::Other, "Tried to deserialize unknown version"))?;
|
||||
}
|
||||
|
||||
Ok(Transaction { prefix, signatures, rct_signatures })
|
||||
Ok(Self { prefix, signatures, rct_signatures })
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn hash(&self) -> [u8; 32] {
|
||||
let mut buf = Vec::with_capacity(2048);
|
||||
if self.prefix.version == 1 {
|
||||
|
@ -359,6 +365,7 @@ impl Transaction {
|
|||
}
|
||||
|
||||
/// Calculate the hash of this transaction as needed for signing it.
|
||||
#[must_use]
|
||||
pub fn signature_hash(&self) -> [u8; 32] {
|
||||
let mut buf = Vec::with_capacity(2048);
|
||||
let mut sig_hash = Vec::with_capacity(96);
|
||||
|
|
|
@ -32,18 +32,21 @@ pub struct SubaddressIndex {
|
|||
}
|
||||
|
||||
impl SubaddressIndex {
|
||||
pub const fn new(account: u32, address: u32) -> Option<SubaddressIndex> {
|
||||
#[must_use]
|
||||
pub const fn new(account: u32, address: u32) -> Option<Self> {
|
||||
if (account == 0) && (address == 0) {
|
||||
return None;
|
||||
}
|
||||
Some(SubaddressIndex { account, address })
|
||||
Some(Self { account, address })
|
||||
}
|
||||
|
||||
pub fn account(&self) -> u32 {
|
||||
#[must_use]
|
||||
pub const fn account(&self) -> u32 {
|
||||
self.account
|
||||
}
|
||||
|
||||
pub fn address(&self) -> u32 {
|
||||
#[must_use]
|
||||
pub const fn address(&self) -> u32 {
|
||||
self.address
|
||||
}
|
||||
}
|
||||
|
@ -58,23 +61,25 @@ pub enum AddressSpec {
|
|||
}
|
||||
|
||||
impl AddressType {
|
||||
pub fn is_subaddress(&self) -> bool {
|
||||
matches!(self, AddressType::Subaddress) ||
|
||||
matches!(self, AddressType::Featured { subaddress: true, .. })
|
||||
#[must_use]
|
||||
pub const fn is_subaddress(&self) -> bool {
|
||||
matches!(self, Self::Subaddress) || matches!(self, Self::Featured { subaddress: true, .. })
|
||||
}
|
||||
|
||||
pub fn payment_id(&self) -> Option<[u8; 8]> {
|
||||
if let AddressType::Integrated(id) = self {
|
||||
#[must_use]
|
||||
pub const fn payment_id(&self) -> Option<[u8; 8]> {
|
||||
if let Self::Integrated(id) = self {
|
||||
Some(*id)
|
||||
} else if let AddressType::Featured { payment_id, .. } = self {
|
||||
} else if let Self::Featured { payment_id, .. } = self {
|
||||
*payment_id
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_guaranteed(&self) -> bool {
|
||||
matches!(self, AddressType::Featured { guaranteed: true, .. })
|
||||
#[must_use]
|
||||
pub const fn is_guaranteed(&self) -> bool {
|
||||
matches!(self, Self::Featured { guaranteed: true, .. })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -142,8 +147,9 @@ impl<B: AddressBytes> AddressMeta<B> {
|
|||
}
|
||||
|
||||
/// Create an address's metadata.
|
||||
pub fn new(network: Network, kind: AddressType) -> Self {
|
||||
AddressMeta { _bytes: PhantomData, network, kind }
|
||||
#[must_use]
|
||||
pub const fn new(network: Network, kind: AddressType) -> Self {
|
||||
Self { _bytes: PhantomData, network, kind }
|
||||
}
|
||||
|
||||
// Returns an incomplete instantiation in the case of Integrated/Featured addresses
|
||||
|
@ -160,7 +166,7 @@ impl<B: AddressBytes> AddressMeta<B> {
|
|||
}
|
||||
_ => None,
|
||||
} {
|
||||
meta = Some(AddressMeta::new(network, kind));
|
||||
meta = Some(Self::new(network, kind));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -168,15 +174,18 @@ impl<B: AddressBytes> AddressMeta<B> {
|
|||
meta.ok_or(AddressError::InvalidByte)
|
||||
}
|
||||
|
||||
pub fn is_subaddress(&self) -> bool {
|
||||
#[must_use]
|
||||
pub const fn is_subaddress(&self) -> bool {
|
||||
self.kind.is_subaddress()
|
||||
}
|
||||
|
||||
pub fn payment_id(&self) -> Option<[u8; 8]> {
|
||||
#[must_use]
|
||||
pub const fn payment_id(&self) -> Option<[u8; 8]> {
|
||||
self.kind.payment_id()
|
||||
}
|
||||
|
||||
pub fn is_guaranteed(&self) -> bool {
|
||||
#[must_use]
|
||||
pub const fn is_guaranteed(&self) -> bool {
|
||||
self.kind.is_guaranteed()
|
||||
}
|
||||
}
|
||||
|
@ -216,8 +225,9 @@ impl<B: AddressBytes> ToString for Address<B> {
|
|||
}
|
||||
|
||||
impl<B: AddressBytes> Address<B> {
|
||||
pub fn new(meta: AddressMeta<B>, spend: EdwardsPoint, view: EdwardsPoint) -> Self {
|
||||
Address { meta, spend, view }
|
||||
#[must_use]
|
||||
pub const fn new(meta: AddressMeta<B>, spend: EdwardsPoint, view: EdwardsPoint) -> Self {
|
||||
Self { meta, spend, view }
|
||||
}
|
||||
|
||||
pub fn from_str_raw(s: &str) -> Result<Self, AddressError> {
|
||||
|
@ -267,7 +277,7 @@ impl<B: AddressBytes> Address<B> {
|
|||
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> {
|
||||
|
@ -280,30 +290,35 @@ impl<B: AddressBytes> Address<B> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn network(&self) -> Network {
|
||||
#[must_use]
|
||||
pub const fn network(&self) -> Network {
|
||||
self.meta.network
|
||||
}
|
||||
|
||||
pub fn is_subaddress(&self) -> bool {
|
||||
#[must_use]
|
||||
pub const fn is_subaddress(&self) -> bool {
|
||||
self.meta.is_subaddress()
|
||||
}
|
||||
|
||||
pub fn payment_id(&self) -> Option<[u8; 8]> {
|
||||
#[must_use]
|
||||
pub const fn payment_id(&self) -> Option<[u8; 8]> {
|
||||
self.meta.payment_id()
|
||||
}
|
||||
|
||||
pub fn is_guaranteed(&self) -> bool {
|
||||
#[must_use]
|
||||
pub const fn is_guaranteed(&self) -> bool {
|
||||
self.meta.is_guaranteed()
|
||||
}
|
||||
}
|
||||
|
||||
/// Instantiation of the Address type with Monero's network bytes.
|
||||
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.
|
||||
impl MoneroAddress {
|
||||
pub fn from<B: AddressBytes>(address: Address<B>) -> MoneroAddress {
|
||||
MoneroAddress::new(
|
||||
#[must_use]
|
||||
pub const fn from<B: AddressBytes>(address: Address<B>) -> Self {
|
||||
Self::new(
|
||||
AddressMeta::new(address.meta.network, address.meta.kind),
|
||||
address.spend,
|
||||
address.view,
|
||||
|
|
|
@ -31,7 +31,7 @@ const TIP_APPLICATION: f64 = (LOCK_WINDOW * BLOCK_TIME) as f64;
|
|||
static DISTRIBUTION_CELL: OnceLock<Mutex<Vec<u64>>> = OnceLock::new();
|
||||
#[allow(non_snake_case)]
|
||||
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)]
|
||||
|
@ -141,6 +141,7 @@ pub struct Decoys {
|
|||
}
|
||||
|
||||
impl Decoys {
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize {
|
||||
self.offsets.len()
|
||||
}
|
||||
|
@ -152,7 +153,7 @@ impl Decoys {
|
|||
ring_len: usize,
|
||||
height: usize,
|
||||
inputs: &[SpendableOutput],
|
||||
) -> Result<Vec<Decoys>, RpcError> {
|
||||
) -> Result<Vec<Self>, RpcError> {
|
||||
#[cfg(not(feature = "std"))]
|
||||
let mut distribution = DISTRIBUTION().lock();
|
||||
#[cfg(feature = "std")]
|
||||
|
@ -265,7 +266,7 @@ impl Decoys {
|
|||
// members
|
||||
}
|
||||
|
||||
res.push(Decoys {
|
||||
res.push(Self {
|
||||
// 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(),
|
||||
offsets: offset(&ring.iter().map(|output| output.0).collect::<Vec<_>>()),
|
||||
|
|
|
@ -30,14 +30,14 @@ pub enum 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 {
|
||||
// Don't perform the xor since this isn't intended to be encrypted with xor
|
||||
PaymentId::Unencrypted(_) => self,
|
||||
PaymentId::Encrypted(id) => {
|
||||
PaymentId::Encrypted((u64::from_le_bytes(id) ^ u64::from_le_bytes(bytes)).to_le_bytes())
|
||||
Self::Unencrypted(_) => self,
|
||||
Self::Encrypted(id) => {
|
||||
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 {
|
||||
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||
match self {
|
||||
PaymentId::Unencrypted(id) => {
|
||||
Self::Unencrypted(id) => {
|
||||
w.write_all(&[PAYMENT_ID_MARKER])?;
|
||||
w.write_all(id)?;
|
||||
}
|
||||
PaymentId::Encrypted(id) => {
|
||||
Self::Encrypted(id) => {
|
||||
w.write_all(&[ENCRYPTED_PAYMENT_ID_MARKER])?;
|
||||
w.write_all(id)?;
|
||||
}
|
||||
|
@ -58,10 +58,10 @@ impl PaymentId {
|
|||
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)? {
|
||||
0 => PaymentId::Unencrypted(read_bytes(r)?),
|
||||
1 => PaymentId::Encrypted(read_bytes(r)?),
|
||||
0 => Self::Unencrypted(read_bytes(r)?),
|
||||
1 => Self::Encrypted(read_bytes(r)?),
|
||||
_ => Err(io::Error::new(io::ErrorKind::Other, "unknown payment ID type"))?,
|
||||
})
|
||||
}
|
||||
|
@ -79,20 +79,20 @@ pub enum ExtraField {
|
|||
impl ExtraField {
|
||||
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||
match self {
|
||||
ExtraField::PublicKey(key) => {
|
||||
Self::PublicKey(key) => {
|
||||
w.write_all(&[1])?;
|
||||
w.write_all(&key.compress().to_bytes())?;
|
||||
}
|
||||
ExtraField::Nonce(data) => {
|
||||
Self::Nonce(data) => {
|
||||
w.write_all(&[2])?;
|
||||
write_vec(write_byte, data, w)?;
|
||||
}
|
||||
ExtraField::MergeMining(height, merkle) => {
|
||||
Self::MergeMining(height, merkle) => {
|
||||
w.write_all(&[3])?;
|
||||
write_varint(&u64::try_from(*height).unwrap(), w)?;
|
||||
w.write_all(merkle)?;
|
||||
}
|
||||
ExtraField::PublicKeys(keys) => {
|
||||
Self::PublicKeys(keys) => {
|
||||
w.write_all(&[4])?;
|
||||
write_vec(write_point, keys, w)?;
|
||||
}
|
||||
|
@ -100,22 +100,22 @@ impl ExtraField {
|
|||
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)? {
|
||||
1 => ExtraField::PublicKey(read_point(r)?),
|
||||
2 => ExtraField::Nonce({
|
||||
1 => Self::PublicKey(read_point(r)?),
|
||||
2 => Self::Nonce({
|
||||
let nonce = read_vec(read_byte, r)?;
|
||||
if nonce.len() > MAX_TX_EXTRA_NONCE_SIZE {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "too long nonce"))?;
|
||||
}
|
||||
nonce
|
||||
}),
|
||||
3 => ExtraField::MergeMining(
|
||||
3 => Self::MergeMining(
|
||||
usize::try_from(read_varint(r)?)
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::Other, "varint for height exceeds usize"))?,
|
||||
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"))?,
|
||||
})
|
||||
}
|
||||
|
@ -124,6 +124,7 @@ impl ExtraField {
|
|||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
|
||||
pub struct Extra(Vec<ExtraField>);
|
||||
impl Extra {
|
||||
#[must_use]
|
||||
pub fn keys(&self) -> Option<(EdwardsPoint, Option<Vec<EdwardsPoint>>)> {
|
||||
let mut key = None;
|
||||
let mut additional = None;
|
||||
|
@ -131,7 +132,7 @@ impl Extra {
|
|||
match field.clone() {
|
||||
ExtraField::PublicKey(this_key) => key = key.or(Some(this_key)),
|
||||
ExtraField::PublicKeys(these_additional) => {
|
||||
additional = additional.or(Some(these_additional))
|
||||
additional = additional.or(Some(these_additional));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
@ -140,6 +141,7 @@ impl Extra {
|
|||
key.map(|key| (key, additional))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn payment_id(&self) -> Option<PaymentId> {
|
||||
for field in &self.0 {
|
||||
if let ExtraField::Nonce(data) = field {
|
||||
|
@ -149,6 +151,7 @@ impl Extra {
|
|||
None
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn data(&self) -> Vec<Vec<u8>> {
|
||||
let mut res = vec![];
|
||||
for field in &self.0 {
|
||||
|
@ -161,8 +164,8 @@ impl Extra {
|
|||
res
|
||||
}
|
||||
|
||||
pub(crate) fn new(key: EdwardsPoint, additional: Vec<EdwardsPoint>) -> Extra {
|
||||
let mut res = Extra(Vec::with_capacity(3));
|
||||
pub(crate) fn new(key: EdwardsPoint, additional: Vec<EdwardsPoint>) -> Self {
|
||||
let mut res = Self(Vec::with_capacity(3));
|
||||
res.push(ExtraField::PublicKey(key));
|
||||
if !additional.is_empty() {
|
||||
res.push(ExtraField::PublicKeys(additional));
|
||||
|
@ -198,14 +201,15 @@ impl Extra {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut buf = vec![];
|
||||
self.write(&mut buf).unwrap();
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Extra> {
|
||||
let mut res = Extra(vec![]);
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||
let mut res = Self(vec![]);
|
||||
let mut field;
|
||||
while {
|
||||
field = ExtraField::read(r);
|
||||
|
|
|
@ -80,7 +80,7 @@ pub(crate) fn shared_key(
|
|||
|
||||
// uniqueness ||
|
||||
let shared_key = if let Some(uniqueness) = uniqueness {
|
||||
[uniqueness.as_ref(), &output_derivation].concat().to_vec()
|
||||
[uniqueness.as_ref(), &output_derivation].concat()
|
||||
} else {
|
||||
output_derivation
|
||||
};
|
||||
|
@ -141,14 +141,17 @@ pub struct ViewPair {
|
|||
}
|
||||
|
||||
impl ViewPair {
|
||||
pub fn new(spend: EdwardsPoint, view: Zeroizing<Scalar>) -> ViewPair {
|
||||
ViewPair { spend, view }
|
||||
#[must_use]
|
||||
pub const fn new(spend: EdwardsPoint, view: Zeroizing<Scalar>) -> Self {
|
||||
Self { spend, view }
|
||||
}
|
||||
|
||||
pub fn spend(&self) -> EdwardsPoint {
|
||||
#[must_use]
|
||||
pub const fn spend(&self) -> EdwardsPoint {
|
||||
self.spend
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn view(&self) -> EdwardsPoint {
|
||||
self.view.deref() * &ED25519_BASEPOINT_TABLE
|
||||
}
|
||||
|
@ -173,6 +176,7 @@ impl ViewPair {
|
|||
}
|
||||
|
||||
/// Returns an address with the provided specification.
|
||||
#[must_use]
|
||||
pub fn address(&self, network: Network, spec: AddressSpec) -> MoneroAddress {
|
||||
let mut spend = self.spend;
|
||||
let mut view: EdwardsPoint = self.view.deref() * &ED25519_BASEPOINT_TABLE;
|
||||
|
@ -241,15 +245,20 @@ impl ZeroizeOnDrop for Scanner {}
|
|||
|
||||
impl Scanner {
|
||||
/// Create a Scanner from a ViewPair.
|
||||
///
|
||||
/// 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 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
|
||||
/// bug (specifically the Guaranteed feature from Featured Addresses).
|
||||
pub fn from_view(pair: ViewPair, burning_bug: Option<HashSet<CompressedEdwardsY>>) -> Scanner {
|
||||
#[must_use]
|
||||
pub fn from_view(pair: ViewPair, burning_bug: Option<HashSet<CompressedEdwardsY>>) -> Self {
|
||||
let mut subaddresses = HashMap::new();
|
||||
subaddresses.insert(pair.spend.compress(), None);
|
||||
Scanner { pair, subaddresses, burning_bug }
|
||||
Self { pair, subaddresses, burning_bug }
|
||||
}
|
||||
|
||||
/// Register a subaddress.
|
||||
|
|
|
@ -32,14 +32,15 @@ impl AbsoluteId {
|
|||
w.write_all(&[self.o])
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut serialized = Vec::with_capacity(32 + 1);
|
||||
self.write(&mut serialized).unwrap();
|
||||
serialized
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<AbsoluteId> {
|
||||
Ok(AbsoluteId { tx: read_bytes(r)?, o: read_byte(r)? })
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||
Ok(Self { tx: read_bytes(r)?, o: read_byte(r)? })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,14 +61,15 @@ impl OutputData {
|
|||
w.write_all(&self.commitment.amount.to_le_bytes())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut serialized = Vec::with_capacity(32 + 32 + 32 + 8);
|
||||
self.write(&mut serialized).unwrap();
|
||||
serialized
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<OutputData> {
|
||||
Ok(OutputData {
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||
Ok(Self {
|
||||
key: read_point(r)?,
|
||||
key_offset: read_scalar(r)?,
|
||||
commitment: Commitment::new(read_scalar(r)?, read_u64(r)?),
|
||||
|
@ -108,13 +110,14 @@ impl Metadata {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut serialized = Vec::with_capacity(1 + 8 + 1);
|
||||
self.write(&mut serialized).unwrap();
|
||||
serialized
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Metadata> {
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||
let subaddress = if read_byte(r)? == 1 {
|
||||
Some(
|
||||
SubaddressIndex::new(read_u32(r)?, read_u32(r)?)
|
||||
|
@ -124,7 +127,7 @@ impl Metadata {
|
|||
None
|
||||
};
|
||||
|
||||
Ok(Metadata {
|
||||
Ok(Self {
|
||||
subaddress,
|
||||
payment_id: read_bytes(r)?,
|
||||
arbitrary_data: {
|
||||
|
@ -148,18 +151,22 @@ pub struct ReceivedOutput {
|
|||
}
|
||||
|
||||
impl ReceivedOutput {
|
||||
pub fn key(&self) -> EdwardsPoint {
|
||||
#[must_use]
|
||||
pub const fn key(&self) -> EdwardsPoint {
|
||||
self.data.key
|
||||
}
|
||||
|
||||
pub fn key_offset(&self) -> Scalar {
|
||||
#[must_use]
|
||||
pub const fn key_offset(&self) -> Scalar {
|
||||
self.data.key_offset
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn commitment(&self) -> Commitment {
|
||||
self.data.commitment.clone()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn arbitrary_data(&self) -> &[Vec<u8>] {
|
||||
&self.metadata.arbitrary_data
|
||||
}
|
||||
|
@ -170,14 +177,15 @@ impl ReceivedOutput {
|
|||
self.metadata.write(w)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut serialized = vec![];
|
||||
self.write(&mut serialized).unwrap();
|
||||
serialized
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<ReceivedOutput> {
|
||||
Ok(ReceivedOutput {
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||
Ok(Self {
|
||||
absolute: AbsoluteId::read(r)?,
|
||||
data: OutputData::read(r)?,
|
||||
metadata: Metadata::read(r)?,
|
||||
|
@ -209,24 +217,28 @@ impl SpendableOutput {
|
|||
pub async fn from<RPC: RpcConnection>(
|
||||
rpc: &Rpc<RPC>,
|
||||
output: ReceivedOutput,
|
||||
) -> Result<SpendableOutput, RpcError> {
|
||||
let mut output = SpendableOutput { output, global_index: 0 };
|
||||
) -> Result<Self, RpcError> {
|
||||
let mut output = Self { output, global_index: 0 };
|
||||
output.refresh_global_index(rpc).await?;
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
pub fn key(&self) -> EdwardsPoint {
|
||||
#[must_use]
|
||||
pub const fn key(&self) -> EdwardsPoint {
|
||||
self.output.key()
|
||||
}
|
||||
|
||||
pub fn key_offset(&self) -> Scalar {
|
||||
#[must_use]
|
||||
pub const fn key_offset(&self) -> Scalar {
|
||||
self.output.key_offset()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn commitment(&self) -> Commitment {
|
||||
self.output.commitment()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn arbitrary_data(&self) -> &[Vec<u8>] {
|
||||
self.output.arbitrary_data()
|
||||
}
|
||||
|
@ -242,8 +254,8 @@ impl SpendableOutput {
|
|||
serialized
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<SpendableOutput> {
|
||||
Ok(SpendableOutput { output: ReceivedOutput::read(r)?, global_index: read_u64(r)? })
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Self> {
|
||||
Ok(Self { output: ReceivedOutput::read(r)?, global_index: read_u64(r)? })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,11 +270,13 @@ impl<O: Clone + Zeroize> Drop for Timelocked<O> {
|
|||
impl<O: Clone + Zeroize> ZeroizeOnDrop for Timelocked<O> {}
|
||||
|
||||
impl<O: Clone + Zeroize> Timelocked<O> {
|
||||
pub fn timelock(&self) -> Timelock {
|
||||
#[must_use]
|
||||
pub const fn timelock(&self) -> Timelock {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Return the outputs if they're not timelocked, or an empty vector if they are.
|
||||
#[must_use]
|
||||
pub fn not_locked(&self) -> Vec<O> {
|
||||
if self.0 == Timelock::None {
|
||||
return self.1.clone();
|
||||
|
@ -271,6 +285,7 @@ impl<O: Clone + Zeroize> Timelocked<O> {
|
|||
}
|
||||
|
||||
/// 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>> {
|
||||
// If the Timelocks are comparable, return the outputs if they're now unlocked
|
||||
if self.0 <= timelock {
|
||||
|
@ -280,6 +295,7 @@ impl<O: Clone + Zeroize> Timelocked<O> {
|
|||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn ignore_timelock(&self) -> Vec<O> {
|
||||
self.1.clone()
|
||||
}
|
||||
|
@ -293,16 +309,11 @@ impl Scanner {
|
|||
return Timelocked(tx.prefix.timelock, vec![]);
|
||||
}
|
||||
|
||||
let extra = Extra::read::<&[u8]>(&mut tx.prefix.extra.as_ref());
|
||||
let extra = if let Ok(extra) = extra {
|
||||
extra
|
||||
} else {
|
||||
let Ok(extra) = Extra::read::<&[u8]>(&mut tx.prefix.extra.as_ref()) else {
|
||||
return Timelocked(tx.prefix.timelock, vec![]);
|
||||
};
|
||||
|
||||
let (tx_key, additional) = if let Some((tx_key, additional)) = extra.keys() {
|
||||
(tx_key, additional)
|
||||
} else {
|
||||
let Some((tx_key, additional)) = extra.keys() else {
|
||||
return Timelocked(tx.prefix.timelock, vec![]);
|
||||
};
|
||||
|
||||
|
@ -324,17 +335,17 @@ impl Scanner {
|
|||
let output_key = output_key.unwrap();
|
||||
|
||||
for key in [Some(Some(&tx_key)), additional.as_ref().map(|additional| additional.get(o))] {
|
||||
let key = if let Some(Some(key)) = key {
|
||||
key
|
||||
} else if let Some(None) = key {
|
||||
// This is non-standard. There were additional keys, yet not one for this output
|
||||
// https://github.com/monero-project/monero/
|
||||
// blob/04a1e2875d6e35e27bb21497988a6c822d319c28/
|
||||
// src/cryptonote_basic/cryptonote_format_utils.cpp#L1062
|
||||
// TODO: Should this return? Where does Monero set the trap handler for this exception?
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
let Some(Some(key)) = key else {
|
||||
if let Some(None) = key {
|
||||
// This is non-standard. There were additional keys, yet not one for this output
|
||||
// https://github.com/monero-project/monero/
|
||||
// blob/04a1e2875d6e35e27bb21497988a6c822d319c28/
|
||||
// src/cryptonote_basic/cryptonote_format_utils.cpp#L1062
|
||||
// TODO: Should this return? Where does Monero set the trap handler for this exception?
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
};
|
||||
let (view_tag, shared_key, payment_id_xor) = shared_key(
|
||||
if self.burning_bug.is_none() { Some(uniqueness(&tx.prefix.inputs)) } else { None },
|
||||
|
|
|
@ -33,8 +33,8 @@ struct WordList {
|
|||
}
|
||||
|
||||
impl WordList {
|
||||
fn new(word_list: Vec<&'static str>, prefix_length: usize) -> WordList {
|
||||
let mut lang = WordList {
|
||||
fn new(word_list: Vec<&'static str>, prefix_length: usize) -> Self {
|
||||
let mut lang = Self {
|
||||
word_list,
|
||||
word_map: HashMap::new(),
|
||||
trimmed_word_map: HashMap::new(),
|
||||
|
@ -74,10 +74,10 @@ fn LANGUAGES() -> &'static HashMap<Language, WordList> {
|
|||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn trim_by_lang(word: &str, lang: Language) -> String {
|
||||
if lang != Language::EnglishOld {
|
||||
word.chars().take(LANGUAGES()[&lang].unique_prefix_length).collect()
|
||||
} else {
|
||||
if lang == Language::EnglishOld {
|
||||
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)]
|
||||
pub struct ClassicSeed(Zeroizing<String>);
|
||||
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)))
|
||||
}
|
||||
|
||||
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)?;
|
||||
|
||||
// Make sure this is a valid scalar
|
||||
|
@ -257,7 +257,7 @@ impl ClassicSeed {
|
|||
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)))
|
||||
}
|
||||
|
||||
|
|
|
@ -7,21 +7,25 @@ use rand_core::{RngCore, CryptoRng};
|
|||
pub(crate) mod classic;
|
||||
use classic::{CLASSIC_SEED_LENGTH, CLASSIC_SEED_LENGTH_WITH_CHECKSUM, ClassicSeed};
|
||||
|
||||
/// Error when decoding a seed.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "std", derive(thiserror::Error))]
|
||||
pub enum SeedError {
|
||||
#[cfg_attr(feature = "std", error("invalid number of words in seed"))]
|
||||
InvalidSeedLength,
|
||||
#[cfg_attr(feature = "std", error("unknown language"))]
|
||||
UnknownLanguage,
|
||||
#[cfg_attr(feature = "std", error("invalid checksum"))]
|
||||
InvalidChecksum,
|
||||
#[cfg_attr(feature = "std", error("english old seeds don't support checksums"))]
|
||||
EnglishOldWithChecksum,
|
||||
#[cfg_attr(feature = "std", error("invalid seed"))]
|
||||
InvalidSeed,
|
||||
#[allow(clippy::std_instead_of_core)]
|
||||
mod seed_error {
|
||||
/// Error when decoding a seed.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "std", derive(thiserror::Error))]
|
||||
pub enum SeedError {
|
||||
#[cfg_attr(feature = "std", error("invalid number of words in seed"))]
|
||||
InvalidSeedLength,
|
||||
#[cfg_attr(feature = "std", error("unknown language"))]
|
||||
UnknownLanguage,
|
||||
#[cfg_attr(feature = "std", error("invalid checksum"))]
|
||||
InvalidChecksum,
|
||||
#[cfg_attr(feature = "std", error("english old seeds don't support checksums"))]
|
||||
EnglishOldWithChecksum,
|
||||
#[cfg_attr(feature = "std", error("invalid seed"))]
|
||||
InvalidSeed,
|
||||
}
|
||||
}
|
||||
pub use seed_error::SeedError;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum Language {
|
||||
|
@ -50,43 +54,46 @@ pub enum Seed {
|
|||
impl fmt::Debug for Seed {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Seed::Classic(_) => f.debug_struct("Seed::Classic").finish_non_exhaustive(),
|
||||
Self::Classic(_) => f.debug_struct("Seed::Classic").finish_non_exhaustive(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Seed {
|
||||
/// Create a new seed.
|
||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R, lang: Language) -> Seed {
|
||||
Seed::Classic(ClassicSeed::new(rng, lang))
|
||||
#[must_use]
|
||||
pub fn new<R: RngCore + CryptoRng>(rng: &mut R, lang: Language) -> Self {
|
||||
Self::Classic(ClassicSeed::new(rng, lang))
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
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)?,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a Seed from entropy.
|
||||
pub fn from_entropy(lang: Language, entropy: Zeroizing<[u8; 32]>) -> Option<Seed> {
|
||||
ClassicSeed::from_entropy(lang, entropy).map(Seed::Classic)
|
||||
#[must_use]
|
||||
pub fn from_entropy(lang: Language, entropy: Zeroizing<[u8; 32]>) -> Option<Self> {
|
||||
ClassicSeed::from_entropy(lang, entropy).map(Self::Classic)
|
||||
}
|
||||
|
||||
/// Convert a seed to a String.
|
||||
pub fn to_string(&self) -> Zeroizing<String> {
|
||||
match self {
|
||||
Seed::Classic(seed) => seed.to_string(),
|
||||
Self::Classic(seed) => seed.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the entropy for this seed.
|
||||
#[must_use]
|
||||
pub fn entropy(&self) -> Zeroizing<[u8; 32]> {
|
||||
match self {
|
||||
Seed::Classic(seed) => seed.entropy(),
|
||||
Self::Classic(seed) => seed.entropy(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ struct SignableTransactionBuilderInternal {
|
|||
impl SignableTransactionBuilderInternal {
|
||||
// Takes in the change address so users don't miss that they have to manually set one
|
||||
// If they don't, all leftover funds will become part of the fee
|
||||
fn new(protocol: Protocol, fee: Fee, change_address: Option<Change>) -> Self {
|
||||
const fn new(protocol: Protocol, fee: Fee, change_address: Option<Change>) -> Self {
|
||||
Self {
|
||||
protocol,
|
||||
fee,
|
||||
|
@ -81,15 +81,17 @@ impl Eq for SignableTransactionBuilder {}
|
|||
|
||||
impl Zeroize for SignableTransactionBuilder {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.write().unwrap().zeroize()
|
||||
self.0.write().unwrap().zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::return_self_not_must_use)]
|
||||
impl SignableTransactionBuilder {
|
||||
fn shallow_copy(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn new(protocol: Protocol, fee: Fee, change_address: Option<Change>) -> Self {
|
||||
Self(Arc::new(RwLock::new(SignableTransactionBuilderInternal::new(
|
||||
protocol,
|
||||
|
|
|
@ -72,7 +72,7 @@ impl SendOutput {
|
|||
output: (usize, (MoneroAddress, u64)),
|
||||
ecdh: EdwardsPoint,
|
||||
R: EdwardsPoint,
|
||||
) -> (SendOutput, Option<[u8; 8]>) {
|
||||
) -> (Self, Option<[u8; 8]>) {
|
||||
let o = output.0;
|
||||
let output = output.1;
|
||||
|
||||
|
@ -80,7 +80,7 @@ impl SendOutput {
|
|||
shared_key(Some(unique).filter(|_| output.0.is_guaranteed()), ecdh, o);
|
||||
|
||||
(
|
||||
SendOutput {
|
||||
Self {
|
||||
R,
|
||||
view_tag,
|
||||
dest: ((&shared_key * &ED25519_BASEPOINT_TABLE) + output.0.spend),
|
||||
|
@ -98,16 +98,16 @@ impl SendOutput {
|
|||
r: &Zeroizing<Scalar>,
|
||||
unique: [u8; 32],
|
||||
output: (usize, (MoneroAddress, u64)),
|
||||
) -> (SendOutput, Option<[u8; 8]>) {
|
||||
) -> (Self, Option<[u8; 8]>) {
|
||||
let address = output.1 .0;
|
||||
SendOutput::internal(
|
||||
Self::internal(
|
||||
unique,
|
||||
output,
|
||||
r.deref() * address.view,
|
||||
if !address.is_subaddress() {
|
||||
r.deref() * &ED25519_BASEPOINT_TABLE
|
||||
} else {
|
||||
if address.is_subaddress() {
|
||||
r.deref() * address.spend
|
||||
} else {
|
||||
r.deref() * &ED25519_BASEPOINT_TABLE
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -116,8 +116,8 @@ impl SendOutput {
|
|||
ecdh: EdwardsPoint,
|
||||
unique: [u8; 32],
|
||||
output: (usize, (MoneroAddress, u64)),
|
||||
) -> (SendOutput, Option<[u8; 8]>) {
|
||||
SendOutput::internal(unique, output, ecdh, ED25519_BASEPOINT_POINT)
|
||||
) -> (Self, Option<[u8; 8]>) {
|
||||
Self::internal(unique, output, ecdh, ED25519_BASEPOINT_POINT)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,6 +211,7 @@ pub struct Fee {
|
|||
}
|
||||
|
||||
impl Fee {
|
||||
#[must_use]
|
||||
pub fn calculate(&self, weight: usize) -> u64 {
|
||||
((((self.per_weight * u64::try_from(weight).unwrap()) - 1) / self.mask) + 1) * self.mask
|
||||
}
|
||||
|
@ -261,8 +262,9 @@ impl fmt::Debug for Change {
|
|||
|
||||
impl Change {
|
||||
/// Create a change output specification from a ViewPair, as needed to maintain privacy.
|
||||
pub fn new(view: &ViewPair, guaranteed: bool) -> Change {
|
||||
Change {
|
||||
#[must_use]
|
||||
pub fn new(view: &ViewPair, guaranteed: bool) -> Self {
|
||||
Self {
|
||||
address: view.address(
|
||||
Network::Mainnet,
|
||||
if !guaranteed {
|
||||
|
@ -275,10 +277,12 @@ impl Change {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create a fingerprintable change output specification which will harm privacy. Only use this
|
||||
/// if you know what you're doing.
|
||||
pub fn fingerprintable(address: MoneroAddress) -> Change {
|
||||
Change { address, view: None }
|
||||
/// Create a fingerprintable change output specification which will harm privacy.
|
||||
///
|
||||
/// Only use this if you know what you're doing.
|
||||
#[must_use]
|
||||
pub const fn fingerprintable(address: MoneroAddress) -> Self {
|
||||
Self { address, view: None }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -296,17 +300,17 @@ impl SignableTransaction {
|
|||
protocol: Protocol,
|
||||
r_seed: Option<Zeroizing<[u8; 32]>>,
|
||||
inputs: Vec<SpendableOutput>,
|
||||
mut payments: Vec<(MoneroAddress, u64)>,
|
||||
payments: Vec<(MoneroAddress, u64)>,
|
||||
change_address: Option<Change>,
|
||||
data: Vec<Vec<u8>>,
|
||||
fee_rate: Fee,
|
||||
) -> Result<SignableTransaction, TransactionError> {
|
||||
) -> Result<Self, TransactionError> {
|
||||
// Make sure there's only one payment ID
|
||||
let mut has_payment_id = {
|
||||
let mut payment_ids = 0;
|
||||
let mut count = |addr: MoneroAddress| {
|
||||
if addr.payment_id().is_some() {
|
||||
payment_ids += 1
|
||||
payment_ids += 1;
|
||||
}
|
||||
};
|
||||
for payment in &payments {
|
||||
|
@ -382,15 +386,16 @@ impl SignableTransaction {
|
|||
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 {
|
||||
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 {
|
||||
#[must_use]
|
||||
pub const fn fee(&self) -> u64 {
|
||||
self.fee
|
||||
}
|
||||
|
||||
|
@ -444,11 +449,8 @@ impl SignableTransaction {
|
|||
0;
|
||||
|
||||
// We need additional keys if we have any subaddresses
|
||||
let mut additional = subaddresses;
|
||||
// Unless the above change view key path is taken
|
||||
if (payments.len() == 2) && has_change_view {
|
||||
additional = false;
|
||||
}
|
||||
// UNLESS there's only two payments and we have the view-key for the change output
|
||||
let additional = if (payments.len() == 2) && has_change_view { false } else { subaddresses };
|
||||
let modified_change_ecdh = subaddresses && (!additional);
|
||||
|
||||
// If we're using the aR rewrite, update tx_public_key from rG to rB
|
||||
|
@ -562,11 +564,13 @@ impl SignableTransaction {
|
|||
}
|
||||
|
||||
/// Returns the eventuality of this transaction.
|
||||
///
|
||||
/// 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
|
||||
/// if the transaction has already been signed and published.
|
||||
#[must_use]
|
||||
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(
|
||||
self.r_seed.as_ref()?,
|
||||
&inputs,
|
||||
|
@ -606,7 +610,7 @@ impl SignableTransaction {
|
|||
|
||||
let (tx_key, additional, outputs, id) = Self::prepare_payments(
|
||||
&r_seed,
|
||||
&self.inputs.iter().map(|input| input.key()).collect::<Vec<_>>(),
|
||||
&self.inputs.iter().map(SpendableOutput::key).collect::<Vec<_>>(),
|
||||
&mut self.payments,
|
||||
uniqueness,
|
||||
);
|
||||
|
@ -656,7 +660,7 @@ impl SignableTransaction {
|
|||
fee,
|
||||
encrypted_amounts,
|
||||
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![] },
|
||||
},
|
||||
|
@ -704,7 +708,9 @@ impl SignableTransaction {
|
|||
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<_>>());
|
||||
}
|
||||
_ => 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)
|
||||
}
|
||||
|
@ -713,13 +719,19 @@ impl SignableTransaction {
|
|||
impl Eventuality {
|
||||
/// Enables building a HashMap of Extra -> Eventuality for efficiently checking if an on-chain
|
||||
/// transaction may match this eventuality.
|
||||
///
|
||||
/// This extra is cryptographically bound to:
|
||||
/// 1) A specific set of inputs (via their output key)
|
||||
/// 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.
|
||||
#[must_use]
|
||||
pub fn extra(&self) -> &[u8] {
|
||||
&self.extra
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn matches(&self, tx: &Transaction) -> bool {
|
||||
if self.payments.len() != tx.prefix.outputs.len() {
|
||||
return false;
|
||||
|
@ -752,9 +764,10 @@ impl Eventuality {
|
|||
}
|
||||
|
||||
// TODO: Remove this when the following for loop is updated
|
||||
if !rct_type.compact_encrypted_amounts() {
|
||||
panic!("created an Eventuality for a very old RctType we don't support proving for");
|
||||
}
|
||||
assert!(
|
||||
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() {
|
||||
// Verify the output, commitment, and encrypted amount.
|
||||
|
@ -804,18 +817,19 @@ impl Eventuality {
|
|||
write_vec(write_byte, &self.extra, w)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
let mut buf = Vec::with_capacity(128);
|
||||
self.write(&mut buf).unwrap();
|
||||
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> {
|
||||
String::from_utf8(read_vec(read_byte, r)?)
|
||||
.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> {
|
||||
|
@ -836,7 +850,7 @@ impl Eventuality {
|
|||
})
|
||||
}
|
||||
|
||||
Ok(Eventuality {
|
||||
Ok(Self {
|
||||
protocol: Protocol::read(r)?,
|
||||
r_seed: Zeroizing::new(read_bytes::<_, 32>(r)?),
|
||||
inputs: read_vec(read_point, r)?,
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use std_shims::{
|
||||
sync::Arc,
|
||||
vec::Vec,
|
||||
io::{self, Read},
|
||||
collections::HashMap,
|
||||
};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::sync::RwLock;
|
||||
|
||||
use zeroize::Zeroizing;
|
||||
|
||||
|
@ -267,14 +268,15 @@ impl SignMachine<Transaction> for TransactionSignMachine {
|
|||
mut commitments: HashMap<Participant, Self::Preprocess>,
|
||||
msg: &[u8],
|
||||
) -> Result<(TransactionSignatureMachine, Self::SignatureShare), FrostError> {
|
||||
if !msg.is_empty() {
|
||||
panic!("message was passed to the TransactionMachine when it generates its own");
|
||||
}
|
||||
assert!(
|
||||
msg.is_empty(),
|
||||
"message was passed to the TransactionMachine when it generates its own"
|
||||
);
|
||||
|
||||
// Find out who's included
|
||||
// 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
|
||||
let mut included = commitments.keys().cloned().collect::<Vec<_>>();
|
||||
let mut included = commitments.keys().copied().collect::<Vec<_>>();
|
||||
included.push(self.i);
|
||||
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
|
||||
// above
|
||||
for map in commitments.iter_mut() {
|
||||
for map in &mut commitments {
|
||||
map.remove(&self.i);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
pub use core::sync::*;
|
||||
pub use alloc::sync::*;
|
||||
|
||||
mod mutex_shim {
|
||||
#[cfg(feature = "std")]
|
||||
|
|
Loading…
Reference in a new issue