Skip to content
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

Open
wants to merge 83 commits into
base: develop
Choose a base branch
from
Open

Batch Amendment #5060

wants to merge 83 commits into from

Conversation

dangell7
Copy link
Collaborator

@dangell7 dangell7 commented Jul 10, 2024

High Level Overview of Change

Context of Change

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (non-breaking change that only restructures code)
  • Performance (increase or change in throughput and/or latency)
  • Tests (you added tests for code that already exists, or your new feature included in this PR)
  • Documentation update
  • Chore (no impact to binary, e.g. .gitignore, formatting, dropping support for older tooling)
  • Release

API Impact

  • Public API: New feature (new methods and/or new fields)
  • Public API: Breaking change (in general, breaking changes should only impact the next api_version)
  • libxrpl change (any change that may affect libxrpl or dependents of libxrpl)
  • Peer protocol change (must be backward compatible or bump the peer protocol version)

Copy link

codecov bot commented Jul 10, 2024

Codecov Report

Attention: Patch coverage is 90.02433% with 41 lines in your changes missing coverage. Please review.

Project coverage is 77.9%. Comparing base (7b18006) to head (04519e6).

Files with missing lines Patch % Lines
src/xrpld/app/tx/detail/Batch.cpp 94.2% 9 Missing ⚠️
src/xrpld/app/tx/detail/Transactor.cpp 87.1% 9 Missing ⚠️
src/xrpld/overlay/detail/PeerImp.cpp 0.0% 8 Missing ⚠️
src/libxrpl/protocol/STTx.cpp 90.9% 6 Missing ⚠️
src/xrpld/app/misc/NetworkOPs.cpp 66.7% 6 Missing ⚠️
src/libxrpl/protocol/TxMeta.cpp 83.3% 1 Missing ⚠️
src/xrpld/app/ledger/detail/OpenLedger.cpp 50.0% 1 Missing ⚠️
src/xrpld/app/tx/detail/apply.cpp 88.9% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            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     
Files with missing lines Coverage Δ
include/xrpl/protocol/Batch.h 100.0% <100.0%> (ø)
include/xrpl/protocol/Feature.h 100.0% <ø> (ø)
include/xrpl/protocol/HashPrefix.h 100.0% <ø> (ø)
include/xrpl/protocol/STTx.h 100.0% <ø> (ø)
include/xrpl/protocol/TER.h 100.0% <ø> (ø)
include/xrpl/protocol/TxMeta.h 93.5% <100.0%> (+1.9%) ⬆️
include/xrpl/protocol/detail/transactions.macro 100.0% <100.0%> (ø)
src/libxrpl/protocol/InnerObjectFormats.cpp 100.0% <100.0%> (ø)
src/libxrpl/protocol/TER.cpp 100.0% <ø> (ø)
src/xrpld/app/tx/detail/ApplyContext.cpp 100.0% <100.0%> (ø)
... and 17 more

... and 5 files with indirect coverage changes

Impacted file tree graph

@ximinez
Copy link
Collaborator

ximinez commented Jul 31, 2024

Also, since it seems this PR is still under active development, could you convert it to a draft?

@dangell7 dangell7 marked this pull request as draft July 31, 2024 19:53
if (not3rdParty)
ctx_.batchPrevious(avi);

ctx_.applyOpenView(subView);
Copy link
Collaborator

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?

Copy link
Collaborator Author

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.

Copy link
Collaborator

@shawnxie999 shawnxie999 Oct 31, 2024

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 👍

Copy link
Collaborator

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.

Copy link
Collaborator

@shawnxie999 shawnxie999 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

partial

src/xrpld/app/tx/detail/apply.cpp Outdated Show resolved Hide resolved
src/xrpld/app/tx/detail/Batch.cpp Outdated Show resolved Hide resolved
Copy link
Collaborator

@shawnxie999 shawnxie999 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

partial

src/xrpld/app/tx/detail/Batch.cpp Outdated Show resolved Hide resolved

