mirror of
https://github.com/serai-dex/serai.git
synced 2025-02-04 04:06:30 +00:00
Implement view tags
This commit is contained in:
parent
755dc84859
commit
bba93a64c2
4 changed files with 44 additions and 25 deletions
|
@ -62,7 +62,7 @@ impl Input {
|
||||||
pub struct Output {
|
pub struct Output {
|
||||||
pub amount: u64,
|
pub amount: u64,
|
||||||
pub key: EdwardsPoint,
|
pub key: EdwardsPoint,
|
||||||
pub tag: Option<u8>,
|
pub view_tag: Option<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Output {
|
impl Output {
|
||||||
|
@ -72,31 +72,34 @@ impl Output {
|
||||||
|
|
||||||
pub fn serialize<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
|
pub fn serialize<W: std::io::Write>(&self, w: &mut W) -> std::io::Result<()> {
|
||||||
write_varint(&self.amount, w)?;
|
write_varint(&self.amount, w)?;
|
||||||
w.write_all(&[2 + (if self.tag.is_some() { 1 } else { 0 })])?;
|
w.write_all(&[2 + (if self.view_tag.is_some() { 1 } else { 0 })])?;
|
||||||
write_point(&self.key, w)?;
|
write_point(&self.key, w)?;
|
||||||
if let Some(tag) = self.tag {
|
if let Some(view_tag) = self.view_tag {
|
||||||
w.write_all(&[tag])?;
|
w.write_all(&[view_tag])?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deserialize<R: std::io::Read>(r: &mut R) -> std::io::Result<Output> {
|
pub fn deserialize<R: std::io::Read>(r: &mut R) -> std::io::Result<Output> {
|
||||||
let amount = read_varint(r)?;
|
let amount = read_varint(r)?;
|
||||||
let mut tag = [0];
|
let mut output_type = [0];
|
||||||
r.read_exact(&mut tag)?;
|
r.read_exact(&mut output_type)?;
|
||||||
if (tag[0] != 2) && (tag[0] != 3) {
|
let view_tag = match output_type[0] {
|
||||||
Err(std::io::Error::new(
|
2 => false,
|
||||||
|
3 => true,
|
||||||
|
_ => Err(std::io::Error::new(
|
||||||
std::io::ErrorKind::Other,
|
std::io::ErrorKind::Other,
|
||||||
"Tried to deserialize unknown/unused output type",
|
"Tried to deserialize unknown/unused output type",
|
||||||
))?;
|
))?,
|
||||||
}
|
};
|
||||||
|
|
||||||
Ok(Output {
|
Ok(Output {
|
||||||
amount,
|
amount,
|
||||||
key: read_point(r)?,
|
key: read_point(r)?,
|
||||||
tag: if tag[0] == 3 {
|
view_tag: if view_tag {
|
||||||
r.read_exact(&mut tag)?;
|
let mut view_tag = [0];
|
||||||
Some(tag[0])
|
r.read_exact(&mut view_tag)?;
|
||||||
|
Some(view_tag[0])
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
|
|
|
@ -35,22 +35,29 @@ pub(crate) fn uniqueness(inputs: &[Input]) -> [u8; 32] {
|
||||||
hash(&u)
|
hash(&u)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hs(8Ra || o) with https://github.com/monero-project/research-lab/issues/103 as an option
|
// Hs("view_tag" || 8Ra || o) and Hs(8Ra || o) with uniqueness inclusion as an option
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub(crate) fn shared_key(
|
pub(crate) fn shared_key(
|
||||||
uniqueness: Option<[u8; 32]>,
|
uniqueness: Option<[u8; 32]>,
|
||||||
s: Scalar,
|
s: Scalar,
|
||||||
P: &EdwardsPoint,
|
P: &EdwardsPoint,
|
||||||
o: usize,
|
o: usize,
|
||||||
) -> Scalar {
|
) -> (u8, Scalar) {
|
||||||
// uniqueness
|
// 8Ra
|
||||||
let mut shared = uniqueness.map_or(vec![], |uniqueness| uniqueness.to_vec());
|
let mut output_derivation = (s * P).mul_by_cofactor().compress().to_bytes().to_vec();
|
||||||
// || 8Ra
|
|
||||||
shared.extend((s * P).mul_by_cofactor().compress().to_bytes().to_vec());
|
|
||||||
// || o
|
// || o
|
||||||
write_varint(&o.try_into().unwrap(), &mut shared).unwrap();
|
write_varint(&o.try_into().unwrap(), &mut output_derivation).unwrap();
|
||||||
// Hs()
|
|
||||||
hash_to_scalar(&shared)
|
let view_tag = hash(&[b"view_tag".as_ref(), &output_derivation].concat())[0];
|
||||||
|
|
||||||
|
// uniqueness ||
|
||||||
|
let shared_key = if let Some(uniqueness) = uniqueness {
|
||||||
|
[uniqueness.as_ref(), &output_derivation].concat().to_vec()
|
||||||
|
} else {
|
||||||
|
output_derivation
|
||||||
|
};
|
||||||
|
|
||||||
|
(view_tag, hash_to_scalar(&shared_key))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn amount_encryption(amount: u64, key: Scalar) -> [u8; 8] {
|
pub(crate) fn amount_encryption(amount: u64, key: Scalar) -> [u8; 8] {
|
||||||
|
|
|
@ -101,12 +101,19 @@ impl Transaction {
|
||||||
for (o, output) in self.prefix.outputs.iter().enumerate() {
|
for (o, output) in self.prefix.outputs.iter().enumerate() {
|
||||||
// TODO: This may be replaceable by pubkeys[o]
|
// TODO: This may be replaceable by pubkeys[o]
|
||||||
for pubkey in &pubkeys {
|
for pubkey in &pubkeys {
|
||||||
let key_offset = shared_key(
|
let (view_tag, key_offset) = shared_key(
|
||||||
Some(uniqueness(&self.prefix.inputs)).filter(|_| guaranteed),
|
Some(uniqueness(&self.prefix.inputs)).filter(|_| guaranteed),
|
||||||
view.view,
|
view.view,
|
||||||
pubkey,
|
pubkey,
|
||||||
o,
|
o,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if let Some(actual_view_tag) = output.view_tag {
|
||||||
|
if actual_view_tag != view_tag {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// P - shared == spend
|
// P - shared == spend
|
||||||
if (output.key - (&key_offset * &ED25519_BASEPOINT_TABLE)) != view.spend {
|
if (output.key - (&key_offset * &ED25519_BASEPOINT_TABLE)) != view.spend {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -38,6 +38,7 @@ pub use multisig::TransactionMachine;
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
struct SendOutput {
|
struct SendOutput {
|
||||||
R: EdwardsPoint,
|
R: EdwardsPoint,
|
||||||
|
view_tag: u8,
|
||||||
dest: EdwardsPoint,
|
dest: EdwardsPoint,
|
||||||
commitment: Commitment,
|
commitment: Commitment,
|
||||||
amount: [u8; 8],
|
amount: [u8; 8],
|
||||||
|
@ -51,7 +52,7 @@ impl SendOutput {
|
||||||
o: usize,
|
o: usize,
|
||||||
) -> SendOutput {
|
) -> SendOutput {
|
||||||
let r = random_scalar(rng);
|
let r = random_scalar(rng);
|
||||||
let shared_key =
|
let (view_tag, shared_key) =
|
||||||
shared_key(Some(unique).filter(|_| output.0.meta.guaranteed), r, &output.0.view, o);
|
shared_key(Some(unique).filter(|_| output.0.meta.guaranteed), r, &output.0.view, o);
|
||||||
|
|
||||||
let spend = output.0.spend;
|
let spend = output.0.spend;
|
||||||
|
@ -63,6 +64,7 @@ impl SendOutput {
|
||||||
}
|
}
|
||||||
AddressType::Subaddress => r * spend,
|
AddressType::Subaddress => r * spend,
|
||||||
},
|
},
|
||||||
|
view_tag,
|
||||||
dest: ((&shared_key * &ED25519_BASEPOINT_TABLE) + spend),
|
dest: ((&shared_key * &ED25519_BASEPOINT_TABLE) + spend),
|
||||||
commitment: Commitment::new(commitment_mask(shared_key), output.1),
|
commitment: Commitment::new(commitment_mask(shared_key), output.1),
|
||||||
amount: amount_encryption(output.1, shared_key),
|
amount: amount_encryption(output.1, shared_key),
|
||||||
|
@ -297,7 +299,7 @@ impl SignableTransaction {
|
||||||
tx_outputs.push(Output {
|
tx_outputs.push(Output {
|
||||||
amount: 0,
|
amount: 0,
|
||||||
key: self.outputs[o].dest,
|
key: self.outputs[o].dest,
|
||||||
tag: Some(0).filter(|_| matches!(self.protocol, Protocol::v16)),
|
view_tag: Some(self.outputs[o].view_tag).filter(|_| matches!(self.protocol, Protocol::v16)),
|
||||||
});
|
});
|
||||||
ecdh_info.push(self.outputs[o].amount);
|
ecdh_info.push(self.outputs[o].amount);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue