-
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
Price Oracle: validate input parameters and extend test coverage: #5013
Conversation
Validate trim, time_threshold, document_id are valid Int, UInt, or string convertible to UInt. Validate base_asset and quote_asset are valid currency. Update error codes. Extend Oracle and GetAggregatePrice unit-tests. Denote unreachable coverage code.
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## develop #5013 +/- ##
=========================================
+ Coverage 70.9% 71.0% +0.1%
=========================================
Files 796 796
Lines 66792 66793 +1
Branches 10998 10986 -12
=========================================
+ Hits 47377 47420 +43
+ Misses 19415 19373 -42
|
Json::StaticString const& field, | ||
unsigned int def = | ||
0) -> std::variant<std::uint32_t, error_code_i> { | ||
if (params.isMember(field)) | ||
{ | ||
if (!params[field].isConvertibleTo(Json::ValueType::uintValue)) | ||
return rpcORACLE_MALFORMED; | ||
if (!validUInt(params, field)) |
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 the difference between these two lines of 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.
If I'm reading the code correctly, it's that the old version accepted booleans and floats and would just convert them?
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.
isConvertible
is
return (other == nullValue && value_.uint_ == 0) || (other == intValue && value_.uint_ <= (unsigned)maxInt) || other == uintValue || other == realValue || other == stringValue || other == booleanValue;
which means it can convert a wider range of other type. For instance, it'll convert 1.2 to 1, which is invalid in this case.
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 reading the code correctly, it's that the old version accepted booleans and floats and would just convert them?
Correct.
|
||
meta = ledger->txRead(prevTx).second; | ||
|
||
prevChain = chain; |
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 does moving this line do? I don't really understand what prevChain
is doing, and there don't appear to be any tests checking this behavior.
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 handles an unlikely case when CreatedNode/ModifiedNode
not found in the inner loop for AffectedNodes
. If this happens then prevChain
is going to be equal to chain
and the iteration stops. I don't know if it's possible to reproduce it unless the ledger state is changed in a way, which is not possible to do with the transactions.
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 moving this line of code make any difference other than the slight efficiency improvement from running fewer times?
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.
prevChain
has to be assigned outside of the inner loop. Initially prevChain
is nullptr
and chain
points to oracle
. If there is no AffectedNodes
then both prevChain
and chain
are unchanged and the check (prevChain == chain)
is not going to fail.
if (params[field].asString().empty()) | ||
return rpcINVALID_PARAMS; |
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.
Isn't this check already included in currencyFromJson
?
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, it could be removed. It's just a bit faster.
BEAST_EXPECT( | ||
oracle.expectLastUpdateTime(testStartTime.count() + 450)); | ||
BEAST_EXPECT(oracle.expectLastUpdateTime( | ||
static_cast<std::uint32_t>(testStartTime.count() + 450))); |
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 there a reason the other lines were switched to using closeTime()
but this one still uses testStartTime
?
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 two lines that used testStartTime
should have not actually used it because testStartTime
is already added in Oracle::set
to the passed in LastUpdateTime
value.
src/test/app/Oracle_test.cpp
Outdated
@@ -622,6 +678,39 @@ struct Oracle_test : public beast::unit_test::suite | |||
} | |||
} | |||
|
|||
void | |||
testInvalidLedgerEntry() |
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 these tests be here or in LedgerRPC_test
?
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. It should be moved.
@@ -39,16 +55,16 @@ using DataSeries = std::vector<std::tuple< | |||
struct CreateArg | |||
{ | |||
std::optional<AccountID> owner = std::nullopt; | |||
std::optional<std::uint32_t> documentID = 1; | |||
std::optional<AnyValue> documentID = 1; |
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.
Why are these types becoming less specific? Isn't that checked at the validation level?
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 wanted to be able to test a wider range of valid/invalid values similar to how QA does it. Unfortunately the test framework does its own validation and will not call rpc if the parameters don't match their defined SField type. The validation is done because the transaction is signed. I left this change so that eventually can extend the testing to support unsigned transactions.
|
||
auto jr = env.rpc("json", "get_aggregate_price", to_string(jv)); | ||
toJson(jv[jss::time_threshold], *timeThreshold); | ||
// Convert "%None%" to None |
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"%None%"
be a global test constant, since it's a special case?
…PLF#5013) * Price Oracle: validate input parameters and extend test coverage: Validate trim, time_threshold, document_id are valid Int, UInt, or string convertible to UInt. Validate base_asset and quote_asset are valid currency. Update error codes. Extend Oracle and GetAggregatePrice unit-tests. Denote unreachable coverage code. * Set one-line LCOV_EXCL_LINE * Move ledger_entry tests to LedgerRPC_test.cpp * Add constants for "None" * Fix LedgerRPC test --------- Co-authored-by: Scott Determan <[email protected]>
High Level Overview of Change
Validate trim, time_threshold, document_id are valid Int, UInt, or string convertible to UInt. Validate base_asset and quote_asset are valid currency. Update error codes. Extend Oracle and GetAggregatePrice unit-tests.
Denote unreachable coverage code.
Type of Change
.gitignore
, formatting, dropping support for older tooling)Before / After
Fixes invalid input parameters conversion to uint. For instance, 1.2 was converted to 1 if passed to trim, time_threshold, or document_id. This only allows valid UInt values. Similarly, base_asset and quote_asset were not validated to be a valid currency code. This fix only allows valid currency codes in base_asset and quote_asset.
Test Plan
Extended Oracle and GetAggregatePrices tests.