mirror of
https://github.com/Xahau/xahaud.git
synced 2026-04-29 15:37:46 +00:00
feat: add proof module for XPOP construction
New module at src/xrpld/app/proof/ with layered design: - ProofBuilder: SHAMap merkle proof extraction (extractProofV1) Binary trie proof with 16-way branching, root hash verification. - LedgerProof: proof-of-ledger (header fields + tx blob/meta + merkle proof) buildLedgerProof() extracts everything from a closed Ledger. - XPOPv1: JSON format builder matching Import.cpp expectations buildXPOPv1() creates complete XPOP with validation signatures. Designed for versioning: v1 JSON (current Import compat), future v2 binary proofs and account state proofs layer on the same core.
This commit is contained in:
52
src/xrpld/app/proof/LedgerProof.h
Normal file
52
src/xrpld/app/proof/LedgerProof.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef RIPPLE_APP_PROOF_LEDGERPROOF_H_INCLUDED
|
||||
#define RIPPLE_APP_PROOF_LEDGERPROOF_H_INCLUDED
|
||||
|
||||
#include <xrpld/app/ledger/Ledger.h>
|
||||
#include <xrpld/app/proof/ProofBuilder.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
#include <optional>
|
||||
|
||||
namespace ripple {
|
||||
namespace proof {
|
||||
|
||||
/// Proof-of-ledger: everything needed to prove a transaction (or account
|
||||
/// state) was in a specific closed ledger. This is the core building
|
||||
/// block for XPOPs and future proof formats.
|
||||
struct LedgerProof
|
||||
{
|
||||
// --- Ledger Header ---
|
||||
std::uint32_t ledgerIndex{0};
|
||||
std::uint64_t totalCoins{0};
|
||||
uint256 parentHash;
|
||||
uint256 txRoot;
|
||||
uint256 accountRoot;
|
||||
std::uint32_t parentCloseTime{0};
|
||||
std::uint32_t closeTime{0};
|
||||
std::uint8_t closeTimeResolution{0};
|
||||
std::uint8_t closeFlags{0};
|
||||
|
||||
/// Recompute the ledger hash from header fields.
|
||||
uint256
|
||||
computeLedgerHash() const;
|
||||
|
||||
// --- Transaction Proof ---
|
||||
/// The transaction blob (serialized STTx).
|
||||
Blob txBlob;
|
||||
|
||||
/// The transaction metadata blob.
|
||||
Blob metaBlob;
|
||||
|
||||
/// Merkle proof from the transaction tree.
|
||||
std::optional<MerkleProof> txProof;
|
||||
};
|
||||
|
||||
/// Build a LedgerProof for a specific transaction in a closed ledger.
|
||||
/// Returns nullopt if the transaction is not found.
|
||||
std::optional<LedgerProof>
|
||||
buildLedgerProof(Ledger const& ledger, uint256 const& txHash);
|
||||
|
||||
} // namespace proof
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
68
src/xrpld/app/proof/ProofBuilder.h
Normal file
68
src/xrpld/app/proof/ProofBuilder.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef RIPPLE_APP_PROOF_PROOFBUILDER_H_INCLUDED
|
||||
#define RIPPLE_APP_PROOF_PROOFBUILDER_H_INCLUDED
|
||||
|
||||
#include <xrpld/shamap/SHAMap.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
namespace proof {
|
||||
|
||||
/// A single node in a binary trie merkle proof.
|
||||
/// Each inner node has 16 branches (SHAMap is a 16-way radix trie).
|
||||
/// The target branch contains either a child ProofNode or is the leaf.
|
||||
/// Non-target branches store their hash (for reconstruction).
|
||||
struct ProofNode
|
||||
{
|
||||
/// Branch hashes. All 16 branches are present.
|
||||
/// The target branch is identified by `targetBranch`.
|
||||
std::array<uint256, 16> branches;
|
||||
|
||||
/// Which branch (0-15) leads deeper toward the target leaf.
|
||||
/// Only meaningful for inner nodes in the path.
|
||||
std::uint8_t targetBranch{0};
|
||||
|
||||
/// True if this is the deepest inner node (branches[targetBranch]
|
||||
/// is the leaf hash, not another inner node).
|
||||
bool isLeafParent{false};
|
||||
};
|
||||
|
||||
/// A merkle proof: path from root to a specific leaf in a SHAMap.
|
||||
/// Verifiable by hashing from leaf up through each ProofNode.
|
||||
struct MerkleProof
|
||||
{
|
||||
/// The key (hash) of the target item.
|
||||
uint256 key;
|
||||
|
||||
/// The leaf hash (hash of the item's content).
|
||||
uint256 leafHash;
|
||||
|
||||
/// Path of inner nodes from root to the leaf's parent.
|
||||
std::vector<ProofNode> path;
|
||||
|
||||
/// Reconstruct the root hash from this proof.
|
||||
/// Returns nullopt if the proof is malformed.
|
||||
std::optional<uint256>
|
||||
computeRoot() const;
|
||||
|
||||
/// Verify this proof against an expected root hash.
|
||||
bool
|
||||
verify(uint256 const& expectedRoot) const;
|
||||
|
||||
/// Serialize to JSON (v1 compatibility — nested arrays).
|
||||
Json::Value
|
||||
toJsonV1() const;
|
||||
};
|
||||
|
||||
/// Extract a merkle proof for a specific item from a SHAMap.
|
||||
/// V1 leaf hash: SHA512Half(txNode prefix + item_data + item_key).
|
||||
/// Returns nullopt if the item is not in the map.
|
||||
std::optional<MerkleProof>
|
||||
extractProofV1(SHAMap const& map, uint256 const& key);
|
||||
|
||||
} // namespace proof
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
72
src/xrpld/app/proof/XPOPv1.h
Normal file
72
src/xrpld/app/proof/XPOPv1.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef RIPPLE_APP_PROOF_XPOPV1_H_INCLUDED
|
||||
#define RIPPLE_APP_PROOF_XPOPV1_H_INCLUDED
|
||||
|
||||
#include <xrpld/app/proof/LedgerProof.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/PublicKey.h>
|
||||
#include <xrpl/protocol/SecretKey.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace ripple {
|
||||
namespace proof {
|
||||
|
||||
/// Validator key pair for signing validations in an XPOP.
|
||||
struct ValidatorKeys
|
||||
{
|
||||
PublicKey masterPublic;
|
||||
SecretKey masterSecret;
|
||||
PublicKey signingPublic; // ephemeral (manifest signing key)
|
||||
SecretKey signingSecret;
|
||||
std::string manifest; // base64-encoded manifest
|
||||
};
|
||||
|
||||
/// Validator List (VL) data for the XPOP.
|
||||
struct VLData
|
||||
{
|
||||
PublicKey masterPublic; // VL master key
|
||||
SecretKey masterSecret;
|
||||
std::string manifest; // base64-encoded VL manifest
|
||||
std::string blob; // base64-encoded VL blob (validator list)
|
||||
std::string signature; // hex signature of the blob
|
||||
std::uint32_t version{1};
|
||||
};
|
||||
|
||||
/// Build an XPOP v1 JSON object from a LedgerProof.
|
||||
///
|
||||
/// The XPOP format matches what Import.cpp::syntaxCheckXPOP expects:
|
||||
/// {
|
||||
/// "ledger": { index, coins, phash, txroot, acroot, pclose, close, cres,
|
||||
/// flags }, "transaction": { blob, meta, proof }, "validation": { data: {
|
||||
/// pubkey: validation_hex, ... }, unl: { ... } }
|
||||
/// }
|
||||
///
|
||||
/// @param proof The ledger proof containing tx blob, meta, and merkle
|
||||
/// proof
|
||||
/// @param validators Validator keys to sign the ledger hash
|
||||
/// @param vl Validator list data
|
||||
/// @return XPOP as a Json::Value
|
||||
Json::Value
|
||||
buildXPOPv1(
|
||||
LedgerProof const& proof,
|
||||
std::vector<ValidatorKeys> const& validators,
|
||||
VLData const& vl);
|
||||
|
||||
/// Convenience: build XPOP from a ReadView + tx hash + validator keys.
|
||||
/// Combines buildLedgerProof + buildXPOPv1.
|
||||
Json::Value
|
||||
buildXPOPv1(
|
||||
ReadView const& ledger,
|
||||
uint256 const& txHash,
|
||||
std::vector<ValidatorKeys> const& validators,
|
||||
VLData const& vl);
|
||||
|
||||
/// Encode an XPOP Json::Value to the hex blob format that ttIMPORT expects
|
||||
/// in sfBlob.
|
||||
std::string
|
||||
xpopToHex(Json::Value const& xpop);
|
||||
|
||||
} // namespace proof
|
||||
} // namespace ripple
|
||||
|
||||
#endif
|
||||
72
src/xrpld/app/proof/detail/LedgerProof.cpp
Normal file
72
src/xrpld/app/proof/detail/LedgerProof.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <xrpld/app/proof/LedgerProof.h>
|
||||
#include <xrpl/protocol/HashPrefix.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace proof {
|
||||
|
||||
uint256
|
||||
LedgerProof::computeLedgerHash() const
|
||||
{
|
||||
return sha512Half(
|
||||
HashPrefix::ledgerMaster,
|
||||
ledgerIndex,
|
||||
totalCoins,
|
||||
parentHash,
|
||||
txRoot,
|
||||
accountRoot,
|
||||
parentCloseTime,
|
||||
closeTime,
|
||||
closeTimeResolution,
|
||||
closeFlags);
|
||||
}
|
||||
|
||||
std::optional<LedgerProof>
|
||||
buildLedgerProof(Ledger const& ledger, uint256 const& txHash)
|
||||
{
|
||||
auto const& info = ledger.info();
|
||||
|
||||
// Find the transaction in the ledger.
|
||||
auto const txResult = ledger.txRead(txHash);
|
||||
if (!txResult.first)
|
||||
return std::nullopt;
|
||||
|
||||
LedgerProof proof;
|
||||
|
||||
// Ledger header fields.
|
||||
proof.ledgerIndex = info.seq;
|
||||
proof.totalCoins = info.drops.drops();
|
||||
proof.parentHash = info.parentHash;
|
||||
proof.txRoot = info.txHash;
|
||||
proof.accountRoot = info.accountHash;
|
||||
proof.parentCloseTime = info.parentCloseTime.time_since_epoch().count();
|
||||
proof.closeTime = info.closeTime.time_since_epoch().count();
|
||||
proof.closeTimeResolution = info.closeTimeResolution.count();
|
||||
proof.closeFlags = info.closeFlags;
|
||||
|
||||
// Transaction blob.
|
||||
{
|
||||
Serializer s;
|
||||
txResult.first->add(s);
|
||||
proof.txBlob = s.peekData();
|
||||
}
|
||||
|
||||
// Transaction metadata.
|
||||
if (txResult.second)
|
||||
{
|
||||
Serializer s;
|
||||
txResult.second->add(s);
|
||||
proof.metaBlob = s.peekData();
|
||||
}
|
||||
|
||||
// Merkle proof from the transaction SHAMap (v1 format).
|
||||
auto const& txMap = ledger.txMap();
|
||||
auto const txKey =
|
||||
sha512Half(HashPrefix::transactionID, makeSlice(proof.txBlob));
|
||||
proof.txProof = extractProofV1(txMap, txKey);
|
||||
|
||||
return proof;
|
||||
}
|
||||
|
||||
} // namespace proof
|
||||
} // namespace ripple
|
||||
305
src/xrpld/app/proof/detail/ProofBuilder.cpp
Normal file
305
src/xrpld/app/proof/detail/ProofBuilder.cpp
Normal file
@@ -0,0 +1,305 @@
|
||||
#include <xrpld/app/proof/ProofBuilder.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/basics/StringUtilities.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/protocol/HashPrefix.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace proof {
|
||||
|
||||
// --- MerkleProof ---
|
||||
|
||||
std::optional<uint256>
|
||||
MerkleProof::computeRoot() const
|
||||
{
|
||||
if (path.empty())
|
||||
return std::nullopt;
|
||||
|
||||
// Start with the leaf hash and work up through the path.
|
||||
uint256 currentHash = leafHash;
|
||||
|
||||
// Path is root-to-leaf, so iterate in reverse.
|
||||
for (auto it = path.rbegin(); it != path.rend(); ++it)
|
||||
{
|
||||
sha512_half_hasher h;
|
||||
using beast::hash_append;
|
||||
hash_append(h, HashPrefix::innerNode);
|
||||
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
if (i == it->targetBranch)
|
||||
hash_append(h, currentHash);
|
||||
else
|
||||
hash_append(h, it->branches[i]);
|
||||
}
|
||||
|
||||
currentHash = static_cast<uint256>(h);
|
||||
}
|
||||
|
||||
return currentHash;
|
||||
}
|
||||
|
||||
bool
|
||||
MerkleProof::verify(uint256 const& expectedRoot) const
|
||||
{
|
||||
auto const computed = computeRoot();
|
||||
return computed && *computed == expectedRoot;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
MerkleProof::toJsonV1() const
|
||||
{
|
||||
// Build the nested array format that Import.cpp expects.
|
||||
// Start from the deepest level and work up.
|
||||
// Each level is an array of 16 entries: 15 hex hashes + 1 nested
|
||||
// array (or the leaf hash at the bottom level).
|
||||
|
||||
// Build bottom-up: the deepest ProofNode's target branch
|
||||
// contains the leaf hash directly.
|
||||
Json::Value current;
|
||||
|
||||
for (auto it = path.rbegin(); it != path.rend(); ++it)
|
||||
{
|
||||
Json::Value level(Json::arrayValue);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
if (i == it->targetBranch)
|
||||
{
|
||||
if (it == path.rbegin())
|
||||
{
|
||||
// Deepest level: the target branch IS the leaf hash
|
||||
level.append(to_string(leafHash));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Inner level: the target branch is the nested proof
|
||||
level.append(current);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
level.append(to_string(it->branches[i]));
|
||||
}
|
||||
}
|
||||
current = level;
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
// --- extractProof ---
|
||||
|
||||
namespace {
|
||||
|
||||
/// Hash a transaction blob: SHA512Half(TXN prefix + blob)
|
||||
uint256
|
||||
hashTxBlob(Slice const& txBlob)
|
||||
{
|
||||
return sha512Half(HashPrefix::transactionID, txBlob);
|
||||
}
|
||||
|
||||
/// Hash a tx+meta pair: SHA512Half(SND prefix + vl(tx) + tx + vl(meta) + meta +
|
||||
/// hashTxBlob(tx)) This matches SHAMapInnerNode's leaf hash computation.
|
||||
uint256
|
||||
hashTxAndMeta(Slice const& txBlob, Slice const& metaBlob)
|
||||
{
|
||||
Serializer s(txBlob.size() + metaBlob.size() + 256);
|
||||
|
||||
// The SHAMap stores tx leaves as:
|
||||
// HashPrefix::txNode + vl(tx) + vl(meta) + txHash
|
||||
// where vl() is the variable-length encoding.
|
||||
|
||||
s.addRaw(txBlob);
|
||||
Serializer metaSer;
|
||||
metaSer.addRaw(metaBlob);
|
||||
|
||||
return sha512Half(
|
||||
HashPrefix::txNode,
|
||||
makeSlice(s.peekData()),
|
||||
makeSlice(metaSer.peekData()),
|
||||
hashTxBlob(txBlob));
|
||||
}
|
||||
|
||||
/// Simple in-memory radix trie node for proof construction.
|
||||
struct TrieNode
|
||||
{
|
||||
uint256 hash;
|
||||
uint256 key;
|
||||
std::array<std::unique_ptr<TrieNode>, 16> children;
|
||||
bool isLeaf{false};
|
||||
|
||||
bool
|
||||
hasChildren() const
|
||||
{
|
||||
for (auto const& c : children)
|
||||
if (c)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
int
|
||||
childCount() const
|
||||
{
|
||||
int count = 0;
|
||||
for (auto const& c : children)
|
||||
if (c)
|
||||
++count;
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
/// Get the nibble at position `depth` in a 256-bit key.
|
||||
int
|
||||
getNibble(uint256 const& key, int depth)
|
||||
{
|
||||
// Each byte has 2 nibbles. depth 0 = high nibble of byte 0.
|
||||
int byteIdx = depth / 2;
|
||||
int nibbleIdx = depth % 2;
|
||||
auto byte = key.data()[byteIdx];
|
||||
return nibbleIdx == 0 ? (byte >> 4) & 0x0F : byte & 0x0F;
|
||||
}
|
||||
|
||||
/// Insert a leaf into the trie.
|
||||
void
|
||||
trieInsert(TrieNode& root, uint256 const& key, uint256 const& leafHash)
|
||||
{
|
||||
TrieNode* node = &root;
|
||||
int depth = 0;
|
||||
|
||||
while (depth < 64) // 256 bits / 4 bits per nibble = 64 max depth
|
||||
{
|
||||
int nibble = getNibble(key, depth);
|
||||
|
||||
if (!node->children[nibble])
|
||||
{
|
||||
// Empty slot — insert leaf here.
|
||||
node->children[nibble] = std::make_unique<TrieNode>();
|
||||
node->children[nibble]->hash = leafHash;
|
||||
node->children[nibble]->key = key;
|
||||
node->children[nibble]->isLeaf = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->children[nibble]->isLeaf)
|
||||
{
|
||||
// Collision — need to split.
|
||||
auto existing = std::move(node->children[nibble]);
|
||||
node->children[nibble] = std::make_unique<TrieNode>();
|
||||
auto* newInner = node->children[nibble].get();
|
||||
|
||||
// Re-insert the existing leaf deeper.
|
||||
int existingNibble = getNibble(existing->key, depth + 1);
|
||||
newInner->children[existingNibble] = std::move(existing);
|
||||
|
||||
// Continue inserting the new leaf.
|
||||
node = newInner;
|
||||
++depth;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Inner node — descend.
|
||||
node = node->children[nibble].get();
|
||||
++depth;
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute hashes bottom-up for the trie.
|
||||
uint256
|
||||
trieComputeHash(TrieNode& node)
|
||||
{
|
||||
if (node.isLeaf)
|
||||
return node.hash;
|
||||
|
||||
sha512_half_hasher h;
|
||||
using beast::hash_append;
|
||||
hash_append(h, HashPrefix::innerNode);
|
||||
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
if (node.children[i])
|
||||
hash_append(h, trieComputeHash(*node.children[i]));
|
||||
else
|
||||
hash_append(h, uint256{});
|
||||
}
|
||||
|
||||
node.hash = static_cast<uint256>(h);
|
||||
return node.hash;
|
||||
}
|
||||
|
||||
/// Extract proof path from root to a specific key.
|
||||
bool
|
||||
trieExtractProof(
|
||||
TrieNode const& node,
|
||||
uint256 const& key,
|
||||
int depth,
|
||||
std::vector<ProofNode>& path,
|
||||
uint256& outLeafHash)
|
||||
{
|
||||
if (node.isLeaf)
|
||||
{
|
||||
if (node.key == key)
|
||||
{
|
||||
outLeafHash = node.hash;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int nibble = getNibble(key, depth);
|
||||
if (!node.children[nibble])
|
||||
return false;
|
||||
|
||||
ProofNode pn;
|
||||
pn.targetBranch = static_cast<std::uint8_t>(nibble);
|
||||
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
if (node.children[i])
|
||||
pn.branches[i] = node.children[i]->hash;
|
||||
// else: already zero-initialized
|
||||
}
|
||||
|
||||
if (node.children[nibble]->isLeaf)
|
||||
{
|
||||
if (node.children[nibble]->key != key)
|
||||
return false;
|
||||
pn.isLeafParent = true;
|
||||
path.push_back(pn);
|
||||
outLeafHash = node.children[nibble]->hash;
|
||||
return true;
|
||||
}
|
||||
|
||||
path.push_back(pn);
|
||||
return trieExtractProof(
|
||||
*node.children[nibble], key, depth + 1, path, outLeafHash);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::optional<MerkleProof>
|
||||
extractProofV1(SHAMap const& map, uint256 const& key)
|
||||
{
|
||||
// Build an in-memory trie from all SHAMap leaves.
|
||||
// V1 leaf hash: SHA512Half(txNode prefix + item_data + item_key)
|
||||
TrieNode root;
|
||||
|
||||
map.visitLeaves([&](boost::intrusive_ptr<SHAMapItem const> const& item) {
|
||||
auto const itemHash =
|
||||
sha512Half(HashPrefix::txNode, item->slice(), item->key());
|
||||
trieInsert(root, item->key(), itemHash);
|
||||
});
|
||||
|
||||
trieComputeHash(root);
|
||||
|
||||
MerkleProof proof;
|
||||
proof.key = key;
|
||||
|
||||
if (!trieExtractProof(root, key, 0, proof.path, proof.leafHash))
|
||||
return std::nullopt;
|
||||
|
||||
return proof;
|
||||
}
|
||||
|
||||
} // namespace proof
|
||||
} // namespace ripple
|
||||
123
src/xrpld/app/proof/detail/XPOPv1.cpp
Normal file
123
src/xrpld/app/proof/detail/XPOPv1.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#include <xrpld/app/proof/XPOPv1.h>
|
||||
#include <xrpl/basics/StringUtilities.h>
|
||||
#include <xrpl/basics/base64.h>
|
||||
#include <xrpl/json/json_writer.h>
|
||||
#include <xrpl/protocol/HashPrefix.h>
|
||||
#include <xrpl/protocol/STValidation.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace proof {
|
||||
|
||||
namespace {
|
||||
|
||||
/// Build a validation STObject, sign it, and serialize to hex.
|
||||
std::string
|
||||
buildValidationHex(
|
||||
uint256 const& ledgerHash,
|
||||
std::uint32_t ledgerSeq,
|
||||
PublicKey const& signingPublic,
|
||||
SecretKey const& signingSecret)
|
||||
{
|
||||
auto val = std::make_shared<STValidation>(
|
||||
NetClock::time_point{},
|
||||
signingPublic,
|
||||
signingSecret,
|
||||
calcNodeID(signingPublic),
|
||||
[&](STValidation& v) {
|
||||
v.setFieldH256(sfLedgerHash, ledgerHash);
|
||||
v.setFieldU32(sfLedgerSequence, ledgerSeq);
|
||||
v.setFlag(vfFullValidation);
|
||||
});
|
||||
|
||||
auto const serialized = val->getSerialized();
|
||||
return strHex(makeSlice(serialized));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Json::Value
|
||||
buildXPOPv1(
|
||||
LedgerProof const& proof,
|
||||
std::vector<ValidatorKeys> const& validators,
|
||||
VLData const& vl)
|
||||
{
|
||||
Json::Value xpop(Json::objectValue);
|
||||
|
||||
// --- ledger section ---
|
||||
Json::Value ledger(Json::objectValue);
|
||||
ledger[jss::index] = proof.ledgerIndex;
|
||||
ledger[jss::coins] = std::to_string(proof.totalCoins);
|
||||
ledger[jss::phash] = to_string(proof.parentHash);
|
||||
ledger[jss::txroot] = to_string(proof.txRoot);
|
||||
ledger[jss::acroot] = to_string(proof.accountRoot);
|
||||
ledger[jss::pclose] = proof.parentCloseTime;
|
||||
ledger[jss::close] = proof.closeTime;
|
||||
ledger[jss::cres] = proof.closeTimeResolution;
|
||||
ledger[jss::flags] = proof.closeFlags;
|
||||
xpop[jss::ledger] = ledger;
|
||||
|
||||
// --- transaction section ---
|
||||
Json::Value transaction(Json::objectValue);
|
||||
transaction[jss::blob] = strHex(makeSlice(proof.txBlob));
|
||||
transaction[jss::meta] = strHex(makeSlice(proof.metaBlob));
|
||||
|
||||
if (proof.txProof)
|
||||
transaction[jss::proof] = proof.txProof->toJsonV1();
|
||||
else
|
||||
transaction[jss::proof] = Json::Value(Json::arrayValue);
|
||||
|
||||
xpop[jss::transaction] = transaction;
|
||||
|
||||
// --- validation section ---
|
||||
auto const ledgerHash = proof.computeLedgerHash();
|
||||
|
||||
Json::Value validation(Json::objectValue);
|
||||
|
||||
// validation.data: map of nodepub → serialized validation hex
|
||||
Json::Value data(Json::objectValue);
|
||||
for (auto const& vk : validators)
|
||||
{
|
||||
auto const nodepub = toBase58(TokenType::NodePublic, vk.signingPublic);
|
||||
data[nodepub] = buildValidationHex(
|
||||
ledgerHash, proof.ledgerIndex, vk.signingPublic, vk.signingSecret);
|
||||
}
|
||||
validation[jss::data] = data;
|
||||
|
||||
// validation.unl: VL data
|
||||
Json::Value unl(Json::objectValue);
|
||||
unl[jss::public_key] = strHex(vl.masterPublic);
|
||||
unl[jss::manifest] = vl.manifest;
|
||||
unl[jss::blob] = vl.blob;
|
||||
unl[jss::signature] = vl.signature;
|
||||
unl[jss::version] = vl.version;
|
||||
validation[jss::unl] = unl;
|
||||
|
||||
xpop[jss::validation] = validation;
|
||||
|
||||
return xpop;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
buildXPOPv1(
|
||||
Ledger const& ledger,
|
||||
uint256 const& txHash,
|
||||
std::vector<ValidatorKeys> const& validators,
|
||||
VLData const& vl)
|
||||
{
|
||||
auto proof = buildLedgerProof(ledger, txHash);
|
||||
if (!proof)
|
||||
return Json::Value{};
|
||||
return buildXPOPv1(*proof, validators, vl);
|
||||
}
|
||||
|
||||
std::string
|
||||
xpopToHex(Json::Value const& xpop)
|
||||
{
|
||||
auto const json = Json::FastWriter().write(xpop);
|
||||
return strHex(json);
|
||||
}
|
||||
|
||||
} // namespace proof
|
||||
} // namespace ripple
|
||||
Reference in New Issue
Block a user