mirror of
https://github.com/XRPLF/rippled.git
synced 2026-09-27 15:28:03 +00:00
406 lines
13 KiB
C++
406 lines
13 KiB
C++
#include <xrpl/basics/base_uint.h>
|
|
#include <xrpl/basics/contract.h>
|
|
#include <xrpl/beast/utility/instrumentation.h>
|
|
#include <xrpl/protocol/Asset.h>
|
|
#include <xrpl/protocol/Indexes.h>
|
|
#include <xrpl/protocol/MPTIssue.h>
|
|
#include <xrpl/protocol/Protocol.h>
|
|
#include <xrpl/protocol/SField.h>
|
|
#include <xrpl/protocol/STBase.h>
|
|
#include <xrpl/protocol/STBitString.h>
|
|
#include <xrpl/protocol/STObject.h>
|
|
#include <xrpl/protocol/Serializer.h>
|
|
#include <xrpl/tx/wasm/HostFuncImpl.h>
|
|
#include <xrpl/tx/wasm/WasmCommon.h>
|
|
|
|
#include <cstdint>
|
|
#include <expected>
|
|
#include <stdexcept>
|
|
#include <utility>
|
|
#include <variant>
|
|
|
|
namespace xrpl {
|
|
|
|
using FieldValue = std::variant<STBase const*, uint256 const*>;
|
|
|
|
template <class T>
|
|
Bytes
|
|
getIntBytes(STBase const* obj)
|
|
{
|
|
static_assert(std::is_integral_v<T>, "Only integral types");
|
|
XRPL_ASSERT(obj, "getIntBytes null pointer");
|
|
|
|
auto const* num(static_cast<STInteger<T> const*>(obj)); // NOLINT
|
|
T const data = adjustWasmEndianess(num->value());
|
|
auto const* b = reinterpret_cast<uint8_t const*>(&data);
|
|
return Bytes{b, b + sizeof(T)};
|
|
}
|
|
|
|
static std::expected<Bytes, HostFunctionError>
|
|
getAnyFieldData(STBase const* obj)
|
|
{
|
|
if (obj == nullptr)
|
|
return std::unexpected(HostFunctionError::FieldNotFound);
|
|
|
|
auto const stype = obj->getSType();
|
|
switch (stype)
|
|
{
|
|
// LCOV_EXCL_START
|
|
case STI_UNKNOWN:
|
|
case STI_NOTPRESENT:
|
|
return std::unexpected(HostFunctionError::FieldNotFound);
|
|
// LCOV_EXCL_STOP
|
|
|
|
case STI_OBJECT:
|
|
case STI_ARRAY:
|
|
case STI_VECTOR256:
|
|
return std::unexpected(HostFunctionError::NotLeafField);
|
|
|
|
case STI_ACCOUNT: {
|
|
auto const* account(static_cast<STAccount const*>(obj)); // NOLINT
|
|
auto const& data = account->value();
|
|
return Bytes{data.begin(), data.end()};
|
|
}
|
|
|
|
case STI_ISSUE: {
|
|
auto const* issue(static_cast<STIssue const*>(obj)); // NOLINT
|
|
Asset const& asset(issue->value());
|
|
// XRP and IOU will be processed by serializer
|
|
if (asset.holds<MPTIssue>())
|
|
{
|
|
auto const& mptIssue = asset.get<MPTIssue>();
|
|
auto const& mptID = mptIssue.getMptID();
|
|
return Bytes{mptID.cbegin(), mptID.cend()};
|
|
}
|
|
break; // Use serializer
|
|
}
|
|
|
|
case STI_VL: {
|
|
auto const* vl(static_cast<STBlob const*>(obj)); // NOLINT
|
|
auto const& data = vl->value();
|
|
if (data.size() > kMaxWasmDataLength)
|
|
{
|
|
return std::unexpected{HostFunctionError::DataFieldTooLarge};
|
|
}
|
|
return Bytes{data.begin(), data.end()};
|
|
}
|
|
|
|
case STI_UINT16:
|
|
return getIntBytes<std::uint16_t>(obj);
|
|
|
|
case STI_UINT32:
|
|
return getIntBytes<std::uint32_t>(obj);
|
|
|
|
// LCOV_EXCL_START
|
|
case STI_UINT64:
|
|
return getIntBytes<std::uint64_t>(obj);
|
|
|
|
case STI_INT32:
|
|
return getIntBytes<std::int32_t>(obj);
|
|
|
|
case STI_INT64:
|
|
return getIntBytes<std::int64_t>(obj);
|
|
// LCOV_EXCL_STOP
|
|
|
|
case STI_UINT256: {
|
|
auto const* uint256Obj(static_cast<STUInt256 const*>(obj)); // NOLINT
|
|
auto const& data = uint256Obj->value();
|
|
return Bytes{data.begin(), data.end()};
|
|
}
|
|
|
|
case STI_AMOUNT:
|
|
case STI_NUMBER:
|
|
default:
|
|
break; // Use serializer
|
|
}
|
|
|
|
Serializer msg;
|
|
obj->add(msg);
|
|
return msg.getData();
|
|
}
|
|
|
|
static std::expected<Bytes, HostFunctionError>
|
|
getAnyFieldData(FieldValue const& variantObj)
|
|
{
|
|
if (STBase const* const* obj = std::get_if<STBase const*>(&variantObj))
|
|
return getAnyFieldData(*obj);
|
|
|
|
if (uint256 const* const* u = std::get_if<uint256 const*>(&variantObj))
|
|
return Bytes((*u)->begin(), (*u)->end());
|
|
|
|
// Unreachable: the variant only holds the two alternatives above. If not, it is an
|
|
// xrpld bug, and `guarded` turns the throw into `InternalFatal`, which stops the run ->
|
|
// tecINTERNAL.
|
|
Throw<std::runtime_error>("field value variant holds neither alternative"); // LCOV_EXCL_LINE
|
|
}
|
|
|
|
static inline bool
|
|
noField(STBase const* field)
|
|
{
|
|
return (field == nullptr) || (STI_NOTPRESENT == field->getSType()) ||
|
|
(STI_UNKNOWN == field->getSType());
|
|
}
|
|
|
|
static std::expected<FieldValue, HostFunctionError>
|
|
locateField(STObject const& obj, FieldLocator const& locator)
|
|
{
|
|
STBase const* field = nullptr;
|
|
auto const& knownSFields = SField::getKnownCodeToField();
|
|
|
|
{
|
|
int32_t const sfieldCode = locator[0];
|
|
auto const it = knownSFields.find(sfieldCode);
|
|
if (it == knownSFields.end())
|
|
return std::unexpected(HostFunctionError::InvalidField);
|
|
|
|
auto const& fname(*it->second);
|
|
field = obj.peekAtPField(fname);
|
|
if (noField(field))
|
|
return std::unexpected(HostFunctionError::FieldNotFound);
|
|
}
|
|
|
|
for (unsigned i = 1; i < locator.size(); ++i)
|
|
{
|
|
int32_t const sfieldCode = locator[i];
|
|
|
|
if (STI_ARRAY == field->getSType())
|
|
{
|
|
auto const* arr = static_cast<STArray const*>(field); // NOLINT
|
|
if (sfieldCode < 0 || std::cmp_greater_equal(sfieldCode, arr->size()))
|
|
return std::unexpected(HostFunctionError::IndexOutOfBounds);
|
|
field = &(arr->operator[](sfieldCode));
|
|
}
|
|
else if (STI_OBJECT == field->getSType())
|
|
{
|
|
auto const* o = static_cast<STObject const*>(field); // NOLINT
|
|
|
|
auto const it = knownSFields.find(sfieldCode);
|
|
if (it == knownSFields.end())
|
|
return std::unexpected(HostFunctionError::InvalidField);
|
|
|
|
auto const& fname(*it->second);
|
|
field = o->peekAtPField(fname);
|
|
}
|
|
else if (STI_VECTOR256 == field->getSType())
|
|
{
|
|
auto const* v = static_cast<STVector256 const*>(field); // NOLINT
|
|
if (sfieldCode < 0 || std::cmp_greater_equal(sfieldCode, v->size()))
|
|
return std::unexpected(HostFunctionError::IndexOutOfBounds);
|
|
return FieldValue(&(v->operator[](sfieldCode)));
|
|
}
|
|
else // simple field must be the last one
|
|
{
|
|
return std::unexpected(HostFunctionError::LocatorMalformed);
|
|
}
|
|
|
|
if (noField(field))
|
|
return std::unexpected(HostFunctionError::FieldNotFound);
|
|
}
|
|
|
|
return FieldValue(field);
|
|
}
|
|
|
|
static inline std::expected<int32_t, HostFunctionError>
|
|
getArrayLen(FieldValue const& variantField)
|
|
{
|
|
if (STBase const* const* field = std::get_if<STBase const*>(&variantField))
|
|
{
|
|
if ((*field)->getSType() == STI_VECTOR256)
|
|
return static_cast<STVector256 const*>(*field)->size(); // NOLINT
|
|
if ((*field)->getSType() == STI_ARRAY)
|
|
return static_cast<STArray const*>(*field)->size(); // NOLINT
|
|
}
|
|
// uint256 is not an array so that variant should still return NO_ARRAY
|
|
|
|
return std::unexpected(HostFunctionError::NoArray); // LCOV_EXCL_LINE
|
|
}
|
|
|
|
std::expected<int32_t, HostFunctionError>
|
|
WasmHostFunctionsImpl::cacheLedgerObj(uint256 const& objId, int32_t cacheIdx)
|
|
{
|
|
auto const& keylet = keylet::unchecked(objId);
|
|
if (cacheIdx < 0 || cacheIdx > maxCache)
|
|
return std::unexpected(HostFunctionError::SlotOutRange);
|
|
|
|
if (cacheIdx == 0)
|
|
{
|
|
for (cacheIdx = 0; cacheIdx < maxCache; ++cacheIdx)
|
|
{
|
|
if (!cache_[cacheIdx])
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cacheIdx--; // convert to 0-based index
|
|
}
|
|
|
|
if (cacheIdx >= maxCache)
|
|
return std::unexpected(HostFunctionError::SlotsFull);
|
|
|
|
cache_[cacheIdx] = ctx_.view().read(keylet);
|
|
if (!cache_[cacheIdx])
|
|
return std::unexpected(HostFunctionError::LedgerObjNotFound);
|
|
return cacheIdx + 1; // return 1-based index
|
|
}
|
|
|
|
// Subsection: top level getters
|
|
|
|
std::expected<Bytes, HostFunctionError>
|
|
WasmHostFunctionsImpl::getTxField(SField const& fname) const
|
|
{
|
|
return getAnyFieldData(ctx_.tx.peekAtPField(fname));
|
|
}
|
|
|
|
std::expected<Bytes, HostFunctionError>
|
|
WasmHostFunctionsImpl::getCurrentLedgerObjField(SField const& fname) const
|
|
{
|
|
auto const sle = getCurrentLedgerObj();
|
|
if (!sle.has_value())
|
|
return std::unexpected(sle.error());
|
|
return getAnyFieldData(sle.value()->peekAtPField(fname));
|
|
}
|
|
|
|
std::expected<Bytes, HostFunctionError>
|
|
WasmHostFunctionsImpl::getLedgerObjField(int32_t cacheIdx, SField const& fname) const
|
|
{
|
|
auto const normalizedIdx = normalizeCacheIndex(cacheIdx);
|
|
if (!normalizedIdx.has_value())
|
|
return std::unexpected(normalizedIdx.error());
|
|
return getAnyFieldData(cache_[normalizedIdx.value()]->peekAtPField(fname));
|
|
}
|
|
|
|
// Subsection: nested getters
|
|
|
|
std::expected<Bytes, HostFunctionError>
|
|
WasmHostFunctionsImpl::getTxNestedField(FieldLocator const& locator) const
|
|
{
|
|
auto const r = locateField(ctx_.tx, locator);
|
|
if (!r)
|
|
return std::unexpected(r.error());
|
|
|
|
return getAnyFieldData(r.value());
|
|
}
|
|
|
|
std::expected<Bytes, HostFunctionError>
|
|
WasmHostFunctionsImpl::getCurrentLedgerObjNestedField(FieldLocator const& locator) const
|
|
{
|
|
auto const sle = getCurrentLedgerObj();
|
|
if (!sle.has_value())
|
|
return std::unexpected(sle.error());
|
|
|
|
auto const r = locateField(*sle.value(), locator);
|
|
if (!r)
|
|
return std::unexpected(r.error());
|
|
|
|
return getAnyFieldData(r.value());
|
|
}
|
|
|
|
std::expected<Bytes, HostFunctionError>
|
|
WasmHostFunctionsImpl::getLedgerObjNestedField(int32_t cacheIdx, FieldLocator const& locator) const
|
|
{
|
|
auto const normalizedIdx = normalizeCacheIndex(cacheIdx);
|
|
if (!normalizedIdx.has_value())
|
|
return std::unexpected(normalizedIdx.error());
|
|
|
|
auto const r = locateField(*cache_[normalizedIdx.value()], locator);
|
|
if (!r)
|
|
return std::unexpected(r.error());
|
|
|
|
return getAnyFieldData(r.value());
|
|
}
|
|
|
|
// Subsection: array length getters
|
|
|
|
std::expected<int32_t, HostFunctionError>
|
|
WasmHostFunctionsImpl::getTxArrayLen(SField const& fname) const
|
|
{
|
|
if (fname.fieldType != STI_ARRAY && fname.fieldType != STI_VECTOR256)
|
|
return std::unexpected(HostFunctionError::NoArray);
|
|
|
|
auto const* field = ctx_.tx.peekAtPField(fname);
|
|
if (noField(field))
|
|
return std::unexpected(HostFunctionError::FieldNotFound);
|
|
|
|
return getArrayLen(field);
|
|
}
|
|
|
|
std::expected<int32_t, HostFunctionError>
|
|
WasmHostFunctionsImpl::getCurrentLedgerObjArrayLen(SField const& fname) const
|
|
{
|
|
if (fname.fieldType != STI_ARRAY && fname.fieldType != STI_VECTOR256)
|
|
return std::unexpected(HostFunctionError::NoArray);
|
|
|
|
auto const sle = getCurrentLedgerObj();
|
|
if (!sle.has_value())
|
|
return std::unexpected(sle.error());
|
|
|
|
auto const* field = sle.value()->peekAtPField(fname);
|
|
if (noField(field))
|
|
return std::unexpected(HostFunctionError::FieldNotFound);
|
|
|
|
return getArrayLen(field);
|
|
}
|
|
|
|
std::expected<int32_t, HostFunctionError>
|
|
WasmHostFunctionsImpl::getLedgerObjArrayLen(int32_t cacheIdx, SField const& fname) const
|
|
{
|
|
if (fname.fieldType != STI_ARRAY && fname.fieldType != STI_VECTOR256)
|
|
return std::unexpected(HostFunctionError::NoArray);
|
|
|
|
auto const normalizedIdx = normalizeCacheIndex(cacheIdx);
|
|
if (!normalizedIdx.has_value())
|
|
return std::unexpected(normalizedIdx.error());
|
|
|
|
auto const* field = cache_[normalizedIdx.value()]->peekAtPField(fname);
|
|
if (noField(field))
|
|
return std::unexpected(HostFunctionError::FieldNotFound);
|
|
|
|
return getArrayLen(field);
|
|
}
|
|
|
|
// Subsection: nested array length getters
|
|
|
|
std::expected<int32_t, HostFunctionError>
|
|
WasmHostFunctionsImpl::getTxNestedArrayLen(FieldLocator const& locator) const
|
|
{
|
|
auto const r = locateField(ctx_.tx, locator);
|
|
if (!r)
|
|
return std::unexpected(r.error());
|
|
|
|
auto const& field = r.value();
|
|
return getArrayLen(field);
|
|
}
|
|
|
|
std::expected<int32_t, HostFunctionError>
|
|
WasmHostFunctionsImpl::getCurrentLedgerObjNestedArrayLen(FieldLocator const& locator) const
|
|
{
|
|
auto const sle = getCurrentLedgerObj();
|
|
if (!sle.has_value())
|
|
return std::unexpected(sle.error());
|
|
auto const r = locateField(*sle.value(), locator);
|
|
if (!r)
|
|
return std::unexpected(r.error());
|
|
|
|
auto const& field = r.value();
|
|
return getArrayLen(field);
|
|
}
|
|
|
|
std::expected<int32_t, HostFunctionError>
|
|
WasmHostFunctionsImpl::getLedgerObjNestedArrayLen(int32_t cacheIdx, FieldLocator const& locator)
|
|
const
|
|
{
|
|
auto const normalizedIdx = normalizeCacheIndex(cacheIdx);
|
|
if (!normalizedIdx.has_value())
|
|
return std::unexpected(normalizedIdx.error());
|
|
|
|
auto const r = locateField(*cache_[normalizedIdx.value()], locator);
|
|
if (!r)
|
|
return std::unexpected(r.error());
|
|
|
|
auto const& field = r.value();
|
|
return getArrayLen(field);
|
|
}
|
|
|
|
} // namespace xrpl
|