-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Batch Amendment #5060
base: develop
Are you sure you want to change the base?
Batch Amendment #5060
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #5060 +/- ##
=========================================
+ Coverage 77.9% 77.9% +0.1%
=========================================
Files 782 785 +3
Lines 66614 66964 +350
Branches 8159 8182 +23
=========================================
+ Hits 51861 52174 +313
- Misses 14753 14790 +37
|
This is not finished. Needs to only revert the transactions submitted not the entire open ledger.
Also, since it seems this PR is still under active development, could you convert it to a draft? |
if (not3rdParty) | ||
ctx_.batchPrevious(avi); | ||
|
||
ctx_.applyOpenView(subView); |
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.
what's the reason for this call here?
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.
batchPrevious
applies the inner transactions to the open view. Before this, they are not actually applied, or they are applied to the subview but not the BatchOuter OpenView.
batchPrevious
is a workaround for the "previousFields issue". The issue is thatwhen the outer batch txn is applied at the end, it applies the old/stale view (from before the inner txns). In the case where the outer batch account has an inner batch txn that adjusts its account root, the inner adjustments are not actually applied. They are on the open view but not applied. So this function applies the open view onto the base view and handles this issue.
This should be thoroughly reviewed. There might be another way. It might be an issue with how the views are applied.
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.
Thanks! I will do some experiments with this. Do you know if this is a known issue that is documented somewhere? Or is it something you found while working on this PR?
Edit: nevermind, this makes sense 👍
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.
Also just curious, does using subView.apply(ctx_.rawView())
not solve this problem? assuming that subView
contains all the changes for the inner txns, shouldn't it include the latest account root state of the "non3rdParty" account? Please correct me if I'm wrong.
- allow only one flag - validate batch signers array (max and valid) - move `calculateBaseFee` - move `preflight2` - no duplicate TxIDs - no duplicate BatchSigners
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.
partial
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.
partial
|
||
if (flags & tfUntilFailure) | ||
{ | ||
result = tesSUCCESS; |
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.
so it's possible that none of the inner txns succeeds and yet the batch txn is "successful"?
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.
partial
void | ||
ApplyContext::applyOpenView(OpenView& open) | ||
{ | ||
if (!base_.open()) |
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.
could you elaborate more on why we are applying when the base is closed, and not when open? perhaps it's helpful to put more into the comment as well. thanks!
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.
have some questions regarding how inner txns states could've been applied
if (pkSigner.empty()) | ||
{ | ||
STArray const& txSigners(signer.getFieldArray(sfSigners)); | ||
ret = checkMultiSign(ctx.view, idAccount, txSigners, ctx.j); |
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.
ret = checkMultiSign(ctx.view, idAccount, txSigners, ctx.j); | |
if (ret = checkMultiSign(ctx.view, idAccount, txSigners, ctx.j); | |
!isTesSuccess(ret)) | |
return ret; |
currently, it would return code of the result of the last signature, but shouldn't we be returning the first error that occurs in the loop? If so, we should probably add a test case to exercise the behavior where the last signer signature is correct, but previous ones are bad.
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.
Thanks for the suggestion. I will add a test case as well
if (!sleAccount) | ||
return tesSUCCESS; | ||
|
||
ret = checkSingleSign( |
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.
same as above
ApplyViewImpl& avi = dynamic_cast<ApplyViewImpl&>(ctx_.view()); | ||
std::vector<STObject> executions; | ||
avi.copyBatchMetaData(executions); | ||
ctx_.discard(); | ||
ApplyViewImpl& avi2 = dynamic_cast<ApplyViewImpl&>(ctx_.view()); | ||
avi2.setBatchMetaData(std::move(executions)); |
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.
what is this block of code here for?
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 to apply the batch executions to the metadata even in the event of a tec failure
if (not3rdParty) | ||
ctx_.batchPrevious(avi); | ||
|
||
ctx_.applyOpenView(subView); |
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.
Also just curious, does using subView.apply(ctx_.rawView())
not solve this problem? assuming that subView
contains all the changes for the inner txns, shouldn't it include the latest account root state of the "non3rdParty" account? Please correct me if I'm wrong.
// Only update the account root entry if the batch transaction was | ||
// not a 3rd party transaction | ||
if (not3rdParty) | ||
ctx_.updateAccountRootEntry(); |
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.
linking to this comment thread
I'm still unclear why exactly we need to explicitly update account root entry here. Does it not work if we apply the state changes from subView
inside the Batch
transactor?
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 get an error when I attempt to apply the subView to the open view. Maybe I'm doing something wrong?
- Update Comments - Update/Change function names - Update/Change argument names - Remove unused preclaim - Fix exit out of signature check (multi & single)
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.
partial
@@ -147,7 +159,8 @@ ApplyStateTable::apply( | |||
} | |||
auto const origNode = to.read(keylet::unchecked(item.first)); | |||
auto curNode = item.second.second; | |||
if ((type == &sfModifiedNode) && (*curNode == *origNode)) | |||
if ((type == &sfModifiedNode) && (*curNode == *origNode) && | |||
!isBatch) |
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.
if i'm understanding correctly, when the transaction type is Batch
, we are going to add the previous and final fields of the object even though they are the same.
This doesn't sound too correct, please correct me if i'm wrong. TY
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.
We are going to add the batchPrevAcctRootFields
. The curNode and origNode are the same on the batch txn because of the way its reapplied in updateAccountRootEntry
. I guess handling this as its own case might be preferred for better understanding?
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.
we can just add a comment here to explain the situation. Also it might be worth add an assertion to make sure that nodes are not modified if it is a batch transaction - this might before useful for future debugging
// If it is a batch txn, the curNode and origNode are the same on the batch txn
// because of the way its reapplied in updateAccountRootEntry
bool const nodesUnmodified = *curNode == *origNode;
if ((type == &sfModifiedNode) && nodesUnmodified &&
!isBatch)
continue;
assert(isBatch && !nodesUnmodified)
@@ -147,7 +159,8 @@ ApplyStateTable::apply( | |||
} | |||
auto const origNode = to.read(keylet::unchecked(item.first)); | |||
auto curNode = item.second.second; | |||
if ((type == &sfModifiedNode) && (*curNode == *origNode)) | |||
if ((type == &sfModifiedNode) && (*curNode == *origNode) && | |||
!isBatch) | |||
continue; | |||
std::uint16_t nodeType = curNode |
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 a batch transaction ever modify an object other than AccountRoot? if not, would be use good to assert that here.
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.
No I dont believe so. I will add that here.
@@ -203,6 +216,13 @@ ApplyStateTable::apply( | |||
prevs.emplace_back(obj); | |||
} | |||
|
|||
if (isBatch && nodeType == ltACCOUNT_ROOT && batchPrev) | |||
{ | |||
// TODO: This could fail if the fields already exist |
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.
is this TODO done? also would love to learn more about this, as the comment itself isn't too comprehensive 😃
- Remove BatchTxn Object - Use Global Flag `tfInnerBatchTxn ` for inner batch txns - Remove Sequence & Ticket workaround - replace sfBatchTxn submit/relay validation with tfInnerBatchTxn - Remove Sequence 0 Requirement
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.
partial
Serializer dataStart = startMultiSigningData(*this); | ||
finishMultiSigningData(accountID, dataStart); | ||
return dataStart.getData(); | ||
}); |
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.
what's the rationale for not returning a Slice
directly in this lambda?
constexpr std::uint32_t tfUniversalV2 = tfFullyCanonicalSig | tfInnerBatchTxn; | ||
constexpr std::uint32_t tfUniversalMask = ~(tfFullyCanonicalSig | tfInnerBatchTxn); |
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.
constexpr std::uint32_t tfUniversalV2 = tfFullyCanonicalSig | tfInnerBatchTxn; | |
constexpr std::uint32_t tfUniversalMask = ~(tfFullyCanonicalSig | tfInnerBatchTxn); | |
constexpr std::uint32_t tfUniversal = tfFullyCanonicalSig | tfInnerBatchTxn; | |
constexpr std::uint32_t tfUniversalMask = ~(tfFullyCanonicalSig | tfInnerBatchTxn); |
we can prob add tfInnerBatchTxn
into tfUniversal
directly, and check in Transactor::preflight1
if tfInnerBatchTxn
is usable
constexpr std::uint32_t tfBridgeModifyMask = ~(tfUniversal | tfClearAccountCreateAmount); | ||
constexpr std::uint32_t tfBridgeModifyMask = ~(tfUniversalV2 | tfClearAccountCreateAmount); | ||
|
||
// Batch Flags |
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.
// Batch Flags | |
// Batch Flags: |
} | ||
|
||
void | ||
addBatchExecutionMetaData(STObject&& batchExecution) |
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.
addBatchExecutionMetaData(STObject&& batchExecution) | |
addBatchExecution(STObject&& batchExecution) |
} | ||
|
||
void | ||
setBatchMetaData(std::vector<STObject>&& batch) |
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.
setBatchMetaData(std::vector<STObject>&& batch) | |
setBatchExecutions(std::vector<STObject>&& batchExecutions) |
{ | ||
auto array = STArray{sfBatchExecutions}; |
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.
{ | |
auto array = STArray{sfBatchExecutions}; | |
{ | |
assert(isBatch); | |
auto array = STArray{sfBatchExecutions}; |
is this condition only valid if it's a Batch transaction? possibly we should assert that it is non empty only if it is a batch tx.
if (!batchExecution.empty()) | ||
{ | ||
auto array = STArray{sfBatchExecutions}; | ||
for (auto element : batchExecution) |
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.
for (auto element : batchExecution) | |
for (auto const& element : batchExecutions) |
@@ -31,7 +31,8 @@ ApplyViewImpl::ApplyViewImpl(ReadView const* base, ApplyFlags flags) | |||
void | |||
ApplyViewImpl::apply(OpenView& to, STTx const& tx, TER ter, beast::Journal j) | |||
{ | |||
items_.apply(to, tx, ter, deliver_, j); | |||
items_.apply( | |||
to, tx, ter, deliver_, batchExecution_, batchPrevAcctRootFields_, j); |
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.
to, tx, ter, deliver_, batchExecution_, batchPrevAcctRootFields_, j); | |
to, tx, ter, deliver_, batchExecutions_, batchPrevAcctRootFields_, j); |
void | ||
setBatchMetaData(std::vector<STObject>&& batch) | ||
{ | ||
batchExecution_ = std::move(batch); |
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.
batchExecution_ = std::move(batch); | |
batchExecutions_ = std::move(batch); |
void | ||
addBatchExecutionMetaData(STObject&& batchExecution) | ||
{ | ||
batchExecution_.push_back(std::move(batchExecution)); |
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.
batchExecution_.push_back(std::move(batchExecution)); | |
batchExecutions_.push_back(std::move(batchExecution)); |
if (tx.isFieldPresent(sfTxnSignature)) | ||
return {Validity::SigBad, "Batch txn contains signature."}; |
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.
What do you think about also accepting an empty string ""
? That's what's used in e.g. multisigning.
High Level Overview of Change
Context of Change
Type of Change
.gitignore
, formatting, dropping support for older tooling)API Impact
libxrpl
change (any change that may affectlibxrpl
or dependents oflibxrpl
)