mirror of
https://github.com/serai-dex/serai.git
synced 2024-12-23 12:09:37 +00:00
Fix the known issue with the DSA
I wrote it to only select TXs with a timelock, not only TXs which are unlocked. This most likely explains why it so heavily selected coinbases. Also moves an InternalError which would've never been hit on mainnet, yet technically isn't an invariant, to only exist when cfg(test).
This commit is contained in:
parent
89eef95fb3
commit
b195db0929
3 changed files with 20 additions and 13 deletions
|
@ -138,7 +138,7 @@ impl<R: RpcConnection> Rpc<R> {
|
||||||
)
|
)
|
||||||
.map_err(|_| RpcError::InvalidNode)?,
|
.map_err(|_| RpcError::InvalidNode)?,
|
||||||
)
|
)
|
||||||
.map_err(|_| RpcError::InternalError("Failed to parse JSON response"))
|
.map_err(|_| RpcError::InvalidNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform a JSON-RPC call with the specified method with the provided parameters
|
/// Perform a JSON-RPC call with the specified method with the provided parameters
|
||||||
|
@ -538,12 +538,10 @@ impl<R: RpcConnection> Rpc<R> {
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, out)| {
|
.map(|(i, out)| {
|
||||||
Ok(Some([rpc_point(&out.key)?, rpc_point(&out.mask)?]).filter(|_| {
|
Ok(
|
||||||
match txs[i].prefix.timelock {
|
Some([rpc_point(&out.key)?, rpc_point(&out.mask)?])
|
||||||
Timelock::Block(t_height) => t_height <= height,
|
.filter(|_| Timelock::Block(height) >= txs[i].prefix.timelock),
|
||||||
_ => false,
|
)
|
||||||
}
|
|
||||||
}))
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,9 +47,11 @@ async fn select_n<'a, R: RngCore + CryptoRng, RPC: RpcConnection>(
|
||||||
count: usize,
|
count: usize,
|
||||||
) -> Result<Vec<(u64, [EdwardsPoint; 2])>, RpcError> {
|
) -> Result<Vec<(u64, [EdwardsPoint; 2])>, RpcError> {
|
||||||
if height >= rpc.get_height().await? {
|
if height >= rpc.get_height().await? {
|
||||||
|
// TODO: Don't use InternalError for the caller's failure
|
||||||
Err(RpcError::InternalError("decoys being requested from too young blocks"))?;
|
Err(RpcError::InternalError("decoys being requested from too young blocks"))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
let mut iters = 0;
|
let mut iters = 0;
|
||||||
let mut confirmed = Vec::with_capacity(count);
|
let mut confirmed = Vec::with_capacity(count);
|
||||||
// Retries on failure. Retries are obvious as decoys, yet should be minimal
|
// Retries on failure. Retries are obvious as decoys, yet should be minimal
|
||||||
|
@ -57,10 +59,13 @@ async fn select_n<'a, R: RngCore + CryptoRng, RPC: RpcConnection>(
|
||||||
let remaining = count - confirmed.len();
|
let remaining = count - confirmed.len();
|
||||||
let mut candidates = Vec::with_capacity(remaining);
|
let mut candidates = Vec::with_capacity(remaining);
|
||||||
while candidates.len() != remaining {
|
while candidates.len() != remaining {
|
||||||
iters += 1;
|
#[cfg(test)]
|
||||||
// This is cheap and on fresh chains, thousands of rounds may be needed
|
{
|
||||||
if iters == 10000 {
|
iters += 1;
|
||||||
Err(RpcError::InternalError("not enough decoy candidates"))?;
|
// This is cheap and on fresh chains, a lot of rounds may be needed
|
||||||
|
if iters == 100 {
|
||||||
|
Err(RpcError::InternalError("hit decoy selection round limit"))?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use a gamma distribution
|
// Use a gamma distribution
|
||||||
|
@ -183,7 +188,7 @@ impl Decoys {
|
||||||
used.insert(o.0);
|
used.insert(o.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Simply create a TX with less than the target amount
|
// TODO: Create a TX with less than the target amount, as allowed by the protocol
|
||||||
if (high - MATURITY) < u64::try_from(inputs.len() * ring_len).unwrap() {
|
if (high - MATURITY) < u64::try_from(inputs.len() * ring_len).unwrap() {
|
||||||
Err(RpcError::InternalError("not enough decoy candidates"))?;
|
Err(RpcError::InternalError("not enough decoy candidates"))?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -273,7 +273,11 @@ impl<O: Clone + Zeroize> Timelocked<O> {
|
||||||
/// Returns None if the Timelocks aren't comparable. Returns Some(vec![]) if none are unlocked.
|
/// Returns None if the Timelocks aren't comparable. Returns Some(vec![]) if none are unlocked.
|
||||||
pub fn unlocked(&self, timelock: Timelock) -> Option<Vec<O>> {
|
pub fn unlocked(&self, timelock: Timelock) -> Option<Vec<O>> {
|
||||||
// If the Timelocks are comparable, return the outputs if they're now unlocked
|
// If the Timelocks are comparable, return the outputs if they're now unlocked
|
||||||
self.0.partial_cmp(&timelock).filter(|_| self.0 <= timelock).map(|_| self.1.clone())
|
if self.0 <= timelock {
|
||||||
|
Some(self.1.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ignore_timelock(&self) -> Vec<O> {
|
pub fn ignore_timelock(&self) -> Vec<O> {
|
||||||
|
|
Loading…
Reference in a new issue