Open UkoeHB opened 3 years ago
@UkoeHB
Not sure why I need to repeat myself. The prime stuff is irrelevant here, because I am not use polynomials. The bin_rotator is C from section 1.A. Section 1.A further only specifies that the hash function be 'keyed'. Sampling k randomly is only mentioned there as an example. All that matters is that each key be unique between different RSS instances, and determined by the tx author.
The polynomial P
is a tuple of constants, and this is the order-0 case. The equations listed in section 1.A are mentioned to be "incomplete", and the authors may have done a "look how to simple it is" while pushing the complexity of uniform distributions to the latter sections and proofs (the proofs make use of the prime field).
...
I don't see a security proof for the equations are you using (mod max(u64)
followed by mod num_outputs
doesn't have uniform distribution for instance).
Even if the adversary knows the key in advance of a tx being constructed, it can't be used to provide any information about the real spend (assuming they can't choose which key the tx author uses, and can't meaningfully choose the chain's composition after learning the key).
I'm possibly being overly conservative here - the key_images
and (especially) pseudo_outs
should have enough bits of unpredictability to match the requirements from the proofs. But injecting entropy via k
leaves little doubt when auditing that portion of the code.
I think we're stuck either choosing tx chaining with larger tx sizes OR smaller compact txes without tx chaining.
Why do you think having all-floating-outputs solves the problems with floating outputs in this proposal? The problems you have pointed out are high-fidelity heuristics that cannot defended against (i.e. rapid appearance of a tx in the mempool after an output it spends appears, presence of a floating output in last 10 blocks, floating output outside decoy-selection-range).
This proposal selects pubkeys from two different processes then merges them into one for use in a single ring-signature. You'll need some good justification for this as all prior work (that I am aware of) is on uniform exponential selection to match the real spend patterns. ... There is a known bias towards newer pubkeys, so having two distinct sets means the spend is more likely to be in the "floating index" stage. Having 1-4 fixed number of floating indexes is arbitrary compared to the uniformity of the exponential distribution over all spendable blocks that we use now.
The best (partial) solution I can muster is to select num_floating_indexes
based on expected/average number of bins that should be in blocks 1-10 given a specific bin_size
. So num_floating_indexes
will no longer be arbitrary and hopefully less likely to leak statistical information, but it still leaves the unexplored funkiness of having two selection processes for decoys.
more entropy from the first stage should be extracted
What?
log(RING_SIZE)
or log2(RING_SIZE)
provides too few bins.
There's still disk access time and bandwidth from memory -> CPU registers. There's always some cost to adding more bytes of stuff. This may be less than the penalty of larger sizes from the signature itself though. I mentioned solely to indicate that there are limitations on how large the bins can be.
Huh? I am recommending a maximally compact design. This proposal has no impact on or opinion about how many total decoys are selected for rings.
Then why list a formula for specifying how many bins there should be?
Alternative The alternative to floating offsets is to define a new input type with only 1 ring member whose on-chain reference (i.e. index) is not signed by tx authors (as discussed earlier in this thread).
This is identical in concept to the floating indexes concept ?
@SamsungGalaxyPlayer
I need to think more about the other two components of this proposal. I think the terrible UX of the 10 block locktime is significant and should be addressed (even with some reasonable tradeoffs) if possible. I care far less about accounting for the atomic swaps use-case.
There's plenty of work, testing, and auditing needed just to get tx-chaining implemented. Although, once implemented the RSS/hash decoy selection algorithm may never get merged because having two different decoy selection algorithms is suboptimal.
The polynomial
p
is a tuple of constants, and this is the order-0 case.
Umm.. the order-0 case of a polynomial is a single constant. The whole paper boils down to proving the relation f_k(j) + P(j) = I_j mod l
is legit. The term P(j) mod l
reduces to a constant in the range 0 <= C < l
if P(x)
is a 0-order polynomial. In other words, the prime field used to construct P(x)
is irrelevant if you can derive C = I_j - f_k(j) mod l
directly.
The trivial case is literally so trivial the authors had full right to give it just one sentence.
I don't see a security proof for the equations are you using (mod max(u64) followed by mod num_outputs doesn't have uniform distribution for instance).
The modular operations in this proposal are all mod l
like in the paper. Bin loci are selected from the transform-space (i.e. inverse transform sampling) of the gamma distribution (mod max(u64)
), and bin members are selected from 'within a bin' (mod bin_size
).
Then why list a formula for specifying how many bins there should be?
?????
The amount of data stored in transactions in this proposal is O(1). You could have 3 total decoys, or 3 million total decoys.
Do you have some fundamental misunderstanding about this proposal lurking under the surface?
This is identical in concept to the floating indexes concept ?
Huh? We already discussed this earlier in the thread. A 1-ring-member input is literally an input that only references the output being spent.
The polynomial p is a tuple of constants, and this is the order-0 case.
Umm.. the order-0 case of a polynomial is a single constant.
Yes, the P
tuple contains 1+ constants. The sarcasm was unnecessary here.
The whole paper boils down to proving the relation f_k(j) + P(j) = I_j mod l is legit. The term P(j) mod l reduces to a constant in the range 0 <= C < l if P(x) is a 0-order polynomial. In other words, the prime field used to construct P(x) is irrelevant if you can derive C = I_j - f_k(j) mod l directly.
The trivial case is literally so trivial the authors had full right to give it just one sentence.
The paper shows the first relationship you describe then also attempts a security proof. It is the latter that I was concerned about in these responses. x + y mod l
has uniform distribution only when l
meets certain properties, otherwise some outputs from that expression are more likely than others. The difference is admittedly trivial though.
Do you know why a prime field is necessary for the 2+ case then?
I don't see a security proof for the equations are you using (mod max(u64) followed by mod num_outputs doesn't have uniform distribution for instance).
The modular operations in this proposal are all mod l like in the paper. Bin loci are selected from the transform-space (i.e. inverse transform sampling) of the gamma distribution (mod max(u64)), and bin members are selected from 'within a bin' (mod bin_size).
Where does BIN_RADIUS
fit into all of this?
There's still disk access time and bandwidth from memory -> CPU registers. There's always some cost to adding more bytes of stuff. This may be less than the penalty of larger sizes from the signature itself though. I mentioned solely to indicate that there are limitations on how large the bins can be.
Huh? I am recommending a maximally compact design. This proposal has no impact on or opinion about how many total decoys are selected for rings.
Then why list a formula for specifying how many bins there should be?
?????
The amount of data stored in transactions in this proposal is O(1). You could have 3 total decoys, or 3 million total decoys.
Do you have some fundamental misunderstanding about this proposal lurking under the surface?
I provided more context. This discussion devolved due to terse responses.
I was specifying how I would determine bin_size
and num_bins
, regardless of how the decoys were encoded (as its not guaranteed to be implemented, tx-chaining itself is a big enough feature in a fork). In the RSS O(1) scheme, that would've been bin_size == 1
; the utility of the bins seemed low with this scheme. You and Justin have persuaded me slightly otherwise - we'd probably have to take real chain-analysis into account though.
I'm still against the sqrt
or log
approaches, especially with RSS (there's no space savings), as it seems the bias should be towards exponentially selected decoys.
Huh? We already discussed this earlier in the thread. A 1-ring-member input is literally an input that only references the output being spent.
My apologies for responding poorly - it still has an obvious privacy leak, so its not really a viable alternative.
I have a question. How does this proposal impact transaction sizes? In particular ring 11 and say ring 17 (CLSAG) and say ring 64 and ring 128 (Trtptych)
x + y mod l
has uniform distribution only whenl
meets certain properties, otherwise some outputs from that expression are more likely than others.
I don't think this is quite right, and it is pertinent here. Whether or not x + y mod l
is uniformly distributed depends on A) how x
and y
are generated, B) the relation between l
and those generation methods. For example, if x
is randomly selected from 0 <= x < l/2
, and y
from 0 <= y <= l/3
, this would clearly not produce a random distribution in x + y mod l
. But, if both x
and y
are selected randomly from 0 <= n < 2*l
, then the result will be uniformly distributed.
Importantly, if n
s generation space is >> l
, then even if n
is not uniformly distributed in its own selection space, n mod l
will be uniformly distributed 'in practice' (i.e. probabilistically). This behavior is used in the paper for reducing polynomials into the index space, because most polynomials are not uniformly distributed. I assume the use of a 'prime' prevents subgroup issues when solving the polynomial for a given input.
EDIT: I just remembered the proposal uses this behavior too, when going from [hash output] -> [selection space]. A hash output is a multiple of 8 bytes, so it will be uniformly distributed in the bin loci space (u64), and the hash space is >> bin width (on the order of 100-10000, compared to 2^128-2^256) so bin members will be effectively uniformly distributed. I'm guessing this is what you have been talking about. It would help if you quote lines from the proposal directly.
If you look at Figure 1 in the paper, steps 5-7 simplify to the stuff in section 1.A when P()
is 0-order, and step 1 (defining the prime) becomes irrelevant.
Where does
BIN_RADIUS
fit into all of this?
BIN_RADIUS
is used to set the default 'bin width', which is the number of of outputs on-chain to select a bin's members from (i.e. the decoys for a ring sig). If BIN_RADIUS = 50
, then each bin will have 101 outputs to select bin members from. The reason I don't use BIN_WIDTH
directly is because most operations related to bins are with respect to bin loci, rather than e.g. the lower bounds.
I was specifying how I would determine bin_size and num_bins, regardless of how the decoys were encoded (as its not guaranteed to be implemented, tx-chaining itself is a big enough feature in a fork). In the RSS O(1) scheme, that would've been bin_size == 1; the utility of the bins seemed low with this scheme. You and Justin have persuaded me slightly otherwise - we'd probably have to take real chain-analysis into account though.
I see, thank you for clearing this up.
I'm still against the sqrt or log approaches, especially with RSS (there's no space savings), as it seems the bias should be towards exponentially selected decoys.
The reason I like sqrt
is because it evenly distributes 'decoy selection' between the two timing attack vectors (spend-time selection, local-vicinity selection). Since we can't know in advance which vector is more prevalent or important, allocating selection to them equally is a solid compromise (in my view).
My apologies for responding poorly - it still has an obvious privacy leak, so its not really a viable alternative.
I think it is the most viable solution, even if the level of viability is too low for it to be accepted. Since it is most viable, it should at least be mentioned for context.
@ArticMine This proposal would reduce tx sizes compared to our current protocol.
The proposal is O(1) with respect to number of decoys.
if you imagine the extreme idea of a 128 member ring, where the first 117 members are chosen from the tip of the chain (or the txpool directly), then this set of 117 decoys can stand to lose some while maintaining the obfuscation.
The problem is some decoys will be permanently removed from the chain due to a double spend, so the transactions that reference them will be permanently invalid.
right.... I am imagining that there is some cryptomagic that allows you to still have a valid ring signature with some ring members missing. Sorta like how a multisig is possible with an incomplete set.
But that doesn't exist.... ?
right.... I am imagining that there is some cryptomagic that allows you to still have a valid ring signature with some ring members missing. Sorta like how a multisig is possible with an incomplete set.
But that doesn't exist.... ?
I think in your scenario you just wouldn't get to the point of verifying the ring signature, because the transaction would reference non-existent outputs in the input ring. That is surely disallowed, but even if you could get past that you would not be able to produce the original signature hash to verify against, given that some data (e.g. output commitments) would be missing or wrong.
well, on second thought, in the case of a double spend / re-org,
given that some data (e.g. output commitments) would be missing or wrong.
those data aren't missing, they are just now on the orphaned chain.
but yeah. not saying that it should be done, but there's probably a hacky way that it could be done. But for sure, this avenue of thought is sufficiently bricked over for me at least.
well it seems I can't brick up this avenue that well.
you could call this a "fray", and its kinda like ethereums uncle blocks... but yeah, in the case of a re-org, you could carry the alt-chain to maintain the data references .... but yeah, pretty ugly and probably opens some attack surfaces.
I think allowing for 1 member rings is actually a pretty solid alternative that makes the network safer/enables a stronger degree of privacy for most users.
Today there are some honest users who rely on making transactions transparent; mining pools making payouts to pool members are one such example. In this case, honest users who benefit Monero are revealing their spent outputs and negatively affecting others on the network, who then include those outputs in their rings. Another example: a potential Thorchain integration, where all transactions that happen on Thorchain would be made public. Allowing an alternative tx (1 member rings) for these good-faith users, without polluting the global output set for other users who want privacy by default (or bloating the chain with unnecessarily large rings), seems like a reasonable use case that makes the network safer for everyone.
Basically, if you're an honest user but you're going to make your transaction public anyway, just make it transparent on the chain in a silo'd part of the chain where others can't use your known spent outputs, so you won't negatively affect other users. Plus, you have the added benefit of marginally cheaper transactions, and it saves space.
Edit: it could work by designating an output as only usable in a 1 member ring going forward. For example, a mining pool knows it's going to pay out the block reward in a new tx in the future, so in the miner tx, the output would be designated as only being usable in a 1 member ring - something along those lines. That way no one else would attempt to use that output as a decoy in their ring.
So, what happens when a bunch of honest users makes a shitload of 1-ringmember outputs? Wouldn't that help the dishonest observers having a smaller set of outputs in need of analyzing?
My assumption is that the feature would be used by people who are either already revealing their spent outputs anyway such as mining pools (this group is adding to the set of outputs in a way that is harmful today -- these additional outputs added to the global set of outputs are not beneficial in any way whatsoever), or would not otherwise add to the set of outputs at all.
I wasn't thinking that it would take away from people who are adding to the output set in a beneficial way (i.e. I didn't think this would alter normal usage of Monero, where the average user doesn't reveal their spent outputs to the world).
Though if there is an incentive of significantly lower fees, it might attract people to want to use it instead of normal ring tx's, and therefore potentially take away from the output set. Perhaps could require a fee on par with normal tx's, and so the only incentive to using it is either altruistic (i.e. can't do harm using it, but it's beneficial for other users), or it allows you to do something you wouldn't have otherwise done.
Realized the above is basically one of the recommendations of An Empirical Analysis of Traceability in the Monero Blockchain (pg. 16)
Avoid including publicly deanonymized transaction outputs as mixins
We have empirically shown the harmful effect of publicly deanonymized (i.e. 0-mixin) transactions on the privacy of other users. Since non-privacy-conscious users may make 0-mixin transactions to reduce fees, Monero had instituted a 2-mixin minimum, and recently increased this to 4. However, even 4+mixin transactions may be publicly deanonymized; in particular, as discussed in Section 5.1, mining pools have a legitimate interest in forgoing anonymity by publicly announcing their blocks and transactions for the sake of accountability. Thus, we propose that Monero develop a convention for flagging such transactions as “public,” so that other users do not include them as mixins.
You are also assuming honest users actually exists, and would be the only ones to utilize this, while you should assume that any user is potential adversarial, and should maximize protection against those friendly honest users :smile:
Fair enough. Thought it through more deeply and there are reasons to think honest users may be pulled into using this feature over using normal tx's, thereby reducing the size of the output set. Here are the scenarios where that might happen:
If the unlock time is done away with for 1-member rings, users who value being able to make quicker payments over privacy may now opt to use 1-member rings over normal tx's. Exchanges probably the largest source of tx's that fit the bill here.
Users may atomic swap Monero for Bitcoin thanks to this feature instead of going to Haveno and exchanging for Bitcoin using their protocol (where spent output data wouldn't be published I believe).
Users who value performance over privacy at the point of tx construction may opt to construct the quicker-to-construct and transmit 1-member ring tx's.
There are probably more I'm not seeing. Thorny tradeoffs.
Fair enough. Thought it through more deeply and there are reasons to think honest users may be pulled into using this feature over using normal tx's, thereby reducing the size of the output set. Here are the scenarios where that might happen:
* If the unlock time is done away with for 1-member rings, users who value being able to make quicker payments over privacy may now opt to use 1-member rings over normal tx's. Exchanges probably the largest source of tx's that fit the bill here. * Users may atomic swap Monero for Bitcoin thanks to this feature instead of going to Haveno and exchanging for Bitcoin using their protocol (where spent output data wouldn't be published I believe). * Users who value performance over privacy at the point of tx construction may opt to construct the quicker-to-construct and transmit 1-member ring tx's.
There are probably more I'm not seeing. Thorny tradeoffs.
Not at all, if you don't want to be part of the anonymity set then don't use the coin.
You smell like a plant with an agenda to weaken the coin.
@Hueristic
My initial angle was to strengthen the anonymity set, which is currently actively being weakened by honest users, which is evident in that mining pool page I linked.
My last comment that you're responding to was fully fleshing out and acknowledging the downsides. If anything that would be the comment you should agree with most from your perspective.
In any case, I'm tapping out of this conversation, since I agree that those downsides, which I explained fully, are significant enough I don't feel comfortable pushing forward 1-member rings.
In theory it would be fine to grab the efficiency gains of the 1-output rings and the flagging that could be shown to wallets, but this isn't realistic or enforceable. Thus, the status quo of the minimum ringsizes seems to be the best option in practice. We already know marking outputs as spent on the wallet side is possible but terrible in practice. Most mining pools now share less information to the public anyway, and Monero network activity has far surpassed these mining pool outputs anyway so it's no longer a significant problem.
I share concerns about the visible outputs through something like Thorchain, and the desire to flag these outputs as a special kind so they can be avoided (along with whatever else should be flagged). From a consensus design decision however, it's best to limit on-chain fuckery as much as possible, or else it's like Monero's past where people widely used 1-ring transactions for no good reason.
We had a similar discussion for coinbase outputs and possibly requiring coinbase-only rings (since those are already marked, no avoiding that). This was passed on mostly for complexity and not-significant-enough-of-a-harm reasons.
Anyway, I'd rather steer this discussion back to the other main topics. Whether Monero should have a public output tier should be a completely separate issue.
@UkoeHB , i would recommend changing the title back and just starting a new issue for ring binning. If you change the original post, then the responses aren't gonna make sense etc etc.
I mean, you can do what you want obvi :) but in 2 years we'll have no idea what happened here.
and its good to have documented why a particular feature / solution was sent back to the drawing board.
Github comments/etc. have a history of edits you can look at. I added the date to my comment about removing tx chaining if people want to look at the old version.
I think @vtnerd has mentioned it might not be good to bake a selection algorithm directly into the consensus rules. This proposal can be modified for use in that context (generate bin locations locally, generate bin members deterministically).
>= lowest_output + bin_radius
and <= highest_output - bin_radius
based on validating node's ledgerThe disadvantages compared to a baked-in selection algorithm are:
I think I have a bit of a clearer explanation for the subtle leak @vtnerd talked about here. I don't yet see a perfect way around it with a simple client-side binning approach I've been trying to think through either.
It's easier to see it if you assume 100 bin members. You'll have a "jar of marbles" so to speak that revolve around the center. So the bin center would be clearly deducible.
If your real output is used as a bin center, then it's fairly trivial there would be no benefit to binning. An observer could just eliminate any outputs that aren't bin centers.
If you take your real output, and try to select a new bin center using the real output, the new bin center is still statistically more likely to be closer to the real output (can qualify this claim further with some kind of proof). Therefore, the outputs that are closer to the bin center are still more likely to be real than the outputs further away.
At this point, I'm trying to reason through if it's possible to avoid this leak by fixing the bin size to 2, and going with an approach that doesn't scale to >2 bin members. But with >2 bin members, I think the above should help make it a bit clearer a leak is introduced with an approach along these lines.
Perhaps the Moser paper's approach for fixing bins may be the best way to go after all.
If you take your real output, and try to select a new bin center using the real output, the new bin center is still statistically more likely to be closer to the real output (can qualify this claim further with some kind of proof).
Unless I am missing something, selecting a bin center at random from around the real spend means the real spend - center
delta will be uniformly distributed (equally likely to be any value).
Yep, nevermind I believe you are right @UkoeHB -- that was me tripping up. Fairly simple python script that should support your claim and show I was wrong:
import random
import statistics
BIN_WIDTH = 100
BIN_RADIUS = int(BIN_WIDTH / 2)
REAL_OUTPUT_INDEX = 150
NUM_SIMULATIONS = 100000
BIN_MEMBERS = 50
init_bin = range(REAL_OUTPUT_INDEX - BIN_RADIUS, REAL_OUTPUT_INDEX + BIN_RADIUS)
real_is_closer_than_bin_members = 0
real_is_further_than_bin_members = 0
deltas = []
for i in range(NUM_SIMULATIONS):
bin_center = random.choice(init_bin)
final_bin = range(bin_center - BIN_RADIUS, bin_center + BIN_RADIUS)
bin_member_distances = []
for j in range(BIN_MEMBERS):
bin_member = random.choice(final_bin)
# on average, expect this distance to be BIN_WIDTH / 4
distance_from_bin_center = abs(bin_member - bin_center)
bin_member_distances.append(distance_from_bin_center)
avg_bin_member_distance_from_bin_center = sum(bin_member_distances) / len(bin_member_distances)
median_bin_member_distance = statistics.median(bin_member_distances)
# on average, expect this to be BIN_WIDTH / 4
real_distance_from_bin_center = abs(REAL_OUTPUT_INDEX - bin_center)
if real_distance_from_bin_center < median_bin_member_distance:
real_is_closer_than_bin_members += 1
elif real_distance_from_bin_center > median_bin_member_distance:
real_is_further_than_bin_members += 1
# my initial claim was that this would be negative with significance
real_and_bin_member_delta = real_distance_from_bin_center - avg_bin_member_distance_from_bin_center
deltas.append(real_and_bin_member_delta)
# but not the case
print("Delta should be close to 0 to show initial claim was wrong:", sum(deltas) / len(deltas))
# my claim was the real would tend to be closer to the bin center than other bin members, not the case with significance
print("Real is closer to bin center than other bin members: ", real_is_closer_than_bin_members, "times")
print("Real is further than bin center than other bin members:", real_is_further_than_bin_members, "times")
EDIT: another sanity check from a different angle:
import random
BIN_WIDTH = 100
BIN_RADIUS = int(BIN_WIDTH / 2)
REAL_OUTPUT_INDEX = 150
NUM_SIMULATIONS = 1000000
BIN_MEMBERS = 20
init_bin = range(REAL_OUTPUT_INDEX - BIN_RADIUS, REAL_OUTPUT_INDEX + BIN_RADIUS)
# is the real's distance from bin center uniformly distributed?
real_output_bin_member_distance_index_counts = {}
for i in range(BIN_MEMBERS + 1):
real_output_bin_member_distance_index_counts[i] = 0
for i in range(NUM_SIMULATIONS):
bin_center = random.choice(init_bin)
final_bin = range(bin_center - BIN_RADIUS, bin_center + BIN_RADIUS)
bin_member_distances = []
selected_bin_members = { REAL_OUTPUT_INDEX: True }
real_distance_from_bin_center = abs(REAL_OUTPUT_INDEX - bin_center)
bin_member_distances.append(real_distance_from_bin_center)
for j in range(BIN_MEMBERS):
bin_member = random.choice(final_bin)
# no duplicates
while bin_member in selected_bin_members:
bin_member = random.choice(final_bin)
selected_bin_members[bin_member] = True
distance_from_bin_center = abs(bin_member - bin_center)
bin_member_distances.append(distance_from_bin_center)
bin_member_distances.sort()
real_output_bin_member_index = bin_member_distances.index(real_distance_from_bin_center)
# sometimes there will be duplicate distances, and index() will always choose the closer one.
# to avoid this, check the next elem in the bin_member_distances array. if it's the same, then
# 50% of the time, just bump the real_output_bin_member_index by 1
if real_output_bin_member_index < len(bin_member_distances) - 1:
if real_distance_from_bin_center == bin_member_distances[real_output_bin_member_index + 1]:
if random.choice(range(2)) == 1:
real_output_bin_member_index += 1
real_output_bin_member_distance_index_counts[real_output_bin_member_index] += 1
# expect roughly equivalent counts for each index
for i in range(BIN_MEMBERS + 1):
print("Idx:", i, " Count:", real_output_bin_member_distance_index_counts[i])
EDIT 2:
A more visual way of seeing the problem:
. is some other output in the chain
x is the real output
y is the bin center
bin radius is 2
Start with x:
..x..
Now here are all plausible bin centers:
x.y..
.xy..
..x.. <- x & y are the same
..yx.
..y.x
Knowing the indexes of x
and y
yields no useful information about x
being real, because x
can be in any position of the bin with equal likelihood.
(Edited again for clarity.)
Question on this part:
// 1. randomly select the real bin's center from around the output
size_t real_bin_max = spend_output_indices[input_index] + BIN_RADIUS;
size_t real_bin_min = spend_output_indices[input_index] - BIN_RADIUS;
// snap bin bounds to allowed range (with adjustments in case of integer overflow)
if (real_bin_max > binning_upper_bound || real_bin_max < spend_output_indices[input_index])
real_bin_max = binning_upper_bound;
if (real_bin_min < min_index || real_bin_min > spend_output_indices[input_index])
real_bin_min = min_index;
size_t real_bin_center = rand_from_range<size_t>(real_bin_min, real_bin_max);
Am I following right that the bin width is likely to shrink if you're at the edge? If your real output is the max output allowed, your theoretical bin center could be between the real output and the real output - BIN_RADIUS
, which would mean in most cases the upper part of the bin is cut off at the edge
Am I following right that the bin width is likely to shrink if you're at the edge?
The bin center selection zone is shrunken/cropped. There isn't any other way to do it, because upper/lower bounds are just that - the boundaries of your data set, and because bin width is fixed - the bin center must be within +/- BIN_RADIUS
of the real spend.
which would mean in most cases the upper part of the bin is cut off at the edge
Yep
I think there may be an issue at the edge. Am I missing something here?
assume bin radius = 2
real output 0 can have bin center at 0, 1, 2
real output 1 can have bin center at 0, 1, 2, 3
real output 2 can have bin center at 0, 1, 2, 3, 4
real output 3 can have bin center at 1, 2, 3, 4, 5
...
Real output 0 has 0 bin center 1/3 times, real output 1 has 0 bin center 1/4 times, real output 2 has 0 bin center 1/5 times. Assuming you have roughly the same number of 0's, 1's, and 2's, then you would expect a higher % of the 0 bin centers to be real output 0.
Therefore, if you know the bin center is 0 (or in real terms, if you know the bin center is the closest possible bin center to the upper bound), your best guess for the real is output 0, next best guess is output 1, etc.
What am I missing?
I don't think you are missing anything, that is correct (and unavoidable).
On the other hand, you will also have a slightly disproportionate 'piling up' of decoys bins at the boundaries. I think if the true spend distribution matches the bin selection distribution, then these two effects cancel each other out (from a high-level statistical pov; if you have special timing knowledge about an output, then binning is less effective at the boundaries of the data set).
I think I have a way to avoid it in the wallet-side algorithm (at least for the tip of the chain), though I'm not sure it's possible to apply here: using fixed bins, similar to how the Moser paper suggests. I.e. you know a group of outputs must fall into a particular bin.
You could say outputs 0-99 in the chain = bin 0
, outputs 100-199 in the chain = bin 1
, etc. all the way until the back of the chain, where the final bin is likely to be smaller. Which seems fine to deal with because the chances of the final bin being used are extremely tiny versus the tip of the chain's bin.
I think that can be applied here. Just define the binning upper bound, pre-define bins relative to the binning upper bound ((upper_bound - BIN_RADIUS) - (2*BIN_RADIUS + 1)*bin_selector
). Then instead of defining bin centers directly, you deterministically select a bin member, then find which bin it belongs to. For the real spend's bin, you'd randomly select a bin member from its bin to map into the uniform distribution.
so transaction chaining is out? is this something that Seraphis can allow?
@r4v3r23 yes, Seraphis allows transaction chaining. The current RingCT protocol could technically do tx chaining with a LOT of code work and protocol changes, but the real spends in chained tx would always be the 'newest' ring member, which is an unpleasant and perhaps not-worthwhile heuristic.
@r4v3r23 yes, Seraphis allows transaction chaining. The current RingCT protocol could technically do tx chaining with a LOT of code work and protocol changes, but the real spends in chained tx would always be the 'newest' ring member, which is an unpleasant and perhaps not-worthwhile heuristic.
would tx chaining allow for spending unconfirmed outputs and remove the 10-block confirmation lock when receiving funds?
would tx chaining allow for spending unconfirmed outputs and remove the 10-block confirmation lock when receiving funds?
Tx chaining lets you make a partial tx that spends outputs that aren't in the chain. However, the 10-block lock time must remain in place. Here's what you can do (Alice and Bob are friends):
I think TX chaining and spending of outputs younger than 10 blocks could be still done with binning if the youngest bin referenced outputs by hash instead of by index. This would increase output sizes (for example by 128 bytes for num_bin_members = 4
), but would make the scheme resistant to reorgs and preserve some privacy when spending such outputs.
In the example given by @UkoeHB Alice could draw some decoys from block X, so Bob will not know which exact output is being spent.
I think TX chaining and spending of outputs younger than 10 blocks could be still done with binning if the youngest bin referenced outputs by hash instead of by index.
This is basically the floating output idea this issue originally proposed. I think it is too flawed to pursue.
@tevador @UkoeHB from the latest getmonero.org Seraphis write-up:
Ignore 10-block lock time when transacting with a trusted party (i.e. allow them to make your tx's membership proofs and submit the tx to the network on your behalf).
is it possible to remove the 10-block lock time in practice without harming privacy across the board by revealing the real output in "trusted" transactions? can this at least remove the lock time on unconfirmed change since its essentially a self-spend with not other party involved?
just trying to get a feel for how this would change UX when transacting
Publicly revealing the spent output weakens all rings that have used that output as a decoy, so that would be a significant hit to the overall privacy of Monero.
right so in practice the 10-block limit stays
Have any problems been found with this deterministic ring selection algorithm? @UkoeHB's current Seraphis code seems to encode the bin centers explicitly by index rather than generating them from a seed.
There are several advantages of selecting the bins deterministically:
binning_upper_bound
was implemented as a blockchain height, this would give us an equivalent of Bitcoin's nLockTime for free. Assuming this field would be signed by the Seraphis ownership proof, it could be used in trustless protocols such as atomic swaps to create a transaction that cannot be submitted to the network for a certain number of blocks. This could entirely replace the current broken timelock feature that itself interferes with deterministic selection.Disadvantages:
If the binning_upper_bound was implemented as a blockchain height, this would give us an equivalent of Bitcoin's nLockTime for free.
If the bin loci are deterministic, but a sub-range of the selection zone is unknown, then you have to brute force rings that only sit within the known range. Since selection distributions greatly favor recent blocks, it may be prohibitively expensive to find viable rings to get a lock time.
Assuming this field would be signed by the Seraphis ownership proof
It cannot be signed by ownership proofs, because that would greatly weaken tx chaining. For example, I want to make multisig txs where the decoy references are selected moments before submitting each tx (this is deferred membership proofs, which is the precursor to tx chaining), in order to make multisig txs more indistinguishable from regular txs. That timing can't be known in advance (when building the signatures). For tx chaining, if binning_upper_bound
is baked into signatures, then you can't chain off an enote if it gets added after binning_upper_bound
.
you have to brute force rings that only sit within the known range
No. You have to defer making the membership proof until the lock time has elapsed and all outputs in the specified range are known. This is the main point of a time lock field. Consensus would reject transactions with binning_upper_bound > current_height
.
I want to make multisig txs where the decoy references are selected moments before submitting each tx
Valid issue, but not insurmountable. Assuming the multisig participants cooperate, they can estimate the required lock time when the signature will be completed.
I guess it's a matter of deciding if we need cheap time locks that are actually usable or the ability to pre-sign a transaction without locking it.
No. You have to defer making the membership proof until the lock time has elapsed and all outputs in the specified range are known.
Ah yes, my mistake.
Assuming the multisig participants cooperate, they can estimate the required lock time when the signature will be completed.
If you aren't online around the right time, then it becomes a brute force problem again. If we want a cleartext min_mineable_height
it would be much simpler to just add one varint to txs for that purpose. A more privacy-oriented solution would use range proofs.
If we want a cleartext min_mineable_height it would be much simpler to just add one varint to txs for that purpose
This has the problem of leaking that the tx was time-locked.
A more privacy-oriented solution would https://github.com/monero-project/research-lab/issues/78#issuecomment-1003195804
More costly to store and verify.
But I digress, this discussion would be more suitable for the time locks issue. I just wanted to point out that ring selection could be used as a proxy time lock.
note: tx chaining was removed after discussion (July 18, 2020)
Table Of Contents
Abstract
Reference on-chain outputs for ring membership deterministically to minimize storage, and introduce a binning strategy to mitigate failures of the decoy selection distribution (e.g. gamma distribution).
Motivation
Monero is planning to implement a next-gen protocol with sublinear scaling that should allow ring sizes on the order of 2^6 to 2^8 (64 to 256) (either Triptych or some alternative). Referencing each ring member individually is inefficient, so 'deterministic' references are beneficial. 'Deterministic' means being able to recover an arbitrary number of on-chain indices from a small tuple of variables that include some kind of entropy.
As discussed in An Empirical Analysis of Traceability in the Monero Blockchain, selecting ring decoys directly from a distribution that spans the entire ledger is not perfect. If an observer has special timing knowledge about a given transaction, and/or knows that the selection distribution does not match the true spend distribution, then they can gain a significant advantage when trying to guess the true spends in that transaction.
That problem can be mitigated by selecting 'clumps' of decoys from the ledger-spanning selection distribution. A 'clump' (or 'bin') of decoys is a small set of decoys that are located very close to each other on the ledger. This way, even if an observer can guess the age of a transaction's true spend to within a few hours or less, the true spend will still be hidden among some decoys.
It isn't ideal to only select decoys that are close to the real spend. Even if some observers have special timing information about transactions, other observers may not. It is therefore useful to combine clumping/binning with selection from the ledger-wide distribution (recommended by Foundations of Ring Sampling).
This proposal describes a deterministic ring member referencing strategy where clumps/bins of decoys are selected from a ledger-wide distribution. Privacy considerations are discussed where appropriate.
Algorithm summary
To set the stage, I will briefly summarize the ring member referencing algorithm here (glossing over all the details, which can be found in the actual algorithm below). Each output spent by a transaction will have its own ring member reference tuple (set of variables to recover its set of ring members from).
binning_upper_bound
, which is the index of the highest output in the ledger that can be referenced by this input. Defining this is necessary so interacting with the ledger-wide selection distribution is deterministic.[true_spend_index - bin_radius, true_spend_index + bin_radius]
, which is equal to the width that bins will have.binning_upper_bound
) to a uniform distribution with a CDF. Its value in the uniform distribution is denotedmapped_real_spend_bin_center
.n
points in the uniform distribution.n'
at random from thosen
points. Definebin_rotator = mapped_real_spend_bin_center - n' mod uniform_distribution.size()
.n
points:n += bin_rotator mod uniform_distribution.size()
. Note thatn'
will now equalmapped_real_spend_bin_center
.n
points from the uniform distribution back into the ledger-wide selection distribution with a reverse CDF. All pointsmapped_n
are 'bin centers', the centers of each of the bins in the final ring member reference set. If bins should have only one member each (bin_radius = 0
), thenmapped_n
can be treated directly as ring member references and you don't need to proceed further.mapped_n
, use a hash function and public entropy to generatem
bin members from the range[mapped_n - bin_radius, mapped_n + bin_radius]
.m'
. Definebin_member_rotator = real_spend_index - m' mod 2*bin_radius + 1
.m
points in all bins:m = [(m - [mapped_real_spend_bin_center - bin_radius]) + bin_member_rotator mod 2*bin_radius + 1] + [mapped_real_spend_bin_center - bin_radius]
. Note thatm'
will now equalreal_spend_index
.binning_upper_bound
,bin_rotator
, andbin_member_rotator
. In practice, onlybin_rotator
andbin_member_rotator
need to be unique between transaction inputs.Binning strategy
For a given tx, reference all its inputs' ring members with the following strategy inspired by How to Squeeze a Crowd.
Bin configuration details
Each transaction has:
binning_upper_bound
(varint): An on-chain index that sets the upper bound for the range of outputs that can be members of bins. There is one of these per transaction.Each input has:
bin_rotator
(unsigned 8-byte integer): Rotates pseudo-randomly generated bin centers around the bin selection distribution.bin_member_rotator
(varint): Rotates pseudo-randomly generated bin members within all bins.Instead of public entropy, we will use a pseudo-random seed computed from parts of the transaction.
Bins
Define the number of bins for each input.
NUM_BINS = floor(sqrt(RING_SIZE))
. [[[TODO: is logarithmic bin division better? other ideas? discussion is ongoing, however there is general consensus that in practice NUM_BINS should be >= current ring size (11)]]]RING_SIZE
: A constant defined by the Monero protocol.Given
uneven_bins = RING_SIZE mod NUM_BINS
, define the number of bin members in each bin.bin_center
(discussed below), sort the bins. Letindex(bin, bins)
give the index ofbin
in vectorbins
.Rationale/Discussion
sqrt()
for deciding the number of bins to balance between selecting bins (ledger-scope) and bin membership (local-scope).Binning algorithm: tx construction
Define the bin configuration details and obtain the list of ring members for each input.
Constants and inputs
Rationale/Discussion
max_index
must be 'spendable', meaning a transaction that references that output won't be rejected if immediately submitted to the network. Monero has a 10-blockDEFAULT_SPENDABLE_AGE
that disallows adding a transaction to the chain if the tx references an output from the previous 10 blocks.DEFAULT_SPENDABLE_AGE
is not wide enough to protect their transaction from reorgs, then they should setmax_index
to an output from a sufficiently low block.Ring seed
Compute a pseudo-random number for generating all the bins and bin members for the transaction's inputs. We use a pseudo-random number in place of public entropy.
Rationale/Discussion
input_seed = H("input_seed", input.key_image)
instead ofring_seed
(i.e. a unique seed per input, that is constant between tx attempts), and also use the samebinning_upper_bound
for each attempt.ring_entropy
and recording it explicitly in transactions. We don't see any meaningful advantage to that approach.Binning upper bound
Select the binning upper bound, which is the maximum index that can be referenced by a bin. The same upper bound will be used for all inputs.
Rationale/Discussion
binning_upper_bound
to reduce storage in transactions. Beyond that, if there were unique upper bounds per input, then observers could use the average binning upper bound of multi-input transactions to more accurately estimate the time those tx were constructed.BINNING_UPPER_BOUND_SELECTION_WIDTH
is the region to randomly select a binning upper bound from. Selecting it from a range of blocks instead of using a fixed distance frommax_index
makes it harder for observers to analyze precisely when a transaction was constructed.binning_upper_bound
instead ofmax_index
. In other words, the algorithmic minimum fee for the block containing the output with indexbinning_upper_bound
should be used to set the transaction's fee. This mitigates observers' ability to use the transaction fee to estimate when a transaction was constructed.Selecting bins
Deterministically select the bins for input
input_index
.Rationale/Discussion
bin_rotator
to deduce which bin has the true spend unless the ledger-wide selection distribution is flawed.Selecting bin members
Deterministically select the bin members for input
input_index
.Rationale/Discussion
bin_member_rotator
to infer anything about which bin contains the true spend, nor which bin member in any given bin is more/less likely to be the true spend.Full ring member set
Binning algorithm: tx validation
Recover the ring members of each input in a transaction
tx
from the bin configuration details.Rationale/Discussion
Modifications for small ring sizes
This algorithm can be adjusted so all bins only have one member each, which can applied as an optimization/upgrade to Monero's current transaction protocol. Doing so would reduce input reference bytes from
(num_inputs x ring_size x varint)
to(varint + num_inputs x (u64 + varint))
.NUM_BINS = RING_SIZE
BIN_RADIUS = 0
get_num_bin_members()
will only return1
, and that each bin center will equal its lone bin member.bin_member_rotator
from bin configuration details.Tx changes
Modify the transaction input structure and the form of the message signed by transaction inputs.
txin_to_key
struct should contain a vector of commitments to outputs (i.e. hashes of each output).txin_to_key
struct should also contain the valuesbin_rotator
andbin_member_rotator
introduced by this proposal.binning_upper_bound
from this proposal:Tx validation
When you encounter a tx to validate, recover the ring members for each input from the bin configuration details defined in this proposal.
Blockchain and tx hashes
Rationale
Why not use the binning strategy from this paper?
Backward compatibility
This proposal changes the structure of tx and tx validation rules, so it requires a hard fork. It is well-suited to being implemented in the same hard fork that rolls out Triptych (or a similar next-gen tx protocol with large ring sizes), but is also compatible with Monero's current transaction protocol.
License
MIT license
References