refactor(export): name transaction phases explicitly

This commit is contained in:
Nicholas Dudfield
2026-07-16 11:02:42 +07:00
parent 0eda858e09
commit 9d44f6fb0c
8 changed files with 82 additions and 62 deletions

View File

@@ -72,7 +72,7 @@ namespace {
struct ResolvedExportShare
{
std::shared_ptr<SLE const> latch;
STTx releaseTarget;
STTx exportSigningPayload;
};
enum class ExportShareResolutionStatus {
@@ -230,28 +230,28 @@ resolveExportShare(
return {ExportShareResolutionStatus::duplicate, std::nullopt};
}
auto const inner = ExportLedgerOps::innerExportedTx(*outer);
if (!inner)
auto const baseTarget = ExportLedgerOps::exportIntentTarget(*outer);
if (!baseTarget)
return {ExportShareResolutionStatus::invalid, std::nullopt};
auto const targetNetworkID = inner->isFieldPresent(sfNetworkID)
? inner->getFieldU32(sfNetworkID)
auto const targetNetworkID = baseTarget->isFieldPresent(sfNetworkID)
? baseTarget->getFieldU32(sfNetworkID)
: std::uint32_t{0};
ExportOriginMemo::Origin const origin{
app.config().NETWORK_ID, targetNetworkID, share.originTxn};
auto const identity = ExportOriginMemo::identityForm(*inner, origin);
auto release = ExportOriginMemo::releaseForm(
*inner,
auto const identity = ExportOriginMemo::identityForm(*baseTarget, origin);
auto signingPayload = ExportOriginMemo::releaseForm(
*baseTarget,
origin,
ExportOriginMemo::Anchor{
share.originLedgerSeq, share.originLedgerHash});
if (!identity || !release ||
if (!identity || !signingPayload ||
ExportResultBuilder::exportIntentHash(identity.value()) !=
latch->getFieldH256(sfDigest))
return {ExportShareResolutionStatus::invalid, std::nullopt};
return {
ExportShareResolutionStatus::resolved,
ResolvedExportShare{latch, std::move(release.value())}};
ResolvedExportShare{latch, std::move(signingPayload.value())}};
}
ExportShare
@@ -519,7 +519,7 @@ ConsensusExtensions::admitExportShare(
auto const signer = calcAccountID(share.signingKey);
auto const data =
buildMultiSigningData(resolved.value->releaseTarget, signer);
buildMultiSigningData(resolved.value->exportSigningPayload, signer);
auto const signatureVerified = verify(
share.signingKey,
data.slice(),
@@ -719,28 +719,28 @@ ConsensusExtensions::onValidatedLedger(
auto const [outer, _] = originLedger->txRead(origin);
if (!outer)
return;
auto const inner =
ExportLedgerOps::innerExportedTx(*outer);
if (!inner)
auto const baseTarget =
ExportLedgerOps::exportIntentTarget(*outer);
if (!baseTarget)
return;
auto const targetNetworkID =
inner->isFieldPresent(sfNetworkID)
? inner->getFieldU32(sfNetworkID)
baseTarget->isFieldPresent(sfNetworkID)
? baseTarget->getFieldU32(sfNetworkID)
: std::uint32_t{0};
auto release = ExportOriginMemo::releaseForm(
*inner,
auto signingPayload = ExportOriginMemo::releaseForm(
*baseTarget,
ExportOriginMemo::Origin{
app_.config().NETWORK_ID,
targetNetworkID,
origin},
ExportOriginMemo::Anchor{originSeq, *originHash});
if (!release)
if (!signingPayload)
return;
auto const signer = calcAccountID(keys.keys->publicKey);
auto const data =
buildMultiSigningData(release.value(), signer);
auto const data = buildMultiSigningData(
signingPayload.value(), signer);
auto const signature = sign(
keys.keys->publicKey,
keys.keys->secretKey,
@@ -2175,7 +2175,7 @@ ConsensusExtensions::agreedExportSignatures(
std::optional<ConsensusExtensions::ExportWitnessMaterial>
ConsensusExtensions::agreedExportWitness(
STTx const& releaseTarget,
STTx const& exportSigningPayload,
uint256 const& origin,
Blob const& committeeBitmap,
std::size_t universeSize,
@@ -2266,7 +2266,8 @@ ConsensusExtensions::agreedExportWitness(
}
auto const signature = sidecar.getFieldVL(sfTxnSignature);
auto const data = buildMultiSigningData(releaseTarget, signer);
auto const data =
buildMultiSigningData(exportSigningPayload, signer);
if (signature.empty() ||
signature.size() >
ExportSigCollectorV2::maxSignatureBytes ||
@@ -2804,8 +2805,9 @@ ConsensusExtensions::onPreBuild(
auto const [outer, _] = originLedger->txRead(origin);
if (!outer || outer->getTxnType() != ttEXPORT)
continue;
auto const inner = ExportLedgerOps::innerExportedTx(*outer);
if (!inner)
auto const baseTarget =
ExportLedgerOps::exportIntentTarget(*outer);
if (!baseTarget)
continue;
auto const universeHash =
@@ -2831,19 +2833,20 @@ ConsensusExtensions::onPreBuild(
if (!committee)
continue;
auto const targetNetworkID = inner->isFieldPresent(sfNetworkID)
? inner->getFieldU32(sfNetworkID)
auto const targetNetworkID =
baseTarget->isFieldPresent(sfNetworkID)
? baseTarget->getFieldU32(sfNetworkID)
: std::uint32_t{0};
auto const release = ExportOriginMemo::releaseForm(
*inner,
auto const signingPayload = ExportOriginMemo::releaseForm(
*baseTarget,
ExportOriginMemo::Origin{
app_.config().NETWORK_ID, targetNetworkID, origin},
ExportOriginMemo::Anchor{originSeq, *originHash});
if (!release)
if (!signingPayload)
continue;
auto material = agreedExportWitness(
release.value(),
signingPayload.value(),
origin,
committeeBitmap,
validatorView->orderedOriginalMasterKeys.size(),
@@ -2853,7 +2856,7 @@ ConsensusExtensions::onPreBuild(
auto witness = ExportResultBuilder::buildSignatureWitness(
origin,
release.value(),
signingPayload.value(),
material->signatures,
validatorView->orderedOriginalMasterKeys.size(),
seq);

View File

@@ -434,7 +434,7 @@ public:
std::optional<ExportWitnessMaterial>
agreedExportWitness(
STTx const& releaseTarget,
STTx const& exportSigningPayload,
uint256 const& origin,
Blob const& committee,
std::size_t universeSize,

View File

@@ -48,8 +48,8 @@ verifyExportSignatureAgainstTx(
return false;
}
auto innerTx = ExportLedgerOps::innerExportedTx(exportTx);
if (!innerTx)
auto embeddedTarget = ExportLedgerOps::embeddedExportedTxn(exportTx);
if (!embeddedTarget)
{
JLOG(j.warn()) << "Export: failed to verify sig"
<< " txHash=" << txHash << " source=" << source
@@ -61,7 +61,8 @@ verifyExportSignatureAgainstTx(
try
{
auto const signerAcctID = calcAccountID(validator);
auto const sigData = buildMultiSigningData(*innerTx, signerAcctID);
auto const sigData =
buildMultiSigningData(*embeddedTarget, signerAcctID);
if (!verify(validator, sigData.slice(), sigSlice))
{
JLOG(j.warn()) << "Export: invalid multisign sig"

View File

@@ -355,25 +355,25 @@ Change::applyExportSignatures()
return tefFAILURE;
auto const origin = ctx_.tx.getFieldH256(sfTransactionHash);
auto target = ExportLedgerOps::innerExportedTx(ctx_.tx);
auto signingPayload = ExportLedgerOps::exportWitnessSigningPayload(ctx_.tx);
auto signatures = ExportResultBuilder::signaturesFromWitness(ctx_.tx);
if (!target || !signatures || signatures->empty())
if (!signingPayload || !signatures || signatures->empty())
return tefFAILURE;
auto const stamp = ExportOriginMemo::parse(*target);
auto const stamp = ExportOriginMemo::parse(*signingPayload);
if (!stamp || !stamp.value().anchor ||
stamp.value().origin.sourceDomain != ctx_.app.config().NETWORK_ID ||
stamp.value().origin.transactionHash != origin)
return tefFAILURE;
auto const targetDomain = target->isFieldPresent(sfNetworkID)
? target->getFieldU32(sfNetworkID)
auto const targetDomain = signingPayload->isFieldPresent(sfNetworkID)
? signingPayload->getFieldU32(sfNetworkID)
: std::uint32_t{0};
if (stamp.value().origin.targetDomain != targetDomain ||
!target->isFieldPresent(sfTicketSequence))
!signingPayload->isFieldPresent(sfTicketSequence))
return tefFAILURE;
auto const account = target->getAccountID(sfAccount);
auto const account = signingPayload->getAccountID(sfAccount);
auto const latchKey = keylet::exportLatch(account, origin);
auto const latch = view().read(latchKey);
// A concurrently ordered explicit erase may remove the latch after the
@@ -387,7 +387,7 @@ Change::applyExportSignatures()
latch->getAccountID(sfAccount) != account ||
latch->getFieldH256(sfTransactionHash) != origin ||
latch->getFieldU32(sfTicketSequence) !=
target->getFieldU32(sfTicketSequence) ||
signingPayload->getFieldU32(sfTicketSequence) ||
stamp.value().anchor->ledgerSequence !=
latch->getFieldU32(sfLedgerSequence))
return tefFAILURE;
@@ -397,7 +397,7 @@ Change::applyExportSignatures()
// The origin-keyed latch can only exist on descendants of the ledger that
// created it. Do not query mutable local history here: qC authenticated the
// anchor bytes, while replay must depend only on transaction and state.
auto const identity = ExportOriginMemo::projectIdentity(*target);
auto const identity = ExportOriginMemo::projectIdentity(*signingPayload);
if (!identity ||
ExportResultBuilder::exportIntentHash(identity.value()) !=
latch->getFieldH256(sfDigest))
@@ -435,7 +435,7 @@ Change::applyExportSignatures()
auto const signer = calcAccountID(witness.signingKey);
if (!signerAccounts.insert(signer).second)
return tefFAILURE;
auto const data = buildMultiSigningData(*target, signer);
auto const data = buildMultiSigningData(*signingPayload, signer);
if (!verify(
witness.signingKey,
data.slice(),

View File

@@ -88,31 +88,31 @@ Export::preclaim(PreclaimContext const& ctx)
ctx.tx.getFieldH256(sfExportUniverseHash) != ctx.view.info().parentHash)
return tecEXPORT_UNIVERSE_MISMATCH;
auto innerTx = ExportLedgerOps::innerExportedTx(ctx.tx);
if (!innerTx)
auto baseTarget = ExportLedgerOps::exportIntentTarget(ctx.tx);
if (!baseTarget)
return temMALFORMED;
if (auto ter =
ExportLedgerOps::validateExportSigningFields(*innerTx, ctx.j);
ExportLedgerOps::validateExportSigningFields(*baseTarget, ctx.j);
!isTesSuccess(ter))
return ter;
if (auto ter = ExportLedgerOps::validateExportAccount(
*innerTx, ctx.tx.getAccountID(sfAccount), ctx.j);
*baseTarget, ctx.tx.getAccountID(sfAccount), ctx.j);
!isTesSuccess(ter))
return ter;
if (auto ter = ExportLedgerOps::validateNetworkID(
*innerTx, ctx.app.config().NETWORK_ID, ctx.j);
*baseTarget, ctx.app.config().NETWORK_ID, ctx.j);
!isTesSuccess(ter))
return ter;
if (auto ter = ExportLedgerOps::validateOriginMemoProjection(
*innerTx, ctx.app.config().NETWORK_ID, ctx.j);
*baseTarget, ctx.app.config().NETWORK_ID, ctx.j);
!isTesSuccess(ter))
return ter;
if (auto ter = ExportLedgerOps::validateTicketSequence(*innerTx, ctx.j);
if (auto ter = ExportLedgerOps::validateTicketSequence(*baseTarget, ctx.j);
!isTesSuccess(ter))
return ter;
@@ -145,8 +145,8 @@ Export::doApply()
// --- Export intent path ---
auto const txId = ctx_.tx.getTransactionID();
auto const currentSeq = view().info().seq;
auto innerTx = ExportLedgerOps::innerExportedTx(ctx_.tx);
if (!innerTx)
auto baseTarget = ExportLedgerOps::exportIntentTarget(ctx_.tx);
if (!baseTarget)
return temMALFORMED;
auto parentLedger = ctx_.replayParentLedger();
@@ -183,11 +183,11 @@ Export::doApply()
if (!committee)
return tecEXPORT_UNIVERSE_MISMATCH;
auto const targetNetworkID = innerTx->isFieldPresent(sfNetworkID)
? innerTx->getFieldU32(sfNetworkID)
auto const targetNetworkID = baseTarget->isFieldPresent(sfNetworkID)
? baseTarget->getFieldU32(sfNetworkID)
: std::uint32_t{0};
auto identity = ExportOriginMemo::identityForm(
*innerTx,
*baseTarget,
ExportOriginMemo::Origin{
ctx_.app.config().NETWORK_ID, targetNetworkID, txId});
if (!identity)

View File

@@ -82,7 +82,7 @@ isPendingExportWorkTxn(STTx const& stx)
}
inline std::optional<STTx>
innerExportedTx(STTx const& stx)
embeddedExportedTxn(STTx const& stx)
{
if (!stx.isFieldPresent(sfExportedTxn))
return std::nullopt;
@@ -103,6 +103,22 @@ innerExportedTx(STTx const& stx)
}
}
inline std::optional<STTx>
exportIntentTarget(STTx const& stx)
{
if (stx.getTxnType() != ttEXPORT)
return std::nullopt;
return embeddedExportedTxn(stx);
}
inline std::optional<STTx>
exportWitnessSigningPayload(STTx const& stx)
{
if (stx.getTxnType() != ttEXPORT_SIGNATURES)
return std::nullopt;
return embeddedExportedTxn(stx);
}
inline std::size_t
exportTxnCount(ReadView const& view)
{

View File

@@ -156,13 +156,13 @@ buildMultiSignedExportedTxn(
STTx
buildSignatureWitness(
uint256 const& exportTxHash,
STTx const& releaseTarget,
STTx const& exportSigningPayload,
PositionedSignatureSnapshot const& signatures,
std::size_t const universeSize,
LedgerIndex currentSeq)
{
auto witnessSignatures = buildWitnessSignatures(signatures, universeSize);
auto const target = normalizeAuthorizationEnvelope(releaseTarget);
auto const target = normalizeAuthorizationEnvelope(exportSigningPayload);
return STTx(ttEXPORT_SIGNATURES, [&](auto& obj) {
obj.setFieldU32(sfLedgerSequence, currentSeq);
obj.setAccountID(sfAccount, AccountID{});

View File

@@ -61,7 +61,7 @@ buildMultiSignedExportedTxn(
STTx
buildSignatureWitness(
uint256 const& exportTxHash,
STTx const& releaseTarget,
STTx const& exportSigningPayload,
PositionedSignatureSnapshot const& signatures,
std::size_t universeSize,
LedgerIndex currentSeq);