mirror of
https://github.com/serai-dex/serai.git
synced 2024-12-22 11:39:35 +00:00
monero: added tx extra variants padding and mysterious minergate (#510)
* monero: read/write tx extra padding * monero: read/write tx extra mysterious minergate variant * Clippy * monero: add tx extra test for minergate + pub key * BufRead --------- Co-authored-by: Luke Parker <lukeparker5132@gmail.com>
This commit is contained in:
parent
cda14ac8b9
commit
4f1f7984a6
4 changed files with 214 additions and 4 deletions
158
coins/monero/src/tests/extra.rs
Normal file
158
coins/monero/src/tests/extra.rs
Normal file
|
@ -0,0 +1,158 @@
|
|||
use crate::{
|
||||
wallet::{ExtraField, Extra, extra::MAX_TX_EXTRA_PADDING_COUNT},
|
||||
serialize::write_varint,
|
||||
};
|
||||
|
||||
use curve25519_dalek::edwards::{EdwardsPoint, CompressedEdwardsY};
|
||||
|
||||
// Borrowed tests from
|
||||
// https://github.com/monero-project/monero/blob/ac02af92867590ca80b2779a7bbeafa99ff94dcb/
|
||||
// tests/unit_tests/test_tx_utils.cpp
|
||||
|
||||
const PUB_KEY_BYTES: [u8; 33] = [
|
||||
1, 30, 208, 98, 162, 133, 64, 85, 83, 112, 91, 188, 89, 211, 24, 131, 39, 154, 22, 228, 80, 63,
|
||||
198, 141, 173, 111, 244, 183, 4, 149, 186, 140, 230,
|
||||
];
|
||||
|
||||
fn pub_key() -> EdwardsPoint {
|
||||
CompressedEdwardsY(PUB_KEY_BYTES[1 .. PUB_KEY_BYTES.len()].try_into().expect("invalid pub key"))
|
||||
.decompress()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn test_write_buf(extra: &Extra, buf: &[u8]) {
|
||||
let mut w: Vec<u8> = vec![];
|
||||
Extra::write(extra, &mut w).unwrap();
|
||||
assert_eq!(buf, w);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_extra() {
|
||||
let buf: Vec<u8> = vec![];
|
||||
let extra = Extra::read::<&[u8]>(&mut buf.as_ref()).unwrap();
|
||||
assert!(extra.0.is_empty());
|
||||
test_write_buf(&extra, &buf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn padding_only_size_1() {
|
||||
let buf: Vec<u8> = vec![0];
|
||||
let extra = Extra::read::<&[u8]>(&mut buf.as_ref()).unwrap();
|
||||
assert_eq!(extra.0, vec![ExtraField::Padding(1)]);
|
||||
test_write_buf(&extra, &buf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn padding_only_size_2() {
|
||||
let buf: Vec<u8> = vec![0, 0];
|
||||
let extra = Extra::read::<&[u8]>(&mut buf.as_ref()).unwrap();
|
||||
assert_eq!(extra.0, vec![ExtraField::Padding(2)]);
|
||||
test_write_buf(&extra, &buf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn padding_only_max_size() {
|
||||
let buf: Vec<u8> = vec![0; MAX_TX_EXTRA_PADDING_COUNT];
|
||||
let extra = Extra::read::<&[u8]>(&mut buf.as_ref()).unwrap();
|
||||
assert_eq!(extra.0, vec![ExtraField::Padding(MAX_TX_EXTRA_PADDING_COUNT)]);
|
||||
test_write_buf(&extra, &buf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn padding_only_exceed_max_size() {
|
||||
let buf: Vec<u8> = vec![0; MAX_TX_EXTRA_PADDING_COUNT + 1];
|
||||
let extra = Extra::read::<&[u8]>(&mut buf.as_ref()).unwrap();
|
||||
assert!(extra.0.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_padding_only() {
|
||||
let buf: Vec<u8> = vec![0, 42];
|
||||
let extra = Extra::read::<&[u8]>(&mut buf.as_ref()).unwrap();
|
||||
assert!(extra.0.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pub_key_only() {
|
||||
let buf: Vec<u8> = PUB_KEY_BYTES.to_vec();
|
||||
let extra = Extra::read::<&[u8]>(&mut buf.as_ref()).unwrap();
|
||||
assert_eq!(extra.0, vec![ExtraField::PublicKey(pub_key())]);
|
||||
test_write_buf(&extra, &buf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extra_nonce_only() {
|
||||
let buf: Vec<u8> = vec![2, 1, 42];
|
||||
let extra = Extra::read::<&[u8]>(&mut buf.as_ref()).unwrap();
|
||||
assert_eq!(extra.0, vec![ExtraField::Nonce(vec![42])]);
|
||||
test_write_buf(&extra, &buf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extra_nonce_only_wrong_size() {
|
||||
let mut buf: Vec<u8> = vec![0; 20];
|
||||
buf[0] = 2;
|
||||
buf[1] = 255;
|
||||
let extra = Extra::read::<&[u8]>(&mut buf.as_ref()).unwrap();
|
||||
assert!(extra.0.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pub_key_and_padding() {
|
||||
let mut buf: Vec<u8> = PUB_KEY_BYTES.to_vec();
|
||||
buf.extend([
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
]);
|
||||
let extra = Extra::read::<&[u8]>(&mut buf.as_ref()).unwrap();
|
||||
assert_eq!(extra.0, vec![ExtraField::PublicKey(pub_key()), ExtraField::Padding(76)]);
|
||||
test_write_buf(&extra, &buf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pub_key_and_invalid_padding() {
|
||||
let mut buf: Vec<u8> = PUB_KEY_BYTES.to_vec();
|
||||
buf.extend([0, 1]);
|
||||
let extra = Extra::read::<&[u8]>(&mut buf.as_ref()).unwrap();
|
||||
assert_eq!(extra.0, vec![ExtraField::PublicKey(pub_key())]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extra_mysterious_minergate_only() {
|
||||
let buf: Vec<u8> = vec![222, 1, 42];
|
||||
let extra = Extra::read::<&[u8]>(&mut buf.as_ref()).unwrap();
|
||||
assert_eq!(extra.0, vec![ExtraField::MysteriousMinergate(vec![42])]);
|
||||
test_write_buf(&extra, &buf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extra_mysterious_minergate_only_large() {
|
||||
let mut buf: Vec<u8> = vec![222];
|
||||
write_varint(&512u64, &mut buf).unwrap();
|
||||
buf.extend_from_slice(&vec![0; 512]);
|
||||
let extra = Extra::read::<&[u8]>(&mut buf.as_ref()).unwrap();
|
||||
assert_eq!(extra.0, vec![ExtraField::MysteriousMinergate(vec![0; 512])]);
|
||||
test_write_buf(&extra, &buf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extra_mysterious_minergate_only_wrong_size() {
|
||||
let mut buf: Vec<u8> = vec![0; 20];
|
||||
buf[0] = 222;
|
||||
buf[1] = 255;
|
||||
let extra = Extra::read::<&[u8]>(&mut buf.as_ref()).unwrap();
|
||||
assert!(extra.0.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extra_mysterious_minergate_and_pub_key() {
|
||||
let mut buf: Vec<u8> = vec![222, 1, 42];
|
||||
buf.extend(PUB_KEY_BYTES.to_vec());
|
||||
let extra = Extra::read::<&[u8]>(&mut buf.as_ref()).unwrap();
|
||||
assert_eq!(
|
||||
extra.0,
|
||||
vec![ExtraField::MysteriousMinergate(vec![42]), ExtraField::PublicKey(pub_key())]
|
||||
);
|
||||
test_write_buf(&extra, &buf);
|
||||
}
|
|
@ -3,3 +3,4 @@ mod clsag;
|
|||
mod bulletproofs;
|
||||
mod address;
|
||||
mod seed;
|
||||
mod extra;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use core::ops::BitXor;
|
||||
use std_shims::{
|
||||
vec::Vec,
|
||||
io::{self, Read, Write},
|
||||
io::{self, Read, BufRead, Write},
|
||||
};
|
||||
|
||||
use zeroize::Zeroize;
|
||||
|
@ -13,6 +13,7 @@ use crate::serialize::{
|
|||
write_point, write_vec,
|
||||
};
|
||||
|
||||
pub const MAX_TX_EXTRA_PADDING_COUNT: usize = 255;
|
||||
pub const MAX_TX_EXTRA_NONCE_SIZE: usize = 255;
|
||||
|
||||
pub const PAYMENT_ID_MARKER: u8 = 0;
|
||||
|
@ -70,15 +71,23 @@ impl PaymentId {
|
|||
// Doesn't bother with padding nor MinerGate
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
|
||||
pub enum ExtraField {
|
||||
Padding(usize),
|
||||
PublicKey(EdwardsPoint),
|
||||
Nonce(Vec<u8>),
|
||||
MergeMining(usize, [u8; 32]),
|
||||
PublicKeys(Vec<EdwardsPoint>),
|
||||
MysteriousMinergate(Vec<u8>),
|
||||
}
|
||||
|
||||
impl ExtraField {
|
||||
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||
match self {
|
||||
ExtraField::Padding(size) => {
|
||||
w.write_all(&[0])?;
|
||||
for _ in 1 .. *size {
|
||||
write_byte(&0u8, w)?;
|
||||
}
|
||||
}
|
||||
ExtraField::PublicKey(key) => {
|
||||
w.write_all(&[1])?;
|
||||
w.write_all(&key.compress().to_bytes())?;
|
||||
|
@ -96,12 +105,39 @@ impl ExtraField {
|
|||
w.write_all(&[4])?;
|
||||
write_vec(write_point, keys, w)?;
|
||||
}
|
||||
ExtraField::MysteriousMinergate(data) => {
|
||||
w.write_all(&[0xDE])?;
|
||||
write_vec(write_byte, data, w)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<ExtraField> {
|
||||
pub fn read<R: BufRead>(r: &mut R) -> io::Result<ExtraField> {
|
||||
Ok(match read_byte(r)? {
|
||||
0 => ExtraField::Padding({
|
||||
// Read until either non-zero, max padding count, or end of buffer
|
||||
let mut size: usize = 1;
|
||||
loop {
|
||||
let buf = r.fill_buf()?;
|
||||
let mut n_consume = 0;
|
||||
for v in buf {
|
||||
if *v != 0u8 {
|
||||
Err(io::Error::other("non-zero value after padding"))?
|
||||
}
|
||||
n_consume += 1;
|
||||
size += 1;
|
||||
if size > MAX_TX_EXTRA_PADDING_COUNT {
|
||||
Err(io::Error::other("padding exceeded max count"))?
|
||||
}
|
||||
}
|
||||
if n_consume == 0 {
|
||||
break;
|
||||
}
|
||||
r.consume(n_consume);
|
||||
}
|
||||
size
|
||||
}),
|
||||
1 => ExtraField::PublicKey(read_point(r)?),
|
||||
2 => ExtraField::Nonce({
|
||||
let nonce = read_vec(read_byte, r)?;
|
||||
|
@ -112,13 +148,14 @@ impl ExtraField {
|
|||
}),
|
||||
3 => ExtraField::MergeMining(read_varint(r)?, read_bytes(r)?),
|
||||
4 => ExtraField::PublicKeys(read_vec(read_point, r)?),
|
||||
0xDE => ExtraField::MysteriousMinergate(read_vec(read_byte, r)?),
|
||||
_ => Err(io::Error::other("unknown extra field"))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
|
||||
pub struct Extra(Vec<ExtraField>);
|
||||
pub struct Extra(pub(crate) Vec<ExtraField>);
|
||||
impl Extra {
|
||||
pub fn keys(&self) -> Option<(Vec<EdwardsPoint>, Option<Vec<EdwardsPoint>>)> {
|
||||
let mut keys = vec![];
|
||||
|
@ -204,7 +241,7 @@ impl Extra {
|
|||
buf
|
||||
}
|
||||
|
||||
pub fn read<R: Read>(r: &mut R) -> io::Result<Extra> {
|
||||
pub fn read<R: BufRead>(r: &mut R) -> io::Result<Extra> {
|
||||
let mut res = Extra(vec![]);
|
||||
let mut field;
|
||||
while {
|
||||
|
|
|
@ -64,6 +64,20 @@ mod shims {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait BufRead: Read {
|
||||
fn fill_buf(&mut self) -> Result<&[u8]>;
|
||||
fn consume(&mut self, amt: usize);
|
||||
}
|
||||
|
||||
impl BufRead for &[u8] {
|
||||
fn fill_buf(&mut self) -> Result<&[u8]> {
|
||||
Ok(*self)
|
||||
}
|
||||
fn consume(&mut self, amt: usize) {
|
||||
*self = &self[amt ..];
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Write {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize>;
|
||||
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
|
||||
|
|
Loading…
Reference in a new issue