if (flags & tfUntilFailure)
{
result = tesSUCCESS;
Copy link
Collaborator

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"?

src/xrpld/app/tx/detail/Batch.cpp Outdated Show resolved Hide resolved
src/xrpld/app/tx/detail/Batch.cpp Outdated Show resolved Hide resolved
src/xrpld/ledger/ApplyViewImpl.h Outdated Show resolved Hide resolved
src/xrpld/app/tx/detail/ApplyContext.cpp Outdated Show resolved Hide resolved
src/xrpld/app/tx/detail/ApplyContext.cpp Outdated Show resolved Hide resolved
src/xrpld/ledger/ApplyViewImpl.h Outdated Show resolved Hide resolved
src/xrpld/app/tx/detail/Batch.cpp Outdated Show resolved Hide resolved
Copy link
Collaborator

@shawnxie999 shawnxie999 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

partial

src/xrpld/app/tx/detail/Transactor.cpp Outdated Show resolved Hide resolved
src/xrpld/app/tx/detail/Transactor.cpp Outdated Show resolved Hide resolved
src/xrpld/app/tx/detail/ApplyContext.cpp Outdated Show resolved Hide resolved
void
ApplyContext::applyOpenView(OpenView& open)
{
if (!base_.open())
Copy link
Collaborator

@shawnxie999 shawnxie999 Nov 4, 2024

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!

src/xrpld/app/tx/detail/Batch.cpp Outdated Show resolved Hide resolved
src/xrpld/app/misc/NetworkOPs.cpp Outdated Show resolved Hide resolved
src/xrpld/app/misc/NetworkOPs.cpp Outdated Show resolved Hide resolved
src/xrpld/app/misc/NetworkOPs.cpp Outdated Show resolved Hide resolved
src/xrpld/app/misc/NetworkOPs.cpp Outdated Show resolved Hide resolved
Copy link
Collaborator

@shawnxie999 shawnxie999 left a 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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.

Copy link
Collaborator Author

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(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

Comment on lines +862 to +867
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));
Copy link
Collaborator

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?

Copy link
Collaborator Author

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

src/xrpld/app/tx/detail/Transactor.cpp Outdated Show resolved Hide resolved
if (not3rdParty)
ctx_.batchPrevious(avi);

ctx_.applyOpenView(subView);
Copy link
Collaborator

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();
Copy link
Collaborator

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?

Copy link
Collaborator Author

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)
Copy link
Collaborator

@shawnxie999 shawnxie999 left a 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)
Copy link
Collaborator

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

Copy link
Collaborator Author

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?

Copy link
Collaborator

@shawnxie999 shawnxie999 Nov 7, 2024

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
Copy link
Collaborator

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.

Copy link
Collaborator Author

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
Copy link
Collaborator

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
Copy link
Collaborator

@shawnxie999 shawnxie999 left a 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();
});
Copy link
Collaborator

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?

Comment on lines +63 to +64
constexpr std::uint32_t tfUniversalV2 = tfFullyCanonicalSig | tfInnerBatchTxn;
constexpr std::uint32_t tfUniversalMask = ~(tfFullyCanonicalSig | tfInnerBatchTxn);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Batch Flags
// Batch Flags:

}

void
addBatchExecutionMetaData(STObject&& batchExecution)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
addBatchExecutionMetaData(STObject&& batchExecution)
addBatchExecution(STObject&& batchExecution)

}

void
setBatchMetaData(std::vector<STObject>&& batch)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
setBatchMetaData(std::vector<STObject>&& batch)
setBatchExecutions(std::vector<STObject>&& batchExecutions)

Comment on lines +134 to +135
{
auto array = STArray{sfBatchExecutions};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{
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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
to, tx, ter, deliver_, batchExecution_, batchPrevAcctRootFields_, j);
to, tx, ter, deliver_, batchExecutions_, batchPrevAcctRootFields_, j);

void
setBatchMetaData(std::vector<STObject>&& batch)
{
batchExecution_ = std::move(batch);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
batchExecution_ = std::move(batch);
batchExecutions_ = std::move(batch);

void
addBatchExecutionMetaData(STObject&& batchExecution)
{
batchExecution_.push_back(std::move(batchExecution));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
batchExecution_.push_back(std::move(batchExecution));
batchExecutions_.push_back(std::move(batchExecution));

Comment on lines +51 to +52
if (tx.isFieldPresent(sfTxnSignature))
return {Validity::SigBad, "Batch txn contains signature."};
Copy link
Collaborator

@mvadari mvadari Nov 7, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants