Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1dfeaf1

Browse files
committedOct 10, 2024
Added proof generation endpoints
1 parent f31c654 commit 1dfeaf1

File tree

6 files changed

+175
-42
lines changed

6 files changed

+175
-42
lines changed
 

‎src/main/QueryServer.cpp

+91
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "ledger/LedgerTxnImpl.h"
99
#include "ledger/LedgerTypeUtils.h"
1010
#include "main/Config.h"
11+
#include "util/ArchivalProofs.h"
1112
#include "util/Logging.h"
1213
#include "util/UnorderedSet.h"
1314
#include "util/XDRStream.h" // IWYU pragma: keep
@@ -77,6 +78,8 @@ QueryServer::QueryServer(const std::string& address, unsigned short port,
7778
mServer.add404(std::bind(&QueryServer::notFound, this, _1, _2, _3));
7879
addRoute("getledgerentryraw", &QueryServer::getLedgerEntryRaw);
7980
addRoute("getledgerentry", &QueryServer::getLedgerEntry);
81+
addRoute("getrestoreproof", &QueryServer::getRestoreProof);
82+
addRoute("getcreationproof", &QueryServer::getCreationProof);
8083

8184
auto workerPids = mServer.start();
8285
for (auto pid : workerPids)
@@ -412,4 +415,92 @@ QueryServer::getLedgerEntry(std::string const& params, std::string const& body,
412415
retStr = Json::FastWriter().write(root);
413416
return true;
414417
}
418+
419+
bool
420+
QueryServer::getRestoreProof(std::string const& params, std::string const& body,
421+
std::string& retStr)
422+
{
423+
ZoneScoped;
424+
Json::Value root;
425+
426+
std::map<std::string, std::vector<std::string>> paramMap;
427+
httpThreaded::server::server::parsePostParams(body, paramMap);
428+
429+
auto keys = paramMap["key"];
430+
auto snapshotLedger = parseOptionalParam<uint32_t>(paramMap, "ledgerSeq");
431+
if (keys.empty())
432+
{
433+
throw std::invalid_argument(
434+
"Must specify ledger key in POST body: key=<LedgerKey in base64 "
435+
"XDR format>");
436+
}
437+
438+
xdr::xvector<ArchivalProof> proof;
439+
auto& hotBL = mHotArchiveBucketListSnapshots.at(std::this_thread::get_id());
440+
for (auto const& key : keys)
441+
{
442+
LedgerKey lk;
443+
fromOpaqueBase64(lk, key);
444+
if (!isPersistentEntry(lk))
445+
{
446+
throw std::invalid_argument(
447+
"Only persistent entries require restoration proofs");
448+
}
449+
450+
if (!addRestorationProof(hotBL, lk, proof, snapshotLedger))
451+
{
452+
throw std::invalid_argument("No valid proof exists for key");
453+
}
454+
}
455+
456+
root["ledger"] = hotBL->getLedgerSeq();
457+
root["proof"] = toOpaqueBase64(proof);
458+
459+
retStr = Json::FastWriter().write(root);
460+
return true;
461+
}
462+
463+
bool
464+
QueryServer::getCreationProof(std::string const& params,
465+
std::string const& body, std::string& retStr)
466+
{
467+
ZoneScoped;
468+
Json::Value root;
469+
470+
std::map<std::string, std::vector<std::string>> paramMap;
471+
httpThreaded::server::server::parsePostParams(body, paramMap);
472+
473+
auto keys = paramMap["key"];
474+
auto snapshotLedger = parseOptionalParam<uint32_t>(paramMap, "ledgerSeq");
475+
if (keys.empty())
476+
{
477+
throw std::invalid_argument(
478+
"Must specify ledger key in POST body: key=<LedgerKey in base64 "
479+
"XDR format>");
480+
}
481+
482+
auto& hotBL = mHotArchiveBucketListSnapshots.at(std::this_thread::get_id());
483+
xdr::xvector<ArchivalProof> proof;
484+
for (auto const& key : keys)
485+
{
486+
LedgerKey lk;
487+
fromOpaqueBase64(lk, key);
488+
if (!isPersistentEntry(lk) || lk.type() != CONTRACT_DATA)
489+
{
490+
throw std::invalid_argument("Only persistent contract data entries "
491+
"require creation proofs");
492+
}
493+
494+
if (!addCreationProof(mSimulateFilterMiss, lk, proof))
495+
{
496+
throw std::invalid_argument("No valid proof exists for key");
497+
}
498+
}
499+
500+
root["ledger"] = snapshotLedger ? *snapshotLedger : hotBL->getLedgerSeq();
501+
root["proof"] = toOpaqueBase64(proof);
502+
503+
retStr = Json::FastWriter().write(root);
504+
return true;
505+
}
415506
}

