Skip to content

Commit

Permalink
Add restore meta for p23
Browse files Browse the repository at this point in the history
  • Loading branch information
SirTyson committed Jan 9, 2025
1 parent 0f31920 commit c42168a
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 23 deletions.
2 changes: 1 addition & 1 deletion src/main/CommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

// clang-format off
// This needs to be included first
#include "bucket/LiveBucketList.h"
#include "rust/RustVecXdrMarshal.h"
// clang-format on

#include "main/CommandLine.h"
#include "bucket/BucketManager.h"
#include "bucket/LiveBucketList.h"
#include "catchup/CatchupConfiguration.h"
#include "catchup/CatchupRange.h"
#include "catchup/ReplayDebugMetaWork.h"
Expand Down
31 changes: 29 additions & 2 deletions src/testdata/ledger-close-meta-v1-protocol-23-soroban.json
Original file line number Diff line number Diff line change
Expand Up @@ -1898,6 +1898,33 @@
"operations": [
{
"changes": [
{
"type": "LEDGER_ENTRY_RESTORED",
"restored": {
"lastModifiedLedgerSeq": 7,
"data": {
"type": "CONTRACT_DATA",
"contractData": {
"ext": {
"v": 0
},
"contract": "CAA3QKIP2SNVXUJTB4HKOGF55JTSSMQGED3FZYNHMNSXYV3DRRMAWA3Y",
"key": {
"type": "SCV_SYMBOL",
"sym": "archived"
},
"durability": "PERSISTENT",
"val": {
"type": "SCV_U64",
"u64": 42
}
}
},
"ext": {
"v": 0
}
}
},
{
"type": "LEDGER_ENTRY_STATE",
"state": {
Expand All @@ -1915,8 +1942,8 @@
}
},
{
"type": "LEDGER_ENTRY_UPDATED",
"updated": {
"type": "LEDGER_ENTRY_RESTORED",
"restored": {
"lastModifiedLedgerSeq": 28,
"data": {
"type": "TTL",
Expand Down
3 changes: 1 addition & 2 deletions src/transactions/InvokeHostFunctionOpFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

// clang-format off
// This needs to be included first
#include "rust/RustVecXdrMarshal.h"
#include "TransactionUtils.h"
#include "main/AppConnector.h"
#include "util/GlobalChecks.h"
#include "util/ProtocolVersion.h"
#include "xdr/Stellar-ledger-entries.h"
Expand All @@ -14,7 +14,6 @@
#include <medida/metrics_registry.h>
#include <xdrpp/types.h>
#include "xdr/Stellar-contract.h"
#include "rust/RustVecXdrMarshal.h"
// clang-format on

#include "ledger/LedgerTxnImpl.h"
Expand Down
8 changes: 8 additions & 0 deletions src/transactions/OperationFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "transactions/SetTrustLineFlagsOpFrame.h"
#include "transactions/TransactionFrame.h"
#include "transactions/TransactionUtils.h"
#include "util/GlobalChecks.h"
#include "util/Logging.h"
#include "util/ProtocolVersion.h"
#include "util/XDRCereal.h"
Expand Down Expand Up @@ -329,4 +330,11 @@ OperationFrame::insertLedgerKeysToPrefetch(UnorderedSet<LedgerKey>& keys) const
// Do nothing by default
return;
}

SorobanResources const&
OperationFrame::getSorobanResources() const
{
releaseAssertOrThrow(isSoroban());
return mParentTx.sorobanResources();
}
}
2 changes: 2 additions & 0 deletions src/transactions/OperationFrame.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,7 @@ class OperationFrame
virtual bool isDexOperation() const;

virtual bool isSoroban() const;

SorobanResources const& getSorobanResources() const;
};
}
102 changes: 98 additions & 4 deletions src/transactions/TransactionFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@

#include <algorithm>
#include <numeric>
#include <unordered_map>
#include <unordered_set>
#include <xdrpp/types.h>

namespace stellar
Expand All @@ -57,6 +59,83 @@ namespace
// Limit to the maximum resource fee allowed for transaction,
// roughly 112 million lumens.
int64_t const MAX_RESOURCE_FEE = 1LL << 50;

#ifdef ENABLE_NEXT_PROTOCOL_VERSION_UNSAFE_FOR_PRODUCTION
// Starting in protocol 23, some operation meta needs to be modified
// to be consumed by downstream systems. In particular, restoration is
// logically a new entry creation from the perspective of ltx and stellar-core
// as a whole, but this change type is reclassified to LEDGER_ENTRY_RESTORED
// for easier consumption downstream.
LedgerEntryChanges
processOpLedgerEntryChanges(std::shared_ptr<OperationFrame const> op,
AbstractLedgerTxn& ltx)
{
if (op->getOperation().body.type() != RESTORE_FOOTPRINT)
{
return ltx.getChanges();
}

auto const& restoreKeys = op->getSorobanResources().footprint.readWrite;
std::unordered_map<LedgerKey, LedgerKey> ttlToDataKey(restoreKeys.size());
for (auto const& key : restoreKeys)
{
ttlToDataKey[getTTLKey(key)] = key;
}

LedgerEntryChanges changes = ltx.getChanges();

// If an entry being restored does not exist in the live BucketList (i.e.
// the entry was previously evicted), the restoreOp will have already
// created the entry in the ltx. In this case, we need to update the
// creation change type to LEDGER_ENTRY_RESTORED. If the entry being
// restored still exists in the BucketList, RestoreOp will only update the
// TTL, so we need to insert the data LEDGER_ENTRY_RESTORED change directly.
std::unordered_set<LedgerKey> restoreChangesToInsert;
for (auto& change : changes)
{
if (change.type() == LEDGER_ENTRY_CREATED)
{
auto le = change.created();
change.type(LEDGER_ENTRY_RESTORED);
change.restored() = le;
}
else if (change.type() == LEDGER_ENTRY_UPDATED)
{
auto ttlLe = change.updated();
releaseAssertOrThrow(ttlLe.data.type() == TTL);

// Update the TTL change from LEDGER_ENTRY_UPDATED to
// LEDGER_ENTRY_RESTORED.
change.type(LEDGER_ENTRY_RESTORED);
change.restored() = ttlLe;
auto dataKey = ttlToDataKey.at(LedgerEntryKey(ttlLe));
restoreChangesToInsert.insert(dataKey);
}
}

// Now insert all the data entries that were not created but already existed
// on the live BucketList
for (auto const& key : restoreChangesToInsert)
{
LedgerEntryChange change;
change.type(LEDGER_ENTRY_RESTORED);

// Note: this is already in the cache since the RestoreOp loaded
// all data keys for size calculation during apply already
auto entry = ltx.getNewestVersion(key);

// If TTL already exists and is just being updated, the
// data entry must also already exist
releaseAssertOrThrow(entry);

change.restored() = entry->ledgerEntry();
changes.push_back(change);
}

return changes;
}
#endif

} // namespace

using namespace std;
Expand Down Expand Up @@ -1648,15 +1727,30 @@ TransactionFrame::applyOperations(SignatureChecker& signatureChecker,
{
success = false;
}

// The operation meta will be empty if the transaction
// doesn't succeed so we may as well not do any work in that
// case
if (success)
{
app.checkOnOperationApply(op->getOperation(), opResult,
ltxOp.getDelta());

// The operation meta will be empty if the transaction
// doesn't succeed so we may as well not do any work in that
// case
operationMetas.emplace_back(ltxOp.getChanges());
LedgerEntryChanges changes;
#ifdef ENABLE_NEXT_PROTOCOL_VERSION_UNSAFE_FOR_PRODUCTION
if (protocolVersionStartsFrom(
ledgerVersion,
LiveBucket::
FIRST_PROTOCOL_SUPPORTING_PERSISTENT_EVICTION))
{
changes = processOpLedgerEntryChanges(op, ltxOp);
}
else
#endif
{
changes = ltxOp.getChanges();
}
operationMetas.emplace_back(changes);
}

if (txRes ||
Expand Down
102 changes: 96 additions & 6 deletions src/transactions/test/InvokeHostFunctionTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2752,6 +2752,92 @@ TEST_CASE_VERSIONS("entry eviction", "[tx][soroban][archival]")
REQUIRE(!evicted);
}
}

SECTION("Restoration Meta")
{
test.invokeRestoreOp({persistentKey}, 20'048);
auto targetRestorationLedger = test.getLCLSeq();

XDRInputFileStream in;
in.open(metaPath);
LedgerCloseMeta lcm;
bool restoreMeta = false;

LedgerKeySet keysToRestore = {persistentKey,
getTTLKey(persistentKey)};
while (in.readOne(lcm))
{
REQUIRE(lcm.v() == 1);
if (lcm.v1().ledgerHeader.header.ledgerSeq ==
targetRestorationLedger)
{
REQUIRE(lcm.v1().evictedTemporaryLedgerKeys.empty());
REQUIRE(
lcm.v1().evictedPersistentLedgerEntries.empty());

REQUIRE(lcm.v1().txProcessing.size() == 1);
auto txMeta = lcm.v1().txProcessing.front();
REQUIRE(
txMeta.txApplyProcessing.v3().operations.size() ==
1);

REQUIRE(txMeta.txApplyProcessing.v3()
.operations[0]
.changes.size() == 2);
for (auto const& change : txMeta.txApplyProcessing.v3()
.operations[0]
.changes)
{

// Only support persistent eviction meta >= p23
LedgerKey lk;
if (protocolVersionStartsFrom(
cfg.TESTING_UPGRADE_LEDGER_PROTOCOL_VERSION,
BucketBase::
FIRST_PROTOCOL_SUPPORTING_PERSISTENT_EVICTION))
{
REQUIRE(change.type() ==
LedgerEntryChangeType::
LEDGER_ENTRY_RESTORED);
lk = LedgerEntryKey(change.restored());
REQUIRE(keysToRestore.find(lk) !=
keysToRestore.end());
keysToRestore.erase(lk);
}
else
{
if (change.type() ==
LedgerEntryChangeType::LEDGER_ENTRY_STATE)
{
lk = LedgerEntryKey(change.state());
REQUIRE(lk == getTTLKey(persistentKey));
keysToRestore.erase(lk);
}
else
{
REQUIRE(change.type() ==
LedgerEntryChangeType::
LEDGER_ENTRY_UPDATED);
lk = LedgerEntryKey(change.updated());
REQUIRE(lk == getTTLKey(persistentKey));

// While we will see the TTL key twice,
// remove the TTL key in the path above and
// the persistent key here to make the check
// easier
keysToRestore.erase(persistentKey);
}
}
}

restoreMeta = true;
break;
}
}

REQUIRE(restoreMeta);
REQUIRE(keysToRestore.empty());
}
}
#endif

Expand Down Expand Up @@ -2942,11 +3028,6 @@ TEST_CASE("persistent entry archival", "[tx][soroban][archival]")
auto lk = client.getContract().getDataKey(
makeSymbolSCVal("key"), ContractDataDurability::PERSISTENT);

auto hotArchive = test.getApp()
.getBucketManager()
.getBucketSnapshotManager()
.copySearchableHotArchiveBucketListSnapshot();

auto evictionLedger = 14;

// Close ledgers until entry is evicted
Expand All @@ -2955,6 +3036,11 @@ TEST_CASE("persistent entry archival", "[tx][soroban][archival]")
closeLedgerOn(test.getApp(), i, 2, 1, 2016);
}

auto hotArchive = test.getApp()
.getBucketManager()
.getBucketSnapshotManager()
.copySearchableHotArchiveBucketListSnapshot();

if (evict)
{
REQUIRE(hotArchive->load(lk));
Expand Down Expand Up @@ -3022,7 +3108,6 @@ TEST_CASE("persistent entry archival", "[tx][soroban][archival]")
SECTION("key accessible after restore")
{
test.invokeRestoreOp({lk}, 20'048);

auto const& stateArchivalSettings =
test.getNetworkCfg().stateArchivalSettings();
auto newExpectedLiveUntilLedger =
Expand All @@ -3031,6 +3116,11 @@ TEST_CASE("persistent entry archival", "[tx][soroban][archival]")

client.get("key", ContractDataDurability::PERSISTENT, 123);

test.getApp()
.getBucketManager()
.getBucketSnapshotManager()
.maybeCopySearchableHotArchiveBucketListSnapshot(hotArchive);

// Restored entries are deleted from Hot Archive
REQUIRE(!hotArchive->load(lk));
}
Expand Down
Loading

0 comments on commit c42168a

Please sign in to comment.