mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-02 08:55:53 +00:00
feat: return an int instead of boolean from finish, display in metadata (#5641)
* create STInt32 and STInt64, use it for sfWasmReturnCode in metadata * get it actually working * add tests * update comment * change type * respond to comments
This commit is contained in:
@@ -71,8 +71,10 @@ class STCurrency;
|
||||
STYPE(STI_VL, 7) \
|
||||
STYPE(STI_ACCOUNT, 8) \
|
||||
STYPE(STI_NUMBER, 9) \
|
||||
STYPE(STI_INT32, 10) \
|
||||
STYPE(STI_INT64, 11) \
|
||||
\
|
||||
/* 10-13 are reserved */ \
|
||||
/* 12-13 are reserved */ \
|
||||
STYPE(STI_OBJECT, 14) \
|
||||
STYPE(STI_ARRAY, 15) \
|
||||
\
|
||||
@@ -352,6 +354,9 @@ using SF_UINT256 = TypedField<STBitString<256>>;
|
||||
using SF_UINT384 = TypedField<STBitString<384>>;
|
||||
using SF_UINT512 = TypedField<STBitString<512>>;
|
||||
|
||||
using SF_INT32 = TypedField<STInteger<std::int32_t>>;
|
||||
using SF_INT64 = TypedField<STInteger<std::int64_t>>;
|
||||
|
||||
using SF_ACCOUNT = TypedField<STAccount>;
|
||||
using SF_AMOUNT = TypedField<STAmount>;
|
||||
using SF_ISSUE = TypedField<STIssue>;
|
||||
|
||||
@@ -81,6 +81,9 @@ using STUInt16 = STInteger<std::uint16_t>;
|
||||
using STUInt32 = STInteger<std::uint32_t>;
|
||||
using STUInt64 = STInteger<std::uint64_t>;
|
||||
|
||||
using STInt32 = STInteger<std::int32_t>;
|
||||
using STInt64 = STInteger<std::int64_t>;
|
||||
|
||||
template <typename Integer>
|
||||
inline STInteger<Integer>::STInteger(Integer v) : value_(v)
|
||||
{
|
||||
|
||||
@@ -231,6 +231,10 @@ public:
|
||||
getFieldH192(SField const& field) const;
|
||||
uint256
|
||||
getFieldH256(SField const& field) const;
|
||||
std::int32_t
|
||||
getFieldI32(SField const& field) const;
|
||||
std::int64_t
|
||||
getFieldI64(SField const& field) const;
|
||||
AccountID
|
||||
getAccountID(SField const& field) const;
|
||||
|
||||
@@ -365,6 +369,10 @@ public:
|
||||
void
|
||||
setFieldH256(SField const& field, uint256 const&);
|
||||
void
|
||||
setFieldI32(SField const& field, std::int32_t);
|
||||
void
|
||||
setFieldI64(SField const& field, std::int64_t);
|
||||
void
|
||||
setFieldVL(SField const& field, Blob const&);
|
||||
void
|
||||
setFieldVL(SField const& field, Slice const&);
|
||||
|
||||
@@ -161,7 +161,8 @@ public:
|
||||
getGasUsed() const
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
hasGasUsed(), "ripple::TxMeta::getGasUsed : non-null batch id");
|
||||
hasGasUsed(),
|
||||
"ripple::TxMeta::getGasUsed : non-null gas used field");
|
||||
return *gasUsed_;
|
||||
}
|
||||
|
||||
@@ -171,6 +172,27 @@ public:
|
||||
return static_cast<bool>(gasUsed_);
|
||||
}
|
||||
|
||||
void
|
||||
setWasmReturnCode(std::int32_t const& wasmReturnCode)
|
||||
{
|
||||
wasmReturnCode_ = wasmReturnCode;
|
||||
}
|
||||
|
||||
std::int32_t
|
||||
getWasmReturnCode() const
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
hasWasmReturnCode(),
|
||||
"ripple::TxMeta::getWasmReturnCode : non-null wasm return code");
|
||||
return *wasmReturnCode_;
|
||||
}
|
||||
|
||||
bool
|
||||
hasWasmReturnCode() const
|
||||
{
|
||||
return static_cast<bool>(wasmReturnCode_);
|
||||
}
|
||||
|
||||
private:
|
||||
uint256 mTransactionID;
|
||||
std::uint32_t mLedger;
|
||||
@@ -178,8 +200,9 @@ private:
|
||||
int mResult;
|
||||
|
||||
std::optional<STAmount> mDelivered;
|
||||
std::optional<std::uint32_t> gasUsed_;
|
||||
std::optional<uint256> parentBatchId_;
|
||||
std::optional<std::uint32_t> gasUsed_;
|
||||
std::optional<std::int32_t> wasmReturnCode_;
|
||||
|
||||
STArray mNodes;
|
||||
};
|
||||
|
||||
@@ -210,6 +210,9 @@ TYPED_SFIELD(sfAssetsMaximum, NUMBER, 3)
|
||||
TYPED_SFIELD(sfAssetsTotal, NUMBER, 4)
|
||||
TYPED_SFIELD(sfLossUnrealized, NUMBER, 5)
|
||||
|
||||
// 32-bit signed (common)
|
||||
TYPED_SFIELD(sfWasmReturnCode, INT32, 1)
|
||||
|
||||
// currency amount (common)
|
||||
TYPED_SFIELD(sfAmount, AMOUNT, 1)
|
||||
TYPED_SFIELD(sfBalance, AMOUNT, 2)
|
||||
|
||||
@@ -251,4 +251,82 @@ STUInt64::getJson(JsonOptions) const
|
||||
return convertToString(value_, 16); // Convert to base 16
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <>
|
||||
STInteger<std::int32_t>::STInteger(SerialIter& sit, SField const& name)
|
||||
: STInteger(name, sit.get32())
|
||||
{
|
||||
}
|
||||
|
||||
template <>
|
||||
SerializedTypeID
|
||||
STInt32::getSType() const
|
||||
{
|
||||
return STI_INT32;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string
|
||||
STInt32::getText() const
|
||||
{
|
||||
return std::to_string(value_);
|
||||
}
|
||||
|
||||
template <>
|
||||
Json::Value
|
||||
STInt32::getJson(JsonOptions) const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template <>
|
||||
STInteger<std::int64_t>::STInteger(SerialIter& sit, SField const& name)
|
||||
: STInteger(name, sit.get64())
|
||||
{
|
||||
}
|
||||
|
||||
template <>
|
||||
SerializedTypeID
|
||||
STInt64::getSType() const
|
||||
{
|
||||
return STI_INT64;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string
|
||||
STInt64::getText() const
|
||||
{
|
||||
return std::to_string(value_);
|
||||
}
|
||||
|
||||
template <>
|
||||
Json::Value
|
||||
STInt64::getJson(JsonOptions) const
|
||||
{
|
||||
auto convertToString = [](int64_t const value, int const base) {
|
||||
XRPL_ASSERT(
|
||||
base == 10 || base == 16,
|
||||
"ripple::STInt64::getJson : base 10 or 16");
|
||||
std::string str(
|
||||
base == 10 ? 20 : 16, 0); // Allocate space depending on base
|
||||
auto ret =
|
||||
std::to_chars(str.data(), str.data() + str.size(), value, base);
|
||||
XRPL_ASSERT(
|
||||
ret.ec == std::errc(),
|
||||
"ripple::STInt64::getJson : to_chars succeeded");
|
||||
str.resize(std::distance(str.data(), ret.ptr));
|
||||
return str;
|
||||
};
|
||||
|
||||
if (auto const& fName = getFName(); fName.shouldMeta(SField::sMD_BaseTen))
|
||||
{
|
||||
return convertToString(value_, 10); // Convert to base 10
|
||||
}
|
||||
|
||||
return convertToString(value_, 16); // Convert to base 16
|
||||
}
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -647,6 +647,18 @@ STObject::getFieldH256(SField const& field) const
|
||||
return getFieldByValue<STUInt256>(field);
|
||||
}
|
||||
|
||||
std::int32_t
|
||||
STObject::getFieldI32(SField const& field) const
|
||||
{
|
||||
return getFieldByValue<STInt32>(field);
|
||||
}
|
||||
|
||||
std::int64_t
|
||||
STObject::getFieldI64(SField const& field) const
|
||||
{
|
||||
return getFieldByValue<STInt64>(field);
|
||||
}
|
||||
|
||||
AccountID
|
||||
STObject::getAccountID(SField const& field) const
|
||||
{
|
||||
@@ -761,6 +773,18 @@ STObject::setFieldH256(SField const& field, uint256 const& v)
|
||||
setFieldUsingSetValue<STUInt256>(field, v);
|
||||
}
|
||||
|
||||
void
|
||||
STObject::setFieldI32(SField const& field, std::int32_t v)
|
||||
{
|
||||
setFieldUsingSetValue<STInt32>(field, v);
|
||||
}
|
||||
|
||||
void
|
||||
STObject::setFieldI64(SField const& field, std::int64_t v)
|
||||
{
|
||||
setFieldUsingSetValue<STInt64>(field, v);
|
||||
}
|
||||
|
||||
void
|
||||
STObject::setFieldV256(SField const& field, STVector256 const& v)
|
||||
{
|
||||
|
||||
@@ -501,30 +501,6 @@ parseLeaf(
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_UINT192: {
|
||||
if (!value.isString())
|
||||
{
|
||||
error = bad_type(json_name, fieldName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint192 num;
|
||||
|
||||
if (auto const s = value.asString(); !num.parseHex(s))
|
||||
{
|
||||
if (!s.empty())
|
||||
{
|
||||
error = invalid_data(json_name, fieldName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
num.zero();
|
||||
}
|
||||
|
||||
ret = detail::make_stvar<STUInt192>(field, num);
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_UINT160: {
|
||||
if (!value.isString())
|
||||
{
|
||||
@@ -549,6 +525,30 @@ parseLeaf(
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_UINT192: {
|
||||
if (!value.isString())
|
||||
{
|
||||
error = bad_type(json_name, fieldName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint192 num;
|
||||
|
||||
if (auto const s = value.asString(); !num.parseHex(s))
|
||||
{
|
||||
if (!s.empty())
|
||||
{
|
||||
error = invalid_data(json_name, fieldName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
num.zero();
|
||||
}
|
||||
|
||||
ret = detail::make_stvar<STUInt192>(field, num);
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_UINT256: {
|
||||
if (!value.isString())
|
||||
{
|
||||
@@ -573,6 +573,46 @@ parseLeaf(
|
||||
break;
|
||||
}
|
||||
|
||||
case STI_INT32:
|
||||
try
|
||||
{
|
||||
if (value.isString())
|
||||
{
|
||||
ret = detail::make_stvar<STInt32>(
|
||||
field,
|
||||
beast::lexicalCastThrow<std::int32_t>(
|
||||
value.asString()));
|
||||
}
|
||||
else if (value.isInt())
|
||||
{
|
||||
ret = detail::make_stvar<STInt32>(field, value.asInt());
|
||||
}
|
||||
else if (value.isUInt())
|
||||
{
|
||||
if (value.asUInt() >
|
||||
static_cast<std::uint32_t>(
|
||||
std::numeric_limits<std::int32_t>::max()))
|
||||
{
|
||||
error = out_of_range(json_name, fieldName);
|
||||
return ret;
|
||||
}
|
||||
ret = detail::make_stvar<STInt32>(
|
||||
field, safe_cast<std::int32_t>(value.asInt()));
|
||||
}
|
||||
else
|
||||
{
|
||||
error = bad_type(json_name, fieldName);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
error = invalid_data(json_name, fieldName);
|
||||
return ret;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case STI_VL:
|
||||
if (!value.isString())
|
||||
{
|
||||
|
||||
@@ -208,6 +208,12 @@ STVar::constructST(SerializedTypeID id, int depth, Args&&... args)
|
||||
case STI_UINT256:
|
||||
construct<STUInt256>(std::forward<Args>(args)...);
|
||||
return;
|
||||
case STI_INT32:
|
||||
construct<STInt32>(std::forward<Args>(args)...);
|
||||
return;
|
||||
case STI_INT64:
|
||||
construct<STInt64>(std::forward<Args>(args)...);
|
||||
return;
|
||||
case STI_VECTOR256:
|
||||
construct<STVector256>(std::forward<Args>(args)...);
|
||||
return;
|
||||
|
||||
@@ -83,6 +83,18 @@ Serializer::addInteger(std::uint64_t i)
|
||||
{
|
||||
return add64(i);
|
||||
}
|
||||
template <>
|
||||
int
|
||||
Serializer::addInteger(std::int32_t i)
|
||||
{
|
||||
return add32(i);
|
||||
}
|
||||
template <>
|
||||
int
|
||||
Serializer::addInteger(std::int64_t i)
|
||||
{
|
||||
return add64(i);
|
||||
}
|
||||
|
||||
int
|
||||
Serializer::addRaw(Blob const& vector)
|
||||
|
||||
@@ -60,6 +60,8 @@ TxMeta::TxMeta(
|
||||
setParentBatchId(obj.getFieldH256(sfParentBatchID));
|
||||
if (obj.isFieldPresent(sfGasUsed))
|
||||
setGasUsed(obj.getFieldU32(sfGasUsed));
|
||||
if (obj.isFieldPresent(sfWasmReturnCode))
|
||||
setWasmReturnCode(obj.getFieldI32(sfWasmReturnCode));
|
||||
}
|
||||
|
||||
TxMeta::TxMeta(uint256 const& txid, std::uint32_t ledger, STObject const& obj)
|
||||
@@ -86,6 +88,9 @@ TxMeta::TxMeta(uint256 const& txid, std::uint32_t ledger, STObject const& obj)
|
||||
|
||||
if (obj.isFieldPresent(sfGasUsed))
|
||||
setGasUsed(obj.getFieldU32(sfGasUsed));
|
||||
|
||||
if (obj.isFieldPresent(sfWasmReturnCode))
|
||||
setWasmReturnCode(obj.getFieldI32(sfWasmReturnCode));
|
||||
}
|
||||
|
||||
TxMeta::TxMeta(uint256 const& txid, std::uint32_t ledger, Blob const& vec)
|
||||
@@ -257,6 +262,8 @@ TxMeta::getAsObject() const
|
||||
metaData.setFieldH256(sfParentBatchID, getParentBatchId());
|
||||
if (hasGasUsed())
|
||||
metaData.setFieldU32(sfGasUsed, getGasUsed());
|
||||
if (hasWasmReturnCode())
|
||||
metaData.setFieldI32(sfWasmReturnCode, getWasmReturnCode());
|
||||
|
||||
return metaData;
|
||||
}
|
||||
|
||||
@@ -2096,6 +2096,8 @@ struct Escrow_test : public beast::unit_test::suite
|
||||
auto const txMeta = env.meta();
|
||||
if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed)))
|
||||
BEAST_EXPECT(txMeta->getFieldU32(sfGasUsed) == allowance);
|
||||
if (BEAST_EXPECT(txMeta->isFieldPresent(sfWasmReturnCode)))
|
||||
BEAST_EXPECT(txMeta->getFieldI32(sfWasmReturnCode) == 1);
|
||||
|
||||
BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0);
|
||||
}
|
||||
@@ -2164,6 +2166,8 @@ struct Escrow_test : public beast::unit_test::suite
|
||||
auto const txMeta = env.meta();
|
||||
if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed)))
|
||||
BEAST_EXPECT(txMeta->getFieldU32(sfGasUsed) == allowance);
|
||||
if (BEAST_EXPECT(txMeta->isFieldPresent(sfWasmReturnCode)))
|
||||
BEAST_EXPECT(txMeta->getFieldI32(sfWasmReturnCode) == 1);
|
||||
|
||||
env.close();
|
||||
BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0);
|
||||
@@ -2213,6 +2217,8 @@ struct Escrow_test : public beast::unit_test::suite
|
||||
auto const txMeta = env.meta();
|
||||
if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed)))
|
||||
BEAST_EXPECT(txMeta->getFieldU32(sfGasUsed) == allowance);
|
||||
if (BEAST_EXPECT(txMeta->isFieldPresent(sfWasmReturnCode)))
|
||||
BEAST_EXPECT(txMeta->getFieldI32(sfWasmReturnCode) == 1);
|
||||
|
||||
BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0);
|
||||
}
|
||||
@@ -2264,6 +2270,8 @@ struct Escrow_test : public beast::unit_test::suite
|
||||
auto const txMeta = env.meta();
|
||||
if (BEAST_EXPECT(txMeta->isFieldPresent(sfGasUsed)))
|
||||
BEAST_EXPECT(txMeta->getFieldU32(sfGasUsed) == allowance);
|
||||
if (BEAST_EXPECT(txMeta->isFieldPresent(sfWasmReturnCode)))
|
||||
BEAST_EXPECT(txMeta->getFieldI32(sfWasmReturnCode) == 1);
|
||||
|
||||
env.close();
|
||||
BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0);
|
||||
@@ -2330,6 +2338,8 @@ struct Escrow_test : public beast::unit_test::suite
|
||||
auto const txMeta = env.meta();
|
||||
if (BEAST_EXPECT(txMeta && txMeta->isFieldPresent(sfGasUsed)))
|
||||
BEAST_EXPECT(txMeta->getFieldU32(sfGasUsed) == 39'596);
|
||||
if (BEAST_EXPECT(txMeta->isFieldPresent(sfWasmReturnCode)))
|
||||
BEAST_EXPECT(txMeta->getFieldI32(sfWasmReturnCode) == 1);
|
||||
|
||||
env.close();
|
||||
BEAST_EXPECT((*env.le(alice))[sfOwnerCount] == 0);
|
||||
|
||||
@@ -48,7 +48,7 @@ struct WasmResult
|
||||
T result;
|
||||
int64_t cost;
|
||||
};
|
||||
typedef WasmResult<bool> EscrowResult;
|
||||
typedef WasmResult<int32_t> EscrowResult;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -59,6 +59,10 @@ ApplyContext::discard()
|
||||
std::optional<TxMeta>
|
||||
ApplyContext::apply(TER ter)
|
||||
{
|
||||
if (wasmReturnCode_.has_value())
|
||||
{
|
||||
view_->setWasmReturnCode(*wasmReturnCode_);
|
||||
}
|
||||
view_->setGasUsed(gasUsed_);
|
||||
return view_->apply(
|
||||
base_, tx, ter, parentBatchId_, flags_ & tapDRY_RUN, journal);
|
||||
|
||||
@@ -113,6 +113,13 @@ public:
|
||||
gasUsed_ = gasUsed;
|
||||
}
|
||||
|
||||
/** Sets the gas used in the metadata */
|
||||
void
|
||||
setWasmReturnCode(std::int32_t const wasmReturnCode)
|
||||
{
|
||||
wasmReturnCode_ = wasmReturnCode;
|
||||
}
|
||||
|
||||
/** Discard changes and start fresh. */
|
||||
void
|
||||
discard();
|
||||
@@ -165,6 +172,7 @@ private:
|
||||
// The ID of the batch transaction we are executing under, if seated.
|
||||
std::optional<uint256 const> parentBatchId_;
|
||||
std::optional<std::uint32_t> gasUsed_;
|
||||
std::optional<std::int32_t> wasmReturnCode_;
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -1282,13 +1282,13 @@ EscrowFinish::doApply()
|
||||
if (re.has_value())
|
||||
{
|
||||
auto reValue = re.value().result;
|
||||
ctx_.setWasmReturnCode(reValue);
|
||||
// TODO: better error handling for this conversion
|
||||
ctx_.setGasUsed(static_cast<uint32_t>(re.value().cost));
|
||||
JLOG(j_.debug()) << "WASM Success: " + std::to_string(reValue)
|
||||
<< ", cost: " << re.value().cost;
|
||||
if (!reValue)
|
||||
if (reValue <= 0)
|
||||
{
|
||||
// ctx_.view().update(slep);
|
||||
return tecWASM_REJECTED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,12 @@ public:
|
||||
gasUsed_ = gasUsed;
|
||||
}
|
||||
|
||||
void
|
||||
setWasmReturnCode(std::int32_t const wasmReturnCode)
|
||||
{
|
||||
wasmReturnCode_ = wasmReturnCode;
|
||||
}
|
||||
|
||||
/** Get the number of modified entries
|
||||
*/
|
||||
std::size_t
|
||||
@@ -100,6 +106,7 @@ public:
|
||||
private:
|
||||
std::optional<STAmount> deliver_;
|
||||
std::optional<std::uint32_t> gasUsed_;
|
||||
std::optional<std::int32_t> wasmReturnCode_;
|
||||
};
|
||||
|
||||
} // namespace ripple
|
||||
|
||||
@@ -118,6 +118,7 @@ ApplyStateTable::apply(
|
||||
std::optional<STAmount> const& deliver,
|
||||
std::optional<uint256 const> const& parentBatchId,
|
||||
std::optional<std::uint32_t> const& gasUsed,
|
||||
std::optional<std::int32_t> const& wasmReturnCode,
|
||||
bool isDryRun,
|
||||
beast::Journal j)
|
||||
{
|
||||
@@ -136,6 +137,8 @@ ApplyStateTable::apply(
|
||||
meta.setParentBatchId(*parentBatchId);
|
||||
if (gasUsed)
|
||||
meta.setGasUsed(*gasUsed);
|
||||
if (wasmReturnCode)
|
||||
meta.setWasmReturnCode(*wasmReturnCode);
|
||||
Mods newMod;
|
||||
for (auto& item : items_)
|
||||
{
|
||||
|
||||
@@ -74,6 +74,7 @@ public:
|
||||
std::optional<STAmount> const& deliver,
|
||||
std::optional<uint256 const> const& parentBatchId,
|
||||
std::optional<std::uint32_t> const& gasUsed,
|
||||
std::optional<std::int32_t> const& wasmReturnCode,
|
||||
bool isDryRun,
|
||||
beast::Journal j);
|
||||
|
||||
|
||||
@@ -36,7 +36,15 @@ ApplyViewImpl::apply(
|
||||
beast::Journal j)
|
||||
{
|
||||
return items_.apply(
|
||||
to, tx, ter, deliver_, parentBatchId, gasUsed_, isDryRun, j);
|
||||
to,
|
||||
tx,
|
||||
ter,
|
||||
deliver_,
|
||||
parentBatchId,
|
||||
gasUsed_,
|
||||
wasmReturnCode_,
|
||||
isDryRun,
|
||||
j);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
|
||||
Reference in New Issue
Block a user