-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add view tags to outputs to reduce wallet scanning time #8061
Conversation
Does this imply a new transaction format for all currently-active transaction types, which includes coinbase, the main RCTTypeCLSAG type, the pre-RCT-to-RCT transition type, and the denomination-to-pre-RCT transition type? How is versioning handled around this? |
Yes, all currently used types. All newly created outputs that could previously be of type On versioning: at the fork height, the wallet (and miners) will start to construct outs of type In the code before this PR, when reading the output for a public key, there were these all over the code base:
|
Hi, early this year I (intentionally) sent myself a very substantial amount of Monero with a 1000000 block lock time. There is still ~3 years remaining on this transaction being unlocked. I realize this locked_transaction feature is getting removed. I was ensured that my transaction would still be unaffected. Thank you |
@sboulden your transaction will still be unaffected. This PR doesn't have any impact on locked outputs, or the locked_transfer feature. This PR would only impact newly created outputs at the fork height. I also sanity checked it locally just to be absolutely certain, your Monero will be just fine :) The proposal to deprecate the locked_transfer feature is over here: monero-project/research-lab#78. No code has been written to move forward on that. And even if deprecation does move forward in this fork or in some future fork, to my knowledge it was never considered to render outputs un-spendable (or to mess with existing locked outputs at all). No one has proposed altering existing locked outputs in any way to my knowledge. |
I appreciate you confirming! Sometimes my mind gets worried that the impact on locked_transfers could be easily overlooked, so I just like to shout it out there. Thanks again for your work on this pull, looking forward to it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add the test vectors for siphash? They can be found in my seraphis_perf
branch.
@@ -628,7 +634,8 @@ namespace cryptonote | |||
} | |||
} | |||
|
|||
bool r = construct_tx_with_tx_key(sender_account_keys, subaddresses, sources, destinations, change_addr, extra, tx, unlock_time, tx_key, additional_tx_keys, rct, rct_config, msout); | |||
bool shuffle_outs = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, because the default value can't be used with an explicit use_view_tags
. These function signatures are disturbing, but that's not your fault.
That's what I get with performance tests. Are these the 2 pre-rct tests you mentioned above?
|
I'd like to point out that there may be a reason to have view tags slightly larger than 8 bits (possibly as a future upgrade). |
@selsta these failing perf tests should be working now :) For reference, the pre-rct tests I was referencing in the description are:
They're commented out and don't run. Slapped a TODO on them |
I'm interested to know why you are not using |
In testing for view tags, I found using |
Sure, but given the overall scan time improvement, is introducing another hash function implementation worth this extra 1.5% speed? The other implication that comes to mind of using |
This PR is likely to be permanent and final, so yes it is worth it to get as much speed as possible while we still can (setting aside assembly-level optimizations).
Ed25519 security is 2^128, there is no fundamental reduction here. Also note that view tags are only 1 byte, so no more than 8 bits can be leaked even in the worst case. (unless @tevador's idea is adopted) |
? I wouldn't be so quick to use absolute statements like this. There's very little in this project that remains "permanent and final".
Ed25519 security is at least 2^128. siphash is at most 2^128. Whilst I accept your point that only 1 byte can ever be leaked, that 1 byte is enough to fingerprint, and it's this hashing of the shared secret that is preventing the fingerprint. |
Using siphash to calculate the view tags perfectly matches the intended use case of the hash function: hashtables. View tags are essentially transforming the list of outputs into many hashtables with the hash keys being shared between the sender and the receiver.
Any hash (even keccak) truncated to 8 bits will have a security of at most 8 bits against preimages and 4 bits against collisions. But even if the whole output was used, siphash doesn't leak any information about he secret key. Even a reduced siphash-1-2 variant requires at least 2^98 operations to recover the secret key. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!
src/crypto/crypto.cpp
Outdated
memwipe(siphash_key, 16); | ||
|
||
// only need a slice of view_tag_full to realize optimal perf/space efficiency | ||
static_assert(sizeof(crypto::view_tag) <= sizeof(view_tag_full)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The message
parameter is only optional since c++17 and the codebase required minimum is c++14.
I'm with @jtgrassie on the siphash discussion. IIRC, the intended use case was/is hashtables, not cryptographic usage. Truncating a sha3 hash is safer, and a 1.5% penalty is low. |
@tevador to follow up on that argument - cryptographic hash functions have a requirement against inversion (pre-image resistance). This is a (lesser?) requirement for siphash - the preimage resistance might be ~2^64. This is critical because it relates to privacy. I don't see a good argument for siphash, unless there is some evidence that pre-image resistance is higher. |
Pre-image resistance is irrelevant here, because the hash message is public knowledge. Siphash is a keyed hash function, and the key in our case is 16 bytes of the sender-receiver derivation. |
} | ||
else if (hf_version < HF_VERSION_VIEW_TAGS) | ||
{ | ||
// require outputs to be of type txout_to_key |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you check that all historical tx outputs are of type txout_to_key? You can check by syncing the node running this code from scratch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep + I synced from scratch running this code. Going to try it again to be safe.
@j-berman please read https://www.reddit.com/r/Monero/comments/jdh5to/psa_a_bug_has_caused_some_nodes_to_get_stuck_on/g98nuwl/?utm_source=reddit&utm_medium=web2x&context=3 |
#7169 in combination with this PR's code ejects |
Nice, so this issue was fixed properly shortly after the CLSAG fork. That means you don't have to address it in this PR at all. |
Rebased to master. Resolved conflicts in: tests/crypto/main.cpp |
Implements view tags as proposed by @UkoeHB in MRL issue monero-project/research-lab#73 At tx construction, the sender adds a 1-byte view tag to each output. The view tag is derived from the sender-receiver shared secret. When scanning for outputs, the receiver can check the view tag for a match, in order to reduce scanning time. When the view tag does not match, the wallet avoids the more expensive EC operations when deriving the output public key using the shared secret.
- monero-project/monero#8061 - also update import location for epee::misc_utils::get_gmt_time
@tevador I'd like to include you in the bounty payout for providing useful input on this PR. I've been able to contact all other reviewers; not sure how best to reach you. |
- monero-project/monero#8061 - also update import location for epee::misc_utils::get_gmt_time
Overview
Implements view tags as proposed by @UkoeHB in in MRL issue monero-project/research-lab#73
At tx construction, the sender adds a 1-byte view tag to each output. The view tag is derived from the sender-receiver shared secret (view tag = first byte of hash(shared secret) . When scanning for outputs, the receiver can check the view tag for a match in order to reduce scanning time. When the view tag matches (expected match 1/256 outputs), the wallet proceeds to derive the output public key to check if the output belongs to the receiver. When the view tag does not match, the wallet avoids the more expensive EC operations when deriving the output public key.1 In tests on my machine, I saw reductions in scanning time upwards of 40% (and a minimum around 30%).
Update: switched to using
cn_fast_hash
to hash the shared secret (edit: see discussion below for more on this choice)As suggested by @UkoeHB, the shared secret is hashed using SipHash 2-4 (the default), a keyed hash function that is specifically designed to protect a secret key and yield collisions, and is speedier thancn_fast_hash
.In order to maintain transaction uniformity, view tags are not supported until the fork height. After the fork height, all outputs must have a view tag. There is a grace period that allows both types at the fork height which allows the pool to clear without kicking tx's people create right at the fork height.
1 It's expected the skippable EC operations would be skipped for approximately 99.6% of outputs (expected false positive rate = 1/28 = 1/256 = 0.4%, therefore expected negative rate = 100% - 0.4% = 99.6%). This is why 1 byte is the optimal sweet spot for max performance gains without needing more bytes. Skipping just 0.4% more outputs does not yield noticeable gains.
Choices I made worth pointing out
For starters, I thought through various approaches deeply, and settled on what I feel is the sanest one. However, I'm absolutely prepared to refactor if necessary. I understand the various sections of the code that touch this fairly well enough at this point, would be happy to refactor if there is a stronger approach.
Added a new
tx_out_to_tagged_key
boost variant type, to replacetx_out_to_key
at the next fork heightThis was likely the most significant choice I made. It seemed to be the most sensible way to fit view tags into the code without introducing overly cumbersome structural changes across the code. I think the biggest issue with this approach is in JSON parsing. Example: on the chain today (with no view tags), the
tx.vout
target serializes to JSON as follows:In this PR, at the fork height when view tags are set to be supported, the
tx.vout
target starts serializing to JSON as follows:This will be a breaking change for anyone who was relying on
target.key
, which I figure isn't ideal. I figure ideally it would instead serialize to the following:Since view tags aren't strictly necessary when reading for the key, it seemed ideal to maintain the prior JSON structure if possible, and not force people to change how they consume the JSON object downstream if they don't need to. But I couldn't get this ideal approach working in a clean way. Perhaps I'm missing something simple.
Generally very much so open to thoughts on this
tx_out_to_tagged_key
approach.2 pre-rct tests aren't working
There are 2 pre-rct tests meant to test view tags on pre-rct outputs after the fork height that aren't working, because the tests aren't set up to support pre-rct outputs past v12, and I felt I spent enough time trying to get them to work. I tested it manually by mining pre-rct outputs, and calling
sweep_unmixable
after the fork height and scanning for them in a wallet making sure they used the view tags as expected, and they did. I figure priority-wise makes sense to move back over to binning/decoy selection work over getting these 2 minor tests working at this point. I added 15 working tests in total, not including those 2 pre-rct ones.Still to-do
blockchain_utilities
fortx_out_to_tagged_key
.EDIT: clarified choice to use siphash + description of view tags