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

Fix the rendering issues of some XDR fields #2876

Merged
merged 4 commits into from Mar 4, 2021

Conversation

hidenori-shinohara
Copy link
Contributor

Description

Resolves #2869

  • The "value0" issue. This seems like a feature rather than a bug (for instance, see this cereal issue ) Long story short, it's there to support multi-entry outputs such as
{          
    "value0": {
        "x": 1,
        "y": 2,
        "z": 3
    },    
    "value1": {           
        "x": 4,                
        "y": 5,
        "z": 6
    },
    "value2": {
        "x": 7,
        "y": 8,
        "z": 9
    }
}
  • xdr_to_string has an optional field name argument. When that's not passed, Cereal uses the word "value0". This PR will make the argument mandatory, so we will print a descriptive name given by a caller, instead of "value0". We already use this in multiple places, and this PR updates the usage of xdr_to_string when the field name argument is not passed.
  • I used the following XDR to make sure that the asset list is rendered correctly. With this PR, the XDR will be rendered as following:
...                             "destAsset": "XLM",
                                "destAmount": 2000000000,
                                "path": [
                                    "CENTUS",
                                    "USD"
                                ]
...

instead of

...
                                "path": [
                                    {
                                        "type": "ASSET_TYPE_CREDIT_ALPHANUM12",
                                        "alphaNum12": {
                                            "assetCode": "43454e545553000000000000",
                                            "issuer": "GCD5VCGHNLC46D5O2SAZCFFP3ZF7JRBQZGSFINXWNY5HCEOE42PH3XH5"
                                        }
                                    },
                                    {
                                        "type": "ASSET_TYPE_CREDIT_ALPHANUM4",
                                        "alphaNum4": {
                                            "assetCode": "55534400",
                                            "issuer": "GDJAG52BJUFI6RBKFTNUEJBDZODJPZBLIHN3SXMR7QIXNRAOJK3XL6P3"
                                        }
                                    }
                                ]
...

AAAAAgAAAACJCjEa69Jmm5xInlCSLmzEAf8u1q/AeXUbhoZQFHOsoQAAAGQAAAAcvpkaFAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABx0aGlzIGlzIGEgbWVtbyBmb3IgZGVidWdnaW5nAAAAAQAAAAAAAAACAAAAAAAAAAA7msoAAAAAANhWYalldqwqmK2G0p/IIaS+64sMI9nfPeBe5RlS2NDCAAAAAAAAAAB3NZQAAAAAAgAAAAJDRU5UVVMAAAAAAAAAAAAAh9qIx2rFzw+u1IGRFK/eS/TEMMmkVDb2bjpxEcTmnn0AAAABVVNEAAAAAADSA3dBTQqPRCos20IkI8uGl+QrQdu5XZH8EXbEDkq3dQAAAAAAAAAA

Checklist

  • Reviewed the contributing document
  • Rebased on top of master (no merge commits)
  • Ran clang-format v5.0.0 (via make format or the Visual Studio extension)
  • Compiles
  • Ran all tests
  • If change impacts performance, include supporting evidence per the performance document

@jonjove
Copy link
Contributor

jonjove commented Jan 13, 2021

Regarding the second part of your change: generally I would want to see both the asset code and the issuer.

@@ -67,8 +67,8 @@ checkOperationResults(xdr::xvector<OperationResult> const& expected,
if (expected[i].code() != actual[i].code())
{
CLOG_ERROR(History, "Expected operation result {} but got {}",
xdr_to_string(expected[i].code()),
xdr_to_string(actual[i].code()));
xdr_to_string(expected[i].code(), "OperationResultCode"),
Copy link
Contributor

Choose a reason for hiding this comment

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

Now that we're printing OperationResultCode, you can change the log to be Expected {} but got {}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed!

@@ -181,7 +182,8 @@ checkResults(Application& app, uint32_t ledger,
CLOG_ERROR(
History,
"Expected result code {} does not agree with {} for tx {}",
xdr_to_string(archiveRes.code()), xdr_to_string(dbRes.code()),
xdr_to_string(archiveRes.code(), "TransactionResultCode"),
Copy link
Contributor

Choose a reason for hiding this comment

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

"result code" is now redundant, change to "Expected {} does not agree with {} for tx {}"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed!

xdr_to_string(actualOpRes));
CLOG_ERROR(
History, "{}",
xdr_to_string(expectedOpRes, "Expected operation result"));
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: it's a little clearer if you make the name an actual XDR type (so OperationResult) and move "expected/actual":
CLOG_ERROR(History, "Expected {}", xdr_to_string(expectedOpRes, "OperationResult"));

Copy link
Contributor Author

Choose a reason for hiding this comment

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

makes sense!

@@ -196,8 +198,10 @@ checkResults(Application& app, uint32_t ledger,
"Expected result code {} does not agree with {} for "
Copy link
Contributor

Choose a reason for hiding this comment

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

Same thing about "result code"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed!

@hidenori-shinohara
Copy link
Contributor Author

hidenori-shinohara commented Jan 13, 2021

Updated the way Asset is handled, and now it'll be printed with both assetCode and issuer as following:

                            "pathPaymentStrictReceiveOp": {
                                "sendAsset": {   
                                    "assetCode": "XLM",           
                                    "issuer": "SDF"                                                                                                
                                },                                                                                                                 
                                "sendMax": 1000000000,          
                                "destination": "GDMFMYNJMV3KYKUYVWDNFH6IEGSL524LBQR5TXZ54BPOKGKS3DIMFIAB",
                                "destAsset": {                                                                                                     
                                    "assetCode": "XLM",
                                    "issuer": "SDF"
                                },
                                "destAmount": 2000000000,
                                "path": [
                                    {   
                                        "assetCode": "CENTUS",
                                        "issuer": "GCD5VCGHNLC46D5O2SAZCFFP3ZF7JRBQZGSFINXWNY5HCEOE42PH3XH5"
                                    },                                  
                                    {            
                                        "assetCode": "USD",    
                                        "issuer": "GDJAG52BJUFI6RBKFTNUEJBDZODJPZBLIHN3SXMR7QIXNRAOJK3XL6P3"
                                    }
                                ]       
                            }   

src/util/XDRCereal.h Outdated Show resolved Hide resolved
@@ -51,6 +51,27 @@ cereal_override(cereal::JSONOutputArchive& ar,
xdr::archive(ar, tmp, field);
}

template <uint32_t N>
void
cereal_override(cereal::JSONOutputArchive& ar,
Copy link
Contributor

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 is necessary once xdrpp/xdrpp#25 is merged.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After several attempts, I think I'll give up fixing this in a more general way such as a new save function in XDRPP or cereal_override for xvector. While I definitely think that it's suboptimal that we need to implement something like this whenever we have an xvector containing elements with cereal_override, it seems that this is our second time, so I hope that this is not that bad. Let me know what you think!

@hidenori-shinohara
Copy link
Contributor Author

Updated the change using clang-format-8

xdr::archive(ar, stellar::assetToString(s), field);
xdr::archive(
ar,
std::make_tuple(
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be changed to render native asset as "native" (no sub-object).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I only know one way to print an xvector of asset and that is to create a vector where each element is an NVP of the corresponding element of xvector. In other words, I'm not sure how I can render native asset without a sub-object and other assets as a sub-object. I made a change to make it clear that native asset is a native asset and there is no assetCode and issuer like following:

"pathPaymentStrictReceiveOp": {    
    "sendAsset": {                                
        "assetType": "native",                                
        "assetCode/issuer": "N/A"                                
    },
...

Let me know what you think!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for jonjove@d7472a0! I would have never come up with this idea.

I updated the PR with the change. I also tested nested containers (which we don't have now, but for future reference & out of curiosity), and it seems to work exactly as we'd expect. hidenori-shinohara/xdrpp@abe8047

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is how asset information is printed with this change:

                        "body": {
                            "type": "PATH_PAYMENT_STRICT_RECEIVE",
                            "pathPaymentStrictReceiveOp": {
                                "sendAsset": "NATIVE",
                                "sendMax": 1000000000,
                                "destination": "GDMFMYNJMV3KYKUYVWDNFH6IEGSL524LBQR5TXZ54BPOKGKS3DIMFIAB",
                                "destAsset": "NATIVE",
                                "destAmount": 2000000000,
                                "path": [
                                    {
                                        "assetCode": "CENTUS",
                                        "issuer": "GCD5VCGHNLC46D5O2SAZCFFP3ZF7JRBQZGSFINXWNY5HCEOE42PH3XH5"
                                    },
                                    {
                                        "assetCode": "USD",
                                        "issuer": "GDJAG52BJUFI6RBKFTNUEJBDZODJPZBLIHN3SXMR7QIXNRAOJK3XL6P3"
                                    }
                                ]
                            }
                        }

? "SDF"
: stellar::KeyUtils::toStrKey(stellar::getIssuer(asset)))),
field);
return std::make_tuple(
Copy link
Contributor

Choose a reason for hiding this comment

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

Same thing as mentioned in the previous commit about rendering native asset.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed!

@@ -51,6 +51,22 @@ cereal_override(cereal::JSONOutputArchive& ar,
xdr::archive(ar, tmp, field);
}

typedef cereal::NameValuePair<std::string> CerealStringNVP;
typedef std::tuple<CerealStringNVP, CerealStringNVP> NVPTuple;
Copy link
Contributor

Choose a reason for hiding this comment

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

This is probably not a great name for this type. In general, this will only work for two NVPs but what about the case where you have 3 or more?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense. Since NVPTuple is only used a few times and it's unclear if we'll use it much more often, I got rid of the typedef.

"Balance not compatible with liabilities for account {}",
xdr_to_string(account));
"Balance not compatible with liabilities for {}",
xdr_to_string(account, "account"));
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick: Maybe "AccountEntry" for consistency?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed!

"while fuzzing: {}"),
txFramePtr->getResultCode(),
xdr_to_string(txFramePtr->getResult(),
"transaction result"));
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick: Maybe "TransactionResult"? (I know that's not exactly what it said before, but given that this is targeted at developers I think it will be clearer)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed!

}
res = checkValid(signatureChecker, ltx, true);
if (res)
{
res = doApply(ltx);
if (Logging::logTrace("Tx"))
{
CLOG_TRACE(Tx, "Operation result: {}", xdr_to_string(mResult));
CLOG_TRACE(Tx, "{}", xdr_to_string(mResult, "Operation result"));
Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick: maybe "OperationResult"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

@MonsieurNicolas MonsieurNicolas added this to In progress in v15.4.0 via automation Feb 15, 2021
@MonsieurNicolas MonsieurNicolas removed this from In progress in v15.3.0 Feb 15, 2021
@hidenori-shinohara
Copy link
Contributor Author

I updated the PR with the cereal_override for all containers, and I also addressed all the comments.

Copy link
Contributor

@jonjove jonjove left a comment

Choose a reason for hiding this comment

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

A couple small comments here about making the comments a bit more helpful to future maintainers. Generally, I'm happy with this. @graydon, how do you feel about the documentation level here?

@@ -124,7 +124,7 @@ xdr_to_string(const T& t, std::string const& name, bool compact = false)
cereal::JSONOutputArchive ar(
os, compact ? cereal::JSONOutputArchive::Options::NoIndent()
: cereal::JSONOutputArchive::Options::Default());
ar(cereal::make_nvp(name, t));
xdr::archive(ar, t, name.c_str());
Copy link
Contributor

Choose a reason for hiding this comment

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

I am pretty sure it's okay, but I just want to confirm that this does the right thing if name.empty() right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for pointing that out! I didn't think about that possibility. I checked the code and it just prints something like

"": {
 ...
}

which I doubt will be very useful. I decided to require that the input string be non-empty.

Initially, I felt that it might be an over-kill, but the original issue was that we have the word value0, so replacing it with the empty string will not really be much of an improvement, so I figured that I'd require it. Let me know what you think!

Copy link
Contributor

@graydon graydon Feb 23, 2021

Choose a reason for hiding this comment

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

I think this is ok, but you might want to do a ReleaseAssert / ReleaseAssertOrThrow (to ensure the assert remains inside release builds) and of course make sure tests all pass. maybe also run them at trace log-level in case we missed any cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@graydon
I didn't know a bout ReleaseAssert! Although after some thinking, I started to wonder that if it'd be better if the assert does NOT remain inside release builds. I'm currently thinking that this issue of printing "": { may not be important enough to warrant an abort or throw in production.

  • I just confirmed that all the tests do pass.

Let me know what you think!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After more thoughts & learning about #2852, I started thinking that it may be better to not have any assertion statements. Printing "": { doesn't seem bad enough to warrant an abort or throw in production.

{
std::vector<std::string> tmp;
for (auto const& h : s)
// setNextName, startNode, and finishNode create a new sub-object
Copy link
Contributor

Choose a reason for hiding this comment

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

You might add some details about the fact that setNextName, startNode, ..., finishNode is basically what ar(cereal::make_nvp(....)) does.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated the comment. As I was updating it, I started to think that explicitly calling prologue and epilogue might be easier for future developers to understand since it'll be more similar to ar(), so I went ahead and replaced them. Let me know what you think!

src/util/XDRCereal.h Show resolved Hide resolved
Copy link
Contributor

@graydon graydon left a comment

Choose a reason for hiding this comment

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

I agree with the current structure for printing containers. Conversation with @jonjove the other day made sense, and this code matches my current recollection of that conversation.

@graydon
Copy link
Contributor

graydon commented Mar 4, 2021

r+ 8df71d8

@latobarita latobarita merged commit edba91c into stellar:master Mar 4, 2021
v15.4.0 automation moved this from In progress to Done Mar 4, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

some xdr fields are not rendered properly in JSON
5 participants