mirror of
https://github.com/serai-dex/serai.git
synced 2024-12-22 19:49:22 +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 bulletproofs;
|
||||||
mod address;
|
mod address;
|
||||||
mod seed;
|
mod seed;
|
||||||
|
mod extra;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use core::ops::BitXor;
|
use core::ops::BitXor;
|
||||||
use std_shims::{
|
use std_shims::{
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
io::{self, Read, Write},
|
io::{self, Read, BufRead, Write},
|
||||||
};
|
};
|
||||||
|
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
@ -13,6 +13,7 @@ use crate::serialize::{
|
||||||
write_point, write_vec,
|
write_point, write_vec,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const MAX_TX_EXTRA_PADDING_COUNT: usize = 255;
|
||||||
pub const MAX_TX_EXTRA_NONCE_SIZE: usize = 255;
|
pub const MAX_TX_EXTRA_NONCE_SIZE: usize = 255;
|
||||||
|
|
||||||
pub const PAYMENT_ID_MARKER: u8 = 0;
|
pub const PAYMENT_ID_MARKER: u8 = 0;
|
||||||
|
@ -70,15 +71,23 @@ impl PaymentId {
|
||||||
// Doesn't bother with padding nor MinerGate
|
// Doesn't bother with padding nor MinerGate
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
|
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
|
||||||
pub enum ExtraField {
|
pub enum ExtraField {
|
||||||
|
Padding(usize),
|
||||||
PublicKey(EdwardsPoint),
|
PublicKey(EdwardsPoint),
|
||||||
Nonce(Vec<u8>),
|
Nonce(Vec<u8>),
|
||||||
MergeMining(usize, [u8; 32]),
|
MergeMining(usize, [u8; 32]),
|
||||||
PublicKeys(Vec<EdwardsPoint>),
|
PublicKeys(Vec<EdwardsPoint>),
|
||||||
|
MysteriousMinergate(Vec<u8>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExtraField {
|
impl ExtraField {
|
||||||
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
pub fn write<W: Write>(&self, w: &mut W) -> io::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
|
ExtraField::Padding(size) => {
|
||||||
|
w.write_all(&[0])?;
|
||||||
|
for _ in 1 .. *size {
|
||||||
|
write_byte(&0u8, w)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
ExtraField::PublicKey(key) => {
|
ExtraField::PublicKey(key) => {
|
||||||
w.write_all(&[1])?;
|
w.write_all(&[1])?;
|
||||||
w.write_all(&key.compress().to_bytes())?;
|
w.write_all(&key.compress().to_bytes())?;
|
||||||
|
@ -96,12 +105,39 @@ impl ExtraField {
|
||||||
w.write_all(&[4])?;
|
w.write_all(&[4])?;
|
||||||
write_vec(write_point, keys, w)?;
|
write_vec(write_point, keys, w)?;
|
||||||
}
|
}
|
||||||
|
ExtraField::MysteriousMinergate(data) => {
|
||||||
|
w.write_all(&[0xDE])?;
|
||||||
|
write_vec(write_byte, data, w)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
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)? {
|
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)?),
|
1 => ExtraField::PublicKey(read_point(r)?),
|
||||||
2 => ExtraField::Nonce({
|
2 => ExtraField::Nonce({
|
||||||
let nonce = read_vec(read_byte, r)?;
|
let nonce = read_vec(read_byte, r)?;
|
||||||
|
@ -112,13 +148,14 @@ impl ExtraField {
|
||||||
}),
|
}),
|
||||||
3 => ExtraField::MergeMining(read_varint(r)?, read_bytes(r)?),
|
3 => ExtraField::MergeMining(read_varint(r)?, read_bytes(r)?),
|
||||||
4 => ExtraField::PublicKeys(read_vec(read_point, r)?),
|
4 => ExtraField::PublicKeys(read_vec(read_point, r)?),
|
||||||
|
0xDE => ExtraField::MysteriousMinergate(read_vec(read_byte, r)?),
|
||||||
_ => Err(io::Error::other("unknown extra field"))?,
|
_ => Err(io::Error::other("unknown extra field"))?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
|
#[derive(Clone, PartialEq, Eq, Debug, Zeroize)]
|
||||||
pub struct Extra(Vec<ExtraField>);
|
pub struct Extra(pub(crate) Vec<ExtraField>);
|
||||||
impl Extra {
|
impl Extra {
|
||||||
pub fn keys(&self) -> Option<(Vec<EdwardsPoint>, Option<Vec<EdwardsPoint>>)> {
|
pub fn keys(&self) -> Option<(Vec<EdwardsPoint>, Option<Vec<EdwardsPoint>>)> {
|
||||||
let mut keys = vec![];
|
let mut keys = vec![];
|
||||||
|
@ -204,7 +241,7 @@ impl Extra {
|
||||||
buf
|
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 res = Extra(vec![]);
|
||||||
let mut field;
|
let mut field;
|
||||||
while {
|
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 {
|
pub trait Write {
|
||||||
fn write(&mut self, buf: &[u8]) -> Result<usize>;
|
fn write(&mut self, buf: &[u8]) -> Result<usize>;
|
||||||
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
|
fn write_all(&mut self, buf: &[u8]) -> Result<()> {
|
||||||
|
|
Loading…
Reference in a new issue