Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/package-filters/rs-packages-no-workflows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ rs-sdk-trusted-context-provider: &sdk_trusted_context_provider
- *context_provider
- *dpp

dash-sdk-contract:
- packages/rs-dash-sdk-contract/**

dapi-grpc: &dapi_grpc
- packages/rs-platform-version/**
- packages/rs-dash-platform-macros/**
Expand Down
4 changes: 4 additions & 0 deletions .github/package-filters/rs-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ rs-sdk-trusted-context-provider: &sdk_trusted_context_provider
- *context_provider
- *dpp

dash-sdk-contract:
- .github/workflows/tests*
- packages/rs-dash-sdk-contract/**

dapi-grpc: &dapi_grpc
- .github/workflows/tests*
- packages/rs-platform-version/**
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/tests-rs-workspace.yml
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,17 @@ jobs:
done
done

# The contract-author declaration crate is a guest dependency: contract
# packages compile it for WebAssembly without std. Whole-workspace
# builds unify features and would hide a std leak, so check the
# standalone no-std graph on the no-std-library wasm target and run the
# crate's tests without default features.
- name: Check guest declaration cut
run: |
rustup target add wasm32v1-none
cargo check -p dash-sdk-contract --no-default-features --target wasm32v1-none --locked
cargo test -p dash-sdk-contract --no-default-features --locked

- name: Detect immutable structure changes
if: github.event_name == 'pull_request'
run: |
Expand Down Expand Up @@ -419,6 +430,7 @@ jobs:
--package token-history-contract \
--package wallet-utils-contract \
--package keyword-search-contract \
--package dash-sdk-contract \
--all-features \
--locked \
-E 'not test(~shield) and (not binary_id(=drive-abci::strategy_tests) or test(~comprehensive_mixed_operations) or test(~process_proposal_collision))'
Expand Down
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ resolver = "2"
members = [
"packages/dapi-grpc",
"packages/rs-dash-platform-macros",
"packages/rs-dash-sdk-contract",
"packages/rs-dpp",
"packages/rs-drive",
"packages/rs-platform-value",
Expand Down
4 changes: 4 additions & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@
- [Binding Patterns](wasm/binding-patterns.md)
- [Error Macros](wasm/error-macros.md)

# DashVM

- [Contract Declarations and the Author API](dashvm/contract-declarations.md)

---

# Appendix
Expand Down
1 change: 1 addition & 0 deletions book/src/contributing/coding-conventions.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ crate that has what the change needs, and no lower.
| A client API | `packages/rs-sdk` | Follow the query checklist in `packages/rs-sdk/README.md`. |
| A JavaScript binding | `packages/wasm-dpp2`, `packages/wasm-sdk` | Mirror the Rust shape; never validate. See `packages/wasm-dpp2/CONVENTIONS.md`. |
| Mobile orchestration (sync, identity registration, DashPay) | `packages/rs-platform-wallet` | The FFI crates and the Swift and Kotlin SDKs marshal; they do not decide. |
| A contract-author declaration or diagnostic | `packages/rs-dash-sdk-contract` | Builds for `wasm32v1-none` with `--no-default-features`; native limits are not duplicated there, they surface through `dash-contract-build`. See [Contract Declarations](../dashvm/contract-declarations.md). |

Three boundaries are enforced by CI and worth knowing by name:

Expand Down
335 changes: 335 additions & 0 deletions book/src/dashvm/contract-declarations.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/check-features/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ fn main() {
("rs-drive-proof-verifier", vec![]),
("rs-platform-wallet", vec![]),
("dash-platform-queries", vec![]),
("rs-dash-sdk-contract", vec![]),
];

for (specific_crate, to_ignore) in crates {
Expand Down
15 changes: 15 additions & 0 deletions packages/rs-dash-sdk-contract/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "dash-sdk-contract"
version.workspace = true
edition = "2021"
rust-version.workspace = true
authors = ["Dash Core Team"]
license = "MIT"
description = "Contract-author SDK for DashVM: declaration model, attribute grammar, diagnostics and canonical manifest"

[dependencies]
thiserror = { version = "2.0.17", default-features = false }

[features]
default = ["std"]
std = ["thiserror/std"]
24 changes: 24 additions & 0 deletions packages/rs-dash-sdk-contract/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# dash-sdk-contract

Contract-author SDK for DashVM: the declaration model behind `#[persistent]`,
`#[index]`, `#[rule]` and `#[entry]`, the attribute grammar those macros
implement, the diagnostics they report, and the canonical manifest a contract
package publishes.

```rust
use dash_sdk_contract::prelude::*;
```

This crate specifies the author-facing model. It carries no proc macros, no
host context and no runtime. It compiles without `std` on `wasm32v1-none`:

```bash
cargo check -p dash-sdk-contract --no-default-features --target wasm32v1-none
cargo test -p dash-sdk-contract
cargo test -p dash-sdk-contract --no-default-features
```

The chapter [Contract Declarations and the Author API](../../book/src/dashvm/contract-declarations.md)
in the Dash Platform Book describes the grammar, the identity rules, the
persistence semantics and what the validator checks versus what native
validation enforces.
136 changes: 136 additions & 0 deletions packages/rs-dash-sdk-contract/src/declare/capability.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
//! The capability catalogue: what a contract may require, and whether the
//! native host supports it today.

use core::fmt;

use super::collections::TypedCollectionKind;

/// Support status of a capability in the native host.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum CapabilityStatus {
/// Supported by the native host today; a manifest requesting it can be
/// deployed.
Native,
/// Specified, declarable, but without a native implementation yet. The
/// validator accepts the declaration; the build crate reports it as a
/// native gap and refuses to call the manifest deployable.
PendingNative,
/// Catalogued but disabled: the declaration is rejected until the
/// capability's semantics are specified. Today only the private document
/// store, whose encryption, key control, query visibility and proof
/// behaviour are not yet specified.
InterfaceDisabled,
}

/// A capability a contract requires, either explicitly (`requires = [...]`)
/// or derived from its declarations.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum CapabilityRequirement {
/// The native access-control primitive. Explicit.
Acl,
/// The native randomness primitive. Explicit.
Randomness,
/// `write = "contract"`: only the contract's own code may create
/// documents. Derived.
ContractWrites,
/// A rule with a native guard expression. Derived.
NativeGuards,
/// A rule with a read-only WASM predicate. Derived.
WasmPredicates,
/// A typed specialized collection of the given kind. Derived.
TypedCollections(TypedCollectionKind),
/// `store = "private"`. Derived.
PrivateStore,
/// Stored receipts (the default policy). Derived.
StoredReceipts,
/// At least one entry, which needs the DashVM runtime. Derived.
Entries,
/// More than one module, which needs bundle linking. Derived.
Modules,
}

impl CapabilityRequirement {
/// Whether the author writes this requirement (as opposed to the validator
/// deriving it).
pub fn is_explicit(&self) -> bool {
matches!(
self,
CapabilityRequirement::Acl | CapabilityRequirement::Randomness
)
}

/// The catalogue status of the requirement.
pub fn status(&self) -> CapabilityStatus {
match self {
CapabilityRequirement::PrivateStore => CapabilityStatus::InterfaceDisabled,
CapabilityRequirement::Acl
| CapabilityRequirement::Randomness
| CapabilityRequirement::ContractWrites
| CapabilityRequirement::NativeGuards
| CapabilityRequirement::WasmPredicates
| CapabilityRequirement::TypedCollections(_)
| CapabilityRequirement::StoredReceipts
| CapabilityRequirement::Entries
| CapabilityRequirement::Modules => CapabilityStatus::PendingNative,
}
}
}

impl fmt::Display for CapabilityRequirement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CapabilityRequirement::Acl => f.write_str("acl"),
CapabilityRequirement::Randomness => f.write_str("randomness"),
CapabilityRequirement::ContractWrites => f.write_str("contract writes"),
CapabilityRequirement::NativeGuards => f.write_str("native guards"),
CapabilityRequirement::WasmPredicates => f.write_str("wasm predicates"),
CapabilityRequirement::TypedCollections(kind) => {
write!(f, "typed collections ({kind})")
}
CapabilityRequirement::PrivateStore => f.write_str("private store"),
CapabilityRequirement::StoredReceipts => f.write_str("stored receipts"),
CapabilityRequirement::Entries => f.write_str("entries"),
CapabilityRequirement::Modules => f.write_str("modules"),
}
}
}

/// Whether the host stores a receipt for every outer invocation.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ReceiptPolicy {
/// One bounded receipt per outer invocation, the default.
#[default]
Stored,
/// No receipts.
Disabled,
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn should_keep_the_private_store_catalogued_but_disabled() {
assert_eq!(
CapabilityRequirement::PrivateStore.status(),
CapabilityStatus::InterfaceDisabled
);
assert!(!CapabilityRequirement::PrivateStore.is_explicit());
}

#[test]
fn should_mark_acl_and_randomness_explicit_and_pending() {
for capability in [
CapabilityRequirement::Acl,
CapabilityRequirement::Randomness,
] {
assert!(capability.is_explicit());
assert_eq!(capability.status(), CapabilityStatus::PendingNative);
}
}

#[test]
fn should_default_receipts_to_stored() {
assert_eq!(ReceiptPolicy::default(), ReceiptPolicy::Stored);
}
}
Loading
Loading