Skip to content

Commit 86a346a

Browse files
authored
Autorestore (#4726)
# Description Implements autorestore functionality for protocol 23. Specifically, this includes: 1. Updating ExtendFootprintOp and InvokeHostFunctionOp to meter reads based on the new disk resource type. 2. Adding automatic restore functionality + resource validity checking in InvokeHostFunctionOp. 3. Implementing meta for auto restore + backfiling prior protocol's meta with the new RESTORE `LedgerEntryChangeType`. Unfortunately, the post-processing of meta is more complex than I would have liked. In particular, meta generation is heavily tied to the LTX and Live BucketList state, which no longer maps directly to the logical state changes required by downstream systems due to state archival. I think LTX is in need of a refactor regardless, but for now I've tried to add some detailed comments and test cases. `METADATA_DEBUG_LEDGERS works` also appears to be a little flaky and I'm not quite sure why. It fails at `REQUIRE(gotToExpectedSize);`, but only sometimes. I think this might have to do with file persistence in tests, but I'm not sure since I have been modifying meta with this change. I also refactored `InvokeHostFunctionOp` with a stateful helper class, as some of the lambda captures were getting out of hand. While there are some renames, it shouldn't be too much code motion (other than the changes required for auto restore), but if this conflicts too much with parallel apply I'm open to other suggestions. # Checklist - [x] Reviewed the [contributing](https://github.com/stellar/stellar-core/blob/master/CONTRIBUTING.md#submitting-changes) document - [x] Rebased on top of master (no merge commits) - [x] Ran `clang-format` v8.0.0 (via `make format` or the Visual Studio extension) - [x] Compiles - [x] Ran all tests - [ ] If change impacts performance, include supporting evidence per the [performance document](https://github.com/stellar/stellar-core/blob/master/performance-eval/performance-eval.md)
2 parents 4022f47 + 6ed5487 commit 86a346a

File tree

52 files changed

+2562
-902
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2562
-902
lines changed

docs/stellar-core_example.cfg

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,10 @@ METADATA_OUTPUT_STREAM=""
629629
# they should only be reduced or disabled if disk space is at a premium.
630630
METADATA_DEBUG_LEDGERS=100
631631

632+
# When true, Core will restore the metadata with LedgerEntryChangeType::LEDGER_ENTRY_RESTORED
633+
# for all protocol versions. When false, restore meta is only applied for protocol versions >= 23.
634+
BACKFILL_RESTORE_META=false
635+
632636
# When true, Core will emit the
633637
# breakdown of the charged resource fees for every Soroban transaction (refundable,
634638
# non-refundable and rent components) in the extension of `SorobanData`.

src/bucket/test/BucketTestUtils.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ LedgerManagerForBucketTests::sealLedgerTxnAndTransferEntriesToBucketList(
245245
{
246246
std::vector<LedgerKey> restoredKeys;
247247
auto restoredKeysMap = ltx.getRestoredHotArchiveKeys();
248-
for (auto const& key : restoredKeysMap)
248+
for (auto const& [key, entry] : restoredKeysMap)
249249
{
250250
// Hot Archive does not track TTLs
251251
if (key.type() == CONTRACT_DATA ||

src/ledger/LedgerManagerImpl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2011,7 +2011,7 @@ LedgerManagerImpl::sealLedgerTxnAndTransferEntriesToBucketList(
20112011
{
20122012
std::vector<LedgerKey> restoredKeys;
20132013
auto const& restoredKeyMap = ltx.getRestoredHotArchiveKeys();
2014-
for (auto const& key : restoredKeyMap)
2014+
for (auto const& [key, entry] : restoredKeyMap)
20152015
{
20162016
// TTL keys are not recorded in the hot archive BucketList
20172017
if (key.type() == CONTRACT_DATA ||

src/ledger/LedgerTxn.cpp

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -637,18 +637,18 @@ LedgerTxn::Impl::commitChild(EntryIterator iter,
637637
printErrorAndAbort("unknown fatal error during commit to LedgerTxn");
638638
}
639639

640-
for (auto const& key : restoredKeys.hotArchive)
640+
for (auto const& [key, entry] : restoredKeys.hotArchive)
641641
{
642-
auto [_, inserted] = mRestoredKeys.hotArchive.emplace(key);
642+
auto [_, inserted] = mRestoredKeys.hotArchive.emplace(key, entry);
643643
if (!inserted)
644644
{
645645
printErrorAndAbort("restored hot archive entry already exists");
646646
}
647647
}
648648

649-
for (auto const& key : restoredKeys.liveBucketList)
649+
for (auto const& [key, entry] : restoredKeys.liveBucketList)
650650
{
651-
auto [_, inserted] = mRestoredKeys.liveBucketList.emplace(key);
651+
auto [_, inserted] = mRestoredKeys.liveBucketList.emplace(key, entry);
652652
if (!inserted)
653653
{
654654
printErrorAndAbort("restored live BucketList entry already exists");
@@ -825,13 +825,13 @@ LedgerTxn::Impl::erase(InternalLedgerKey const& key)
825825
}
826826
}
827827

828-
void
828+
LedgerTxnEntry
829829
LedgerTxn::restoreFromHotArchive(LedgerEntry const& entry, uint32_t ttl)
830830
{
831-
getImpl()->restoreFromHotArchive(*this, entry, ttl);
831+
return getImpl()->restoreFromHotArchive(*this, entry, ttl);
832832
}
833833

834-
void
834+
LedgerTxnEntry
835835
LedgerTxn::Impl::restoreFromHotArchive(LedgerTxn& self,
836836
LedgerEntry const& entry, uint32_t ttl)
837837
{
@@ -852,33 +852,37 @@ LedgerTxn::Impl::restoreFromHotArchive(LedgerTxn& self,
852852
ttlEntry.data.type(TTL);
853853
ttlEntry.data.ttl().liveUntilLedgerSeq = ttl;
854854
ttlEntry.data.ttl().keyHash = ttlKey.ttl().keyHash;
855-
create(self, ttlEntry);
855+
auto ttlLtxe = create(self, ttlEntry);
856856

857857
// Mark the keys as restored
858-
auto addKey = [this](LedgerKey const& key) {
859-
auto [_, inserted] = mRestoredKeys.hotArchive.insert(key);
858+
auto addEntry = [this](LedgerEntry const& entry, LedgerKey const& key) {
859+
auto [_, inserted] = mRestoredKeys.hotArchive.emplace(key, entry);
860860
if (!inserted)
861861
{
862862
throw std::runtime_error("Key already removed from hot archive");
863863
}
864864
};
865-
addKey(LedgerEntryKey(entry));
866-
addKey(ttlKey);
865+
addEntry(entry, LedgerEntryKey(entry));
866+
addEntry(ttlLtxe.current(), ttlKey);
867+
868+
return ttlLtxe;
867869
}
868870

869-
void
870-
LedgerTxn::restoreFromLiveBucketList(LedgerKey const& key, uint32_t ttl)
871+
LedgerTxnEntry
872+
LedgerTxn::restoreFromLiveBucketList(LedgerEntry const& entry, uint32_t ttl)
871873
{
872-
getImpl()->restoreFromLiveBucketList(*this, key, ttl);
874+
return getImpl()->restoreFromLiveBucketList(*this, entry, ttl);
873875
}
874876

875-
void
877+
LedgerTxnEntry
876878
LedgerTxn::Impl::restoreFromLiveBucketList(LedgerTxn& self,
877-
LedgerKey const& key, uint32_t ttl)
879+
LedgerEntry const& entry,
880+
uint32_t ttl)
878881
{
879882
throwIfSealed();
880883
throwIfChild();
881884

885+
auto key = LedgerEntryKey(entry);
882886
if (!isPersistentEntry(key))
883887
{
884888
throw std::runtime_error("Key type not supported for restoration");
@@ -898,16 +902,18 @@ LedgerTxn::Impl::restoreFromLiveBucketList(LedgerTxn& self,
898902
ttlLtxe.current().data.ttl().liveUntilLedgerSeq = ttl;
899903

900904
// Mark the keys as restored
901-
auto addKey = [this](LedgerKey const& key) {
902-
auto [_, inserted] = mRestoredKeys.liveBucketList.insert(key);
905+
auto addEntry = [this](LedgerEntry const& entry, LedgerKey const& key) {
906+
auto [_, inserted] = mRestoredKeys.liveBucketList.emplace(key, entry);
903907
if (!inserted)
904908
{
905909
throw std::runtime_error(
906910
"Key already restored from Live BucketList");
907911
}
908912
};
909-
addKey(key);
910-
addKey(ttlKey);
913+
addEntry(entry, key);
914+
addEntry(ttlLtxe.current(), ttlKey);
915+
916+
return ttlLtxe;
911917
}
912918

913919
void
@@ -1578,25 +1584,25 @@ LedgerTxn::Impl::getAllEntries(std::vector<LedgerEntry>& initEntries,
15781584
deadEntries.swap(resDead);
15791585
}
15801586

1581-
UnorderedSet<LedgerKey> const&
1587+
UnorderedMap<LedgerKey, LedgerEntry> const&
15821588
LedgerTxn::getRestoredHotArchiveKeys() const
15831589
{
15841590
return getImpl()->getRestoredHotArchiveKeys();
15851591
}
15861592

1587-
UnorderedSet<LedgerKey> const&
1593+
UnorderedMap<LedgerKey, LedgerEntry> const&
15881594
LedgerTxn::Impl::getRestoredHotArchiveKeys() const
15891595
{
15901596
return mRestoredKeys.hotArchive;
15911597
}
15921598

1593-
UnorderedSet<LedgerKey> const&
1599+
UnorderedMap<LedgerKey, LedgerEntry> const&
15941600
LedgerTxn::getRestoredLiveBucketListKeys() const
15951601
{
15961602
return getImpl()->getRestoredLiveBucketListKeys();
15971603
}
15981604

1599-
UnorderedSet<LedgerKey> const&
1605+
UnorderedMap<LedgerKey, LedgerEntry> const&
16001606
LedgerTxn::Impl::getRestoredLiveBucketListKeys() const
16011607
{
16021608
return mRestoredKeys.liveBucketList;

src/ledger/LedgerTxn.h

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -315,11 +315,13 @@ struct InflationWinner
315315
};
316316

317317
// Tracks the set of both TTL keys and corresponding code/data keys that have
318-
// been restored.
318+
// been restored. Maps LedgerKey -> LedgerEntry at the point of restoration. For
319+
// contract code/data, this is the original, restored value. For TTL entries,
320+
// this is the value after applying the minimum rent required to restore.
319321
struct RestoredKeys
320322
{
321-
UnorderedSet<LedgerKey> hotArchive;
322-
UnorderedSet<LedgerKey> liveBucketList;
323+
UnorderedMap<LedgerKey, LedgerEntry> hotArchive;
324+
UnorderedMap<LedgerKey, LedgerEntry> liveBucketList;
323325
};
324326

325327
class AbstractLedgerTxn;
@@ -581,20 +583,20 @@ class AbstractLedgerTxn : public AbstractLedgerTxnParent
581583
// will create both data data/contract entry and
582584
// corresponding TTL entry. Prior to this call, the data/contract key
583585
// and TTL key must not exist in the live BucketList or any parent ltx,
584-
// throws otherwise.
586+
// throws otherwise. Returns the TTL entry created.
585587
// - restoreFromLiveBucketlist:
586588
// Indicates that an entry in the live BucketList is being restored and
587589
// updates the TTL entry accordingly. TTL key must exist, throws
588-
// otherwise.
590+
// otherwise. Returns the TTL entry that was modified.
589591
// All of these functions throw if the AbstractLedgerTxn is sealed or if
590592
// the AbstractLedgerTxn has a child.
591593
virtual LedgerTxnHeader loadHeader() = 0;
592594
virtual LedgerTxnEntry create(InternalLedgerEntry const& entry) = 0;
593595
virtual void erase(InternalLedgerKey const& key) = 0;
594-
virtual void restoreFromHotArchive(LedgerEntry const& entry,
595-
uint32_t ttl) = 0;
596-
virtual void restoreFromLiveBucketList(LedgerKey const& key,
597-
uint32_t ttl) = 0;
596+
virtual LedgerTxnEntry restoreFromHotArchive(LedgerEntry const& entry,
597+
uint32_t ttl) = 0;
598+
virtual LedgerTxnEntry restoreFromLiveBucketList(LedgerEntry const& entry,
599+
uint32_t ttl) = 0;
598600
virtual LedgerTxnEntry load(InternalLedgerKey const& key) = 0;
599601
virtual ConstLedgerTxnEntry
600602
loadWithoutRecord(InternalLedgerKey const& key) = 0;
@@ -637,11 +639,11 @@ class AbstractLedgerTxn : public AbstractLedgerTxnParent
637639
virtual void getAllEntries(std::vector<LedgerEntry>& initEntries,
638640
std::vector<LedgerEntry>& liveEntries,
639641
std::vector<LedgerKey>& deadEntries) = 0;
640-
// Returns set of TTL and corresponding contract/data keys that have been
642+
// Returns map of TTL and corresponding contract/data keys that have been
641643
// restored from the Hot Archive/Live Bucket List.
642-
virtual UnorderedSet<LedgerKey> const&
644+
virtual UnorderedMap<LedgerKey, LedgerEntry> const&
643645
getRestoredHotArchiveKeys() const = 0;
644-
virtual UnorderedSet<LedgerKey> const&
646+
virtual UnorderedMap<LedgerKey, LedgerEntry> const&
645647
getRestoredLiveBucketListKeys() const = 0;
646648

647649
// Returns all TTL keys that have been modified (create, update, and
@@ -740,8 +742,10 @@ class LedgerTxn : public AbstractLedgerTxn
740742
LedgerTxnEntry create(InternalLedgerEntry const& entry) override;
741743

742744
void erase(InternalLedgerKey const& key) override;
743-
void restoreFromHotArchive(LedgerEntry const& entry, uint32_t ttl) override;
744-
void restoreFromLiveBucketList(LedgerKey const& key, uint32_t ttl) override;
745+
LedgerTxnEntry restoreFromHotArchive(LedgerEntry const& entry,
746+
uint32_t ttl) override;
747+
LedgerTxnEntry restoreFromLiveBucketList(LedgerEntry const& entry,
748+
uint32_t ttl) override;
745749

746750
UnorderedMap<LedgerKey, LedgerEntry> getAllOffers() override;
747751

@@ -778,8 +782,9 @@ class LedgerTxn : public AbstractLedgerTxn
778782
std::vector<LedgerKey>& deadEntries) override;
779783
LedgerKeySet getAllTTLKeysWithoutSealing() const override;
780784

781-
UnorderedSet<LedgerKey> const& getRestoredHotArchiveKeys() const override;
782-
UnorderedSet<LedgerKey> const&
785+
UnorderedMap<LedgerKey, LedgerEntry> const&
786+
getRestoredHotArchiveKeys() const override;
787+
UnorderedMap<LedgerKey, LedgerEntry> const&
783788
getRestoredLiveBucketListKeys() const override;
784789

785790
std::shared_ptr<InternalLedgerEntry const>

src/ledger/LedgerTxnImpl.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -365,15 +365,17 @@ class LedgerTxn::Impl
365365
// throws an exception, then
366366
// - the prepared statement cache may be, but is not guaranteed to be,
367367
// modified
368-
void restoreFromHotArchive(LedgerTxn& self, LedgerEntry const& entry,
369-
uint32_t ttl);
368+
LedgerTxnEntry restoreFromHotArchive(LedgerTxn& self,
369+
LedgerEntry const& entry,
370+
uint32_t ttl);
370371

371372
// restoreFromLiveBucketList has the basic exception safety guarantee. If it
372373
// throws an exception, then
373374
// - the prepared statement cache may be, but is not guaranteed to be,
374375
// modified
375-
void restoreFromLiveBucketList(LedgerTxn& self, LedgerKey const& key,
376-
uint32_t ttl);
376+
LedgerTxnEntry restoreFromLiveBucketList(LedgerTxn& self,
377+
LedgerEntry const& entry,
378+
uint32_t ttl);
377379

378380
// getAllOffers has the basic exception safety guarantee. If it throws an
379381
// exception, then
@@ -450,8 +452,10 @@ class LedgerTxn::Impl
450452
std::vector<LedgerKey>& deadEntries);
451453
// getRestoredHotArchiveKeys and getRestoredLiveBucketListKeys
452454
// have the strong exception safety guarantee
453-
UnorderedSet<LedgerKey> const& getRestoredHotArchiveKeys() const;
454-
UnorderedSet<LedgerKey> const& getRestoredLiveBucketListKeys() const;
455+
UnorderedMap<LedgerKey, LedgerEntry> const&
456+
getRestoredHotArchiveKeys() const;
457+
UnorderedMap<LedgerKey, LedgerEntry> const&
458+
getRestoredLiveBucketListKeys() const;
455459

456460
LedgerKeySet getAllTTLKeysWithoutSealing() const;
457461

src/ledger/test/InMemoryLedgerTxn.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,15 +274,16 @@ InMemoryLedgerTxn::erase(InternalLedgerKey const& key)
274274
throw std::runtime_error("called erase on InMemoryLedgerTxn");
275275
}
276276

277-
void
277+
LedgerTxnEntry
278278
InMemoryLedgerTxn::restoreFromHotArchive(LedgerEntry const& entry, uint32_t ttl)
279279
{
280280
throw std::runtime_error(
281281
"called restoreFromHotArchive on InMemoryLedgerTxn");
282282
}
283283

284-
void
285-
InMemoryLedgerTxn::restoreFromLiveBucketList(LedgerKey const& key, uint32_t ttl)
284+
LedgerTxnEntry
285+
InMemoryLedgerTxn::restoreFromLiveBucketList(LedgerEntry const& entry,
286+
uint32_t ttl)
286287
{
287288
throw std::runtime_error(
288289
"called restoreFromLiveBucketList on InMemoryLedgerTxn");

src/ledger/test/InMemoryLedgerTxn.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,10 @@ class InMemoryLedgerTxn : public LedgerTxn
112112

113113
LedgerTxnEntry create(InternalLedgerEntry const& entry) override;
114114
void erase(InternalLedgerKey const& key) override;
115-
void restoreFromHotArchive(LedgerEntry const& entry, uint32_t ttl) override;
116-
void restoreFromLiveBucketList(LedgerKey const& key, uint32_t ttl) override;
115+
LedgerTxnEntry restoreFromHotArchive(LedgerEntry const& entry,
116+
uint32_t ttl) override;
117+
LedgerTxnEntry restoreFromLiveBucketList(LedgerEntry const& entry,
118+
uint32_t ttl) override;
117119
LedgerTxnEntry load(InternalLedgerKey const& key) override;
118120
ConstLedgerTxnEntry
119121
loadWithoutRecord(InternalLedgerKey const& key) override;

src/ledger/test/LedgerTxnTests.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,8 @@ TEST_CASE("LedgerTxn commit into LedgerTxn", "[ledgertxn]")
256256

257257
SECTION("live BL restore key does not exist")
258258
{
259-
REQUIRE_THROWS(ltx1.restoreFromLiveBucketList(randomKeys[0], 42));
259+
REQUIRE_THROWS(
260+
ltx1.restoreFromLiveBucketList(randomEntries[0], 42));
260261
}
261262

262263
auto checkKey = [](auto const& keySet, auto const& dataKey) {
@@ -316,15 +317,15 @@ TEST_CASE("LedgerTxn commit into LedgerTxn", "[ledgertxn]")
316317
// Populate live BL with key, then restore it
317318
ltx1.create(randomEntries[0]);
318319
ltx1.create(getTTLEntry(randomKeys[0]));
319-
ltx1.restoreFromLiveBucketList(randomKeys[0], 42);
320+
ltx1.restoreFromLiveBucketList(randomEntries[0], 42);
320321

321322
SECTION("rollback")
322323
{
323324
{
324325
LedgerTxn ltx2(ltx1);
325326
ltx2.create(randomEntries[1]);
326327
ltx2.create(getTTLEntry(randomKeys[1]));
327-
ltx2.restoreFromLiveBucketList(randomKeys[1], 42);
328+
ltx2.restoreFromLiveBucketList(randomEntries[1], 42);
328329
}
329330

330331
REQUIRE(ltx1.getRestoredHotArchiveKeys().empty());
@@ -341,7 +342,7 @@ TEST_CASE("LedgerTxn commit into LedgerTxn", "[ledgertxn]")
341342
LedgerTxn ltx2(ltx1);
342343
ltx2.create(randomEntries[1]);
343344
ltx2.create(getTTLEntry(randomKeys[1]));
344-
ltx2.restoreFromLiveBucketList(randomKeys[1], 42);
345+
ltx2.restoreFromLiveBucketList(randomEntries[1], 42);
345346
ltx2.commit();
346347
}
347348

src/main/Config.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ Config::Config() : NODE_SEED(SecretKey::random())
324324

325325
EMIT_CLASSIC_EVENTS = false;
326326
BACKFILL_STELLAR_ASSET_EVENTS = false;
327+
BACKFILL_RESTORE_META = false;
327328

328329
#ifdef BUILD_TESTS
329330
TEST_CASES_ENABLED = false;
@@ -1737,7 +1738,9 @@ Config::processConfig(std::shared_ptr<cpptoml::table> t)
17371738
{"EMIT_CLASSIC_EVENTS",
17381739
[&]() { EMIT_CLASSIC_EVENTS = readBool(item); }},
17391740
{"BACKFILL_STELLAR_ASSET_EVENTS",
1740-
[&]() { BACKFILL_STELLAR_ASSET_EVENTS = readBool(item); }}};
1741+
[&]() { BACKFILL_STELLAR_ASSET_EVENTS = readBool(item); }},
1742+
{"BACKFILL_RESTORE_META",
1743+
[&]() { BACKFILL_RESTORE_META = readBool(item); }}};
17411744

17421745
auto it = confProcessor.find(item.first);
17431746
if (it != confProcessor.end())

0 commit comments

Comments
 (0)