‎src/main/QueryServer.h

+9
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ class QueryServer
5959
bool getLedgerEntry(std::string const& params, std::string const& body,
6060
std::string& retStr);
6161

62+
// Returns restoration/creation proofs for the given set of keys.
63+
bool getRestoreProof(std::string const& params, std::string const& body,
64+
std::string& retStr);
65+
66+
// Test string for artificial miss:
67+
// AAAABgAAAAEBuCkP1Jtb0TMPDqcYvepnKTIGIPZc4adjZXxXY4xYCwAAAA8AAAAEbWlzcwAAAAE=
68+
bool getCreationProof(std::string const& params, std::string const& body,
69+
std::string& retStr);
70+
6271
public:
6372
QueryServer(const std::string& address, unsigned short port, int maxClient,
6473
size_t threadPoolSize,

‎src/transactions/test/InvokeHostFunctionTests.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -2993,7 +2993,7 @@ TEST_CASE("evicted persistent entries"
29932993
xdr::xvector<ArchivalProof> proofs;
29942994
auto wrongLk = client.getContract().getDataKey(
29952995
makeSymbolSCVal("null"), ContractDataDurability::PERSISTENT);
2996-
REQUIRE(!addRestorationProof(test.getApp(), wrongLk, proofs));
2996+
REQUIRE(!addRestorationProof(hotArchive, wrongLk, proofs));
29972997

29982998
// RestoreOp should fail with no proof/ empty proof
29992999
SorobanResources restoreResources;
@@ -3015,7 +3015,7 @@ TEST_CASE("evicted persistent entries"
30153015
.restoreFootprintResult()
30163016
.code() == RESTORE_FOOTPRINT_MALFORMED);
30173017

3018-
REQUIRE(addRestorationProof(test.getApp(), lk, proofs));
3018+
REQUIRE(addRestorationProof(hotArchive, lk, proofs));
30193019

30203020
// Should succeed after adding proper proofs
30213021
test.invokeRestoreOp({lk}, expectedRefundableFeeCharged, proofs);
@@ -3084,7 +3084,7 @@ TEST_CASE("persistent entry archival filters", "[soroban][archival]")
30843084

30853085
xdr::xvector<ArchivalProof> proofs;
30863086
addCreationProof(
3087-
test.getApp(),
3087+
cfg.ARTIFICIALLY_SIMULATE_ARCHIVE_FILTER_MISS,
30883088
client.getContract().getDataKey(makeSymbolSCVal("miss"),
30893089
ContractDataDurability::PERSISTENT),
30903090
proofs);

‎src/util/ArchivalProofs.cpp

+49-32
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
#include "util/UnorderedSet.h"
1313
#include "xdr/Stellar-ledger-entries.h"
1414
#include "xdr/Stellar-transaction.h"
15+
#include <memory>
16+
#include <optional>
17+
#include <stdexcept>
1518

1619
namespace stellar
1720
{
@@ -100,43 +103,43 @@ isCreatedKeyProven(Application& app, LedgerKey const& lk,
100103
}
101104

102105
bool
103-
addCreationProof(Application& app, LedgerKey const& lk,
106+
addCreationProof(bool simulateBloomMiss, LedgerKey const& lk,
104107
xdr::xvector<ArchivalProof>& proofs)
105108
{
106109
#ifdef BUILD_TESTS
107-
// For now only support proof generation for testing
108-
releaseAssertOrThrow(
109-
app.getConfig().ARTIFICIALLY_SIMULATE_ARCHIVE_FILTER_MISS);
110-
111-
// Only persistent contract data entries need creation proofs
112-
if (lk.type() != CONTRACT_DATA ||
113-
lk.contractData().durability != PERSISTENT)
110+
if (simulateBloomMiss)
114111
{
115-
return true;
116-
}
112+
// Only persistent contract data entries need creation proofs
113+
if (lk.type() != CONTRACT_DATA ||
114+
lk.contractData().durability != PERSISTENT)
115+
{
116+
return true;
117+
}
117118

118-
for (auto& proof : proofs)
119-
{
120-
if (proof.body.t() == NONEXISTENCE)
119+
for (auto& proof : proofs)
121120
{
122-
for (auto const& key : proof.body.nonexistenceProof().keysToProve)
121+
if (proof.body.t() == NONEXISTENCE)
123122
{
124-
if (key == lk)
123+
for (auto const& key :
124+
proof.body.nonexistenceProof().keysToProve)
125125
{
126-
// Proof already exists
127-
return true;
126+
if (key == lk)
127+
{
128+
// Proof already exists
129+
return true;
130+
}
128131
}
129-
}
130132

131-
proof.body.nonexistenceProof().keysToProve.push_back(lk);
132-
return true;
133+
proof.body.nonexistenceProof().keysToProve.push_back(lk);
134+
return true;
135+
}
133136
}
134-
}
135137

136-
proofs.emplace_back();
137-
auto& nonexistenceProof = proofs.back();
138-
nonexistenceProof.body.t(NONEXISTENCE);
139-
nonexistenceProof.body.nonexistenceProof().keysToProve.push_back(lk);
138+
proofs.emplace_back();
139+
auto& nonexistenceProof = proofs.back();
140+
nonexistenceProof.body.t(NONEXISTENCE);
141+
nonexistenceProof.body.nonexistenceProof().keysToProve.push_back(lk);
142+
}
140143
#endif
141144

142145
return true;
@@ -238,18 +241,32 @@ getRestoredEntryFromProof(Application& app, LedgerKey const& lk,
238241
}
239242

240243
bool
241-
addRestorationProof(Application& app, LedgerKey const& lk,
242-
xdr::xvector<ArchivalProof>& proofs)
244+
addRestorationProof(
245+
std::shared_ptr<SearchableHotArchiveBucketListSnapshot> hotArchive,
246+
LedgerKey const& lk, xdr::xvector<ArchivalProof>& proofs,
247+
std::optional<uint32_t> ledgerSeq)
243248
{
244249
#ifdef BUILD_TESTS
245250
// For now only support proof generation for testing
246-
releaseAssertOrThrow(
247-
app.getConfig().REQUIRE_PROOFS_FOR_ALL_EVICTED_ENTRIES);
248251
releaseAssertOrThrow(isPersistentEntry(lk));
249252

250-
auto hotBL =
251-
app.getBucketManager().getSearchableHotArchiveBucketListSnapshot();
252-
auto entry = hotBL->load(lk);
253+
std::shared_ptr<HotArchiveBucketEntry> entry = nullptr;
254+
if (ledgerSeq)
255+
{
256+
auto entryOp = hotArchive->loadKeysFromLedger({lk}, *ledgerSeq);
257+
if (!entryOp)
258+
{
259+
throw std::invalid_argument("LedgerSeq not found");
260+
}
261+
262+
entry =
263+
std::make_shared<HotArchiveBucketEntry>(std::move(entryOp->at(0)));
264+
}
265+
else
266+
{
267+
entry = hotArchive->load(lk);
268+
}
269+
253270
if (!entry ||
254271
entry->type() != HotArchiveBucketEntryType::HOT_ARCHIVE_ARCHIVED)
255272
{

‎src/util/ArchivalProofs.h

+11-3
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
#include "xdr/Stellar-transaction.h"
88
#include <xdrpp/types.h>
99

10+
#include <optional>
11+
1012
namespace stellar
1113
{
1214

1315
class Application;
16+
class SearchableHotArchiveBucketListSnapshot;
1417

1518
// Returns true if the proof structure is valid. This function does not check
1619
// the validity of the proof itself, just that the structure is correct (i.e. no
@@ -26,7 +29,7 @@ bool isCreatedKeyProven(Application& app, LedgerKey const& lk,
2629

2730
// Adds a creation proof for lk to proofs. Returns true if a proof was added or
2831
// is not necessary. Returns false if no valid proof exists.
29-
bool addCreationProof(Application& app, LedgerKey const& lk,
32+
bool addCreationProof(bool simulateBloomMiss, LedgerKey const& lk,
3033
xdr::xvector<ArchivalProof>& proofs);
3134

3235
// Returns true if the proof structure is valid. This function does not check
@@ -47,6 +50,11 @@ getRestoredEntryFromProof(Application& app, LedgerKey const& lk,
4750

4851
// Adds a restoration proof for lk to proofs. Returns true if a proof was added
4952
// or is not necessary. Returns false if no valid proof exists.
50-
bool addRestorationProof(Application& app, LedgerKey const& lk,
51-
xdr::xvector<ArchivalProof>& proofs);
53+
// If ledgerSeq is specified, the entry will be restored based on the snapshot
54+
// at that ledger. TODO: Fix race condition when ledgerSeq is populated by
55+
// loading keys in batches.
56+
bool addRestorationProof(
57+
std::shared_ptr<SearchableHotArchiveBucketListSnapshot> hotArchive,
58+
LedgerKey const& lk, xdr::xvector<ArchivalProof>& proofs,
59+
std::optional<uint32_t> ledgerSeq = std::nullopt);
5260
}

‎src/util/test/ArchivalProofsTests.cpp

+12-4
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ TEST_CASE("creation proofs", "[archival][soroban]")
5151
// Empty proof fails
5252
REQUIRE(checkCreationProofValidity(proofs));
5353
REQUIRE(!isCreatedKeyProven(*app, LedgerEntryKey(entries[0]), proofs));
54-
REQUIRE(addCreationProof(*app, LedgerEntryKey(entries[0]), proofs));
54+
REQUIRE(addCreationProof(
55+
app->getConfig().ARTIFICIALLY_SIMULATE_ARCHIVE_FILTER_MISS,
56+
LedgerEntryKey(entries[0]), proofs));
5557
REQUIRE(checkCreationProofValidity(proofs));
5658
REQUIRE(isCreatedKeyProven(*app, LedgerEntryKey(entries[0]), proofs));
5759

@@ -63,7 +65,9 @@ TEST_CASE("creation proofs", "[archival][soroban]")
6365
REQUIRE(!isCreatedKeyProven(*app, LedgerEntryKey(entries[1]), proofs));
6466

6567
// Proofs work for multiple keys
66-
REQUIRE(addCreationProof(*app, LedgerEntryKey(entries[1]), proofs));
68+
REQUIRE(addCreationProof(
69+
app->getConfig().ARTIFICIALLY_SIMULATE_ARCHIVE_FILTER_MISS,
70+
LedgerEntryKey(entries[1]), proofs));
6771
REQUIRE(checkCreationProofValidity(proofs));
6872
REQUIRE(isCreatedKeyProven(*app, LedgerEntryKey(entries[1]), proofs));
6973
REQUIRE(isCreatedKeyProven(*app, LedgerEntryKey(entries[0]), proofs));
@@ -84,12 +88,16 @@ TEST_CASE("creation proofs", "[archival][soroban]")
8488

8589
REQUIRE(checkCreationProofValidity(proofs));
8690
REQUIRE(isCreatedKeyProven(*app, LedgerEntryKey(temp), proofs));
87-
REQUIRE(addCreationProof(*app, LedgerEntryKey(temp), proofs));
91+
REQUIRE(addCreationProof(
92+
app->getConfig().ARTIFICIALLY_SIMULATE_ARCHIVE_FILTER_MISS,
93+
LedgerEntryKey(temp), proofs));
8894
REQUIRE(checkCreationProofValidity(proofs));
8995
REQUIRE(proofs.size() == 0);
9096

9197
REQUIRE(isCreatedKeyProven(*app, LedgerEntryKey(code), proofs));
92-
REQUIRE(addCreationProof(*app, LedgerEntryKey(code), proofs));
98+
REQUIRE(addCreationProof(
99+
app->getConfig().ARTIFICIALLY_SIMULATE_ARCHIVE_FILTER_MISS,
100+
LedgerEntryKey(code), proofs));
93101
REQUIRE(checkCreationProofValidity(proofs));
94102
REQUIRE(proofs.size() == 0);
95103
}

0 commit comments

Comments
 (0)
Please sign in to comment.