mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-12 14:09:25 +00:00
Compare commits
4 Commits
b2c962cd3e
...
6357bc0ed4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6357bc0ed4 | ||
|
|
2334725ec8 | ||
|
|
0631607b8f | ||
|
|
d847ec5efb |
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -8337,7 +8337,6 @@ dependencies = [
|
|||||||
"serai-env",
|
"serai-env",
|
||||||
"serai-message-queue",
|
"serai-message-queue",
|
||||||
"serai-processor-messages",
|
"serai-processor-messages",
|
||||||
"serde",
|
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sp-application-crypto",
|
"sp-application-crypto",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
|||||||
@@ -220,23 +220,6 @@ fn rpc_point(point: &str) -> Result<EdwardsPoint, RpcError> {
|
|||||||
.ok_or_else(|| RpcError::InvalidNode(format!("invalid point: {point}")))
|
.ok_or_else(|| RpcError::InvalidNode(format!("invalid point: {point}")))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read an EPEE VarInt, distinct from the VarInts used throughout the rest of the protocol
|
|
||||||
fn read_epee_vi<R: io::Read>(reader: &mut R) -> io::Result<u64> {
|
|
||||||
let vi_start = read_byte(reader)?;
|
|
||||||
let len = match vi_start & 0b11 {
|
|
||||||
0 => 1,
|
|
||||||
1 => 2,
|
|
||||||
2 => 4,
|
|
||||||
3 => 8,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
let mut vi = u64::from(vi_start >> 2);
|
|
||||||
for i in 1 .. len {
|
|
||||||
vi |= u64::from(read_byte(reader)?) << (((i - 1) * 8) + 6);
|
|
||||||
}
|
|
||||||
Ok(vi)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An RPC connection to a Monero daemon.
|
/// An RPC connection to a Monero daemon.
|
||||||
///
|
///
|
||||||
/// This is abstract such that users can use an HTTP library (which being their choice), a
|
/// This is abstract such that users can use an HTTP library (which being their choice), a
|
||||||
@@ -511,26 +494,29 @@ pub trait Rpc: Sync + Clone + Debug {
|
|||||||
|
|
||||||
/// Get the output indexes of the specified transaction.
|
/// Get the output indexes of the specified transaction.
|
||||||
async fn get_o_indexes(&self, hash: [u8; 32]) -> Result<Vec<u64>, RpcError> {
|
async fn get_o_indexes(&self, hash: [u8; 32]) -> Result<Vec<u64>, RpcError> {
|
||||||
/*
|
|
||||||
TODO: Use these when a suitable epee serde lib exists
|
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
|
||||||
struct Request {
|
|
||||||
txid: [u8; 32],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
|
||||||
struct OIndexes {
|
|
||||||
o_indexes: Vec<u64>,
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Given the immaturity of Rust epee libraries, this is a homegrown one which is only validated
|
// Given the immaturity of Rust epee libraries, this is a homegrown one which is only validated
|
||||||
// to work against this specific function
|
// to work against this specific function
|
||||||
|
|
||||||
// Header for EPEE, an 8-byte magic and a version
|
// Header for EPEE, an 8-byte magic and a version
|
||||||
const EPEE_HEADER: &[u8] = b"\x01\x11\x01\x01\x01\x01\x02\x01\x01";
|
const EPEE_HEADER: &[u8] = b"\x01\x11\x01\x01\x01\x01\x02\x01\x01";
|
||||||
|
|
||||||
|
// Read an EPEE VarInt, distinct from the VarInts used throughout the rest of the protocol
|
||||||
|
fn read_epee_vi<R: io::Read>(reader: &mut R) -> io::Result<u64> {
|
||||||
|
let vi_start = read_byte(reader)?;
|
||||||
|
let len = match vi_start & 0b11 {
|
||||||
|
0 => 1,
|
||||||
|
1 => 2,
|
||||||
|
2 => 4,
|
||||||
|
3 => 8,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let mut vi = u64::from(vi_start >> 2);
|
||||||
|
for i in 1 .. len {
|
||||||
|
vi |= u64::from(read_byte(reader)?) << (((i - 1) * 8) + 6);
|
||||||
|
}
|
||||||
|
Ok(vi)
|
||||||
|
}
|
||||||
|
|
||||||
let mut request = EPEE_HEADER.to_vec();
|
let mut request = EPEE_HEADER.to_vec();
|
||||||
// Number of fields (shifted over 2 bits as the 2 LSBs are reserved for metadata)
|
// Number of fields (shifted over 2 bits as the 2 LSBs are reserved for metadata)
|
||||||
request.push(1 << 2);
|
request.push(1 << 2);
|
||||||
@@ -550,29 +536,58 @@ pub trait Rpc: Sync + Clone + Debug {
|
|||||||
|
|
||||||
(|| {
|
(|| {
|
||||||
let mut res = None;
|
let mut res = None;
|
||||||
let mut is_okay = false;
|
let mut has_status = false;
|
||||||
|
|
||||||
if read_bytes::<_, { EPEE_HEADER.len() }>(&mut indexes)? != EPEE_HEADER {
|
if read_bytes::<_, { EPEE_HEADER.len() }>(&mut indexes)? != EPEE_HEADER {
|
||||||
Err(io::Error::other("invalid header"))?;
|
Err(io::Error::other("invalid header"))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let read_object = |reader: &mut &[u8]| -> io::Result<Vec<u64>> {
|
let read_object = |reader: &mut &[u8]| -> io::Result<Vec<u64>> {
|
||||||
|
// Read the amount of fields
|
||||||
let fields = read_byte(reader)? >> 2;
|
let fields = read_byte(reader)? >> 2;
|
||||||
|
|
||||||
for _ in 0 .. fields {
|
for _ in 0 .. fields {
|
||||||
|
// Read the length of the field's name
|
||||||
let name_len = read_byte(reader)?;
|
let name_len = read_byte(reader)?;
|
||||||
|
// Read the name of the field
|
||||||
let name = read_raw_vec(read_byte, name_len.into(), reader)?;
|
let name = read_raw_vec(read_byte, name_len.into(), reader)?;
|
||||||
|
|
||||||
let type_with_array_flag = read_byte(reader)?;
|
let type_with_array_flag = read_byte(reader)?;
|
||||||
|
// The type of this field, without the potentially set array flag
|
||||||
let kind = type_with_array_flag & (!0x80);
|
let kind = type_with_array_flag & (!0x80);
|
||||||
|
let has_array_flag = type_with_array_flag != kind;
|
||||||
|
|
||||||
let iters = if type_with_array_flag != kind { read_epee_vi(reader)? } else { 1 };
|
// Read this many instances of the field
|
||||||
|
let iters = if has_array_flag { read_epee_vi(reader)? } else { 1 };
|
||||||
|
|
||||||
if (&name == b"o_indexes") && (kind != 5) {
|
// Check the field type
|
||||||
Err(io::Error::other("o_indexes weren't u64s"))?;
|
{
|
||||||
|
#[allow(clippy::match_same_arms)]
|
||||||
|
let (expected_type, expected_array_flag) = match name.as_slice() {
|
||||||
|
b"o_indexes" => (5, true),
|
||||||
|
b"status" => (10, false),
|
||||||
|
b"untrusted" => (11, false),
|
||||||
|
b"credits" => (5, false),
|
||||||
|
b"top_hash" => (10, false),
|
||||||
|
// On-purposely prints name as a byte vector to prevent printing arbitrary strings
|
||||||
|
// This is a self-describing format so we don't have to error here, yet we don't
|
||||||
|
// claim this to be a complete deserialization function
|
||||||
|
// To ensure it works for this specific use case, it's best to ensure it's limited
|
||||||
|
// to this specific use case (ensuring we have less variables to deal with)
|
||||||
|
_ => Err(io::Error::other(format!("unrecognized field in get_o_indexes: {name:?}")))?,
|
||||||
|
};
|
||||||
|
if (expected_type != kind) || (expected_array_flag != has_array_flag) {
|
||||||
|
let fmt_array_bool = |array_bool| if array_bool { "array" } else { "not array" };
|
||||||
|
Err(io::Error::other(format!(
|
||||||
|
"field {name:?} was {kind} ({}), expected {expected_type} ({})",
|
||||||
|
fmt_array_bool(has_array_flag),
|
||||||
|
fmt_array_bool(expected_array_flag)
|
||||||
|
)))?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let f = match kind {
|
let read_field_as_bytes = match kind {
|
||||||
|
/*
|
||||||
// i64
|
// i64
|
||||||
1 => |reader: &mut &[u8]| read_raw_vec(read_byte, 8, reader),
|
1 => |reader: &mut &[u8]| read_raw_vec(read_byte, 8, reader),
|
||||||
// i32
|
// i32
|
||||||
@@ -581,8 +596,10 @@ pub trait Rpc: Sync + Clone + Debug {
|
|||||||
3 => |reader: &mut &[u8]| read_raw_vec(read_byte, 2, reader),
|
3 => |reader: &mut &[u8]| read_raw_vec(read_byte, 2, reader),
|
||||||
// i8
|
// i8
|
||||||
4 => |reader: &mut &[u8]| read_raw_vec(read_byte, 1, reader),
|
4 => |reader: &mut &[u8]| read_raw_vec(read_byte, 1, reader),
|
||||||
|
*/
|
||||||
// u64
|
// u64
|
||||||
5 => |reader: &mut &[u8]| read_raw_vec(read_byte, 8, reader),
|
5 => |reader: &mut &[u8]| read_raw_vec(read_byte, 8, reader),
|
||||||
|
/*
|
||||||
// u32
|
// u32
|
||||||
6 => |reader: &mut &[u8]| read_raw_vec(read_byte, 4, reader),
|
6 => |reader: &mut &[u8]| read_raw_vec(read_byte, 4, reader),
|
||||||
// u16
|
// u16
|
||||||
@@ -591,6 +608,7 @@ pub trait Rpc: Sync + Clone + Debug {
|
|||||||
8 => |reader: &mut &[u8]| read_raw_vec(read_byte, 1, reader),
|
8 => |reader: &mut &[u8]| read_raw_vec(read_byte, 1, reader),
|
||||||
// double
|
// double
|
||||||
9 => |reader: &mut &[u8]| read_raw_vec(read_byte, 8, reader),
|
9 => |reader: &mut &[u8]| read_raw_vec(read_byte, 8, reader),
|
||||||
|
*/
|
||||||
// string, or any collection of bytes
|
// string, or any collection of bytes
|
||||||
10 => |reader: &mut &[u8]| {
|
10 => |reader: &mut &[u8]| {
|
||||||
let len = read_epee_vi(reader)?;
|
let len = read_epee_vi(reader)?;
|
||||||
@@ -602,55 +620,47 @@ pub trait Rpc: Sync + Clone + Debug {
|
|||||||
},
|
},
|
||||||
// bool
|
// bool
|
||||||
11 => |reader: &mut &[u8]| read_raw_vec(read_byte, 1, reader),
|
11 => |reader: &mut &[u8]| read_raw_vec(read_byte, 1, reader),
|
||||||
|
/*
|
||||||
// object, errors here as it shouldn't be used on this call
|
// object, errors here as it shouldn't be used on this call
|
||||||
12 => {
|
12 => {
|
||||||
|_: &mut &[u8]| Err(io::Error::other("node used object in reply to get_o_indexes"))
|
|_: &mut &[u8]| Err(io::Error::other("node used object in reply to get_o_indexes"))
|
||||||
}
|
}
|
||||||
// array, so far unused
|
// array, so far unused
|
||||||
13 => |_: &mut &[u8]| Err(io::Error::other("node used the unused array type")),
|
13 => |_: &mut &[u8]| Err(io::Error::other("node used the unused array type")),
|
||||||
|
*/
|
||||||
_ => |_: &mut &[u8]| Err(io::Error::other("node used an invalid type")),
|
_ => |_: &mut &[u8]| Err(io::Error::other("node used an invalid type")),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut bytes_res = vec![];
|
let mut bytes_res = vec![];
|
||||||
for _ in 0 .. iters {
|
for _ in 0 .. iters {
|
||||||
bytes_res.push(f(reader)?);
|
bytes_res.push(read_field_as_bytes(reader)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut actual_res = Vec::with_capacity(bytes_res.len());
|
let mut actual_res = Vec::with_capacity(bytes_res.len());
|
||||||
match name.as_slice() {
|
match name.as_slice() {
|
||||||
b"o_indexes" => {
|
b"o_indexes" => {
|
||||||
for o_index in bytes_res {
|
for o_index in bytes_res {
|
||||||
actual_res.push(u64::from_le_bytes(
|
actual_res.push(read_u64(&mut o_index.as_slice())?);
|
||||||
o_index
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| io::Error::other("node didn't provide 8 bytes for a u64"))?,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
res = Some(actual_res);
|
res = Some(actual_res);
|
||||||
}
|
}
|
||||||
b"status" => {
|
b"status" => {
|
||||||
if bytes_res
|
if bytes_res
|
||||||
.first()
|
.first()
|
||||||
.ok_or_else(|| io::Error::other("status wasn't a string"))?
|
.ok_or_else(|| io::Error::other("status was a 0-length array"))?
|
||||||
.as_slice() !=
|
.as_slice() !=
|
||||||
b"OK"
|
b"OK"
|
||||||
{
|
{
|
||||||
// TODO: Better handle non-OK responses
|
|
||||||
Err(io::Error::other("response wasn't OK"))?;
|
Err(io::Error::other("response wasn't OK"))?;
|
||||||
}
|
}
|
||||||
is_okay = true;
|
has_status = true;
|
||||||
}
|
}
|
||||||
_ => continue,
|
b"untrusted" | b"credits" | b"top_hash" => continue,
|
||||||
}
|
_ => Err(io::Error::other("unrecognized field in get_o_indexes"))?,
|
||||||
|
|
||||||
if is_okay && res.is_some() {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Didn't return a response with a status
|
if !has_status {
|
||||||
// (if the status wasn't okay, we would've already errored)
|
|
||||||
if !is_okay {
|
|
||||||
Err(io::Error::other("response didn't contain a status"))?;
|
Err(io::Error::other("response didn't contain a status"))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -661,7 +671,7 @@ pub trait Rpc: Sync + Clone + Debug {
|
|||||||
|
|
||||||
read_object(&mut indexes)
|
read_object(&mut indexes)
|
||||||
})()
|
})()
|
||||||
.map_err(|_| RpcError::InvalidNode("invalid binary response".to_string()))
|
.map_err(|e| RpcError::InvalidNode(format!("invalid binary response: {e:?}")))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the output distribution.
|
/// Get the output distribution.
|
||||||
|
|||||||
3
coins/monero/tests/tests.rs
Normal file
3
coins/monero/tests/tests.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
// TODO
|
||||||
|
#[test]
|
||||||
|
fn test() {}
|
||||||
@@ -169,11 +169,11 @@ const MONERO_MAINNET_BYTES: AddressBytes = match AddressBytes::new(18, 19, 42, 7
|
|||||||
Some(bytes) => bytes,
|
Some(bytes) => bytes,
|
||||||
None => panic!("mainnet byte constants conflicted"),
|
None => panic!("mainnet byte constants conflicted"),
|
||||||
};
|
};
|
||||||
const MONERO_STAGENET_BYTES: AddressBytes = match AddressBytes::new(53, 54, 63, 111) {
|
const MONERO_STAGENET_BYTES: AddressBytes = match AddressBytes::new(24, 25, 36, 86) {
|
||||||
Some(bytes) => bytes,
|
Some(bytes) => bytes,
|
||||||
None => panic!("stagenet byte constants conflicted"),
|
None => panic!("stagenet byte constants conflicted"),
|
||||||
};
|
};
|
||||||
const MONERO_TESTNET_BYTES: AddressBytes = match AddressBytes::new(24, 25, 36, 86) {
|
const MONERO_TESTNET_BYTES: AddressBytes = match AddressBytes::new(53, 54, 63, 111) {
|
||||||
Some(bytes) => bytes,
|
Some(bytes) => bytes,
|
||||||
None => panic!("testnet byte constants conflicted"),
|
None => panic!("testnet byte constants conflicted"),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -172,7 +172,6 @@ impl InternalScanner {
|
|||||||
// Our subtracting of a prime-order element means any torsion will be preserved
|
// Our subtracting of a prime-order element means any torsion will be preserved
|
||||||
// If someone wanted to malleate output keys with distinct torsions, only one will be
|
// If someone wanted to malleate output keys with distinct torsions, only one will be
|
||||||
// scanned accordingly (the one which has matching torsion of the spend key)
|
// scanned accordingly (the one which has matching torsion of the spend key)
|
||||||
// TODO: If there's a torsioned spend key, can we spend outputs to it?
|
|
||||||
let subaddress_spend_key =
|
let subaddress_spend_key =
|
||||||
output_key - (&output_derivations.shared_key * ED25519_BASEPOINT_TABLE);
|
output_key - (&output_derivations.shared_key * ED25519_BASEPOINT_TABLE);
|
||||||
self.subaddresses.get(&subaddress_spend_key.compress())
|
self.subaddresses.get(&subaddress_spend_key.compress())
|
||||||
|
|||||||
@@ -9,6 +9,21 @@ use crate::{
|
|||||||
address::{Network, AddressType, SubaddressIndex, MoneroAddress},
|
address::{Network, AddressType, SubaddressIndex, MoneroAddress},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// An error while working with a ViewPair.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
#[cfg_attr(feature = "std", derive(thiserror::Error))]
|
||||||
|
pub enum ViewPairError {
|
||||||
|
/// The spend key was torsioned.
|
||||||
|
///
|
||||||
|
/// Torsioned spend keys are of questionable spendability. This library avoids that question by
|
||||||
|
/// rejecting such ViewPairs.
|
||||||
|
// CLSAG seems to support it if the challenge does a torsion clear, FCMP++ should ship with a
|
||||||
|
// torsion clear, yet it's not worth it to modify CLSAG sign to generate challenges until the
|
||||||
|
// torsion clears and ensure spendability (nor can we reasonably guarantee that in the future)
|
||||||
|
#[cfg_attr(feature = "std", error("torsioned spend key"))]
|
||||||
|
TorsionedSpendKey,
|
||||||
|
}
|
||||||
|
|
||||||
/// The pair of keys necessary to scan transactions.
|
/// The pair of keys necessary to scan transactions.
|
||||||
///
|
///
|
||||||
/// This is composed of the public spend key and the private view key.
|
/// This is composed of the public spend key and the private view key.
|
||||||
@@ -20,8 +35,11 @@ pub struct ViewPair {
|
|||||||
|
|
||||||
impl ViewPair {
|
impl ViewPair {
|
||||||
/// Create a new ViewPair.
|
/// Create a new ViewPair.
|
||||||
pub fn new(spend: EdwardsPoint, view: Zeroizing<Scalar>) -> Self {
|
pub fn new(spend: EdwardsPoint, view: Zeroizing<Scalar>) -> Result<Self, ViewPairError> {
|
||||||
ViewPair { spend, view }
|
if !spend.is_torsion_free() {
|
||||||
|
Err(ViewPairError::TorsionedSpendKey)?;
|
||||||
|
}
|
||||||
|
Ok(ViewPair { spend, view })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The public spend key for this ViewPair.
|
/// The public spend key for this ViewPair.
|
||||||
@@ -86,8 +104,8 @@ pub struct GuaranteedViewPair(pub(crate) ViewPair);
|
|||||||
|
|
||||||
impl GuaranteedViewPair {
|
impl GuaranteedViewPair {
|
||||||
/// Create a new GuaranteedViewPair.
|
/// Create a new GuaranteedViewPair.
|
||||||
pub fn new(spend: EdwardsPoint, view: Zeroizing<Scalar>) -> Self {
|
pub fn new(spend: EdwardsPoint, view: Zeroizing<Scalar>) -> Result<Self, ViewPairError> {
|
||||||
GuaranteedViewPair(ViewPair::new(spend, view))
|
ViewPair::new(spend, view).map(GuaranteedViewPair)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The public spend key for this GuaranteedViewPair.
|
/// The public spend key for this GuaranteedViewPair.
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ pub fn random_address() -> (Scalar, ViewPair, MoneroAddress) {
|
|||||||
let view = Zeroizing::new(Scalar::random(&mut OsRng));
|
let view = Zeroizing::new(Scalar::random(&mut OsRng));
|
||||||
(
|
(
|
||||||
spend,
|
spend,
|
||||||
ViewPair::new(spend_pub, view.clone()),
|
ViewPair::new(spend_pub, view.clone()).unwrap(),
|
||||||
MoneroAddress::new(
|
MoneroAddress::new(
|
||||||
Network::Mainnet,
|
Network::Mainnet,
|
||||||
AddressType::Legacy,
|
AddressType::Legacy,
|
||||||
@@ -52,7 +52,7 @@ pub fn random_guaranteed_address() -> (Scalar, GuaranteedViewPair, MoneroAddress
|
|||||||
let view = Zeroizing::new(Scalar::random(&mut OsRng));
|
let view = Zeroizing::new(Scalar::random(&mut OsRng));
|
||||||
(
|
(
|
||||||
spend,
|
spend,
|
||||||
GuaranteedViewPair::new(spend_pub, view.clone()),
|
GuaranteedViewPair::new(spend_pub, view.clone()).unwrap(),
|
||||||
MoneroAddress::new(
|
MoneroAddress::new(
|
||||||
Network::Mainnet,
|
Network::Mainnet,
|
||||||
AddressType::Legacy,
|
AddressType::Legacy,
|
||||||
@@ -240,7 +240,7 @@ macro_rules! test {
|
|||||||
let view_priv = Zeroizing::new(Scalar::random(&mut OsRng));
|
let view_priv = Zeroizing::new(Scalar::random(&mut OsRng));
|
||||||
let mut outgoing_view = Zeroizing::new([0; 32]);
|
let mut outgoing_view = Zeroizing::new([0; 32]);
|
||||||
OsRng.fill_bytes(outgoing_view.as_mut());
|
OsRng.fill_bytes(outgoing_view.as_mut());
|
||||||
let view = ViewPair::new(spend_pub, view_priv.clone());
|
let view = ViewPair::new(spend_pub, view_priv.clone()).unwrap();
|
||||||
let addr = view.legacy_address(Network::Mainnet);
|
let addr = view.legacy_address(Network::Mainnet);
|
||||||
|
|
||||||
let miner_tx = get_miner_tx_output(&rpc, &view).await;
|
let miner_tx = get_miner_tx_output(&rpc, &view).await;
|
||||||
@@ -258,7 +258,7 @@ macro_rules! test {
|
|||||||
&ViewPair::new(
|
&ViewPair::new(
|
||||||
&Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,
|
&Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,
|
||||||
Zeroizing::new(Scalar::random(&mut OsRng))
|
Zeroizing::new(Scalar::random(&mut OsRng))
|
||||||
),
|
).unwrap(),
|
||||||
),
|
),
|
||||||
rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
|
rpc.get_fee_rate(FeePriority::Unimportant).await.unwrap(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -109,7 +109,8 @@ test!(
|
|||||||
let mut outgoing_view = Zeroizing::new([0; 32]);
|
let mut outgoing_view = Zeroizing::new([0; 32]);
|
||||||
OsRng.fill_bytes(outgoing_view.as_mut());
|
OsRng.fill_bytes(outgoing_view.as_mut());
|
||||||
let change_view =
|
let change_view =
|
||||||
ViewPair::new(&Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE, view_priv.clone());
|
ViewPair::new(&Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE, view_priv.clone())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut builder = SignableTransactionBuilder::new(
|
let mut builder = SignableTransactionBuilder::new(
|
||||||
rct_type,
|
rct_type,
|
||||||
@@ -123,7 +124,8 @@ test!(
|
|||||||
let sub_view = ViewPair::new(
|
let sub_view = ViewPair::new(
|
||||||
&Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,
|
&Scalar::random(&mut OsRng) * ED25519_BASEPOINT_TABLE,
|
||||||
Zeroizing::new(Scalar::random(&mut OsRng)),
|
Zeroizing::new(Scalar::random(&mut OsRng)),
|
||||||
);
|
)
|
||||||
|
.unwrap();
|
||||||
builder
|
builder
|
||||||
.add_payment(sub_view.subaddress(Network::Mainnet, SubaddressIndex::new(0, 1).unwrap()), 1);
|
.add_payment(sub_view.subaddress(Network::Mainnet, SubaddressIndex::new(0, 1).unwrap()), 1);
|
||||||
(builder.build().unwrap(), (change_view, sub_view))
|
(builder.build().unwrap(), (change_view, sub_view))
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ workspace = true
|
|||||||
async-trait = { version = "0.1", default-features = false }
|
async-trait = { version = "0.1", default-features = false }
|
||||||
zeroize = { version = "1", default-features = false, features = ["std"] }
|
zeroize = { version = "1", default-features = false, features = ["std"] }
|
||||||
thiserror = { version = "1", default-features = false }
|
thiserror = { version = "1", default-features = false }
|
||||||
serde = { version = "1", default-features = false, features = ["std", "derive"] }
|
|
||||||
|
|
||||||
# Libs
|
# Libs
|
||||||
rand_core = { version = "0.6", default-features = false, features = ["std", "getrandom"] }
|
rand_core = { version = "0.6", default-features = false, features = ["std", "getrandom"] }
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ impl Monero {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn view_pair(spend: EdwardsPoint) -> GuaranteedViewPair {
|
fn view_pair(spend: EdwardsPoint) -> GuaranteedViewPair {
|
||||||
GuaranteedViewPair::new(spend.0, Zeroizing::new(additional_key::<Monero>(0).0))
|
GuaranteedViewPair::new(spend.0, Zeroizing::new(additional_key::<Monero>(0).0)).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn address_internal(spend: EdwardsPoint, subaddress: Option<SubaddressIndex>) -> Address {
|
fn address_internal(spend: EdwardsPoint, subaddress: Option<SubaddressIndex>) -> Address {
|
||||||
@@ -351,6 +351,7 @@ impl Monero {
|
|||||||
payments.push(Payment {
|
payments.push(Payment {
|
||||||
address: Address::new(
|
address: Address::new(
|
||||||
ViewPair::new(EdwardsPoint::generator().0, Zeroizing::new(Scalar::ONE.0))
|
ViewPair::new(EdwardsPoint::generator().0, Zeroizing::new(Scalar::ONE.0))
|
||||||
|
.unwrap()
|
||||||
.legacy_address(MoneroNetwork::Mainnet),
|
.legacy_address(MoneroNetwork::Mainnet),
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
@@ -413,7 +414,7 @@ impl Monero {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
fn test_view_pair() -> ViewPair {
|
fn test_view_pair() -> ViewPair {
|
||||||
ViewPair::new(*EdwardsPoint::generator(), Zeroizing::new(Scalar::ONE.0))
|
ViewPair::new(*EdwardsPoint::generator(), Zeroizing::new(Scalar::ONE.0)).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ async fn mint_and_burn_test() {
|
|||||||
use monero_wallet::{rpc::Rpc, ViewPair, address::Network};
|
use monero_wallet::{rpc::Rpc, ViewPair, address::Network};
|
||||||
|
|
||||||
let addr = ViewPair::new(ED25519_BASEPOINT_POINT, Zeroizing::new(Scalar::ONE))
|
let addr = ViewPair::new(ED25519_BASEPOINT_POINT, Zeroizing::new(Scalar::ONE))
|
||||||
|
.unwrap()
|
||||||
.legacy_address(Network::Mainnet);
|
.legacy_address(Network::Mainnet);
|
||||||
|
|
||||||
let rpc = producer_handles.monero(ops).await;
|
let rpc = producer_handles.monero(ops).await;
|
||||||
@@ -353,7 +354,7 @@ async fn mint_and_burn_test() {
|
|||||||
|
|
||||||
// Grab the first output on the chain
|
// Grab the first output on the chain
|
||||||
let rpc = handles[0].monero(&ops).await;
|
let rpc = handles[0].monero(&ops).await;
|
||||||
let view_pair = ViewPair::new(ED25519_BASEPOINT_POINT, Zeroizing::new(Scalar::ONE));
|
let view_pair = ViewPair::new(ED25519_BASEPOINT_POINT, Zeroizing::new(Scalar::ONE)).unwrap();
|
||||||
let mut scanner = Scanner::new(view_pair.clone());
|
let mut scanner = Scanner::new(view_pair.clone());
|
||||||
let output = scanner
|
let output = scanner
|
||||||
.scan(&rpc, &rpc.get_block_by_number(1).await.unwrap())
|
.scan(&rpc, &rpc.get_block_by_number(1).await.unwrap())
|
||||||
@@ -578,7 +579,8 @@ async fn mint_and_burn_test() {
|
|||||||
{
|
{
|
||||||
use monero_wallet::{transaction::Transaction, rpc::Rpc, ViewPair, Scanner};
|
use monero_wallet::{transaction::Transaction, rpc::Rpc, ViewPair, Scanner};
|
||||||
let rpc = handles[0].monero(&ops).await;
|
let rpc = handles[0].monero(&ops).await;
|
||||||
let mut scanner = Scanner::new(ViewPair::new(monero_spend, Zeroizing::new(monero_view)));
|
let mut scanner =
|
||||||
|
Scanner::new(ViewPair::new(monero_spend, Zeroizing::new(monero_view)).unwrap());
|
||||||
|
|
||||||
// Check for up to 5 minutes
|
// Check for up to 5 minutes
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
|
|||||||
@@ -411,6 +411,7 @@ impl Coordinator {
|
|||||||
rpc
|
rpc
|
||||||
.generate_blocks(
|
.generate_blocks(
|
||||||
&ViewPair::new(ED25519_BASEPOINT_POINT, Zeroizing::new(Scalar::ONE))
|
&ViewPair::new(ED25519_BASEPOINT_POINT, Zeroizing::new(Scalar::ONE))
|
||||||
|
.unwrap()
|
||||||
.legacy_address(Network::Mainnet),
|
.legacy_address(Network::Mainnet),
|
||||||
1,
|
1,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ impl Wallet {
|
|||||||
let view_key = Scalar::random(&mut OsRng);
|
let view_key = Scalar::random(&mut OsRng);
|
||||||
|
|
||||||
let view_pair =
|
let view_pair =
|
||||||
ViewPair::new(ED25519_BASEPOINT_POINT * spend_key, Zeroizing::new(view_key));
|
ViewPair::new(ED25519_BASEPOINT_POINT * spend_key, Zeroizing::new(view_key)).unwrap();
|
||||||
|
|
||||||
let rpc = SimpleRequestRpc::new(rpc_url).await.expect("couldn't connect to the Monero RPC");
|
let rpc = SimpleRequestRpc::new(rpc_url).await.expect("couldn't connect to the Monero RPC");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user