#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace xrpl { STObject::STObject(STObject&& other) : STBase(other.getFName()), v_(std::move(other.v_)), mType(other.mType) { } STObject::STObject(SField const& name) : STBase(name), mType(nullptr) { } STObject::STObject(SOTemplate const& type, SField const& name) : STBase(name) { set(type); } STObject::STObject(SOTemplate const& type, SerialIter& sit, SField const& name) : STBase(name) { v_.reserve(type.size()); set(sit); applyTemplate(type); // May throw } STObject::STObject(SerialIter& sit, SField const& name, int depth) noexcept(false) : STBase(name), mType(nullptr) { if (depth > 10) Throw("Maximum nesting depth of STObject exceeded"); set(sit, depth); } STObject STObject::makeInnerObject(SField const& name) { STObject obj{name}; // The if is complicated because inner object templates were added in // two phases: // 1. If there are no available Rules, then always apply the template. // 2. fixInnerObjTemplate added templates to two AMM inner objects. // 3. fixInnerObjTemplate2 added templates to all remaining inner objects. std::optional const& rules = getCurrentTransactionRules(); bool const isAMMObj = name == sfAuctionSlot || name == sfVoteEntry; if (!rules || (rules->enabled(fixInnerObjTemplate) && isAMMObj) || (rules->enabled(fixInnerObjTemplate2) && !isAMMObj)) { if (SOTemplate const* elements = InnerObjectFormats::getInstance().findSOTemplateBySField(name)) obj.set(*elements); } return obj; } STBase* STObject::copy(std::size_t n, void* buf) const { return emplace(n, buf, *this); } STBase* STObject::move(std::size_t n, void* buf) { return emplace(n, buf, std::move(*this)); } SerializedTypeID STObject::getSType() const { return STI_OBJECT; } bool STObject::isDefault() const { return v_.empty(); } void STObject::add(Serializer& s) const { add(s, withAllFields); // just inner elements } STObject& STObject::operator=(STObject&& other) { setFName(other.getFName()); mType = other.mType; v_ = std::move(other.v_); return *this; } void STObject::set(SOTemplate const& type) { v_.clear(); v_.reserve(type.size()); mType = &type; for (auto const& elem : type) { if (elem.style() != soeREQUIRED) v_.emplace_back(detail::nonPresentObject, elem.sField()); else v_.emplace_back(detail::defaultObject, elem.sField()); } } void STObject::applyTemplate(SOTemplate const& type) { auto throwFieldErr = [](std::string const& field, char const* description) { std::stringstream ss; ss << "Field '" << field << "' " << description; std::string text{ss.str()}; JLOG(debugLog().error()) << "STObject::applyTemplate failed: " << text; Throw(text); }; mType = &type; decltype(v_) v; v.reserve(type.size()); for (auto const& e : type) { auto const iter = std::find_if(v_.begin(), v_.end(), [&](detail::STVar const& b) { return b.get().getFName() == e.sField(); }); if (iter != v_.end()) { if ((e.style() == soeDEFAULT) && iter->get().isDefault()) { throwFieldErr(e.sField().fieldName, "may not be explicitly set to default."); } v.emplace_back(std::move(*iter)); v_.erase(iter); } else { if (e.style() == soeREQUIRED) { throwFieldErr(e.sField().fieldName, "is required but missing."); } v.emplace_back(detail::nonPresentObject, e.sField()); } } for (auto const& e : v_) { // Anything left over in the object must be discardable if (!e->getFName().isDiscardable()) { throwFieldErr(e->getFName().getName(), "found in disallowed location."); } } // Swap the template matching data in for the old data, // freeing any leftover junk v_.swap(v); } void STObject::applyTemplateFromSField(SField const& sField) { SOTemplate const* elements = InnerObjectFormats::getInstance().findSOTemplateBySField(sField); if (elements) applyTemplate(*elements); // May throw } // return true = terminated with end-of-object bool STObject::set(SerialIter& sit, int depth) { bool reachedEndOfObject = false; v_.clear(); // Consume data in the pipe until we run out or reach the end while (!sit.empty()) { int type; int field; // Get the metadata for the next field sit.getFieldID(type, field); // The object termination marker has been found and the termination // marker has been consumed. Done deserializing. if (type == STI_OBJECT && field == 1) { reachedEndOfObject = true; break; } if (type == STI_ARRAY && field == 1) { JLOG(debugLog().error()) << "Encountered object with embedded end-of-array marker"; Throw("Illegal end-of-array marker in object"); } auto const& fn = SField::getField(type, field); if (fn.isInvalid()) { JLOG(debugLog().error()) << "Unknown field: field_type=" << type << ", field_name=" << field; Throw("Unknown field"); } // Unflatten the field v_.emplace_back(sit, fn, depth + 1); // If the object type has a known SOTemplate then set it. if (auto const obj = dynamic_cast(&(v_.back().get()))) obj->applyTemplateFromSField(fn); // May throw } // We want to ensure that the deserialized object does not contain any // duplicate fields. This is a key invariant: auto const sf = getSortedFields(*this, withAllFields); auto const dup = std::adjacent_find(sf.cbegin(), sf.cend(), [](STBase const* lhs, STBase const* rhs) { return lhs->getFName() == rhs->getFName(); }); if (dup != sf.cend()) Throw("Duplicate field detected"); return reachedEndOfObject; } bool STObject::hasMatchingEntry(STBase const& t) { STBase const* o = peekAtPField(t.getFName()); if (!o) return false; return t == *o; } std::string STObject::getFullText() const { std::string ret; bool first = true; if (getFName().hasName()) { ret = getFName().getName(); ret += " = {"; } else ret = "{"; for (auto const& elem : v_) { if (elem->getSType() != STI_NOTPRESENT) { if (!first) ret += ", "; else first = false; ret += elem->getFullText(); } } ret += "}"; return ret; } std::string STObject::getText() const { std::string ret = "{"; bool first = false; for (auto const& elem : v_) { if (!first) { ret += ", "; first = false; } ret += elem->getText(); } ret += "}"; return ret; } bool STObject::isEquivalent(STBase const& t) const { STObject const* v = dynamic_cast(&t); if (!v) return false; if (mType != nullptr && v->mType == mType) { return std::equal( begin(), end(), v->begin(), v->end(), [](STBase const& st1, STBase const& st2) { return (st1.getSType() == st2.getSType()) && st1.isEquivalent(st2); }); } auto const sf1 = getSortedFields(*this, withAllFields); auto const sf2 = getSortedFields(*v, withAllFields); return std::equal( sf1.begin(), sf1.end(), sf2.begin(), sf2.end(), [](STBase const* st1, STBase const* st2) { return (st1->getSType() == st2->getSType()) && st1->isEquivalent(*st2); }); } uint256 STObject::getHash(HashPrefix prefix) const { Serializer s; s.add32(prefix); add(s, withAllFields); return s.getSHA512Half(); } uint256 STObject::getSigningHash(HashPrefix prefix) const { Serializer s; s.add32(prefix); add(s, omitSigningFields); return s.getSHA512Half(); } int STObject::getFieldIndex(SField const& field) const { if (mType != nullptr) return mType->getIndex(field); int i = 0; for (auto const& elem : v_) { if (elem->getFName() == field) return i; ++i; } return -1; } STBase const& STObject::peekAtField(SField const& field) const { int index = getFieldIndex(field); if (index == -1) throwFieldNotFound(field); return peekAtIndex(index); } STBase& STObject::getField(SField const& field) { int index = getFieldIndex(field); if (index == -1) throwFieldNotFound(field); return getIndex(index); } SField const& STObject::getFieldSType(int index) const { return v_[index]->getFName(); } STBase const* STObject::peekAtPField(SField const& field) const { int index = getFieldIndex(field); if (index == -1) return nullptr; return peekAtPIndex(index); } STBase* STObject::getPField(SField const& field, bool createOkay) { int index = getFieldIndex(field); if (index == -1) { if (createOkay && isFree()) return getPIndex(emplace_back(detail::defaultObject, field)); return nullptr; } return getPIndex(index); } bool STObject::isFieldPresent(SField const& field) const { int index = getFieldIndex(field); if (index == -1) return false; return peekAtIndex(index).getSType() != STI_NOTPRESENT; } STObject& STObject::peekFieldObject(SField const& field) { return peekField(field); } STArray& STObject::peekFieldArray(SField const& field) { return peekField(field); } bool STObject::setFlag(std::uint32_t f) { STUInt32* t = dynamic_cast(getPField(sfFlags, true)); if (!t) return false; t->setValue(t->value() | f); return true; } bool STObject::clearFlag(std::uint32_t f) { STUInt32* t = dynamic_cast(getPField(sfFlags)); if (!t) return false; t->setValue(t->value() & ~f); return true; } bool STObject::isFlag(std::uint32_t f) const { return (getFlags() & f) == f; } std::uint32_t STObject::getFlags(void) const { STUInt32 const* t = dynamic_cast(peekAtPField(sfFlags)); if (!t) return 0; return t->value(); } STBase* STObject::makeFieldPresent(SField const& field) { int index = getFieldIndex(field); if (index == -1) { if (!isFree()) throwFieldNotFound(field); return getPIndex(emplace_back(detail::nonPresentObject, field)); } STBase* f = getPIndex(index); if (f->getSType() != STI_NOTPRESENT) return f; v_[index] = detail::STVar(detail::defaultObject, f->getFName()); return getPIndex(index); } void STObject::makeFieldAbsent(SField const& field) { int index = getFieldIndex(field); if (index == -1) throwFieldNotFound(field); STBase const& f = peekAtIndex(index); if (f.getSType() == STI_NOTPRESENT) return; v_[index] = detail::STVar(detail::nonPresentObject, f.getFName()); } bool STObject::delField(SField const& field) { int index = getFieldIndex(field); if (index == -1) return false; delField(index); return true; } void STObject::delField(int index) { v_.erase(v_.begin() + index); } SOEStyle STObject::getStyle(SField const& field) const { return mType ? mType->style(field) : soeINVALID; } unsigned char STObject::getFieldU8(SField const& field) const { return getFieldByValue(field); } std::uint16_t STObject::getFieldU16(SField const& field) const { return getFieldByValue(field); } std::uint32_t STObject::getFieldU32(SField const& field) const { return getFieldByValue(field); } std::uint64_t STObject::getFieldU64(SField const& field) const { return getFieldByValue(field); } uint128 STObject::getFieldH128(SField const& field) const { return getFieldByValue(field); } uint160 STObject::getFieldH160(SField const& field) const { return getFieldByValue(field); } uint192 STObject::getFieldH192(SField const& field) const { return getFieldByValue(field); } uint256 STObject::getFieldH256(SField const& field) const { return getFieldByValue(field); } std::int32_t STObject::getFieldI32(SField const& field) const { return getFieldByValue(field); } AccountID STObject::getAccountID(SField const& field) const { return getFieldByValue(field); } Blob STObject::getFieldVL(SField const& field) const { STBlob empty; STBlob const& b = getFieldByConstRef(field, empty); return Blob(b.data(), b.data() + b.size()); } STAmount const& STObject::getFieldAmount(SField const& field) const { static STAmount const empty{}; return getFieldByConstRef(field, empty); } STPathSet const& STObject::getFieldPathSet(SField const& field) const { static STPathSet const empty{}; return getFieldByConstRef(field, empty); } STVector256 const& STObject::getFieldV256(SField const& field) const { static STVector256 const empty{}; return getFieldByConstRef(field, empty); } STObject STObject::getFieldObject(SField const& field) const { STObject const empty{field}; auto ret = getFieldByConstRef(field, empty); if (ret != empty) ret.applyTemplateFromSField(field); return ret; } STArray const& STObject::getFieldArray(SField const& field) const { static STArray const empty{}; return getFieldByConstRef(field, empty); } STCurrency const& STObject::getFieldCurrency(SField const& field) const { static STCurrency const empty{}; return getFieldByConstRef(field, empty); } STNumber const& STObject::getFieldNumber(SField const& field) const { static STNumber const empty{}; return getFieldByConstRef(field, empty); } void STObject::set(std::unique_ptr v) { set(std::move(*v.get())); } void STObject::set(STBase&& v) { auto const i = getFieldIndex(v.getFName()); if (i != -1) { v_[i] = std::move(v); } else { if (!isFree()) Throw("missing field in templated STObject"); v_.emplace_back(std::move(v)); } } void STObject::setFieldU8(SField const& field, unsigned char v) { setFieldUsingSetValue(field, v); } void STObject::setFieldU16(SField const& field, std::uint16_t v) { setFieldUsingSetValue(field, v); } void STObject::setFieldU32(SField const& field, std::uint32_t v) { setFieldUsingSetValue(field, v); } void STObject::setFieldU64(SField const& field, std::uint64_t v) { setFieldUsingSetValue(field, v); } void STObject::setFieldH128(SField const& field, uint128 const& v) { setFieldUsingSetValue(field, v); } void STObject::setFieldH256(SField const& field, uint256 const& v) { setFieldUsingSetValue(field, v); } void STObject::setFieldI32(SField const& field, std::int32_t v) { setFieldUsingSetValue(field, v); } void STObject::setFieldV256(SField const& field, STVector256 const& v) { setFieldUsingSetValue(field, v); } void STObject::setAccountID(SField const& field, AccountID const& v) { setFieldUsingSetValue(field, v); } void STObject::setFieldVL(SField const& field, Blob const& v) { setFieldUsingSetValue(field, Buffer(v.data(), v.size())); } void STObject::setFieldVL(SField const& field, Slice const& s) { setFieldUsingSetValue(field, Buffer(s.data(), s.size())); } void STObject::setFieldAmount(SField const& field, STAmount const& v) { setFieldUsingAssignment(field, v); } void STObject::setFieldCurrency(SField const& field, STCurrency const& v) { setFieldUsingAssignment(field, v); } void STObject::setFieldIssue(SField const& field, STIssue const& v) { setFieldUsingAssignment(field, v); } void STObject::setFieldNumber(SField const& field, STNumber const& v) { setFieldUsingAssignment(field, v); } void STObject::setFieldPathSet(SField const& field, STPathSet const& v) { setFieldUsingAssignment(field, v); } void STObject::setFieldArray(SField const& field, STArray const& v) { setFieldUsingAssignment(field, v); } void STObject::setFieldObject(SField const& field, STObject const& v) { setFieldUsingAssignment(field, v); } Json::Value STObject::getJson(JsonOptions options) const { Json::Value ret(Json::objectValue); for (auto const& elem : v_) { if (elem->getSType() != STI_NOTPRESENT) ret[elem->getFName().getJsonName()] = elem->getJson(options); } return ret; } bool STObject::operator==(STObject const& obj) const { // This is not particularly efficient, and only compares data elements // with binary representations int matches = 0; for (auto const& t1 : v_) { if ((t1->getSType() != STI_NOTPRESENT) && t1->getFName().isBinary()) { // each present field must have a matching field bool match = false; for (auto const& t2 : obj.v_) { if (t1->getFName() == t2->getFName()) { if (t2 != t1) return false; match = true; ++matches; break; } } if (!match) return false; } } int fields = 0; for (auto const& t2 : obj.v_) { if ((t2->getSType() != STI_NOTPRESENT) && t2->getFName().isBinary()) ++fields; } if (fields != matches) return false; return true; } void STObject::add(Serializer& s, WhichFields whichFields) const { // Depending on whichFields, signing fields are either serialized or // not. Then fields are added to the Serializer sorted by fieldCode. std::vector const fields{getSortedFields(*this, whichFields)}; // insert sorted for (STBase const* const field : fields) { // When we serialize an object inside another object, // the type associated by rule with this field name // must be OBJECT, or the object cannot be deserialized SerializedTypeID const sType{field->getSType()}; XRPL_ASSERT( (sType != STI_OBJECT) || (field->getFName().fieldType == STI_OBJECT), "xrpl::STObject::add : valid field type"); XRPL_ASSERT_PARTS( getStyle(field->getFName()) != soeDEFAULT || !field->isDefault(), "xrpl::STObject::add", "non-default value"); field->addFieldID(s); field->add(s); if (sType == STI_ARRAY || sType == STI_OBJECT) s.addFieldID(sType, 1); } } std::vector STObject::getSortedFields(STObject const& objToSort, WhichFields whichFields) { std::vector sf; sf.reserve(objToSort.getCount()); // Choose the fields that we need to sort. for (detail::STVar const& elem : objToSort.v_) { STBase const& base = elem.get(); if ((base.getSType() != STI_NOTPRESENT) && base.getFName().shouldInclude(whichFields)) { sf.push_back(&base); } } // Sort the fields by fieldCode. std::sort(sf.begin(), sf.end(), [](STBase const* lhs, STBase const* rhs) { return lhs->getFName().fieldCode < rhs->getFName().fieldCode; }); return sf; } } // namespace xrpl