From 74310d180f9d85ea4be5ad71b7393d73855189ee Mon Sep 17 00:00:00 2001 From: olatechy0 <302475381+olatechy0@users.noreply.github.com> Date: Wed, 28 Oct 2026 15:45:17 +0100 Subject: [PATCH] revert: remove unauthorized co-authored schema commit The versioned-XDR schema change was pushed into PR #139 by the maintainer-controlled account olatechy0 after the PR was opened. It was not independent external work and must not be counted as such. Revert it to keep the contribution history honest. Issue #12 remains open and on the Wave board. This reverts commit ca9139cb. Also restores the demo expected output and test counts, and documents the contribution-integrity rule in docs/APPEAL.md. --- CHANGELOG.md | 9 -- README.md | 7 +- crates/devkit-cli/src/main.rs | 22 +-- crates/devkit-cli/tests/xdr_schema.rs | 39 ----- crates/devkit-xdr/src/lib.rs | 132 ++--------------- crates/devkit-xdr/tests/schema_snapshots.rs | 133 ----------------- .../devkit-xdr/tests/snapshots/envelope.json | 23 --- crates/devkit-xdr/tests/snapshots/event.json | 21 --- crates/devkit-xdr/tests/snapshots/scval.json | 7 - demo/README.md | 3 +- docs/APPEAL.md | 10 ++ docs/ARCHITECTURE.md | 9 +- docs/BUILD_REPORT.md | 2 +- docs/CLI.md | 14 +- docs/FINAL_AUDIT.md | 2 +- docs/PROJECT_STATUS.md | 3 +- docs/SCVAL.md | 115 --------------- docs/XDR.md | 134 ------------------ 18 files changed, 44 insertions(+), 641 deletions(-) delete mode 100644 crates/devkit-cli/tests/xdr_schema.rs delete mode 100644 crates/devkit-xdr/tests/schema_snapshots.rs delete mode 100644 crates/devkit-xdr/tests/snapshots/envelope.json delete mode 100644 crates/devkit-xdr/tests/snapshots/event.json delete mode 100644 crates/devkit-xdr/tests/snapshots/scval.json delete mode 100644 docs/SCVAL.md delete mode 100644 docs/XDR.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 39bf6cc..bc7e492 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,15 +5,6 @@ All notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] - -### Added - -- `devkit-xdr`: versioned decoded-XDR JSON schema (`schema_version` `1.0`) for - `ScVal`, `TransactionEnvelope`, and `ContractEvent`, an opt-in `raw_xdr` - echo (`--raw`), golden snapshots, and the `docs/XDR.md` and `docs/SCVAL.md` - schema references. - ## [0.1.0] - 2026-10-28 First tagged release. Prebuilt binaries for Linux x86_64, macOS arm64, and diff --git a/README.md b/README.md index 48b3a8a..f19b615 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,8 @@ The core works fully **offline** and never requires AI. - `devkit-core` — shared errors, network profiles, output format. - `devkit-xdr` — decode `ScVal`, `TransactionEnvelope`, and `ContractEvent` from - base64 XDR into a versioned JSON document (`schema_version` 1.0) with an - opt-in `raw_xdr` echo; classify Stellar strkeys; bounded malformed-input - handling. See [docs/XDR.md](docs/XDR.md) and [docs/SCVAL.md](docs/SCVAL.md). + base64 XDR into JSON; classify Stellar strkeys; bounded malformed-input + handling. - `devkit-rpc` — endpoint validation, typed protocol layer, deterministic mock transport, retry policy, and a bounded HTTP JSON-RPC transport. - `devkit-contract` — non-executing WASM validation, import/export/custom-section @@ -125,8 +124,6 @@ WASM module, emits SARIF, and optionally checks testnet RPC health. See - [Architecture](docs/ARCHITECTURE.md) - [Getting started](docs/GETTING_STARTED.md) - [CLI reference](docs/CLI.md) -- [XDR JSON schema](docs/XDR.md) -- [SCVal JSON schema](docs/SCVAL.md) - [Configuration](docs/CONFIGURATION.md) - [Implemented capabilities](docs/IMPLEMENTED_CAPABILITIES.md) - [Testnet demo](demo/README.md) diff --git a/crates/devkit-cli/src/main.rs b/crates/devkit-cli/src/main.rs index b2eb619..fd9e51f 100644 --- a/crates/devkit-cli/src/main.rs +++ b/crates/devkit-cli/src/main.rs @@ -49,10 +49,6 @@ struct Cli { #[arg(long, value_enum, default_value_t = FormatArg::Terminal, global = true)] format: FormatArg, - /// Include the raw base64 XDR input in decoded JSON output. - #[arg(long, global = true)] - raw: bool, - #[command(subcommand)] command: Command, } @@ -177,9 +173,6 @@ fn print_json_error(category: &str, message: &str) { fn run(cli: Cli) -> Result { let format: OutputFormat = cli.format.into(); - let options = xdr::DecodeOptions { - include_raw: cli.raw, - }; let config_path = cli.config.clone(); match cli.command { Command::Version => { @@ -199,23 +192,20 @@ fn run(cli: Cli) -> Result { } Command::Scval { value } => { let value = read_value(value)?; - let decoded = - xdr::inspect_scval_base64_with_options(&value, options).map_err(runtime)?; - print_value(format, &decoded); + let decoded = xdr::inspect_scval_base64(&value).map_err(runtime)?; + print_value(format, &xdr::summary("scval", &decoded)); Ok(EXIT_SUCCESS) } Command::Envelope { value } => { let value = read_value(value)?; - let decoded = - xdr::inspect_envelope_base64_with_options(&value, options).map_err(runtime)?; - print_value(format, &decoded); + let decoded = xdr::inspect_envelope_base64(&value).map_err(runtime)?; + print_value(format, &xdr::summary("transaction_envelope", &decoded)); Ok(EXIT_SUCCESS) } Command::Event { value } => { let value = read_value(value)?; - let decoded = - xdr::inspect_event_base64_with_options(&value, options).map_err(runtime)?; - print_value(format, &decoded); + let decoded = xdr::inspect_event_base64(&value).map_err(runtime)?; + print_value(format, &xdr::summary("contract_event", &decoded)); Ok(EXIT_SUCCESS) } Command::InspectWasm { path } => { diff --git a/crates/devkit-cli/tests/xdr_schema.rs b/crates/devkit-cli/tests/xdr_schema.rs deleted file mode 100644 index 52c1af9..0000000 --- a/crates/devkit-cli/tests/xdr_schema.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::process::{Command, Output}; - -fn run_cli(args: &[&str]) -> Output { - Command::new(env!("CARGO_BIN_EXE_stellar-foundry")) - .args(args) - .output() - .expect("run CLI") -} - -#[test] -fn scval_json_output_is_versioned() { - let output = run_cli(&["--format", "json", "scval", "AAAAAwAAACo="]); - assert!( - output.status.success(), - "CLI failed: {}", - String::from_utf8_lossy(&output.stderr) - ); - let value: serde_json::Value = - serde_json::from_slice(&output.stdout).expect("valid JSON output"); - assert_eq!(value["schema_version"], "1.0"); - assert_eq!(value["kind"], "scval"); - assert_eq!(value["value"]["u32"], 42); - assert!(value.get("raw_xdr").is_none()); -} - -#[test] -fn raw_flag_echoes_the_base64_input() { - let output = run_cli(&["--format", "json", "--raw", "scval", "AAAAAwAAACo="]); - assert!( - output.status.success(), - "CLI failed: {}", - String::from_utf8_lossy(&output.stderr) - ); - let value: serde_json::Value = - serde_json::from_slice(&output.stdout).expect("valid JSON output"); - assert_eq!(value["schema_version"], "1.0"); - assert_eq!(value["kind"], "scval"); - assert_eq!(value["raw_xdr"], "AAAAAwAAACo="); -} diff --git a/crates/devkit-xdr/src/lib.rs b/crates/devkit-xdr/src/lib.rs index 94554a2..d1ea258 100644 --- a/crates/devkit-xdr/src/lib.rs +++ b/crates/devkit-xdr/src/lib.rs @@ -10,25 +10,9 @@ use devkit_core::DevkitError; use serde::Serialize; -use serde_json::Value; +use serde_json::{json, Value}; use stellar_xdr::{ContractEvent, Limits, ReadXdr, ScVal, TransactionEnvelope}; -/// Version of the decoded-XDR JSON schema emitted by this crate. -/// -/// The schema covers the document envelope (`schema_version`, `kind`, `value`, -/// and optional `raw_xdr`). See `docs/XDR.md` and `docs/SCVAL.md`. -pub const XDR_JSON_SCHEMA_VERSION: &str = "1.0"; - -/// Options controlling decoded-XDR output. -#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] -pub struct DecodeOptions { - /// Include the canonical base64 XDR input as `raw_xdr`. - /// - /// Off by default so machine-readable output stays compact; the raw - /// encoding is always available behind this flag. - pub include_raw: bool, -} - /// Maximum recursion depth applied when decoding untrusted XDR. /// /// Bounds stack usage for deeply nested values. @@ -62,81 +46,24 @@ pub struct StrkeyInfo { pub value: String, } -/// Build a versioned decoded-XDR document. -/// -/// The envelope is the stable part of the schema: -/// -/// ```json -/// { -/// "schema_version": "1.0", -/// "kind": "scval", -/// "value": { "...": "XDR-JSON payload" }, -/// "raw_xdr": "AAAAAwAAACo=" -/// } -/// ``` -/// -/// `raw_xdr` is only present when [`DecodeOptions::include_raw`] is set. -fn document(kind: &str, value: Value, input: &str, options: DecodeOptions) -> Value { - let mut object = serde_json::Map::new(); - object.insert( - "schema_version".to_string(), - Value::String(XDR_JSON_SCHEMA_VERSION.to_string()), - ); - object.insert("kind".to_string(), Value::String(kind.to_string())); - object.insert("value".to_string(), value); - if options.include_raw { - object.insert("raw_xdr".to_string(), Value::String(input.to_string())); - } - Value::Object(object) -} - -/// Decodes a base64 `ScVal` and returns a versioned JSON document. +/// Decodes a base64 `ScVal` and returns developer-friendly JSON. pub fn inspect_scval_base64(input: &str) -> Result { - inspect_scval_base64_with_options(input, DecodeOptions::default()) + let value = ScVal::from_xdr_base64(input.trim(), limits()).map_err(DevkitError::decode)?; + serde_json::to_value(&value).map_err(DevkitError::decode) } -/// Decodes a base64 `ScVal` with explicit output options. -pub fn inspect_scval_base64_with_options( - input: &str, - options: DecodeOptions, -) -> Result { - let input = input.trim(); - let value = ScVal::from_xdr_base64(input, limits()).map_err(DevkitError::decode)?; - let value = serde_json::to_value(&value).map_err(DevkitError::decode)?; - Ok(document("scval", value, input, options)) -} - -/// Decodes a base64 `TransactionEnvelope` and returns a versioned JSON document. +/// Decodes a base64 `TransactionEnvelope` and returns developer-friendly JSON. pub fn inspect_envelope_base64(input: &str) -> Result { - inspect_envelope_base64_with_options(input, DecodeOptions::default()) -} - -/// Decodes a base64 `TransactionEnvelope` with explicit output options. -pub fn inspect_envelope_base64_with_options( - input: &str, - options: DecodeOptions, -) -> Result { - let input = input.trim(); - let value = - TransactionEnvelope::from_xdr_base64(input, limits()).map_err(DevkitError::decode)?; - let value = serde_json::to_value(&value).map_err(DevkitError::decode)?; - Ok(document("transaction_envelope", value, input, options)) + let value = TransactionEnvelope::from_xdr_base64(input.trim(), limits()) + .map_err(DevkitError::decode)?; + serde_json::to_value(&value).map_err(DevkitError::decode) } -/// Decodes a base64 `ContractEvent` and returns a versioned JSON document. +/// Decodes a base64 `ContractEvent` and returns developer-friendly JSON. pub fn inspect_event_base64(input: &str) -> Result { - inspect_event_base64_with_options(input, DecodeOptions::default()) -} - -/// Decodes a base64 `ContractEvent` with explicit output options. -pub fn inspect_event_base64_with_options( - input: &str, - options: DecodeOptions, -) -> Result { - let input = input.trim(); - let value = ContractEvent::from_xdr_base64(input, limits()).map_err(DevkitError::decode)?; - let value = serde_json::to_value(&value).map_err(DevkitError::decode)?; - Ok(document("contract_event", value, input, options)) + let value = + ContractEvent::from_xdr_base64(input.trim(), limits()).map_err(DevkitError::decode)?; + serde_json::to_value(&value).map_err(DevkitError::decode) } /// Classifies a Stellar strkey. @@ -158,12 +85,9 @@ pub fn describe_strkey(input: &str) -> Result { }) } -/// Builds a versioned document for an already-decoded value. -/// -/// Prefer the `inspect_*` functions, which decode under bounded limits. This -/// helper remains for callers that already hold a decoded value. +/// Builds a compact summary object for a decoded value, used by the CLI. pub fn summary(kind: &str, value: &Value) -> Value { - document(kind, value.clone(), "", DecodeOptions::default()) + json!({ "kind": kind, "value": value }) } #[cfg(test)] @@ -179,38 +103,10 @@ mod tests { fn decodes_scval_u32_roundtrip() { let encoded = roundtrip_scval(&ScVal::U32(42)); let decoded = inspect_scval_base64(&encoded).unwrap(); - assert_eq!(decoded["schema_version"], XDR_JSON_SCHEMA_VERSION); - assert_eq!(decoded["kind"], "scval"); - assert!(decoded.get("value").is_some(), "missing value: {decoded}"); let text = serde_json::to_string(&decoded).unwrap(); assert!(text.contains("42"), "unexpected json: {text}"); } - #[test] - fn raw_xdr_is_opt_in_and_preserved_verbatim() { - let encoded = roundtrip_scval(&ScVal::U32(7)); - let default = inspect_scval_base64(&encoded).unwrap(); - assert!(default.get("raw_xdr").is_none()); - - let with_raw = inspect_scval_base64_with_options( - &format!(" {encoded}\n"), - DecodeOptions { include_raw: true }, - ) - .unwrap(); - assert_eq!(with_raw["raw_xdr"], encoded); - assert_eq!(with_raw["schema_version"], XDR_JSON_SCHEMA_VERSION); - } - - #[test] - fn summary_uses_the_versioned_envelope() { - let value = serde_json::json!({ "u32": 1 }); - let summary = summary("scval", &value); - assert_eq!(summary["schema_version"], XDR_JSON_SCHEMA_VERSION); - assert_eq!(summary["kind"], "scval"); - assert_eq!(summary["value"], value); - assert!(summary.get("raw_xdr").is_none()); - } - #[test] fn decodes_scval_bool_roundtrip() { let encoded = roundtrip_scval(&ScVal::Bool(true)); diff --git a/crates/devkit-xdr/tests/schema_snapshots.rs b/crates/devkit-xdr/tests/schema_snapshots.rs deleted file mode 100644 index b9434ea..0000000 --- a/crates/devkit-xdr/tests/schema_snapshots.rs +++ /dev/null @@ -1,133 +0,0 @@ -//! Golden snapshots and structural validation for the versioned XDR schema. -//! -//! The snapshot files under `tests/snapshots/` are the reviewable contract for -//! schema version 1.0. If a change intentionally alters them, bump -//! `XDR_JSON_SCHEMA_VERSION` and document the migration in `docs/XDR.md`. - -use std::path::{Path, PathBuf}; - -use devkit_xdr::{ - inspect_envelope_base64_with_options, inspect_event_base64_with_options, - inspect_scval_base64_with_options, DecodeOptions, XDR_JSON_SCHEMA_VERSION, -}; -use stellar_xdr::{ - BytesM, ContractEvent, ContractEventBody, ContractEventType, ContractEventV0, ContractId, - DecoratedSignature, ExtensionPoint, Hash, Limits, Memo, MuxedAccount, Preconditions, ScVal, - SequenceNumber, Signature, SignatureHint, Transaction, TransactionEnvelope, TransactionExt, - TransactionV1Envelope, Uint256, VecM, WriteXdr, -}; - -fn snapshot_path(name: &str) -> PathBuf { - Path::new(env!("CARGO_MANIFEST_DIR")) - .join("tests") - .join("snapshots") - .join(format!("{name}.json")) -} - -fn assert_snapshot(name: &str, value: &serde_json::Value) { - let actual = format!("{}\n", serde_json::to_string_pretty(value).unwrap()); - let path = snapshot_path(name); - if !path.exists() { - std::fs::create_dir_all(path.parent().unwrap()).unwrap(); - std::fs::write(&path, &actual).unwrap(); - panic!( - "created missing snapshot {}; re-run the test to compare", - path.display() - ); - } - let expected = std::fs::read_to_string(&path).unwrap(); - assert_eq!(actual, expected, "snapshot {} drifted", path.display()); -} - -fn assert_document(document: &serde_json::Value, kind: &str, raw_xdr: Option<&str>) { - let object = document.as_object().expect("document is an object"); - assert_eq!(object["schema_version"], XDR_JSON_SCHEMA_VERSION); - assert_eq!(object["kind"], kind); - assert!(object.contains_key("value"), "document has no value"); - - let allowed = if raw_xdr.is_some() { - vec!["schema_version", "kind", "value", "raw_xdr"] - } else { - vec!["schema_version", "kind", "value"] - }; - for key in object.keys() { - assert!( - allowed.contains(&key.as_str()), - "unexpected document field `{key}`" - ); - } - assert_eq!(object.get("raw_xdr").and_then(|v| v.as_str()), raw_xdr); -} - -fn sample_envelope() -> TransactionEnvelope { - TransactionEnvelope::Tx(TransactionV1Envelope { - tx: Transaction { - source_account: MuxedAccount::Ed25519(Uint256([0x22; 32])), - fee: 100, - seq_num: SequenceNumber(42), - cond: Preconditions::None, - memo: Memo::None, - operations: VecM::try_from(Vec::new()).unwrap(), - ext: TransactionExt::V0, - }, - signatures: VecM::try_from(vec![DecoratedSignature { - hint: SignatureHint([0xab; 4]), - signature: Signature(BytesM::try_from(vec![0xcd; 64]).unwrap()), - }]) - .unwrap(), - }) -} - -fn sample_event() -> ContractEvent { - ContractEvent { - ext: ExtensionPoint::V0, - contract_id: Some(ContractId(Hash([0x11; 32]))), - type_: ContractEventType::Contract, - body: ContractEventBody::V0(ContractEventV0 { - topics: VecM::try_from(vec![ScVal::Symbol( - "transfer".try_into().expect("valid symbol"), - )]) - .unwrap(), - data: ScVal::U32(7), - }), - } -} - -#[test] -fn scval_document_matches_golden_snapshot_and_schema() { - let encoded = ScVal::U32(42).to_xdr_base64(Limits::none()).unwrap(); - let document = inspect_scval_base64_with_options(&encoded, DecodeOptions::default()).unwrap(); - assert_document(&document, "scval", None); - assert_snapshot("scval", &document); -} - -#[test] -fn envelope_document_matches_golden_snapshot_and_schema() { - let encoded = sample_envelope().to_xdr_base64(Limits::none()).unwrap(); - let document = - inspect_envelope_base64_with_options(&encoded, DecodeOptions::default()).unwrap(); - assert_document(&document, "transaction_envelope", None); - assert_snapshot("envelope", &document); -} - -#[test] -fn event_document_matches_golden_snapshot_and_schema() { - let encoded = sample_event().to_xdr_base64(Limits::none()).unwrap(); - let document = inspect_event_base64_with_options(&encoded, DecodeOptions::default()).unwrap(); - assert_document(&document, "contract_event", None); - assert_snapshot("event", &document); -} - -#[test] -fn raw_xdr_is_only_present_when_requested() { - let encoded = ScVal::Bool(true).to_xdr_base64(Limits::none()).unwrap(); - let plain = inspect_scval_base64_with_options(&encoded, DecodeOptions::default()).unwrap(); - assert_document(&plain, "scval", None); - - let raw = inspect_scval_base64_with_options( - &format!("{encoded}\n"), - DecodeOptions { include_raw: true }, - ) - .unwrap(); - assert_document(&raw, "scval", Some(&encoded)); -} diff --git a/crates/devkit-xdr/tests/snapshots/envelope.json b/crates/devkit-xdr/tests/snapshots/envelope.json deleted file mode 100644 index e2371a7..0000000 --- a/crates/devkit-xdr/tests/snapshots/envelope.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "kind": "transaction_envelope", - "schema_version": "1.0", - "value": { - "tx": { - "signatures": [ - { - "hint": "abababab", - "signature": "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" - } - ], - "tx": { - "cond": "none", - "ext": "v0", - "fee": 100, - "memo": "none", - "operations": [], - "seq_num": "42", - "source_account": "GARCEIRCEIRCEIRCEIRCEIRCEIRCEIRCEIRCEIRCEIRCEIRCEIRCFRVX" - } - } - } -} diff --git a/crates/devkit-xdr/tests/snapshots/event.json b/crates/devkit-xdr/tests/snapshots/event.json deleted file mode 100644 index b530def..0000000 --- a/crates/devkit-xdr/tests/snapshots/event.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "kind": "contract_event", - "schema_version": "1.0", - "value": { - "body": { - "v0": { - "data": { - "u32": 7 - }, - "topics": [ - { - "symbol": "transfer" - } - ] - } - }, - "contract_id": "CAIRCEIRCEIRCEIRCEIRCEIRCEIRCEIRCEIRCEIRCEIRCEIRCEIRDB3V", - "ext": "v0", - "type": "contract" - } -} diff --git a/crates/devkit-xdr/tests/snapshots/scval.json b/crates/devkit-xdr/tests/snapshots/scval.json deleted file mode 100644 index cd25e70..0000000 --- a/crates/devkit-xdr/tests/snapshots/scval.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "kind": "scval", - "schema_version": "1.0", - "value": { - "u32": 42 - } -} diff --git a/demo/README.md b/demo/README.md index d053978..c69d961 100644 --- a/demo/README.md +++ b/demo/README.md @@ -43,13 +43,12 @@ $env:SKIP_NETWORK = "1"; pwsh demo/run-demo.ps1 ## Expected output (abridged) ```json -{"kind":"scval","schema_version":"1.0","value":{"i128":"100"}} +{"kind":"scval","value":{"i128":"100"}} ``` ```json { "kind": "transaction_envelope", - "schema_version": "1.0", "value": { "tx": { "tx": { diff --git a/docs/APPEAL.md b/docs/APPEAL.md index 063f037..f6bbc3e 100644 --- a/docs/APPEAL.md +++ b/docs/APPEAL.md @@ -78,6 +78,16 @@ it first. Example: | Scope is distinct | `docs/WHY_DEVKIT.md` | | Repository gate | `cargo fmt --all -- --check`; `cargo clippy --locked --workspace --all-targets --all-features -- -D warnings`; `cargo test --locked --workspace --all-features` | +## Contribution integrity + +Do **not** present commits from accounts controlled by the maintainer as +independent external contributions. That includes the maintainer's own alternate +accounts. Reviewers can correlate account ownership, email domains, and activity +patterns, and sockpuppeting is a terms violation. + +Use only genuinely independent contributors as external-contribution evidence. +If there are none yet, say so and describe the outreach plan instead. + ## Do not claim - That the project is approved, funded, or guaranteed points. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 3aab909..bfef842 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -67,12 +67,9 @@ Design rules: - Failures are structured, never panics. Malformed input is a first-class test case; `fixtures/malformed/` and `crates/devkit-xdr/tests/malformed_corpus.rs` cover the regression corpus. -- JSON is emitted as a versioned document: `schema_version`, `kind`, `value`, - and an opt-in `raw_xdr`. The envelope and the frozen XDR-JSON payload rules - are specified in [`XDR.md`](XDR.md), with SCVal details in - [`SCVAL.md`](SCVAL.md). Additive changes bump the schema minor version; - breaking changes require a major bump and a migration note. -- Golden snapshots in `crates/devkit-xdr/tests/snapshots/` pin the schema. +- JSON is produced by `stellar-xdr`'s `serde` representation today. A curated, + versioned schema is future work; until then the JSON shape is documented as + **unstable**. ## RPC architecture diff --git a/docs/BUILD_REPORT.md b/docs/BUILD_REPORT.md index dbca01a..4730112 100644 --- a/docs/BUILD_REPORT.md +++ b/docs/BUILD_REPORT.md @@ -37,7 +37,7 @@ See [ARCHITECTURE.md](ARCHITECTURE.md). ## Tests -51 tests across unit, integration, malformed-corpus, and CLI tests. Run with +43 tests across unit, integration, malformed-corpus, and CLI tests. Run with `cargo test --locked --workspace --all-features`. CI runs the same suite on Linux, Windows, and macOS, plus an MSRV job on Rust 1.91.0. diff --git a/docs/CLI.md b/docs/CLI.md index 6b8e2f7..f907175 100644 --- a/docs/CLI.md +++ b/docs/CLI.md @@ -27,17 +27,15 @@ Validates a Stellar strkey and reports its kind (`account`, `contract`, or ### `scval ` -Decodes a base64 XDR `ScVal` into a versioned JSON document. Pass `-` to read one value from stdin, for example `printf 'AAAAAwAAACo=' | stellar-foundry --format json scval -`. +Decodes a base64 XDR `ScVal` into JSON. Pass `-` to read one value from stdin, for example `printf 'AAAAAwAAACo=' | stellar-foundry --format json scval -`. ### `envelope ` -Decodes a base64 XDR `TransactionEnvelope` into a versioned JSON document. Pass `-` to read one value from stdin. +Decodes a base64 XDR `TransactionEnvelope` into JSON. Pass `-` to read one value from stdin. ### `event ` -Decodes a base64 XDR `ContractEvent` into a versioned JSON document. Pass `-` to read one value from stdin. - -Decoded output includes `schema_version`, `kind`, and `value`. Add the global `--raw` flag to also echo the canonical base64 input as `raw_xdr`. See [XDR.md](XDR.md) and [SCVAL.md](SCVAL.md) for the schema and migration policy. +Decodes a base64 XDR `ContractEvent` into JSON. Pass `-` to read one value from stdin. When `-` is supplied, stdin must be piped or redirected; the CLI never waits for interactive terminal input. Input is limited to 16 MiB and must be valid UTF-8. Only one value is read; streaming multiple values is not supported. @@ -88,7 +86,5 @@ The category is one of `usage`, `decode`, `encode`, `invalid_input`, `configurat ## Output stability - `terminal` output is human-oriented and may change. -- `json` output for `scval`, `envelope`, and `event` is machine-readable and - versioned. Consumers must check `schema_version` (currently `1.0`) before - using the payload; see [XDR.md](XDR.md). Other JSON shapes remain - human-oriented until they gain their own versioned envelope. +- `json` output is machine-readable. Until a versioned schema is published + (tracked as an issue), treat the shape as unstable. diff --git a/docs/FINAL_AUDIT.md b/docs/FINAL_AUDIT.md index 7f3c924..3c524f9 100644 --- a/docs/FINAL_AUDIT.md +++ b/docs/FINAL_AUDIT.md @@ -9,7 +9,7 @@ Audit date: 2026-10-28. Commit: `aec759c`. | Item | Status | Evidence | | ---- | ------ | -------- | | Build succeeds | Verified | `cargo build` | -| Tests pass | Verified | `cargo test --locked --workspace --all-features` (51 tests); post-merge CI | +| Tests pass | Verified | `cargo test --locked --workspace --all-features` (43 tests); post-merge CI | | Lint passes | Verified | `cargo clippy --locked --all-targets --all-features -- -D warnings` | | Formatting passes | Verified | `cargo fmt --all -- --check` | | CI passes | Verified | CI run 40690141880 on Linux/Windows/macOS | diff --git a/docs/PROJECT_STATUS.md b/docs/PROJECT_STATUS.md index 40c3dcd..48b7861 100644 --- a/docs/PROJECT_STATUS.md +++ b/docs/PROJECT_STATUS.md @@ -14,8 +14,7 @@ The merge commit is `aec759c`. CI and the RustSec audit are green on `main`. - `devkit-core`: structured errors, network profiles, output formats. - `devkit-xdr`: bounded decoding of base64 `ScVal`, `TransactionEnvelope`, and - `ContractEvent` into a schema-versioned JSON document (1.0) with an opt-in raw - XDR echo; strkey classification; malformed-input regression fixtures. + `ContractEvent`; strkey classification; malformed-input regression fixtures. - `stellar-foundry` CLI: `version`, `doctor`, `strkey`, `scval`, `envelope`, `event`, `endpoint`, `inspect-wasm`, `analyze-wasm`, and `rpc-health`; terminal/JSON output, SARIF output for analysis, and exit codes 0/2/3. diff --git a/docs/SCVAL.md b/docs/SCVAL.md deleted file mode 100644 index bf811fb..0000000 --- a/docs/SCVAL.md +++ /dev/null @@ -1,115 +0,0 @@ -# SCVal JSON schema - -This page defines the `scval` document emitted by `devkit-xdr` and the -`stellar-foundry scval` command. The shared envelope is specified in -[`XDR.md`](XDR.md); this page covers the `value` payload. - -Current schema version: **1.0**. - -## Schema - -```json -{ - "schema_version": "1.0", - "kind": "scval", - "value": { "u32": 42 } -} -``` - -- `schema_version` — `1.0`. -- `kind` — always `scval`. -- `value` — exactly one tagged `ScVal` object or unit string. -- `raw_xdr` — optional base64 input, enabled with `--raw`. - -A decoder must reject a document whose `kind` is not `scval` or whose major -schema version is not `1`. - -## Variant mapping - -| SCVal variant | JSON shape | Example | -| ------------- | ---------- | ------- | -| `Bool` | `{"bool": true}` | `{"bool":false}` | -| `Void` | `"void"` | `"void"` | -| `Error` | `{"error": {...}}` | `{"error":{"contract":1}}` | -| `U32` | `{"u32": 42}` | number | -| `I32` | `{"i32": -1}` | number | -| `U64` | `{"u64": "42"}` | string, no precision loss | -| `I64` | `{"i64": "-1"}` | string | -| `Timepoint` | `{"timepoint": "1700000000"}` | string | -| `Duration` | `{"duration": "60"}` | string | -| `U128` | `{"u128": {"hi": "...", "lo": "..."}}` | parts | -| `I128` | `{"i128": {"hi": "...", "lo": "..."}}` | parts | -| `U256` | `{"u256": {"hi_hi": "...", "hi_lo": "...", "lo_hi": "...", "lo_lo": "..."}}` | parts | -| `I256` | `{"i256": {...}}` | parts | -| `Bytes` | `{"bytes": ""}` | lowercase hex | -| `String` | `{"string": "..."}` | UTF-8 | -| `Symbol` | `{"symbol": "transfer"}` | UTF-8 | -| `Vec` | `{"vec": [ ... ]}` or `{"vec": null}` | recursive `ScVal` | -| `Map` | `{"map": [{"key": ..., "value": ...}]}` or `null` | recursive, ordered | -| `Address` | `{"address": "G..."}` / `"C..."` | strkey | -| `ContractInstance` | `{"contract_instance": {...}}` | XDR-JSON | -| `LedgerKeyContractInstance` | `"ledger_key_contract_instance"` | unit string | -| `LedgerKeyNonce` | `{"ledger_key_nonce": {...}}` | XDR-JSON | -| `ExecutableTag` | `{"executable_tag": "..."}` | string | - -The table reflects the `stellar-xdr` 28 serde mapping frozen by schema 1.0. If a -future `stellar-xdr` release adds a variant, the decoder reports a decode error -until the variant is documented here and the schema minor version is bumped. - -## Examples - -### Integer - -```console -$ stellar-foundry --format json scval AAAAAwAAACo= -{"kind":"scval","schema_version":"1.0","value":{"u32":42}} -``` - -### Symbol and vector - -```json -{ - "schema_version": "1.0", - "kind": "scval", - "value": { - "vec": [ - { "symbol": "transfer" }, - { "u32": 7 } - ] - } -} -``` - -### Map - -Maps are arrays of `key`/`value` pairs so arbitrary SCVal keys survive JSON -round-trips without string coercion: - -```json -{ - "schema_version": "1.0", - "kind": "scval", - "value": { - "map": [ - { "key": { "symbol": "admin" }, "value": { "address": "G..." } } - ] - } -} -``` - -## Recursion and limits - -- Nested `Vec`, `Map`, and contract-instance values use the same schema - recursively. -- Decoding is bounded to depth 128 and 16 MiB of XDR. Exceeding a limit returns - a structured decode error; no partial document is emitted. -- The `value` payload never contains raw binary; bytes and hashes are hex and - ids are strkeys. - -## Stability - -Within schema 1.x, existing variant tags and shapes do not change. Additive -changes (new documented variants or optional fields) bump the minor version. -Removing or renaming a tag requires schema 2.0 and a migration note in -`XDR.md`. Golden SCVal output is covered by -`crates/devkit-xdr/tests/snapshots/scval.json`. diff --git a/docs/XDR.md b/docs/XDR.md deleted file mode 100644 index d05c509..0000000 --- a/docs/XDR.md +++ /dev/null @@ -1,134 +0,0 @@ -# XDR JSON schema - -`devkit-xdr` emits a versioned document for every decoded value. The envelope is -the stable contract; the `value` member is the XDR-JSON mapping defined below. - -Current schema version: **1.0** (`devkit_xdr::XDR_JSON_SCHEMA_VERSION`). - -## Document envelope - -```json -{ - "schema_version": "1.0", - "kind": "scval", - "value": { "u32": 42 } -} -``` - -| Field | Type | Presence | Meaning | -| ----- | ---- | -------- | ------- | -| `schema_version` | string | always | Schema version in `MAJOR.MINOR` form. | -| `kind` | string | always | `scval`, `transaction_envelope`, or `contract_event`. | -| `value` | any JSON | always | Decoded XDR-JSON payload (see below). | -| `raw_xdr` | string | opt-in | Canonical, trimmed base64 XDR input. | - -`raw_xdr` is omitted by default so reports stay compact. Request it with -`--raw` on the CLI or `DecodeOptions { include_raw: true }` in the library. - -```console -$ stellar-foundry --format json --raw scval AAAAAwAAACo= -{"kind":"scval","raw_xdr":"AAAAAwAAACo=","schema_version":"1.0","value":{"u32":42}} -``` - -The raw encoding is always recoverable: the input is base64 XDR and the decoder -never rewrites it. The flag only echoes it into the document. - -## Kinds - -### `scval` - -`value` is the XDR-JSON mapping of a single `ScVal`. See -[`SCVAL.md`](SCVAL.md) for the variant table and examples. - -### `transaction_envelope` - -`value` is the XDR-JSON mapping of a `TransactionEnvelope`. The top-level key -identifies the envelope variant: - -- `tx_v0` — legacy `TransactionV0Envelope` -- `tx` — `TransactionV1Envelope` (contains `tx` and `signatures`) -- `tx_fee_bump` — `FeeBumpTransactionEnvelope` - -Example (`tx` variant, abbreviated): - -```json -{ - "schema_version": "1.0", - "kind": "transaction_envelope", - "value": { - "tx": { - "tx": { - "source_account": "G...", - "fee": 100, - "seq_num": "42", - "cond": "none", - "memo": "none", - "operations": [] - }, - "signatures": [ - { "hint": "abababab", "signature": "cdcd..." } - ] - } - } -} -``` - -### `contract_event` - -`value` is the XDR-JSON mapping of a `ContractEvent`: - -| Field | Meaning | -| ----- | ------- | -| `type` | `system`, `contract`, or `diagnostic` | -| `contract_id` | strkey contract id, or `null` for system events | -| `body.v0.topics` | array of `ScVal` values, each in the `scval` schema | -| `body.v0.data` | `ScVal` payload, in the `scval` schema | -| `ext` | extension point (`v0` in schema 1.0) | - -## XDR-JSON payload rules - -The `value` payload is the deterministic `stellar-xdr` serde mapping, frozen as -part of schema 1.0. Rules that consumers may rely on: - -- Enums and union variants are tagged objects or strings using `snake_case` - names, for example `{"u32":42}`, `{"symbol":"transfer"}`, `"none"`. -- 64-bit integers use JSON strings (`"42"`), matching `stellar-xdr`'s - `NumberOrString` mapping, so no precision is lost. -- Byte arrays are lowercase hex strings; hashes and signature hints are hex. -- Account, muxed-account, and contract ids are strkeys. -- Field names use `snake_case` (`seq_num`, `source_account`). -- Maps and vectors preserve XDR order; the decoder does not reorder them. -- Undecoded or unknown future XDR variants are a decode error, not silent data - loss. - -## Versioning and migration - -- `MAJOR` changes when an existing field is removed, renamed, retyped, or - reordered in a way that breaks consumers. A major bump requires a migration - section in this file and a new snapshot set. -- `MINOR` changes are additive: new optional fields, new `kind` values, or new - documented XDR variants. Existing fields keep their names and meanings. -- Every successfully decoded document carries `schema_version`. Decode errors - are returned through the existing structured error type, not as documents. -- Unknown fields must be ignored by consumers. - -### Migrating from the unversioned output - -Before schema 1.0, CLI output was `{ "kind": ..., "value": ... }` with no -version. Migration is additive: - -1. Read `schema_version`; reject documents whose major version is not `1`. -2. Keep reading `kind` and `value` as before. -3. Treat `raw_xdr` as optional and only present when requested. - -No field was removed or renamed, so existing consumers that ignored unknown -fields continue to work. - -## Tests - -- `crates/devkit-xdr/tests/schema_snapshots.rs` validates the envelope and - compares all three kinds against committed golden files in - `crates/devkit-xdr/tests/snapshots/`. -- Unit tests assert that `raw_xdr` is opt-in and preserved verbatim. -- Changing a snapshot requires updating this document and bumping the schema - version when the change is not additive.