20 #include <ripple/basics/safe_cast.h>
21 #include <ripple/beast/unit_test.h>
22 #include <ripple/protocol/InnerObjectFormats.h>
23 #include <ripple/protocol/LedgerFormats.h>
24 #include <ripple/protocol/TxFormats.h>
26 #include "org/xrpl/rpc/v1/ledger_objects.pb.h"
27 #include "org/xrpl/rpc/v1/transaction.pb.h"
46 google::protobuf::FieldDescriptor::Type::TYPE_UINT32;
49 google::protobuf::FieldDescriptor::Type::TYPE_UINT64;
52 google::protobuf::FieldDescriptor::Type::TYPE_BYTES;
55 google::protobuf::FieldDescriptor::Type::TYPE_STRING;
58 google::protobuf::FieldDescriptor::Type::TYPE_MESSAGE;
68 bool prevUpper =
false;
71 char const ch = fmtName[i];
72 bool const upper = std::isupper(ch);
73 if (i > 0 && !prevUpper && upper)
84 template <
typename KeyType>
91 for (
SOElement const& element : soTemplate)
93 SField const& sField = element.sField();
111 if (excludedSFields.count(sField.
fieldCode))
125 if constexpr (std::is_same_v<KeyType, TxType>)
133 if (excludedTxFields.count(sField.
fieldCode))
138 if constexpr (std::is_same_v<KeyType, LedgerEntryType>)
148 map<LedgerEntryType, std::vector<SField const*>>
155 if (
auto const iter = gRPCOmitFields.find(fmtId);
156 iter != gRPCOmitFields.end())
164 [&sField](
SField const*
const omit) {
165 return *omit == sField;
203 {
"AccountTxnID",
"AccountTransactionID"},
204 {
"Fee",
"XRPDropsAmount"},
205 {
"PreviousTxnID",
"PreviousTransactionID"},
206 {
"PreviousTxnLgrSeq",
"PreviousTransactionLedgerSequence"},
207 {
"Signature",
"PaymentChannelSignature"},
208 {
"SigningPubKey",
"SigningPublicKey"},
209 {
"TakerGetsCurrency",
"TakerGetsCurreny"},
210 {
"TxnSignature",
"TransactionSignature"},
214 auto const iter = sFieldToGRPC.
find(sField.
getName());
216 iter != sFieldToGRPC.
end() ? iter->second : sField.
getName();
218 sFields.
insert({std::move(gRPCName), &sField});
227 google::protobuf::Descriptor
const*
const pbufDescriptor,
228 google::protobuf::Descriptor
const*
const commonFields,
233 namespace pbuf = google::protobuf;
238 auto checkFieldDesc = [
this, &sFields, &knownFormatName](
239 pbuf::FieldDescriptor
const*
const
244 if (fieldDesc->is_repeated())
250 name = fieldDesc->camelcase_name();
251 name[0] = toupper(name[0]);
254 if (
size_t const i = name.
find(
"Unl"); i != std::string::npos)
262 if (
size_t const i = name.
find(
"Nft"); i != std::string::npos)
268 if (!sFields.count(name))
271 std::string(
"Repeated Protobuf Descriptor '") + name +
272 "' expected in KnownFormat '" + knownFormatName +
285 pbuf::Descriptor
const*
const entryDesc =
286 fieldDesc->message_type();
287 if (entryDesc ==
nullptr)
290 name = entryDesc->name();
291 if (!sFields.count(name))
295 entryDesc->name() +
"' expected in KnownFormat '" +
296 knownFormatName +
"' and not found",
311 for (
int i = 0; i < pbufDescriptor->field_count(); ++i)
313 pbuf::FieldDescriptor
const*
const fieldDesc =
314 pbufDescriptor->field(i);
318 checkFieldDesc(fieldDesc);
326 for (
int i = 0; i < commonFields->field_count(); ++i)
330 pbuf::FieldDescriptor
const*
const fieldDesc =
331 commonFields->field(i);
333 if (fieldDesc ==
nullptr ||
334 fieldDesc->containing_oneof() !=
nullptr ||
338 checkFieldDesc(fieldDesc);
344 if (!sFields.empty())
347 std::string(
"Protobuf Descriptor '") + pbufDescriptor->name() +
348 "' did not account for all fields in KnownFormat '" +
349 knownFormatName +
"'. Left over field: `" +
350 sFields.
begin()->first +
"'",
362 google::protobuf::Descriptor
const*
const entryDesc,
363 SField const*
const sField)
366 namespace pbuf = google::protobuf;
370 if (entryDesc->field_count() == 0 || entryDesc->oneof_decl_count() != 1)
373 std::string(
"Protobuf Descriptor '") + entryDesc->name() +
374 "' expected to have multiple OneOf fields and nothing else",
380 pbuf::FieldDescriptor
const*
const fieldDesc = entryDesc->field(0);
381 if (fieldDesc ==
nullptr)
384 std::string(
"Internal test failure. Unhandled nullptr "
385 "in FieldDescriptor for '") +
386 entryDesc->name() +
"'",
394 entryDesc->name() ==
"CurrencyAmount")
405 std::string(
"Unhandled OneOf Protobuf Descriptor '") +
406 entryDesc->name() +
"'",
413 google::protobuf::Descriptor
const*
const entryDesc,
414 SField const*
const sField)
417 namespace pbuf = google::protobuf;
419 if (entryDesc->field_count() <= 1 || entryDesc->oneof_decl_count() != 0)
422 std::string(
"Protobuf Descriptor '") + entryDesc->name() +
423 "' expected to have multiple fields and nothing else",
435 google::protobuf::FieldDescriptor::Type fieldType;
438 operator<(FieldContents
const& other)
const
440 return this->fieldName < other.fieldName;
446 return this->fieldName == other.fieldName &&
447 this->fieldType == other.fieldType;
480 specialEntries.begin(),
481 specialEntries.end(),
482 [entryDesc, sField](SpecialEntry
const& entry) {
483 return entryDesc->name() == entry.descriptorName &&
484 sField->fieldType == entry.sFieldType;
486 iter != specialEntries.end())
489 if (!BEAST_EXPECT(sField->
fieldType == iter->sFieldType))
494 entryDesc->field_count() == iter->fields.size()))
497 for (
int i = 0; i < entryDesc->field_count(); ++i)
499 pbuf::FieldDescriptor
const*
const fieldDesc =
502 FieldContents
const contents{
503 fieldDesc->name(), fieldDesc->type()};
506 iter->fields.find(contents) != iter->fields.end()))
520 if (innerFormat ==
nullptr)
523 "SOTemplate for field '" + sField->
getName() +
"' not found",
536 entryDesc,
nullptr, sField->
getName(), std::move(sFields));
542 google::protobuf::Descriptor
const*
const entryDesc,
543 SField const*
const sField)
546 namespace pbuf = google::protobuf;
550 if (entryDesc->field_count() != 1 || entryDesc->oneof_decl_count() != 0)
553 std::string(
"Protobuf Descriptor '") + entryDesc->name() +
554 "' expected to be one field and nothing else",
560 pbuf::FieldDescriptor
const*
const fieldDesc = entryDesc->field(0);
561 if (fieldDesc ==
nullptr)
564 std::string(
"Internal test failure. Unhandled nullptr "
565 "in FieldDescriptor for '") +
566 entryDesc->name() +
"'",
579 sTypeToFieldDescType{
597 if (
auto const iter = sTypeToFieldDescType.find(sField->
fieldType);
598 iter != sTypeToFieldDescType.end() &&
599 iter->second == fieldDesc->type())
607 sFieldCodeToFieldDescType{
612 if (
auto const iter = sFieldCodeToFieldDescType.find(sField->
fieldCode);
613 iter != sFieldCodeToFieldDescType.end() &&
614 iter->second == fieldDesc->type())
648 pbuf::Descriptor
const*
const entry2Desc =
649 fieldDesc->message_type();
651 if (entry2Desc ==
nullptr)
654 std::string(
"Unexpected gRPC. ") + fieldDesc->name() +
655 " MESSAGE with null Descriptor",
662 if (messageMap.at(sField->
fieldCode) != entry2Desc->name())
666 "Internal test error. Mismatch between SField '") +
667 sField->
getName() +
"' and gRPC Descriptor name '" +
668 entry2Desc->name() +
"'",
682 std::string(
"Internal test error. Unhandled FieldDescriptor '") +
683 entryDesc->name() +
"' has type `" + fieldDesc->type_name() +
692 google::protobuf::FieldDescriptor
const*
const fieldDesc,
693 SField const*
const sField)
696 namespace pbuf = google::protobuf;
698 pbuf::Descriptor
const*
const entryDesc = fieldDesc->message_type();
699 if (entryDesc ==
nullptr)
702 std::string(
"Expected Descriptor for repeated type ") +
716 if (noFurtherDetail.count(sField->
getName()))
737 if (!repeatsWhat.count(sField->
getName()))
740 std::string(
"Unexpected repeated type ") + fieldDesc->name(),
754 google::protobuf::Descriptor
const*
const entryDesc,
755 SField const*
const sField)
757 if (entryDesc->nested_type_count() != 0 ||
758 entryDesc->enum_type_count() != 0 ||
759 entryDesc->extension_range_count() != 0 ||
760 entryDesc->reserved_range_count() != 0)
763 std::string(
"Protobuf Descriptor '") + entryDesc->name() +
764 "' uses unsupported protobuf features",
771 if (entryDesc->oneof_decl_count() > 0)
774 if (entryDesc->field_count() > 1)
781 template <
typename FmtType,
typename FmtName>
784 google::protobuf::Descriptor
const*
const pbufDescriptor,
785 google::protobuf::Descriptor
const*
const commonFields,
790 namespace pbuf = google::protobuf;
794 if (knownFormatItem->
getName() != pbufDescriptor->name() &&
795 knownFormatItem->
getName() +
"Object" != pbufDescriptor->name())
798 std::string(
"Protobuf Descriptor '") + pbufDescriptor->name() +
799 "' and KnownFormat::Item '" + knownFormatItem->
getName() +
800 "' don't have the same name",
820 template <
typename FmtType,
typename FmtName>
825 google::protobuf::Descriptor
const*
const commonFields,
826 google::protobuf::OneofDescriptor
const*
const oneOfDesc)
829 namespace grpc = org::xrpl::rpc::v1;
830 namespace pbuf = google::protobuf;
832 if (!BEAST_EXPECT(oneOfDesc !=
nullptr))
841 for (
auto const& item : knownFormat)
843 if constexpr (std::is_same_v<FmtType, LedgerEntryType>)
849 notSupported.begin(),
851 item.getType()) != notSupported.end())
855 if constexpr (std::is_same_v<FmtType, TxType>)
862 notSupported.begin(),
864 item.getType()) != notSupported.end())
877 BEAST_EXPECT(formatTypes.size() == oneOfDesc->field_count());
884 for (
auto i = 0; i < oneOfDesc->field_count(); ++i)
886 pbuf::FieldDescriptor
const*
const fieldDesc = oneOfDesc->field(i);
894 "' is not TYPE_MESSAGE",
900 auto const fmtIter = formatTypes.find(fieldDesc->name());
902 if (fmtIter == formatTypes.cend())
906 "' not found in " + knownFormatName,
913 validateFields<FmtType, FmtName>(
914 fieldDesc->message_type(), commonFields, fmtIter->second);
918 formatTypes.erase(fieldDesc->name());
922 for (
auto const& spare : formatTypes)
925 knownFormatName +
" '" + spare.second->getName() +
926 "' does not have a corresponding gRPC OneOf",
936 testcase(
"Ledger object validation");
938 org::xrpl::rpc::v1::LedgerObject
const ledgerObject;
943 ledgerObject.GetDescriptor(),
944 ledgerObject.GetDescriptor()->FindOneofByName(
"object"));
952 testcase(
"Transaction validation");
954 org::xrpl::rpc::v1::Transaction
const txData;
959 txData.GetDescriptor(),
960 txData.GetDescriptor()->FindOneofByName(
"transaction_data"));