Skip to content

Commit 2d7e322

Browse files
authored
Bump stellar-xdr to 27.0.0 (#29)
### What - Bump `stellar-xdr` from 26.0.0 to 27.0.0, renaming `stellar_xdr::curr::` paths to `stellar_xdr::` (v27 exports all types at the crate root). - Add a regression test that parses a tx set whose Soroban auth entry uses the CAP-0071 `SOROBAN_CREDENTIALS_ADDRESS_V2` credentials arm. ### Why `scan --verify` currently reports 28 checkpoints (ledgers 63,401,919–63,479,423) as failed `transactions` files on all three pubnet archives. The files are valid: they contain CAP-0071 (protocol 27) `AddressV2` Soroban credentials — which stellar-xdr 26 cannot decode, so healthy archives are flagged as corrupt. With this bump, all 28 previously-failing checkpoints verify clean on `core_live_001/002/003`, and a full sweep of `core_live_001` from ledger 63,400,000 to the archive tip (18,904 files) passes with zero failures.
1 parent 41e7904 commit 2d7e322

10 files changed

Lines changed: 103 additions & 59 deletions

Cargo.lock

Lines changed: 4 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ normalize-path = { version = "0.2.1" }
6969
sha2 = "0.10"
7070
# NB: the stellar-xdr needs to keep up-to-date with the latest major (protocol)
7171
# version or it may fail to parse the latest ledger format
72-
stellar-xdr = { version = "26.0.0", default-features = true, features = [] }
72+
stellar-xdr = { version = "27.0.0", default-features = true, features = [] }
7373
async-compression = { version = "0.4", features = ["tokio", "gzip"] }
7474
flate2 = "1.1.9"
7575
hex = "0.4"

src/report.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::xdr_verify::HashExt;
2020
use serde::{Deserialize, Serialize};
2121
use std::collections::BTreeMap;
2222
use std::path::Path;
23-
use stellar_xdr::curr::Hash;
23+
use stellar_xdr::Hash;
2424
use thiserror::Error;
2525

2626
/// Current schema version.

src/tests/repair_op_test.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,9 +1865,7 @@ fn tamper_ledger_break_chain(archive_path: &Path) -> u32 {
18651865
use flate2::read::GzDecoder;
18661866
use sha2::{Digest, Sha256};
18671867
use std::io::Read;
1868-
use stellar_xdr::curr::{
1869-
Frame, Hash, LedgerHeaderHistoryEntry, Limited, Limits, ReadXdr, WriteXdr,
1870-
};
1868+
use stellar_xdr::{Frame, Hash, LedgerHeaderHistoryEntry, Limited, Limits, ReadXdr, WriteXdr};
18711869

18721870
let files = get_files_by_pattern(archive_path, "/ledger-");
18731871
assert!(!files.is_empty(), "no ledger files found");

src/tests/report_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use crate::report::{ArchiveReport, Summary, REPORT_VERSION};
44
use crate::utils::{FailureTracker, FileFlags};
5-
use stellar_xdr::curr::Hash;
5+
use stellar_xdr::Hash;
66

77
fn sample_tracker() -> FailureTracker {
88
let mut t = FailureTracker::default();

src/tests/utils.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use std::collections::HashMap;
2323
use std::path::{Path, PathBuf};
2424
use std::sync::{Arc, Mutex};
2525
use std::time::Instant;
26-
use stellar_xdr::curr::Hash;
26+
use stellar_xdr::Hash;
2727
use tokio::io::{AsyncReadExt, AsyncWriteExt};
2828
use tokio::net::TcpListener;
2929
use tower_http::services::ServeDir;
@@ -540,10 +540,10 @@ pub fn parse_transaction_entries(
540540

541541
pub(crate) fn read_and_parse_ledger_file(
542542
path: &Path,
543-
) -> Vec<stellar_xdr::curr::LedgerHeaderHistoryEntry> {
543+
) -> Vec<stellar_xdr::LedgerHeaderHistoryEntry> {
544544
use flate2::read::GzDecoder;
545545
use std::io::Read as _;
546-
use stellar_xdr::curr::{Frame, LedgerHeaderHistoryEntry, Limited, Limits, ReadXdr};
546+
use stellar_xdr::{Frame, LedgerHeaderHistoryEntry, Limited, Limits, ReadXdr};
547547

548548
let data = std::fs::read(path).expect("Failed to read ledger file");
549549
let mut decoder = GzDecoder::new(&data[..]);
@@ -560,9 +560,9 @@ pub(crate) fn read_and_parse_ledger_file(
560560
.collect()
561561
}
562562

563-
pub(crate) fn recompute_entry_hash(entry: &mut stellar_xdr::curr::LedgerHeaderHistoryEntry) {
563+
pub(crate) fn recompute_entry_hash(entry: &mut stellar_xdr::LedgerHeaderHistoryEntry) {
564564
use sha2::{Digest, Sha256};
565-
use stellar_xdr::curr::{Limits, WriteXdr};
565+
use stellar_xdr::{Limits, WriteXdr};
566566

567567
let header_xdr = entry
568568
.header
@@ -573,12 +573,12 @@ pub(crate) fn recompute_entry_hash(entry: &mut stellar_xdr::curr::LedgerHeaderHi
573573

574574
pub(crate) fn write_ledger_header_entries_to_file(
575575
path: &Path,
576-
entries: &[stellar_xdr::curr::LedgerHeaderHistoryEntry],
576+
entries: &[stellar_xdr::LedgerHeaderHistoryEntry],
577577
) {
578578
use flate2::write::GzEncoder;
579579
use flate2::Compression;
580580
use std::io::Write as _;
581-
use stellar_xdr::curr::{Limits, WriteXdr};
581+
use stellar_xdr::{Limits, WriteXdr};
582582

583583
let mut data = Vec::new();
584584
for entry in entries {

src/tests/xdr_verification_e2e_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use flate2::Compression;
1515
use rstest::rstest;
1616
use std::io::Write;
1717
use std::path::{Path, PathBuf};
18-
use stellar_xdr::curr::Hash;
18+
use stellar_xdr::Hash;
1919
use tempfile::TempDir;
2020

2121
fn pubnet_old_txset_archive_path() -> PathBuf {

src/tests/xdr_verify_test.rs

Lines changed: 80 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,18 @@ use crate::xdr_verify::{
99
use rstest::rstest;
1010
use sha2::{Digest, Sha256};
1111
use std::collections::BTreeMap;
12-
use stellar_xdr::curr::{
13-
AccountId, CreateAccountOp, GeneralizedTransactionSet, Hash, LedgerHeader, LedgerHeaderExt,
12+
use stellar_xdr::{
13+
AccountId, ContractId, CreateAccountOp, GeneralizedTransactionSet, Hash, HostFunction,
14+
InvokeContractArgs, InvokeHostFunctionOp, LedgerHeader, LedgerHeaderExt,
1415
LedgerHeaderHistoryEntry, LedgerHeaderHistoryEntryExt, LedgerScpMessages, Limits, Memo,
15-
MuxedAccount, Operation, OperationBody, Preconditions, PublicKey, ScpHistoryEntry,
16-
ScpHistoryEntryV0, SequenceNumber, TimePoint, Transaction, TransactionEnvelope,
17-
TransactionHistoryEntry, TransactionHistoryEntryExt, TransactionHistoryResultEntry,
18-
TransactionHistoryResultEntryExt, TransactionPhase, TransactionResult, TransactionResultExt,
19-
TransactionResultPair, TransactionResultResult, TransactionResultSet, TransactionSet,
20-
TransactionSetV1, TransactionV0, TransactionV0Envelope, TransactionV0Ext,
21-
TransactionV1Envelope, Uint256, VecM, WriteXdr,
16+
MuxedAccount, Operation, OperationBody, Preconditions, PublicKey, ScAddress, ScSymbol, ScVal,
17+
ScpHistoryEntry, ScpHistoryEntryV0, SequenceNumber, SorobanAddressCredentials,
18+
SorobanAuthorizationEntry, SorobanAuthorizedFunction, SorobanAuthorizedInvocation,
19+
SorobanCredentials, TimePoint, Transaction, TransactionEnvelope, TransactionHistoryEntry,
20+
TransactionHistoryEntryExt, TransactionHistoryResultEntry, TransactionHistoryResultEntryExt,
21+
TransactionPhase, TransactionResult, TransactionResultExt, TransactionResultPair,
22+
TransactionResultResult, TransactionResultSet, TransactionSet, TransactionSetV1, TransactionV0,
23+
TransactionV0Envelope, TransactionV0Ext, TransactionV1Envelope, Uint256, VecM, WriteXdr,
2224
};
2325

2426
fn frame_xdr<T: WriteXdr>(entry: &T) -> Vec<u8> {
@@ -76,7 +78,49 @@ fn tx_v1_envelope(id: u8) -> TransactionEnvelope {
7678
cond: Preconditions::None,
7779
memo: Memo::None,
7880
operations: vec![create_account_operation(id)].try_into().unwrap(),
79-
ext: stellar_xdr::curr::TransactionExt::V0,
81+
ext: stellar_xdr::TransactionExt::V0,
82+
},
83+
signatures: VecM::default(),
84+
})
85+
}
86+
87+
/// Soroban transaction whose auth entry uses the CAP-0071 (protocol 27)
88+
/// `SOROBAN_CREDENTIALS_ADDRESS_V2` arm, which pre-27 XDR cannot decode.
89+
fn tx_soroban_envelope_with_address_v2_auth(id: u8) -> TransactionEnvelope {
90+
let invoke_args = InvokeContractArgs {
91+
contract_address: ScAddress::Contract(ContractId(Hash([id; 32]))),
92+
function_name: ScSymbol("transfer".try_into().unwrap()),
93+
args: VecM::default(),
94+
};
95+
let auth = SorobanAuthorizationEntry {
96+
credentials: SorobanCredentials::AddressV2(SorobanAddressCredentials {
97+
address: ScAddress::Account(account_id(id)),
98+
nonce: 1,
99+
signature_expiration_ledger: 100,
100+
signature: ScVal::Void,
101+
}),
102+
root_invocation: SorobanAuthorizedInvocation {
103+
function: SorobanAuthorizedFunction::ContractFn(invoke_args.clone()),
104+
sub_invocations: VecM::default(),
105+
},
106+
};
107+
TransactionEnvelope::Tx(TransactionV1Envelope {
108+
tx: Transaction {
109+
source_account: muxed_account(id),
110+
fee: 100,
111+
seq_num: SequenceNumber(i64::from(id) + 1),
112+
cond: Preconditions::None,
113+
memo: Memo::None,
114+
operations: vec![Operation {
115+
source_account: None,
116+
body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp {
117+
host_function: HostFunction::InvokeContract(invoke_args),
118+
auth: vec![auth].try_into().unwrap(),
119+
}),
120+
}]
121+
.try_into()
122+
.unwrap(),
123+
ext: stellar_xdr::TransactionExt::V0,
80124
},
81125
signatures: VecM::default(),
82126
})
@@ -106,8 +150,8 @@ fn v1_history_entry(
106150
prev_hash: [u8; 32],
107151
txs: Vec<TransactionEnvelope>,
108152
) -> TransactionHistoryEntry {
109-
let component = stellar_xdr::curr::TxSetComponent::TxsetCompTxsMaybeDiscountedFee(
110-
stellar_xdr::curr::TxSetComponentTxsMaybeDiscountedFee {
153+
let component = stellar_xdr::TxSetComponent::TxsetCompTxsMaybeDiscountedFee(
154+
stellar_xdr::TxSetComponentTxsMaybeDiscountedFee {
111155
base_fee: None,
112156
txs: txs.try_into().unwrap(),
113157
},
@@ -166,11 +210,11 @@ fn create_minimal_ledger_header(
166210
LedgerHeader {
167211
ledger_version: 21,
168212
previous_ledger_hash: Hash(prev_hash),
169-
scp_value: stellar_xdr::curr::StellarValue {
213+
scp_value: stellar_xdr::StellarValue {
170214
tx_set_hash: Hash(tx_set_hash),
171215
close_time: TimePoint(0),
172216
upgrades: VecM::default(),
173-
ext: stellar_xdr::curr::StellarValueExt::Basic,
217+
ext: stellar_xdr::StellarValueExt::Basic,
174218
},
175219
tx_set_result_hash: Hash(result_hash),
176220
bucket_list_hash: Hash([0; 32]),
@@ -408,6 +452,22 @@ fn test_parse_transaction_entries_v1_non_empty() {
408452
assert!(!is_empty_tx_set_hash(&parsed[&100], &Hash(prev_hash)));
409453
}
410454

455+
#[test]
456+
fn test_parse_transaction_entries_v1_with_cap71_address_v2_credentials() {
457+
let prev_hash = [0x24; 32];
458+
let entry = v1_history_entry(
459+
100,
460+
prev_hash,
461+
vec![tx_soroban_envelope_with_address_v2_auth(1)],
462+
);
463+
let parsed = parse_transaction_entries(&frame_xdr(&entry)).unwrap();
464+
465+
let TransactionHistoryEntryExt::V1(generalized) = &entry.ext else {
466+
panic!("expected V1 entry");
467+
};
468+
assert_eq!(parsed[&100], compute_v1_tx_set_hash(generalized).unwrap());
469+
}
470+
411471
#[test]
412472
fn test_compute_v0_tx_set_hash_matches_manual_hash() {
413473
let prev_hash = [0x10; 32];
@@ -450,14 +510,12 @@ fn test_compute_v1_tx_set_hash_matches_manual_hash() {
450510
let generalized = GeneralizedTransactionSet::V1(TransactionSetV1 {
451511
previous_ledger_hash: Hash([0x11; 32]),
452512
phases: vec![TransactionPhase::V0(
453-
vec![
454-
stellar_xdr::curr::TxSetComponent::TxsetCompTxsMaybeDiscountedFee(
455-
stellar_xdr::curr::TxSetComponentTxsMaybeDiscountedFee {
456-
base_fee: None,
457-
txs: vec![tx_v1_envelope(1)].try_into().unwrap(),
458-
},
459-
),
460-
]
513+
vec![stellar_xdr::TxSetComponent::TxsetCompTxsMaybeDiscountedFee(
514+
stellar_xdr::TxSetComponentTxsMaybeDiscountedFee {
515+
base_fee: None,
516+
txs: vec![tx_v1_envelope(1)].try_into().unwrap(),
517+
},
518+
)]
461519
.try_into()
462520
.unwrap(),
463521
)]

src/utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use bytes::Buf;
22
use std::collections::{BTreeMap, BTreeSet};
33
use std::sync::atomic::{AtomicU64, Ordering};
4-
use stellar_xdr::curr::Hash;
4+
use stellar_xdr::Hash;
55
use thiserror::Error;
66
use tracing::{debug, error, info, warn};
77

src/xdr_verify.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use sha2::{Digest, Sha256};
2626
use std::collections::{BTreeMap, HashMap};
2727
use std::io::Cursor;
2828
use std::sync::Mutex;
29-
use stellar_xdr::curr::{
29+
use stellar_xdr::{
3030
Frame, GeneralizedTransactionSet, Hash, LedgerHeaderHistoryEntry, Limited, Limits, ReadXdr,
3131
ScpHistoryEntry, TransactionHistoryEntry, TransactionHistoryEntryExt,
3232
TransactionHistoryResultEntry, TransactionSetV1, VecM, WriteXdr,
@@ -44,13 +44,13 @@ pub(crate) const EMPTY_XDR_ARRAY_HASH: Hash = Hash([
4444
/// All-zero hash. Used as a sentinel for "no result entry expected."
4545
const ZERO_HASH: Hash = Hash([0; 32]);
4646

47-
/// SHA-256 of `data` returned as a `stellar_xdr::curr::Hash` (the same newtype
47+
/// SHA-256 of `data` returned as a `stellar_xdr::Hash` (the same newtype
4848
/// used by ledger headers, tx-set hashes, etc.).
4949
fn sha256(data: &[u8]) -> Hash {
5050
Hash(Sha256::digest(data).into())
5151
}
5252

53-
/// Extension methods for `stellar_xdr::curr::Hash`.
53+
/// Extension methods for `stellar_xdr::Hash`.
5454
pub(crate) trait HashExt {
5555
/// Hex-encode the 32 bytes (lowercase, no prefix).
5656
fn to_hex(&self) -> String;
@@ -730,10 +730,10 @@ pub(crate) fn is_empty_tx_set_hash(expected: &Hash, prev_hash: &Hash) -> bool {
730730
///
731731
/// This matches stellar-core's `computeNonGeneralizedTxSetContentsHash()`.
732732
pub(crate) fn compute_v0_tx_set_hash(
733-
tx_set: &stellar_xdr::curr::TransactionSet,
733+
tx_set: &stellar_xdr::TransactionSet,
734734
) -> Result<Hash, StorageError> {
735735
let mut serialized_txs = Vec::with_capacity(tx_set.txs.len());
736-
for tx in tx_set.txs.iter() {
736+
for tx in &tx_set.txs {
737737
let tx_xdr = tx.to_xdr(Limits::none()).map_err(|e| {
738738
StorageError::fatal(format!("failed to serialize TransactionEnvelope: {}", e))
739739
})?;
@@ -760,7 +760,7 @@ pub(crate) fn compute_v0_tx_set_hash(
760760
/// The V1 hash is simply SHA256 of the entire XDR-serialized struct.
761761
/// This matches stellar-core's `xdrSha256(xdrTxSet)`.
762762
pub(crate) fn compute_v1_tx_set_hash(
763-
generalized_tx_set: &stellar_xdr::curr::GeneralizedTransactionSet,
763+
generalized_tx_set: &stellar_xdr::GeneralizedTransactionSet,
764764
) -> Result<Hash, StorageError> {
765765
let xdr = generalized_tx_set.to_xdr(Limits::none()).map_err(|e| {
766766
StorageError::fatal(format!(

0 commit comments

Comments
 (0)