Skip to content

Commit

Permalink
Add apply time diagnostic errors for soroban ops
Browse files Browse the repository at this point in the history
  • Loading branch information
sisuresh authored and graydon committed Aug 31, 2023
1 parent 1906626 commit 1464cec
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 24 deletions.
7 changes: 7 additions & 0 deletions src/transactions/BumpFootprintExpirationOpFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#ifdef ENABLE_NEXT_PROTOCOL_VERSION_UNSAFE_FOR_PRODUCTION
#include "transactions/BumpFootprintExpirationOpFrame.h"
#include "TransactionUtils.h"

namespace stellar
{
Expand Down Expand Up @@ -101,6 +102,12 @@ BumpFootprintExpirationOpFrame::doApply(Application& app,
metrics.mLedgerReadByte += entrySize + expirationSize;
if (resources.readBytes < metrics.mLedgerReadByte)
{
mParentTx.pushSimpleDiagnosticError(
SCE_BUDGET, SCEC_EXCEEDED_LIMIT,
"operation byte-read resources exceeds amount specified",
{makeU64SCVal(metrics.mLedgerReadByte),
makeU64SCVal(resources.readBytes)});

innerResult().code(
BUMP_FOOTPRINT_EXPIRATION_RESOURCE_LIMIT_EXCEEDED);
return false;
Expand Down
86 changes: 65 additions & 21 deletions src/transactions/InvokeHostFunctionOpFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,25 +91,6 @@ getLedgerInfo(AbstractLedgerTxn& ltx, Config const& cfg,
return info;
}

bool
validateContractLedgerEntry(LedgerEntry const& le, size_t entrySize,
SorobanNetworkConfig const& config)
{
// check contract code size limit
if (le.data.type() == CONTRACT_CODE &&
config.maxContractSizeBytes() < le.data.contractCode().code.size())
{
return false;
}
// check contract data entry size limit
if (le.data.type() == CONTRACT_DATA &&
config.maxContractDataEntrySizeBytes() < entrySize)
{
return false;
}
return true;
}

DiagnosticEvent
metricsEvent(bool success, std::string&& topic, uint32 value)
{
Expand Down Expand Up @@ -354,6 +335,35 @@ InvokeHostFunctionOpFrame::maybePopulateDiagnosticEvents(
}
}

bool
InvokeHostFunctionOpFrame::validateContractLedgerEntry(
LedgerEntry const& le, size_t entrySize, SorobanNetworkConfig const& config)
{
// check contract code size limit
if (le.data.type() == CONTRACT_CODE &&
config.maxContractSizeBytes() < le.data.contractCode().code.size())
{
mParentTx.pushSimpleDiagnosticError(
SCE_BUDGET, SCEC_EXCEEDED_LIMIT,
"WASM size exceeds network config maximum contract size",
{makeU64SCVal(le.data.contractCode().code.size()),
makeU64SCVal(config.maxContractSizeBytes())});
return false;
}
// check contract data entry size limit
if (le.data.type() == CONTRACT_DATA &&
config.maxContractDataEntrySizeBytes() < entrySize)
{
mParentTx.pushSimpleDiagnosticError(
SCE_BUDGET, SCEC_EXCEEDED_LIMIT,
"ContractData size exceeds network config maximum size",
{makeU64SCVal(entrySize),
makeU64SCVal(config.maxContractDataEntrySizeBytes())});
return false;
}
return true;
}

bool
InvokeHostFunctionOpFrame::doApply(Application& app, AbstractLedgerTxn& ltx,
Hash const& sorobanBasePrngSeed)
Expand Down Expand Up @@ -445,6 +455,12 @@ InvokeHostFunctionOpFrame::doApply(Application& app, AbstractLedgerTxn& ltx,

if (resources.readBytes < metrics.mLedgerReadByte)
{
mParentTx.pushSimpleDiagnosticError(
SCE_BUDGET, SCEC_EXCEEDED_LIMIT,
"operation byte-read resources exceeds amount specified",
{makeU64SCVal(metrics.mLedgerReadByte),
makeU64SCVal(resources.readBytes)});

this->innerResult().code(
INVOKE_HOST_FUNCTION_RESOURCE_LIMIT_EXCEEDED);
return false;
Expand Down Expand Up @@ -506,9 +522,22 @@ InvokeHostFunctionOpFrame::doApply(Application& app, AbstractLedgerTxn& ltx,
metrics.mInvokeTimeNsecs = static_cast<uint32>(out.time_nsecs);
if (!out.success)
{
if (resources.instructions < out.cpu_insns ||
sorobanConfig.txMemoryLimit() < out.mem_bytes)
if (resources.instructions < out.cpu_insns)
{
mParentTx.pushSimpleDiagnosticError(
SCE_BUDGET, SCEC_EXCEEDED_LIMIT,
"operation instructions exceeds amount specified",
{makeU64SCVal(out.cpu_insns),
makeU64SCVal(resources.instructions)});
innerResult().code(INVOKE_HOST_FUNCTION_RESOURCE_LIMIT_EXCEEDED);
}
else if (sorobanConfig.txMemoryLimit() < out.mem_bytes)
{
mParentTx.pushSimpleDiagnosticError(
SCE_BUDGET, SCEC_EXCEEDED_LIMIT,
"operation memory usage exceeds network config limit",
{makeU64SCVal(out.mem_bytes),
makeU64SCVal(sorobanConfig.txMemoryLimit())});
innerResult().code(INVOKE_HOST_FUNCTION_RESOURCE_LIMIT_EXCEEDED);
}
else
Expand Down Expand Up @@ -544,6 +573,11 @@ InvokeHostFunctionOpFrame::doApply(Application& app, AbstractLedgerTxn& ltx,
metrics.noteWriteEntry(isCodeKey(lk), keySize, entrySize);
if (resources.writeBytes < metrics.mLedgerWriteByte)
{
mParentTx.pushSimpleDiagnosticError(
SCE_BUDGET, SCEC_EXCEEDED_LIMIT,
"operation byte-write resources exceeds amount specified",
{makeU64SCVal(metrics.mLedgerWriteByte),
makeU64SCVal(resources.writeBytes)});
innerResult().code(
INVOKE_HOST_FUNCTION_RESOURCE_LIMIT_EXCEEDED);
return false;
Expand Down Expand Up @@ -612,6 +646,11 @@ InvokeHostFunctionOpFrame::doApply(Application& app, AbstractLedgerTxn& ltx,
if (sorobanConfig.txMaxContractEventsSizeBytes() <
metrics.mEmitEventByte)
{
mParentTx.pushSimpleDiagnosticError(
SCE_BUDGET, SCEC_EXCEEDED_LIMIT,
"total events size exceeds network config maximum",
{makeU64SCVal(metrics.mEmitEventByte),
makeU64SCVal(sorobanConfig.txMaxContractEventsSizeBytes())});
innerResult().code(INVOKE_HOST_FUNCTION_RESOURCE_LIMIT_EXCEEDED);
return false;
}
Expand All @@ -625,6 +664,11 @@ InvokeHostFunctionOpFrame::doApply(Application& app, AbstractLedgerTxn& ltx,
metrics.mEmitEventByte += out.result_value.data.size();
if (sorobanConfig.txMaxContractEventsSizeBytes() < metrics.mEmitEventByte)
{
mParentTx.pushSimpleDiagnosticError(
SCE_BUDGET, SCEC_EXCEEDED_LIMIT,
"return value pushes events size above network config maximum",
{makeU64SCVal(metrics.mEmitEventByte),
makeU64SCVal(sorobanConfig.txMaxContractEventsSizeBytes())});
innerResult().code(INVOKE_HOST_FUNCTION_RESOURCE_LIMIT_EXCEEDED);
return false;
}
Expand Down
3 changes: 3 additions & 0 deletions src/transactions/InvokeHostFunctionOpFrame.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ class InvokeHostFunctionOpFrame : public OperationFrame
InvokeHostFunctionOutput const& output,
HostFunctionMetrics const& metrics);

bool validateContractLedgerEntry(LedgerEntry const& le, size_t entrySize,
SorobanNetworkConfig const& config);

InvokeHostFunctionOp const& mInvokeHostFunction;

public:
Expand Down
25 changes: 23 additions & 2 deletions src/transactions/RestoreFootprintOpFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#ifdef ENABLE_NEXT_PROTOCOL_VERSION_UNSAFE_FOR_PRODUCTION
#include "transactions/RestoreFootprintOpFrame.h"
#include "TransactionUtils.h"

namespace stellar
{
Expand Down Expand Up @@ -97,6 +98,11 @@ RestoreFootprintOpFrame::doApply(Application& app, AbstractLedgerTxn& ltx,
metrics.mLedgerReadByte += entrySize + expirationSize;
if (resources.readBytes < metrics.mLedgerReadByte)
{
mParentTx.pushSimpleDiagnosticError(
SCE_BUDGET, SCEC_EXCEEDED_LIMIT,
"operation byte-read resources exceeds amount specified",
{makeU64SCVal(metrics.mLedgerReadByte),
makeU64SCVal(resources.readBytes)});
innerResult().code(RESTORE_FOOTPRINT_RESOURCE_LIMIT_EXCEEDED);
return false;
}
Expand All @@ -116,9 +122,24 @@ RestoreFootprintOpFrame::doApply(Application& app, AbstractLedgerTxn& ltx,
// writes come out of refundable fee, so only add entrySize
metrics.mLedgerWriteByte += entrySize;

if (resources.writeBytes < metrics.mLedgerWriteByte ||
resources.readBytes < metrics.mLedgerReadByte)
if (resources.readBytes < metrics.mLedgerReadByte)
{
mParentTx.pushSimpleDiagnosticError(
SCE_BUDGET, SCEC_EXCEEDED_LIMIT,
"operation byte-read resources exceeds amount specified",
{makeU64SCVal(metrics.mLedgerReadByte),
makeU64SCVal(resources.readBytes)});
innerResult().code(RESTORE_FOOTPRINT_RESOURCE_LIMIT_EXCEEDED);
return false;
}

if (resources.writeBytes < metrics.mLedgerWriteByte)
{
mParentTx.pushSimpleDiagnosticError(
SCE_BUDGET, SCEC_EXCEEDED_LIMIT,
"operation byte-write resources exceeds amount specified",
{makeU64SCVal(metrics.mLedgerWriteByte),
makeU64SCVal(resources.writeBytes)});
innerResult().code(RESTORE_FOOTPRINT_RESOURCE_LIMIT_EXCEEDED);
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion src/transactions/TransactionFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ TransactionFrame::pushContractEvents(xdr::xvector<ContractEvent>&& evts)
void
TransactionFrame::pushDiagnosticEvents(xdr::xvector<DiagnosticEvent>&& evts)
{
mDiagnosticEvents = evts;
mDiagnosticEvents.insert(mDiagnosticEvents.end(), evts.begin(), evts.end());
}

void
Expand Down

3 comments on commit 1464cec

@latobarita
Copy link
Contributor

Choose a reason for hiding this comment

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

saw approval from MonsieurNicolas
at graydon@1464cec

@latobarita
Copy link
Contributor

Choose a reason for hiding this comment

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

merging graydon/stellar-core/tx-submit-diagnostics = 1464cec into auto

@latobarita
Copy link
Contributor

Choose a reason for hiding this comment

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

graydon/stellar-core/tx-submit-diagnostics = 1464cec merged ok, testing candidate = 8b45cf90

Please sign in to comment.