-
Notifications
You must be signed in to change notification settings - Fork 8
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
perf: avoid holding write-lock across prove() #136
perf: avoid holding write-lock across prove() #136
Conversation
Previously a write-lock was held across `create_transaction()` which includes a lengthy (potentially minutes) call to `prove`. This would be unacceptably bad for concurrency. This commit makes create_transaction() take &self instead of &mut self. Only a single mutation was being performed inside add_change() and this is now moved into `send()` RPC after `create_transaction()` completes. The write-lock acquisition is like-wise moved, so it should only be held very briefly while adding a record to wallet's `expected_utxos`.
406af2f
to
8b0c954
Compare
About the code I'm a bit skeptical about new Your questions
A These two options are handled by Footnotes |
Thx for the helpful explanations.
yes, so this is getting towards the crux of my q's. I am trying to figure out why it is being handled as an expected UTXO, and if there would be any problem changing it to an announced one instead. I am also curious why it is treated differently from other UTXO's that I send to my own wallet. I analyze as follows:
edit: one final observation/question: if change UTXO do not have an announcement on the blockchain as other UTXO do, then it would seem that makes them distinguishable as change to observers. That seems harmful to privacy, no? I would think it opens up some kind of avenue for analysis... |
When a transaction is initiated, the sender gets to select a digest of randomness, which is 320 bytes, and the receiver gets to dictate a digest of randomness (communicated via the receiving address), also 320 bytes. The sender needs a way to communicate the sender-randomness to the receiver, as the receiver cannot spend the UTXO without the sender randomness. This data can be communicated off-chain (not yet supported), or it can be attached to the block and communicated in an encrypted way on-chain. The latter makes the transaction big and will thus very significantly add to the transaction fees. Notice the fields of pub struct MonitoredUtxo {
pub utxo: Utxo,
// Mapping from block digest to membership proof
pub blockhash_to_membership_proof: VecDeque<(Digest, MsMembershipProof)>,
pub number_of_mps_per_utxo: usize,
// hash of the block, if any, in which this UTXO was spent
pub spent_in_block: Option<(Digest, Duration, BlockHeight)>,
// hash of the block, if any, in which this UTXO was confirmed
pub confirmed_in_block: Option<(Digest, Duration, BlockHeight)>,
/// Indicator used to mark the UTXO as belonging to an abandoned fork
/// Indicates what was the block tip when UTXO was marked as abandoned
pub abandoned_at: Option<(Digest, Duration, BlockHeight)>,
} And the pub struct MsMembershipProof {
pub sender_randomness: Digest,
pub receiver_preimage: Digest,
pub auth_path_aocl: MmrMembershipProof<Hash>,
pub target_chunks: ChunkDictionary,
}
Transaction size in the block is an important consideration here. Currently, the announced UTXOs use a lot of space in the block because they are using asymmetric cryptography. Using an announcement scheme with symmetric cryptography for e.g. the change transactions, would allow the sender to save a good amount of data. See #5.
I think the opposite suggestion describes a future where Neptune is a successful blockchain better: All UTXOs will be announced offchain, and the on-chain transactions will contain no broadcast information in order to save on transaction fees. |
By running the test Alternatively you can run |
Ok, got it. Short answer then is that using expected_utxos saves 2776 bytes of blockchain space per UTXO vs using a public announcement. Which still leaves my followup question: why don't other UTXOS that I send to my own wallet also use expected_utxos? I believe the answer probably is: they should, but that optimization has not been coded yet. Ideally create_transaction() would return a Vec and the caller would add all to wallet's expected_utxos. @Sword-Smith please let me know if you agree. If so, I will proceed to update this PR with that solution.
wait a sec, Is this intended to replace the expected_utxos mechanism? If so, then it would seem we don't need to optimize for that mechanism now and should impl #5 instead. now I'm a bit confused again. |
Addresses #122
note: the logic changes are in src/models/state/mod.rs and src/rpc_server.rs. All other files are just updating syntax in unit tests.
Previously a write-lock was held across
create_transaction()
which includes a lengthy (potentially minutes) call toprove
. This would be unacceptably bad for concurrency as most p2p and rpc operations would block waiting for the lock untilprove()
finally finishes.This commit makes create_transaction() take
&self
instead of&mut self
. So it is now a read-only op.Only a single mutation was being performed inside
add_change()
which is indirectly called bycreate_transaction()
. This is now moved intosend()
RPC aftercreate_transaction()
completes. The write-lock acquisition is likewise moved, so it should only be held very briefly while adding a record to wallet'sexpected_utxos
.I am making this a draft PR because:
a) 4 tests are failing due to the change and need to be fixed.
b) I have some q's I would like to resolve about updating the wallet before I consider this ready to merge.
c) it's possible this could be done a different way or more elegantly. I don't really like that create_transaction() now returns a tuple instead of just a
Transaction
.I put my q's in a comment within
send()
. I will expand on them here:Is it really necessary to add the change Utxo to wallet's expected UTXOs when creating a Tx? Why can't the wallet be informed of change UTXO when Tx enters mempool and/or block is confirmed like any other incoming utxo? I ask because If it is not necessary, we could then avoid any mutation/write (and write lock) during send().
A comment in the code indicates that we add the change UTXO to pool of expected incoming UTXOs so that we can synchronize it after it is confirmed. I don't understand this. a) What is meant by "synchronize" and why wouldn't it synchronize? b) what is special about this change UTXO vs the other UTXOs in the transaction we are sending (that we don't write anywhere)?
Regarding making this commit more elegant. I created a new struct ChangeUtxoData to return data from CreateTransaction for the caller to update wallet if necessary. I'm not 100% certain this new struct is necessary. There is already
UtxoReceiverData
which is similar. Anyway, I would say this area is worth reviewing. And also if it turns out we don't really have to update the wallet state, thenChangeUtxoData
can go away anyway.