-
Notifications
You must be signed in to change notification settings - Fork 973
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
CAP-0023 implementation #2591
CAP-0023 implementation #2591
Conversation
ee4394d
to
2f195ed
Compare
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.
Completed a preliminary review of everything after the LedgerTxn
changes, excluding the tests. Looks pretty good, but a few important comments in 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.
I did a full review of this PR - see comments.
Overall this is pretty close to mergeable (good job!):
- I added a couple questions that happen to be CAP issues/suggestions that we missed
- I think you have a few missing test cases that would have uncovered a couple bugs
src/xdr/Stellar-ledger-entries.x
Outdated
case CLAIM_PREDICATE_AFTER_ABSOLUTE_TIME: | ||
int64 absAfter; | ||
case CLAIM_PREDICATE_BEFORE_RELATIVE_TIME: | ||
int64 relBefore; // Seconds since closeTime of the ledger in which the |
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.
you need to run clang-format
manually on the .x files (make sure to comment the %#include
before doing so)
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.
done
CLAIM_PREDICATE_AND = 1, | ||
CLAIM_PREDICATE_OR = 2, | ||
CLAIM_PREDICATE_BEFORE_ABSOLUTE_TIME = 3, | ||
CLAIM_PREDICATE_AFTER_ABSOLUTE_TIME = 4, |
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.
CAP question - should we remove the AFTER (or BEFORE) predicates and just add a NOT
operator?
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 thought about this (it was actually my original approach) but I had some concerns that it could be a future foot-gun. For example, suppose we added a predicate like CLAIM_PREDICATE_HASH_PREIMAGE
where you have to reveal a SHA256-preimage in order to claim. NOT
doesn't behave particularly well with this predicate because it would be satisfied by default. Maybe this concern is overblown, because you could always configure the predicates incorrectly anyway. What do you think? I'm pretty flexible on this.
src/test/TestAccount.cpp
Outdated
REQUIRE(claimableBalance.amount == amount); | ||
REQUIRE(claimableBalance.balanceID == balanceID); | ||
REQUIRE(claimableBalance.createdBy == getPublicKey()); | ||
REQUIRE(claimableBalance.reserve == |
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.
warning: signed vs unsigned comparison
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 be fixed.
int64_t | ||
relativeToAbsolute(TimePoint closeTime, int64_t relative) | ||
{ | ||
return closeTime > (INT64_MAX - relative) ? INT64_MAX |
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.
warning: signed/unsigned comparison
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 be fixed.
return false; | ||
} | ||
|
||
auto ok = addBalance(header, sourceAccount, -amount); |
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.
warning ok
shadows line 156
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.
fixed
src/transactions/OperationFrame.cpp
Outdated
@@ -47,7 +49,7 @@ getNeededThreshold(LedgerTxnEntry const& account, ThresholdLevel const level) | |||
|
|||
shared_ptr<OperationFrame> | |||
OperationFrame::makeHelper(Operation const& op, OperationResult& res, | |||
TransactionFrame& tx) | |||
TransactionFrame& tx, size_t index) |
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.
per my other comment elsewhere, size_t
is overkill for this as we later have to downcast it. Better to just downcast it once (here). Not sure why it was size_t
before in TransactionFrame::makeOperation
.
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.
fixed
e1ce5de
to
42f2e20
Compare
42f2e20
to
8b447a5
Compare
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.
os
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've resolved all issues that I consider resolved; added a couple new ones.
Next step is to rebase on top of master and squash commits so that you can address the SQL related issues.
mDatabase.getSession() << "CREATE TABLE claimablebalance (" | ||
<< "balanceid VARCHAR(56) " << coll | ||
<< " PRIMARY KEY, " | ||
<< "createdby VARCHAR(56) " << coll |
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'm dumb for not having noticed this earlier: why do we need to expose all those internal fields?
I think the minimal set is
balanceid(HEX) | claimablebalanceentry (XDR) | lastmodified (BIGINT)
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 we end up with a lot less code
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 makes sense to me, but I noticed that this wasn't done for the other ledger entries. I want to make sure there wasn't a specific reason for listing the fields individually before I change this. @jonjove?
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 reason other ledger entries aren't like this is historical. It's possible we will move this direction in the long run. We already moved slightly in this direction with #2593.
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.
Makes sense. 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.
I made the change to use a claimablebalanceentry
column. I also updated db-schema.md
.
@@ -440,6 +440,7 @@ Database::initialize() | |||
mApp.getLedgerTxnRoot().dropOffers(); | |||
mApp.getLedgerTxnRoot().dropTrustLines(); | |||
mApp.getLedgerTxnRoot().dropData(); | |||
mApp.getLedgerTxnRoot().dropClaimableBalances(); |
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.
you still need to do this
@@ -102,6 +102,8 @@ LedgerEntryIsValid::checkIsValid(LedgerEntry const& le, uint32_t ledgerSeq, | |||
return checkIsValid(le.data.offer(), version); | |||
case DATA: | |||
return checkIsValid(le.data.data(), version); | |||
case CLAIMABLE_BALANCE: | |||
return checkIsValid(le.data.claimableBalance(), version); |
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 open an issue: this invariant should also check that we don't modify immutable fields. For claimable balance, all fields are immutable.
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.
Added an issue - #2620
@@ -23,6 +23,24 @@ calculateDeltaBalance(std::shared_ptr<LedgerEntry const> const& current, | |||
return (current ? current->data.account().balance : 0) - | |||
(previous ? previous->data.account().balance : 0); | |||
} | |||
if (let == CLAIMABLE_BALANCE) | |||
{ | |||
int64_t reserveDelta = |
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 written as if reserve and amount are mutable, maybe OK in this context (albeit a bit confusing as the code doesn't work at all if asset gets modified) but see my comment in the other invariant on validity
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 will be simplified when we rebase CAP-33 on top, because the reserve is not stored in that case. But I the code should work correctly in the case that asset
changed. This would obviously be a bug if it were to happen, but for clarity and safety the code should account for it (after all, invariants are all about catching bugs).
|
||
for (uint32_t i = 0; i < 4; i++) | ||
{ | ||
SECTION("predicate at level " + std::to_string(i)) |
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.
from recent findings, let's be defensive and avoid using to_string
-> use fmt::format
instead
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.
done
8b447a5
to
0ac1858
Compare
docs/db-schema.md
Outdated
|
||
Field | Type | Description | ||
------|------|--------------- | ||
balanceid | VARCHAR(56) PRIMARY KEY | (BASE64) |
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.
balanceid
is a base64 encoded xdr object -> type should be "XDR", also description should saythat it's a ClaimableBalanceID
; we useBASE64
as a type when there is no good XDR type.claimablebalanceentry
is a based64 encoded xdr object -> type should be "XDR" (and description should say that it's aClaimableBalanceEntry
)
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.
done
@@ -440,6 +440,7 @@ Database::initialize() | |||
mApp.getLedgerTxnRoot().dropOffers(); | |||
mApp.getLedgerTxnRoot().dropTrustLines(); | |||
mApp.getLedgerTxnRoot().dropData(); | |||
mApp.getLedgerTxnRoot().dropClaimableBalances(); |
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.
yes you need to call mApp.getLedgerTxnRoot().dropClaimableBalances();
from Database::applySchemaUpgrade
when upgrading to schema version 13
otherwise the table won't be created
// under the Apache License, Version 2.0. See the COPYING file at the root | ||
// of this distribution or at http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
#include "crypto/KeyUtils.h" |
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 think you need those 2 "crypto" includes
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.
removed
|
||
mDatabase.getSession() << "DROP TABLE IF EXISTS claimablebalance;"; | ||
mDatabase.getSession() << "CREATE TABLE claimablebalance (" | ||
<< "balanceid VARCHAR(56) " << coll |
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.
56 is wrong (needs to be updated in the md file as well):
I think it's base64(4+32) = 4 * round_up(36/3) = 4*12 = 48
mDatabase.getSession() << "CREATE TABLE claimablebalance (" | ||
<< "balanceid VARCHAR(56) " << coll | ||
<< " PRIMARY KEY, " | ||
<< "claimablebalanceentry TEXT " << coll |
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 should store not just ClaimableBalanceEntry
but the full LedgerEntry
in this column, this duplicates a small amount of data but avoids adding an extra column for ledgerext
(this was added in the latest round of changes to the database)
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 support this approach as well. We can always change it later if the performance is worse-than-desired, but this approach will considerably simplify future development.
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 would also allow us to remove the lastmodified
column right?
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.
Nevermind. The lastmodified
column is required.
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 made the change to store the full LedgerEntry
.
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.
Nice work! This looks largely correct to me at this point, and @MonsieurNicolas had already identified most of the remaining issues. Most of my comments are picking out minor details. The main issue here for me is that the tests are pretty confusing: it was hard for me to tell exactly what is being tested in various places, and also hard for me to tell if we achieved the desired coverage. For example, I wasn't able to pick out a test that created a claimable balance entry in a transaction where the operation and transaction have different source accounts (is there such a test?). Is there anything we can do to improve the overall clarity of the tests?
mDatabase.getSession() << "CREATE TABLE claimablebalance (" | ||
<< "balanceid VARCHAR(56) " << coll | ||
<< " PRIMARY KEY, " | ||
<< "claimablebalanceentry TEXT " << coll |
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 support this approach as well. We can always change it later if the performance is worse-than-desired, but this approach will considerably simplify future development.
@@ -23,6 +23,24 @@ calculateDeltaBalance(std::shared_ptr<LedgerEntry const> const& current, | |||
return (current ? current->data.account().balance : 0) - | |||
(previous ? previous->data.account().balance : 0); | |||
} | |||
if (let == CLAIMABLE_BALANCE) | |||
{ | |||
int64_t reserveDelta = |
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 will be simplified when we rebase CAP-33 on top, because the reserve is not stored in that case. But I the code should work correctly in the case that asset
changed. This would obviously be a bug if it were to happen, but for clarity and safety the code should account for it (after all, invariants are all about catching bugs).
// authorize trustline | ||
if (!isNative) | ||
{ | ||
SECTION("no trust") |
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.
Does this all actually work as you intend? The sequential sections with intermittent code is a pretty confusing pattern. It leaves me wondering, what is supposed to happen here and what actually does happen here?
Are these early sections like "no trust" and "not authorized" even required? The code in those sections should have no effect other than paying fees and consuming sequence numbers, so the sections might not really be doing anything.
In summary, I'm confused by this.
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 cleaned up the tests and squashed with 572598aa0be8290908319e5d1d98e83c09c5520f.
0ac1858
to
42501fe
Compare
@jonjove You make a good point about the test cases. They're confusing to look at, and the pattern where I use sequential sections with intermittent code can be hard to follow. I did this to avoid duplicating code, but the end result isn't great. I'll fix this.
|
18ec217
to
cb0d1cf
Compare
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 looks almost ready to me now. The tests are vastly easier to parse, so thank you for redesigning that! The only test that I don't remember seeing is one in which we create a claimable balance and then claim it in the same transaction. I expect this to work, but I think we should test it because it is an edge case in the sense that if you are doing this then you aren't really taking advantage of the behavior of claimable balance.
mDatabase.getSession() << "CREATE TABLE claimablebalance (" | ||
<< "balanceid VARCHAR(48) " << coll | ||
<< " PRIMARY KEY, " | ||
<< "claimablebalanceentry TEXT NOT NULL, " |
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 think this name is a good description anymore, given that we are storing the LedgerEntry
.
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.
Changed to ledgerentry
.
ex_CREATE_CLAIMABLE_BALANCE_NO_TRUST); | ||
} | ||
|
||
// create and modify trustlines regardless of asset type so the |
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'm a little confused about this comment. acc1
can't have the same balance in both scenarios because you are executing some transactions only if !isNative
. Do I misunderstand?
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 the comment should be updated to say the number of subentries is the same in both scenarios.
} | ||
} | ||
|
||
SECTION("multiple create and claims") |
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.
Not sure I understand the purpose of this test? Unless I'm mistaken, it effectively does "create -> claim -> fund -> create -> claim" with each step in its own transaction. Was the intention to do that as a single transaction?
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 test here was to make sure that the claim doesn't have any unintended side effects that could get in the way of another create/claim later on.
cb0d1cf
to
29c1cab
Compare
@jonjove I made the last round of PR changes and squashed/rebased. I also added a test for creating and claiming in the same tx under the section |
r+ 29c1cab |
@latobarita: retry |
Description
Implements https://github.com/stellar/stellar-protocol/blob/master/core/cap-0023.md
Checklist
clang-format
v5.0.0 (viamake format
or the Visual Studio extension)