mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-11 05:29:25 +00:00
Changes meant for the previous commit
This commit is contained in:
@@ -99,100 +99,100 @@ impl Decoys {
|
||||
pub fn len(&self) -> usize {
|
||||
self.offsets.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn select<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
rpc: &Rpc,
|
||||
height: usize,
|
||||
inputs: &[SpendableOutput]
|
||||
) -> Result<Vec<Decoys>, RpcError> {
|
||||
// Convert the inputs in question to the raw output data
|
||||
let mut outputs = Vec::with_capacity(inputs.len());
|
||||
for input in inputs {
|
||||
outputs.push((
|
||||
rpc.get_o_indexes(input.tx).await?[input.o],
|
||||
[input.key, input.commitment.calculate()]
|
||||
));
|
||||
}
|
||||
pub(crate) async fn select<R: RngCore + CryptoRng>(
|
||||
rng: &mut R,
|
||||
rpc: &Rpc,
|
||||
height: usize,
|
||||
inputs: &[SpendableOutput]
|
||||
) -> Result<Vec<Decoys>, RpcError> {
|
||||
// Convert the inputs in question to the raw output data
|
||||
let mut outputs = Vec::with_capacity(inputs.len());
|
||||
for input in inputs {
|
||||
outputs.push((
|
||||
rpc.get_o_indexes(input.tx).await?[input.o],
|
||||
[input.key, input.commitment.calculate()]
|
||||
));
|
||||
}
|
||||
|
||||
let distribution = rpc.get_output_distribution(height).await?;
|
||||
let high = distribution[distribution.len() - 1];
|
||||
let per_second = {
|
||||
let blocks = distribution.len().min(BLOCKS_PER_YEAR);
|
||||
let outputs = high - distribution[distribution.len().saturating_sub(blocks + 1)];
|
||||
(outputs as f64) / ((blocks * BLOCK_TIME) as f64)
|
||||
};
|
||||
let distribution = rpc.get_output_distribution(height).await?;
|
||||
let high = distribution[distribution.len() - 1];
|
||||
let per_second = {
|
||||
let blocks = distribution.len().min(BLOCKS_PER_YEAR);
|
||||
let outputs = high - distribution[distribution.len().saturating_sub(blocks + 1)];
|
||||
(outputs as f64) / ((blocks * BLOCK_TIME) as f64)
|
||||
};
|
||||
|
||||
let mut used = HashSet::<u64>::new();
|
||||
for o in &outputs {
|
||||
used.insert(o.0);
|
||||
}
|
||||
let mut used = HashSet::<u64>::new();
|
||||
for o in &outputs {
|
||||
used.insert(o.0);
|
||||
}
|
||||
|
||||
let mut res = Vec::with_capacity(inputs.len());
|
||||
for (i, o) in outputs.iter().enumerate() {
|
||||
// If there's only the target amount of decoys available, remove the index of the output we're spending
|
||||
// So we don't infinite loop while ignoring it
|
||||
// TODO: If we're spending 2 outputs of a possible 11 outputs, this will still fail
|
||||
used.remove(&o.0);
|
||||
let mut res = Vec::with_capacity(inputs.len());
|
||||
for (i, o) in outputs.iter().enumerate() {
|
||||
// If there's only the target amount of decoys available, remove the index of the output we're spending
|
||||
// So we don't infinite loop while ignoring it
|
||||
// TODO: If we're spending 2 outputs of a possible 11 outputs, this will still fail
|
||||
used.remove(&o.0);
|
||||
|
||||
// Select the full amount of ring members in decoys, instead of just the actual decoys, in order
|
||||
// to increase sample size
|
||||
let mut decoys = select_n(rng, rpc, height, &distribution, high, per_second, &mut used, DECOYS).await?;
|
||||
decoys.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
// Select the full amount of ring members in decoys, instead of just the actual decoys, in order
|
||||
// to increase sample size
|
||||
let mut decoys = select_n(rng, rpc, height, &distribution, high, per_second, &mut used, DECOYS).await?;
|
||||
decoys.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
|
||||
// Add back this output
|
||||
used.insert(o.0);
|
||||
// Add back this output
|
||||
used.insert(o.0);
|
||||
|
||||
// Make sure the TX passes the sanity check that the median output is within the last 40%
|
||||
// This actually checks the median is within the last third, a slightly more aggressive boundary,
|
||||
// as the height used in this calculation will be slightly under the height this is sanity
|
||||
// checked against
|
||||
let target_median = high * 2 / 3;
|
||||
// Make sure the TX passes the sanity check that the median output is within the last 40%
|
||||
// This actually checks the median is within the last third, a slightly more aggressive boundary,
|
||||
// as the height used in this calculation will be slightly under the height this is sanity
|
||||
// checked against
|
||||
let target_median = high * 2 / 3;
|
||||
|
||||
// Sanity checks are only run when 1000 outputs are available
|
||||
// We run this check whenever it's possible to satisfy
|
||||
// This means we need the middle possible decoy to be above the target_median
|
||||
// TODO: This will break if timelocks are used other than maturity on very small chains/chains
|
||||
// of any size which use timelocks extremely frequently, as it'll try to satisfy an impossible
|
||||
// condition
|
||||
// Reduce target_median by each timelocked output found?
|
||||
if (high - MATURITY) >= target_median {
|
||||
while decoys[DECOYS / 2].0 < target_median {
|
||||
// If it's not, update the bottom half with new values to ensure the median only moves up
|
||||
for m in 0 .. DECOYS / 2 {
|
||||
// We could not remove this, saving CPU time and removing low values as possibilities, yet
|
||||
// it'd increase the amount of decoys required to create this transaction and some banned
|
||||
// outputs may be the best options
|
||||
used.remove(&decoys[m].0);
|
||||
// Sanity checks are only run when 1000 outputs are available
|
||||
// We run this check whenever it's possible to satisfy
|
||||
// This means we need the middle possible decoy to be above the target_median
|
||||
// TODO: This will break if timelocks are used other than maturity on very small chains/chains
|
||||
// of any size which use timelocks extremely frequently, as it'll try to satisfy an impossible
|
||||
// condition
|
||||
// Reduce target_median by each timelocked output found?
|
||||
if (high - MATURITY) >= target_median {
|
||||
while decoys[DECOYS / 2].0 < target_median {
|
||||
// If it's not, update the bottom half with new values to ensure the median only moves up
|
||||
for m in 0 .. DECOYS / 2 {
|
||||
// We could not remove this, saving CPU time and removing low values as possibilities, yet
|
||||
// it'd increase the amount of decoys required to create this transaction and some banned
|
||||
// outputs may be the best options
|
||||
used.remove(&decoys[m].0);
|
||||
}
|
||||
|
||||
decoys.splice(
|
||||
0 .. DECOYS / 2,
|
||||
select_n(rng, rpc, height, &distribution, high, per_second, &mut used, DECOYS / 2).await?
|
||||
);
|
||||
decoys.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
}
|
||||
|
||||
decoys.splice(
|
||||
0 .. DECOYS / 2,
|
||||
select_n(rng, rpc, height, &distribution, high, per_second, &mut used, DECOYS / 2).await?
|
||||
);
|
||||
decoys.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
}
|
||||
|
||||
// Replace the closest selected decoy with the actual
|
||||
let mut replace = 0;
|
||||
let mut distance = u64::MAX;
|
||||
for m in 0 .. decoys.len() {
|
||||
let diff = decoys[m].0.abs_diff(o.0);
|
||||
if diff < distance {
|
||||
replace = m;
|
||||
distance = diff;
|
||||
}
|
||||
}
|
||||
|
||||
decoys[replace] = outputs[i];
|
||||
res.push(Decoys {
|
||||
i: u8::try_from(replace).unwrap(),
|
||||
offsets: offset(&decoys.iter().map(|output| output.0).collect::<Vec<_>>()),
|
||||
ring: decoys.iter().map(|output| output.1).collect()
|
||||
});
|
||||
}
|
||||
|
||||
// Replace the closest selected decoy with the actual
|
||||
let mut replace = 0;
|
||||
let mut distance = u64::MAX;
|
||||
for m in 0 .. decoys.len() {
|
||||
let diff = decoys[m].0.abs_diff(o.0);
|
||||
if diff < distance {
|
||||
replace = m;
|
||||
distance = diff;
|
||||
}
|
||||
}
|
||||
|
||||
decoys[replace] = outputs[i];
|
||||
res.push(Decoys {
|
||||
i: u8::try_from(replace).unwrap(),
|
||||
offsets: offset(&decoys.iter().map(|output| output.0).collect::<Vec<_>>()),
|
||||
ring: decoys.iter().map(|output| output.1).collect()
|
||||
});
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user