Skip to content

SPIKE: Protocol 28 (CAP-0085) - #1929

Draft
sisuresh wants to merge 7 commits into
stellar:mainfrom
sisuresh:p28-cap-0085
Draft

SPIKE: Protocol 28 (CAP-0085)#1929
sisuresh wants to merge 7 commits into
stellar:mainfrom
sisuresh:p28-cap-0085

Conversation

@sisuresh

Copy link
Copy Markdown
Contributor

Changes

  • Pin stellar-xdrsisuresh/rs-stellar-xdr@e1170cd9 (Bump version to 0.0.4 #553) and the three soroban-env crates → sisuresh/rs-soroban-env@78026e8b (Update expanded test files missed in #1699 #1704), both on fork branch p28-cap-0085.
  • Enable the env next feature (aggregates cap_0085_executable_ref) + the xdr cap_0085_executable_ref feature across the workspace; forward next through soroban-sdk-macros' soroban-env-common.
  • env.rs: import the new ExecutableTagObject host object (CAP-0085 adds it plus 3 host fns, surfaced via the auto-generated Env bindings — the only source change the build required).
  • Bump default test-ledger protocol 27 → 28; regenerate cost-estimate metering expect![] + JSON test snapshots; regen top-level and fuzz Cargo.lock.
  • deny.toml: allow the sisuresh git source; semver-checks excludes soroban-sdk + soroban-ledger-snapshot (ed25519-dalek 3.0.0 baseline break, mirrors CAP-0084).

Deferred

  • tests-expanded/* regeneration — needs linux cross-compile + host-dependent wasm bytes; adopt the + lines from the CI (linux) job log rather than regenerating on macOS.
  • No high-level SDK wrappers added for the new host fns (create_external_ref_contract / update_current_contract_executable_ref / create_executable_tag) — none required by build/tests; flagged for human review.
  • Productionize pins → merged stellar/ revs once Bump version to 0.0.4 #553 / Update expanded test files missed in #1699 #1704 land.

Upstream: stellar/rs-stellar-xdr#553, stellar/rs-soroban-env#1704 (canonical draft: stellar/rs-soroban-env#1703).
Downstream: handled in separate per-repo release passes.

sisuresh added 3 commits July 10, 2026 01:02
…cutable_ref

Pin stellar-xdr -> sisuresh/rs-stellar-xdr@e1170cd9 (stellar#553) and the three
soroban-env crates -> sisuresh/rs-soroban-env@78026e8b (stellar#1704), both on
fork branch p28-cap-0085. Enable the env 'next' feature (which aggregates
cap_0085_executable_ref) and the stellar-xdr 'cap_0085_executable_ref'
feature across the workspace; forward 'next' through soroban-sdk-macros'
soroban-env-common.

env.rs: import the new ExecutableTagObject host object (CAP-0085 adds it
plus 3 host fns, surfaced via call_macro_with_all_host_functions!).
testutils: bump default test ledger protocol 27 -> 28.
cost_estimate: regenerate expect![] metering values for env 28.
deny.toml: allow the sisuresh git source for the SPIKE pins.
Regenerate top-level and fuzz Cargo.lock.
Regenerated by cargo test: default ledger protocol 27 -> 28 and the
contractenvmetav0 interface-version stamp in embedded contract wasm.
…apshot from semver-checks

ed25519-dalek 3.0.0 breaks the cargo-semver-checks baseline rustdoc build;
the 27->28 bump is MAJOR so no semver signal is lost. Mirrors the CAP-0084
SPIKE workaround. Remove once ed25519-dalek 3.0.0 is yanked or capped.
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedlibfuzzer-sys@​0.4.6 ⏵ 0.4.1347 -1110093 -7100100

View full report

@socket-security

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: cargo libc is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: ?cargo/libfuzzer-sys@0.4.13cargo/curve25519-dalek@4.1.3cargo/ed25519-dalek@2.1.1cargo/sha2@0.10.9cargo/rand@0.8.5cargo/serde_with@3.12.0cargo/proptest@1.5.0cargo/ark-bls12-381@0.5.0cargo/ark-bn254@0.5.0cargo/ark-ff@0.5.0cargo/libc@0.2.186

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore cargo/libc@0.2.186. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: cargo zerocopy is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: ?cargo/rand@0.8.5cargo/proptest@1.5.0cargo/ark-bls12-381@0.5.0cargo/ark-bn254@0.5.0cargo/ark-ff@0.5.0cargo/zerocopy@0.8.54

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore cargo/zerocopy@0.8.54. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

sisuresh added 3 commits July 25, 2026 08:31
Re-point the env crates from the sisuresh/rs-soroban-env fork (stellar#1704, never
merged) to stellar/rs-soroban-env main at 358d8baf, which carries the merged
CAP-85 host implementation (stellar#1703) and its follow-up test (stellar#1711).

Re-point stellar-xdr from the sisuresh fork (stellar#553, closed) to
stellar/rs-stellar-xdr at a90de0c0 -- exactly the rev rs-soroban-env pins --
so the git source stays unified across the graph with no [patch] redirect.

Both revs self-report their versions, so the exact '=' constraints are
restored. deny.toml's temporary 'sisuresh' allow-org entry is reverted to
just ['stellar']; no git source outside the stellar org remains in Cargo.lock.

cargo check -p soroban-sdk passes.
The pin bump alone left CAP-85 unreachable from contract code: soroban-sdk
declares 'mod env' privately and does not re-export the internal Env trait, so
create_executable_tag, create_external_ref_contract and
update_current_contract_executable_ref had no public caller.

Add ExecutableTag, wrapping ExecutableTagObject, constructible only via
ExecutableTag::new -> create_executable_tag, matching the protocol rule that a
tag may not be forged. It converts to and from Val so it can be used directly
as a storage key, which is how the owner contract writes the executable
reference entry.

Add Deployer::update_current_contract_executable_ref and
DeployerWithAddress::deploy_external_ref alongside their Wasm counterparts.

Docs state the trust relationship explicitly: the owner of a referenced entry
can substitute arbitrary code into every contract referring to it. CAP-85 flags
this as documentation-only, since the protocol cannot enforce it.

Verified: probe contract builds to wasm32v1-none and its import table carries
l.h, l.i and l.j -- the three CAP-85 host functions.
The SPIKE enabled soroban-env-{common,guest,host}/next because at the time env
main was still protocol 27 and CAP-85's host functions, gated on
min_supported_protocol 28, were only reachable from the next protocol.

env main is now 28.0.0, so 'next' no longer selects protocol 28 -- it selects
29. LEDGER_PROTOCOL_VERSION in soroban-env-macros is 28 without the feature and
29 with it, and that value is baked into every contract's env meta. The result
was that contracts built with this SDK were rejected outright by a protocol-28
host:

  HostError: Error(WasmVm, InvalidInput)
  "contract protocol number is newer than host", 29

Dropping the feature makes contracts declare 28, and the CAP-85 host functions
remain available because they are part of protocol 28 now.

The stellar-xdr cap_0085_executable_ref feature is unrelated to this and stays:
it gates the XDR types, not the protocol number.

Verified against stellar/quickstart:nightly-next running protocol 28: upload
now succeeds where it previously failed simulation.
@sisuresh

Copy link
Copy Markdown
Contributor Author

Pushed three commits productionizing this SPIKE, after using it to validate CAP-0085 end to end against stellar/quickstart:nightly-next at protocol 28.

07fad4c0 — drop the fork pins. The env crates were pinned to sisuresh/rs-soroban-env for #1704, which never merged, and stellar-xdr to sisuresh/rs-stellar-xdr for #553, which is closed. Neither is needed now: stellar/rs-soroban-env@358d8baf carries the merged CAP-85 host (#1703) plus its follow-up test (#1711), and itself pins stellar/rs-stellar-xdr@a90de0c0 with cap_0085_executable_ref — so pinning that same rev keeps the git source unified with no [patch] redirect. Exact = version constraints restored; deny.toml's temporary sisuresh allow-org reverted. No git source outside the stellar org remains in Cargo.lock.

8f535980 — add the contract-facing surface. The pin bump alone left CAP-85 unreachable from contract code: soroban-sdk declares mod env privately and doesn't re-export the internal Env trait, so create_executable_tag, create_external_ref_contract and update_current_contract_executable_ref had no public caller. Adds ExecutableTag (constructible only via ExecutableTag::new, matching the protocol rule that a tag can't be forged; converts to/from Val so it works directly as a storage key), plus Deployer::update_current_contract_executable_ref and DeployerWithAddress::deploy_external_ref alongside their Wasm counterparts. Docs state the trust relationship explicitly — the owner of a referenced entry can substitute arbitrary code into every contract referring to it.

b30d8e3d — remove the next feature. This one is load-bearing. Enabling soroban-env-*/next was right when env main was protocol 27, but env main is 28 now, so next selects 29. LEDGER_PROTOCOL_VERSION is baked into each contract's env meta, and a protocol-28 host rejects the result outright:

HostError: Error(WasmVm, InvalidInput)
"contract protocol number is newer than host", 29

Every contract built from this branch as it stood was unusable on a p28 network. Dropping the feature makes contracts declare 28; the CAP-85 host functions stay available because they're part of protocol 28 now. The stellar-xdr cap_0085_executable_ref feature is unrelated and stays — it gates the XDR types, not the protocol number.

Testing

Validated against a locally built stellar/quickstart:nightly-next at protocol 28 (core c444c073, rpc protocol-next, horizon protocol-next). Six fixture contracts, 28 transactions, all successful. Covered: atomic fleet upgrade (three contracts re-pointed by one set_tag), all eight protocol-enforced storage invariants, self-update with deferred application, the ref→Wasm→ref round trip, TTL scoping across instance / reference entry / code, get_address_executable resolution, and the executable_update event shape. No defects found in core, RPC or Horizon.

Contracts build to wasm32v1-none with the three host functions in the import table (l.h, l.i, l.j).

Not run: this repo's own test suite — cargo check -p soroban-sdk is clean, but the snapshot tests in particular are worth a look given the env bump.

The CLI counterpart is stellar/stellar-cli#2659, which patches its soroban-* deps at this branch until this lands.

auth::ContractExecutable is what the host hands a custom account's
__check_auth inside CreateContractHostFnContext, and it had only
Wasm(BytesN<32>). CAP-85 requires the new variant here specifically so custom
accounts can take the reference into account when authorizing a creation.

Without it no custom account can authorize an external-ref creation at all,
whatever policy it wants: __check_auth fails while deserializing its own
arguments, because "ExternalRef" is not among the discriminants it knows.
Verified on futurenet against a Wasm-only account:

  Error(Value, MissingValue) -> VM call trapped with HostError
                             -> Error(Auth, InvalidAction)

which is also exactly the backwards-incompatibility CAP-85 documents for
accounts predating the change. An account built with this variant parses the
same context and decides on it: two accounts differing only in their trusted
owner produced AUTHORIZED and REJECTED respectively.

Shape follows the CAP -- ContractExecutableRef { owner: Address, tag: String }
-- and matches the wire form observed in both the recorded auth entry and the
executable_update event: SCVec([SCSymbol("ExternalRef"), SCMap([owner, tag])]).

StellarAsset is deliberately not added; it was already absent from this enum
before CAP-85 and is out of scope here.

The type carries export = false on the non-spec-shaking path, as its siblings
do, so contract specs and their snapshots are unaffected.
@sisuresh

Copy link
Copy Markdown
Contributor Author

Pushed a fourth commit, e8bc3f06, adding the CAP-0085 variant to the authorization context enum.

auth::ContractExecutable — what the host hands a custom account's __check_auth inside CreateContractHostFnContext — had only Wasm(BytesN<32>). CAP-85 requires the new variant here specifically so custom accounts can consider the reference when authorizing a creation.

This turned out to be load-bearing rather than cosmetic. Verified on futurenet with a Wasm-only account (i.e. one written as it would have been before CAP-85) authorizing an external-ref creation:

Error(Value, MissingValue)  ->  VM call trapped with HostError
                            ->  Error(Auth, InvalidAction)

It fails while deserializing its own arguments, because "ExternalRef" is not among the discriminants it knows — before any of its own logic runs. So without this variant no custom account can authorize an external-ref creation, whatever policy it wants.

That is also exactly the backwards-incompatibility CAP-85 documents:

The new enum variant may not be parsed by the custom accounts that are not aware of the new executable type, so they won't be able to authorize the contract creation with executable reference. This is expected, as we err on the side of caution.

An account built with the variant parses the same context and decides on it. Two accounts differing only in their stored trusted owner, evaluated in enforce mode:

account trusts actual owner outcome
manager2 manager2 AUTHORIZED
manager1 manager2 REJECTED (clean contract error)

Shape follows the CAP — ContractExecutableRef { owner: Address, tag: String } — and matches the wire form seen in both the recorded auth entry and the executable_update event: SCVec([SCSymbol("ExternalRef"), SCMap([owner, tag])]).

StellarAsset is deliberately not added; it was already absent from this enum before CAP-85 and is out of scope. The new type carries export = false on the non-spec-shaking path like its siblings, so contract specs and their snapshots are unaffected.

Not verified: this repo's test suite. cargo check -p soroban-sdk is clean, but cargo test -p soroban-sdk --lib cannot run in my checkout — it needs pre-built test_*.wasm fixtures that aren't present, and it fails identically with this commit reverted. CI is the first real run.

Validation

Whole CAP-85 matrix re-run against futurenet (protocol 28, core c1075639, rpc 0c06b410, horizon 4460e7f9) after the earlier local quickstart run; both agree. Also closed since the last comment: CREATE_CONTRACT_V2 with EXTERNAL_REF and constructor args, explicit controls proving the storage-invariant panics are tag-specific rather than operation-specific, ..._ttl_v2 for its one contract-reachable scope, and a from-scratch Horizon reingest over the CAP-85 ledger range. Transaction meta decoded cleanly across all 39 transactions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant