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
[RFC][libra-framework] Refactored prologue out of LibraAccount #5881
Conversation
@@ -0,0 +1,213 @@ | |||
address 0x1 { | |||
|
|||
module LibraTransaction { |
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.
This is the newly created module for transaction prologues and epilogues (what used to be LibraWriteSetManager
).
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.
I wonder if we can come up with a name conveying that this has transaction processing business logic rather than defining the structure of transactions (e.g., there's no struct Transaction
here). Maybe TransactionManager
, TransactionLifecycle
, TransactionProcessing
?
sender: address, | ||
/// This function can only be called during the epilogue as any invokation during the execution body will cause | ||
/// the sequence number to be poisoned. | ||
public fun charge_transaction<Token>( |
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.
Note that although this function is public, it is not invokable from regular user scripts as it will cause the sequence number to be incremented and thus fail the assertion at line 885.
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.
I don't understand--can't the caller choose any value they want for txn_sequence_number
? E.g.
fun my_evil_function(signer: &signer) {
let any_num_i_want = 7;
LibraAccount::epilogue(signer, signer, 0, any_num_i_want);
}
It is possible to support a new type of visibility in Move, similar to java's protected or rust's pub(crate), where the methods that use this visibility can only be called by the modules at the same address. |
I'd really love for support like that. But IMHO we don't have a clear path towards it anytime soon as supporting such feature will actually need to change the bytecode format + bytecode verifier, which seems to be a bit heavy right now. |
I really like this change, but I have reservations about doing this kind of refactoring so long after our feature-complete date. What about if we put all the prologue/epilogue code into LibraAccount (where most of it is already) instead of creating a new module? It wouldn't be as nicely factored but it seems like it would achieve most of the benefits with far less churn in the code. |
I think the major concern is that we need to expose |
Why? Wouldn't all the callers be in the LibraAccount module so that |
/// This function can only be called during the epilogue as any invokation during the execution body will cause | ||
/// the sequence number to be poisoned. | ||
public fun charge_transaction<Token>( | ||
vm_signer: &signer, |
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.
unused? Did you mean to assert Signer::address_of(vm_signer) == VM_RESERVED_ADDRESS
?
sender: address, | ||
/// This function can only be called during the epilogue as any invokation during the execution body will cause | ||
/// the sequence number to be poisoned. | ||
public fun charge_transaction<Token>( |
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.
I don't understand--can't the caller choose any value they want for txn_sequence_number
? E.g.
fun my_evil_function(signer: &signer) {
let any_num_i_want = 7;
LibraAccount::epilogue(signer, signer, 0, any_num_i_want);
}
) { | ||
assert( | ||
LibraTransactionPublishingOption::is_module_allowed(sender), | ||
PROLOGUE_EMODULE_NOT_ALLOWED |
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.
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.
I have a PR up to use Errors everywhere. We should probably get that in first
|
||
/// The common prologue is invoked at the beginning of every transaction | ||
/// It verifies: | ||
/// - The account's auth key matches the transaction's public 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.
Add a comment about chain_id
?
/// It verifies: | ||
/// - The account's auth key matches the transaction's public key | ||
/// - That the account has enough balance to pay for all of the gas | ||
/// - That the sequence number matches the transaction's sequence 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.
/// - That the sequence number matches the transaction's sequence key | |
/// - That the account's sequence number matches the transaction's sequence number |
assert(sender == CoreAddresses::LIBRA_ROOT_ADDRESS(), PROLOGUE_EINVALID_WRITESET_SENDER); | ||
|
||
// Currency code don't matter here as it won't be charged anyway. | ||
LibraAccount::check_sender<LBR::LBR>(account, 0, writeset_sequence_number, writeset_public_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.
Should we call prologue_common
here instead of this to resolve the TODO below?
) { | ||
assert(sender == CoreAddresses::LIBRA_ROOT_ADDRESS(), PROLOGUE_EINVALID_WRITESET_SENDER); | ||
|
||
// Currency code don't matter here as it won't be charged anyway. |
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.
This + the 0 gas price feels a bit icky and is making me wonder whether we want separate LibraAccount::gas_prologue
and LibraAccount::replay_prevention_prologue
s (or similar...)
// Charge for gas | ||
assert(txn_max_gas_units >= gas_units_remaining, Errors::invalid_argument(EGAS)); | ||
let gas_used = txn_max_gas_units - gas_units_remaining; | ||
assert((txn_gas_price as u128) * (gas_used as u128) <= MAX_U64, Errors::limit_exceeded(EGAS)); |
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.
Maybe do assert(txn_gas_price <= MAX_U64 / gas_used)
to avoid the u128
conversions?
) { | ||
let sender = Signer::address_of(account); | ||
|
||
// Charge for gas |
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.
I find this comment confusing given that the actual charging happens in LibraAccount
. This part is just sanity-checking the gas price and gas unit values.
|
||
/// The failure_epilogue is invoked at the end of transactions when the transaction is aborted during execution or | ||
/// during `success_epilogue`. | ||
fun failure_epilogue<Token>( |
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.
Agreed with you that these are now the same and we can kill one of them.
Also very worried about making a change like this so late.
Do we have an answer on this? I think that if we don't need to expose new public APIs, we shouldn't do it. Even if it means more code duplication. Additionally, we should really re-evaluate this after #5876. There would only need to be a single epilogue in LibraAccount and might help us look at things differently |
Motivation
Moved all transaction related logic outside of LibraAccount and refactored prologue and epilogue logic into a single module.
For the LibraAccount module there will be two public apis:
check_sender
andcharge_transaction
, which is the LibraAccount related logic that is extracted out from the old prologue and epilogue.Justification:
I haven't fixed the code in libra_vm invokation so tests are guarenteed to fail. Just want to know how people feel like about this rework. Will fixup tests if people are happy about this.
Have you read the Contributing Guidelines on pull requests?
Yes
Test Plan
Haven't tested yet.