@feltdb/core 0.13.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/dist/agent-runtime.d.ts +64 -2
  2. package/dist/agent-runtime.d.ts.map +1 -1
  3. package/dist/agent-runtime.js +121 -11
  4. package/dist/agent-state.d.ts +68 -0
  5. package/dist/agent-state.d.ts.map +1 -1
  6. package/dist/agent-state.js +210 -12
  7. package/dist/agent.d.ts +10 -1
  8. package/dist/agent.d.ts.map +1 -1
  9. package/dist/agent.js +10 -1
  10. package/dist/build-identity.json +12 -4
  11. package/dist/cli/commands.js +214 -10
  12. package/dist/cli/publish-engine.js +116 -16
  13. package/dist/collection.d.ts +3 -1
  14. package/dist/collection.d.ts.map +1 -1
  15. package/dist/collection.js +33 -8
  16. package/dist/create/build-identity.json +12 -4
  17. package/dist/create/cli.js +1 -1
  18. package/dist/create/create.js +4 -6
  19. package/dist/create/managed-account.js +93 -35
  20. package/dist/create/package-versions.js +1 -1
  21. package/dist/create/server-source/Cargo.lock +8 -511
  22. package/dist/create/server-source/Cargo.toml +2 -13
  23. package/dist/create/server-source/crates/feltdb-client/Cargo.toml +1 -1
  24. package/dist/create/server-source/crates/feltdb-server/Cargo.toml +1 -1
  25. package/dist/create/server-source/crates/feltdb-server/README.md +6 -1
  26. package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +9 -1
  27. package/dist/create/server-source/crates/feltdb-server/src/auth.rs +85 -25
  28. package/dist/create/server-source/crates/feltdb-server/src/backup.rs +4 -0
  29. package/dist/create/server-source/crates/feltdb-server/src/console_accounts.rs +1009 -0
  30. package/dist/create/server-source/crates/feltdb-server/src/control_authorization.rs +441 -0
  31. package/dist/create/server-source/crates/feltdb-server/src/identity.rs +68 -6
  32. package/dist/create/server-source/crates/feltdb-server/src/key_management.rs +355 -23
  33. package/dist/create/server-source/crates/feltdb-server/src/lib.rs +3 -0
  34. package/dist/create/server-source/crates/feltdb-server/src/main.rs +1134 -96
  35. package/dist/create/server-source/crates/feltdb-server/src/redaction.rs +208 -0
  36. package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +198 -19
  37. package/dist/create/server-source/crates/feltdb-server/tests/transaction_precondition_contract.rs +403 -0
  38. package/dist/create/server-source/crates/feltdb-wasm/Cargo.toml +1 -1
  39. package/dist/db.d.ts +75 -10
  40. package/dist/db.d.ts.map +1 -1
  41. package/dist/db.js +168 -29
  42. package/dist/deployment.d.ts +19 -0
  43. package/dist/deployment.d.ts.map +1 -1
  44. package/dist/deployment.js +38 -2
  45. package/dist/embedded-query.d.ts.map +1 -1
  46. package/dist/embedded-query.js +5 -4
  47. package/dist/embedded-transaction.d.ts +8 -4
  48. package/dist/embedded-transaction.d.ts.map +1 -1
  49. package/dist/embedded-transaction.js +55 -19
  50. package/dist/error-codes.d.ts +36 -2
  51. package/dist/error-codes.d.ts.map +1 -1
  52. package/dist/error-codes.js +50 -3
  53. package/dist/feltdb.d.ts +7 -0
  54. package/dist/feltdb.d.ts.map +1 -1
  55. package/dist/file-db.d.ts +27 -0
  56. package/dist/file-db.d.ts.map +1 -1
  57. package/dist/file-db.js +145 -7
  58. package/dist/http-client.d.ts +11 -3
  59. package/dist/http-client.d.ts.map +1 -1
  60. package/dist/http-client.js +27 -14
  61. package/dist/http-db.d.ts +43 -1
  62. package/dist/http-db.d.ts.map +1 -1
  63. package/dist/http-db.js +287 -59
  64. package/dist/index.js +1 -1
  65. package/dist/local-state-lock.d.ts +34 -0
  66. package/dist/local-state-lock.d.ts.map +1 -0
  67. package/dist/local-state-lock.js +225 -0
  68. package/dist/managed-recovery.d.ts +6 -0
  69. package/dist/managed-recovery.d.ts.map +1 -1
  70. package/dist/managed-recovery.js +9 -0
  71. package/dist/reactive-graph.d.ts +0 -3
  72. package/dist/reactive-graph.d.ts.map +1 -1
  73. package/dist/reactive-graph.js +11 -1
  74. package/dist/runtime-registry.d.ts +7 -2
  75. package/dist/runtime-registry.d.ts.map +1 -1
  76. package/dist/runtime-registry.js +2 -2
  77. package/dist/studio/components/index.js +1 -1
  78. package/dist/studio/{components-BSGmHfif.js → components-CJmzntBi.js} +2 -1
  79. package/dist/studio/index.js +1 -1
  80. package/dist/studio-app/assets/{feltdb_wasm-3hDngl1h.js → feltdb_wasm-DtybUY0q.js} +1 -1
  81. package/dist/studio-app/assets/feltdb_wasm_bg-DsgIUwaB.wasm +0 -0
  82. package/dist/studio-app/assets/index-C4OffsZl.js +37 -0
  83. package/dist/studio-app/index.html +1 -1
  84. package/dist/transaction.d.ts +27 -11
  85. package/dist/transaction.d.ts.map +1 -1
  86. package/dist/transaction.js +20 -14
  87. package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
  88. package/dist/wasm/package.json +1 -1
  89. package/dist/workload.d.ts +86 -6
  90. package/dist/workload.d.ts.map +1 -1
  91. package/dist/workload.js +51 -3
  92. package/dist/workspace/workspace-connection.d.ts.map +1 -1
  93. package/dist/workspace/workspace-connection.js +14 -3
  94. package/package.json +1 -1
  95. package/dist/create/server-source/crates/feltdb/Cargo.lock +0 -175
  96. package/dist/create/server-source/crates/feltdb/Cargo.toml +0 -102
  97. package/dist/create/server-source/crates/feltdb/LICENSE +0 -21
  98. package/dist/create/server-source/crates/feltdb/README.md +0 -75
  99. package/dist/create/server-source/crates/feltdb/benches/baselines/gate-13-redux.json +0 -370
  100. package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +0 -589
  101. package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +0 -247
  102. package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +0 -440
  103. package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +0 -275
  104. package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +0 -381
  105. package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +0 -467
  106. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +0 -489
  107. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +0 -281
  108. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +0 -606
  109. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +0 -531
  110. package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +0 -462
  111. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +0 -505
  112. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +0 -484
  113. package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +0 -758
  114. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +0 -421
  115. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +0 -329
  116. package/dist/create/server-source/crates/feltdb/src/acceptance_tests.rs +0 -1705
  117. package/dist/create/server-source/crates/feltdb/src/acquisition.rs +0 -286
  118. package/dist/create/server-source/crates/feltdb/src/admission.rs +0 -185
  119. package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +0 -507
  120. package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +0 -539
  121. package/dist/create/server-source/crates/feltdb/src/agent_state.rs +0 -320
  122. package/dist/create/server-source/crates/feltdb/src/agent_transition_contract.rs +0 -1545
  123. package/dist/create/server-source/crates/feltdb/src/analytics.rs +0 -521
  124. package/dist/create/server-source/crates/feltdb/src/application.rs +0 -2674
  125. package/dist/create/server-source/crates/feltdb/src/application_runtime.rs +0 -1070
  126. package/dist/create/server-source/crates/feltdb/src/authority.rs +0 -479
  127. package/dist/create/server-source/crates/feltdb/src/authority_failover.rs +0 -847
  128. package/dist/create/server-source/crates/feltdb/src/authorization.rs +0 -1030
  129. package/dist/create/server-source/crates/feltdb/src/authorization_security_tests.rs +0 -1122
  130. package/dist/create/server-source/crates/feltdb/src/bin/feltdb-authority-client.rs +0 -32
  131. package/dist/create/server-source/crates/feltdb/src/bin/feltdb-authority.rs +0 -19
  132. package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +0 -1474
  133. package/dist/create/server-source/crates/feltdb/src/capabilities/mod.rs +0 -8
  134. package/dist/create/server-source/crates/feltdb/src/capabilities/search.rs +0 -418
  135. package/dist/create/server-source/crates/feltdb/src/capabilities/vector.rs +0 -482
  136. package/dist/create/server-source/crates/feltdb/src/capability.rs +0 -903
  137. package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +0 -263
  138. package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +0 -77
  139. package/dist/create/server-source/crates/feltdb/src/causal_backlog_bound.rs +0 -496
  140. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +0 -2574
  141. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +0 -212
  142. package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +0 -422
  143. package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +0 -445
  144. package/dist/create/server-source/crates/feltdb/src/content_distribution.rs +0 -465
  145. package/dist/create/server-source/crates/feltdb/src/convergence.rs +0 -634
  146. package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +0 -427
  147. package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +0 -376
  148. package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +0 -365
  149. package/dist/create/server-source/crates/feltdb/src/cron.rs +0 -294
  150. package/dist/create/server-source/crates/feltdb/src/dedup_bound_investigation.rs +0 -483
  151. package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +0 -477
  152. package/dist/create/server-source/crates/feltdb/src/distributed_tests.rs +0 -1003
  153. package/dist/create/server-source/crates/feltdb/src/distributed_transactions.rs +0 -1334
  154. package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +0 -368
  155. package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +0 -231
  156. package/dist/create/server-source/crates/feltdb/src/durable_operation_identity.rs +0 -482
  157. package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +0 -203
  158. package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +0 -317
  159. package/dist/create/server-source/crates/feltdb/src/equality_index.rs +0 -595
  160. package/dist/create/server-source/crates/feltdb/src/execution.rs +0 -426
  161. package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +0 -214
  162. package/dist/create/server-source/crates/feltdb/src/indexing.rs +0 -776
  163. package/dist/create/server-source/crates/feltdb/src/lib.rs +0 -7274
  164. package/dist/create/server-source/crates/feltdb/src/managed_cas_tests.rs +0 -234
  165. package/dist/create/server-source/crates/feltdb/src/materialization.rs +0 -184
  166. package/dist/create/server-source/crates/feltdb/src/metrics.rs +0 -266
  167. package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +0 -310
  168. package/dist/create/server-source/crates/feltdb/src/multi_operation_transaction.rs +0 -736
  169. package/dist/create/server-source/crates/feltdb/src/observability.rs +0 -379
  170. package/dist/create/server-source/crates/feltdb/src/operation.rs +0 -290
  171. package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +0 -439
  172. package/dist/create/server-source/crates/feltdb/src/operation_log.rs +0 -349
  173. package/dist/create/server-source/crates/feltdb/src/p1_application_atomicity.rs +0 -332
  174. package/dist/create/server-source/crates/feltdb/src/p1_atomicity_acceptance.rs +0 -718
  175. package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +0 -487
  176. package/dist/create/server-source/crates/feltdb/src/peer_registry.rs +0 -166
  177. package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +0 -289
  178. package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +0 -566
  179. package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +0 -3391
  180. package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +0 -203
  181. package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +0 -261
  182. package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +0 -502
  183. package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +0 -216
  184. package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +0 -303
  185. package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +0 -323
  186. package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +0 -531
  187. package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +0 -406
  188. package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +0 -436
  189. package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +0 -387
  190. package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +0 -378
  191. package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +0 -359
  192. package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +0 -372
  193. package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +0 -404
  194. package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +0 -376
  195. package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +0 -461
  196. package/dist/create/server-source/crates/feltdb/src/policy_evaluation.rs +0 -2125
  197. package/dist/create/server-source/crates/feltdb/src/production_api.rs +0 -418
  198. package/dist/create/server-source/crates/feltdb/src/provenance.rs +0 -207
  199. package/dist/create/server-source/crates/feltdb/src/query_execution_diagnostics.rs +0 -126
  200. package/dist/create/server-source/crates/feltdb/src/query_performance.rs +0 -218
  201. package/dist/create/server-source/crates/feltdb/src/read_basis.rs +0 -257
  202. package/dist/create/server-source/crates/feltdb/src/references.rs +0 -404
  203. package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +0 -401
  204. package/dist/create/server-source/crates/feltdb/src/replica_acknowledgements.rs +0 -501
  205. package/dist/create/server-source/crates/feltdb/src/replica_membership.rs +0 -680
  206. package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +0 -163
  207. package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +0 -133
  208. package/dist/create/server-source/crates/feltdb/src/routing.rs +0 -409
  209. package/dist/create/server-source/crates/feltdb/src/semantic_decision.rs +0 -2029
  210. package/dist/create/server-source/crates/feltdb/src/semantic_query.rs +0 -599
  211. package/dist/create/server-source/crates/feltdb/src/sharding.rs +0 -549
  212. package/dist/create/server-source/crates/feltdb/src/state_conflict_contract.rs +0 -516
  213. package/dist/create/server-source/crates/feltdb/src/state_contract.rs +0 -5894
  214. package/dist/create/server-source/crates/feltdb/src/state_diff_contract.rs +0 -222
  215. package/dist/create/server-source/crates/feltdb/src/state_facade.rs +0 -320
  216. package/dist/create/server-source/crates/feltdb/src/state_hash.rs +0 -291
  217. package/dist/create/server-source/crates/feltdb/src/state_model.rs +0 -4158
  218. package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +0 -379
  219. package/dist/create/server-source/crates/feltdb/src/state_trigger.rs +0 -673
  220. package/dist/create/server-source/crates/feltdb/src/storage.rs +0 -273
  221. package/dist/create/server-source/crates/feltdb/src/submission.rs +0 -471
  222. package/dist/create/server-source/crates/feltdb/src/sync.rs +0 -495
  223. package/dist/create/server-source/crates/feltdb/src/sync_contract.rs +0 -822
  224. package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +0 -433
  225. package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +0 -462
  226. package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +0 -965
  227. package/dist/create/server-source/crates/feltdb/src/transaction_preconditions.rs +0 -1120
  228. package/dist/create/server-source/crates/feltdb/src/transactions.rs +0 -643
  229. package/dist/create/server-source/crates/feltdb/src/trigger.rs +0 -390
  230. package/dist/create/server-source/crates/feltdb/src/trigger_contract.rs +0 -749
  231. package/dist/create/server-source/crates/feltdb/src/versioned_object_substrate.rs +0 -708
  232. package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +0 -1133
  233. package/dist/create/server-source/crates/feltdb/src/workflow.rs +0 -713
  234. package/dist/create/server-source/crates/feltdb/src/workflow_acceptance_tests.rs +0 -510
  235. package/dist/create/server-source/crates/feltdb/src/workflow_integration.rs +0 -354
  236. package/dist/create/server-source/crates/feltdb/src/workflow_runtime.rs +0 -594
  237. package/dist/create/server-source/crates/feltdb/src/workload.rs +0 -2886
  238. package/dist/create/server-source/crates/feltdb/src/workload_diagnostics.rs +0 -456
  239. package/dist/create/server-source/crates/feltdb/tests/agent_state_native.rs +0 -1188
  240. package/dist/create/server-source/crates/feltdb/tests/agent_transition_contract_conformance.rs +0 -2884
  241. package/dist/create/server-source/crates/feltdb/tests/authority_process.rs +0 -297
  242. package/dist/create/server-source/crates/feltdb/tests/authority_transaction_identity.rs +0 -534
  243. package/dist/create/server-source/crates/feltdb/tests/bounded_read_contract.rs +0 -132
  244. package/dist/create/server-source/crates/feltdb/tests/branching_evidence.rs +0 -299
  245. package/dist/create/server-source/crates/feltdb/tests/common/mod.rs +0 -96
  246. package/dist/create/server-source/crates/feltdb/tests/compaction_stall_contract.rs +0 -272
  247. package/dist/create/server-source/crates/feltdb/tests/crash_durability_contract.rs +0 -467
  248. package/dist/create/server-source/crates/feltdb/tests/current_revision_authority_evidence.rs +0 -309
  249. package/dist/create/server-source/crates/feltdb/tests/durable_backup_contract.rs +0 -445
  250. package/dist/create/server-source/crates/feltdb/tests/durable_corruption_contract.rs +0 -518
  251. package/dist/create/server-source/crates/feltdb/tests/durable_format_compatibility.rs +0 -392
  252. package/dist/create/server-source/crates/feltdb/tests/feltdb_state_boundary_tests.rs +0 -850
  253. package/dist/create/server-source/crates/feltdb/tests/fixtures/agent_transition_corpus.json +0 -23340
  254. package/dist/create/server-source/crates/feltdb/tests/fixtures/state_conflict_contract_corpus.json +0 -1916
  255. package/dist/create/server-source/crates/feltdb/tests/fixtures/state_diff_contract_corpus.json +0 -1878
  256. package/dist/create/server-source/crates/feltdb/tests/fixtures/trigger_contract_corpus.json +0 -1862
  257. package/dist/create/server-source/crates/feltdb/tests/generalized_version_control_evidence.rs +0 -123
  258. package/dist/create/server-source/crates/feltdb/tests/generalized_version_control_substrate_completion.rs +0 -299
  259. package/dist/create/server-source/crates/feltdb/tests/local_process_ownership.rs +0 -69
  260. package/dist/create/server-source/crates/feltdb/tests/managed_incident_regression.rs +0 -191
  261. package/dist/create/server-source/crates/feltdb/tests/operational_health_contract.rs +0 -278
  262. package/dist/create/server-source/crates/feltdb/tests/pr34_query_collection.rs +0 -234
  263. package/dist/create/server-source/crates/feltdb/tests/pr35_equality_index.rs +0 -947
  264. package/dist/create/server-source/crates/feltdb/tests/pr7_self_authorization_proof.rs +0 -404
  265. package/dist/create/server-source/crates/feltdb/tests/pr8_vocabulary_assessment.rs +0 -916
  266. package/dist/create/server-source/crates/feltdb/tests/pr9_phase2_boundary_tests.rs +0 -1045
  267. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3a_path_a_tests.rs +0 -347
  268. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_authorized_mutations.rs +0 -361
  269. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_role_based_authorization.rs +0 -330
  270. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_simple_auth_delete.rs +0 -93
  271. package/dist/create/server-source/crates/feltdb/tests/pr9_phase3c_team_delete_role_authorization.rs +0 -622
  272. package/dist/create/server-source/crates/feltdb/tests/pr9_teams_role_based_access.rs +0 -517
  273. package/dist/create/server-source/crates/feltdb/tests/production_certification.rs +0 -1153
  274. package/dist/create/server-source/crates/feltdb/tests/production_contract.rs +0 -771
  275. package/dist/create/server-source/crates/feltdb/tests/production_readiness_contract.rs +0 -1383
  276. package/dist/create/server-source/crates/feltdb/tests/reconciliation_application.rs +0 -868
  277. package/dist/create/server-source/crates/feltdb/tests/reconciliation_wire_format_evidence.rs +0 -221
  278. package/dist/create/server-source/crates/feltdb/tests/replicated_history_contract.rs +0 -417
  279. package/dist/create/server-source/crates/feltdb/tests/resource_scoped_revisions.rs +0 -338
  280. package/dist/create/server-source/crates/feltdb/tests/revision_identity_contract.rs +0 -1039
  281. package/dist/create/server-source/crates/feltdb/tests/revision_model_decision.rs +0 -739
  282. package/dist/create/server-source/crates/feltdb/tests/revision_retention_boundary_evidence.rs +0 -452
  283. package/dist/create/server-source/crates/feltdb/tests/saas_authorization_integration.rs +0 -81
  284. package/dist/create/server-source/crates/feltdb/tests/saas_invitation_lifecycle.rs +0 -438
  285. package/dist/create/server-source/crates/feltdb/tests/semantic_decision.rs +0 -1165
  286. package/dist/create/server-source/crates/feltdb/tests/semantic_query.rs +0 -767
  287. package/dist/create/server-source/crates/feltdb/tests/semantic_query_contract.rs +0 -363
  288. package/dist/create/server-source/crates/feltdb/tests/state_conflict_contract_conformance.rs +0 -1799
  289. package/dist/create/server-source/crates/feltdb/tests/state_diff_contract_conformance.rs +0 -1316
  290. package/dist/create/server-source/crates/feltdb/tests/state_model_integration.rs +0 -358
  291. package/dist/create/server-source/crates/feltdb/tests/state_persistence_integration.rs +0 -365
  292. package/dist/create/server-source/crates/feltdb/tests/state_ref_authority.rs +0 -918
  293. package/dist/create/server-source/crates/feltdb/tests/state_store_boundary_evidence.rs +0 -299
  294. package/dist/create/server-source/crates/feltdb/tests/storage_independent_versioned_object_substrate.rs +0 -328
  295. package/dist/create/server-source/crates/feltdb/tests/sync_divergence_evidence.rs +0 -255
  296. package/dist/create/server-source/crates/feltdb/tests/three_way_input_boundary_evidence.rs +0 -249
  297. package/dist/create/server-source/crates/feltdb/tests/trigger_contract_conformance.rs +0 -994
  298. package/dist/create/server-source/crates/feltdb/tests/workload_envelope_contract.rs +0 -484
  299. package/dist/studio-app/assets/feltdb_wasm_bg-CFQT4gCy.wasm +0 -0
  300. package/dist/studio-app/assets/index-Dql1hS5m.js +0 -30
@@ -1,1316 +0,0 @@
1
- //! Conformance, determinism and capability proofs for the `feltdb.state.diff`
2
- //! deterministic contract.
3
- //!
4
- //! The second specimen of the pattern proved for `feltdb.trigger.match`:
5
- //!
6
- //! ```text
7
- //! Contract semantics are authoritative.
8
- //! Runtime implementation is replaceable.
9
- //!
10
- //! NativeStateDiff(a, b) == WasmStateDiff(a, b) for every fixture
11
- //! ```
12
- //!
13
- //! The corpus in `tests/fixtures/state_diff_contract_corpus.json` is committed
14
- //! rather than generated, and carries the canonical output each case must
15
- //! produce. Three comparisons run over it:
16
- //!
17
- //! 1. native output == committed expectation (runs everywhere);
18
- //! 2. WASM output == committed expectation (needs `wasm32-unknown-unknown`);
19
- //! 3. native output == WASM output, byte for byte.
20
- //!
21
- //! The first makes the suite meaningful where the WASM toolchain is absent: a
22
- //! change in the native diff's semantics is caught by the same bytes the WASM
23
- //! implementation was measured against.
24
-
25
- use feltdb::state_diff_contract::{
26
- canonical_input_bytes, evaluate_canonical, StateDiffInput, STATE_DIFF_CONTRACT_ID,
27
- STATE_DIFF_CONTRACT_VERSION,
28
- };
29
- use serde::Deserialize;
30
- use serde_json::Value;
31
- use std::path::{Path, PathBuf};
32
- use std::process::Command;
33
- use std::sync::OnceLock;
34
-
35
- // ---------------------------------------------------------------------------
36
- // Corpus
37
- // ---------------------------------------------------------------------------
38
-
39
- #[derive(Debug, Deserialize)]
40
- struct Corpus {
41
- corpus_version: u32,
42
- contract: String,
43
- contract_version: u32,
44
- #[allow(dead_code)]
45
- about: String,
46
- cases: Vec<Case>,
47
- }
48
-
49
- #[derive(Debug, Deserialize)]
50
- struct Case {
51
- name: String,
52
- #[allow(dead_code)]
53
- why: String,
54
- /// A well-formed contract input, canonically encoded by the harness.
55
- #[serde(default)]
56
- input: Option<StateDiffInput>,
57
- /// Bytes fed to the contract verbatim, for non-canonical and malformed
58
- /// encodings that a typed value cannot express.
59
- #[serde(default)]
60
- raw_input: Option<String>,
61
- /// The canonical encoding of `input`, recorded so that every consumer of
62
- /// the corpus presents the same bytes.
63
- ///
64
- /// A consumer in another language cannot always reproduce it: JavaScript
65
- /// has one number type, so it cannot encode the difference between `42` and
66
- /// `42.0` that this contract's value equality depends on.
67
- #[serde(default)]
68
- canonical_input: Option<String>,
69
- /// The canonical output both implementations must produce.
70
- #[serde(default)]
71
- expected_output: Option<String>,
72
- }
73
-
74
- impl Case {
75
- /// The exact bytes this case presents to a contract implementation.
76
- fn bytes(&self) -> Vec<u8> {
77
- match (&self.input, &self.raw_input) {
78
- (Some(input), None) => canonical_input_bytes(input),
79
- (None, Some(raw)) => raw.as_bytes().to_vec(),
80
- _ => panic!(
81
- "case {} must have exactly one of input/raw_input",
82
- self.name
83
- ),
84
- }
85
- }
86
-
87
- fn expected(&self) -> &str {
88
- self.expected_output.as_deref().unwrap_or_else(|| {
89
- panic!(
90
- "case {} has no expected_output; regenerate the corpus with \
91
- FELTDB_STATE_DIFF_CONTRACT_BLESS=1",
92
- self.name
93
- )
94
- })
95
- }
96
- }
97
-
98
- fn corpus_path() -> PathBuf {
99
- Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/state_diff_contract_corpus.json")
100
- }
101
-
102
- fn corpus() -> &'static Corpus {
103
- static CORPUS: OnceLock<Corpus> = OnceLock::new();
104
- CORPUS.get_or_init(|| {
105
- let bytes = std::fs::read(corpus_path()).expect("conformance corpus is committed");
106
- serde_json::from_slice(&bytes).expect("conformance corpus is well formed")
107
- })
108
- }
109
-
110
- #[test]
111
- fn corpus_addresses_this_contract_at_this_version() {
112
- let corpus = corpus();
113
- assert_eq!(corpus.corpus_version, 1);
114
- assert_eq!(corpus.contract, STATE_DIFF_CONTRACT_ID);
115
- assert_eq!(corpus.contract_version, STATE_DIFF_CONTRACT_VERSION);
116
- assert!(
117
- corpus.cases.len() >= 110,
118
- "the corpus is the proof; it should not shrink silently"
119
- );
120
- let mut names: Vec<&str> = corpus.cases.iter().map(|case| case.name.as_str()).collect();
121
- names.sort_unstable();
122
- let unique = names.len();
123
- names.dedup();
124
- assert_eq!(unique, names.len(), "case names identify cases");
125
- }
126
-
127
- /// Every required category of behaviour is represented by at least one case.
128
- ///
129
- /// A guard against the corpus quietly losing a dimension it was built to
130
- /// cover, not a substitute for reading it.
131
- #[test]
132
- fn corpus_covers_every_required_category() {
133
- let names: Vec<&str> = corpus().cases.iter().map(|c| c.name.as_str()).collect();
134
- for required in [
135
- // basic
136
- "identical_objects",
137
- "both_empty_objects",
138
- "empty_to_populated_object",
139
- "populated_to_empty_object",
140
- "single_scalar_replacement",
141
- // object changes
142
- "object_addition",
143
- "object_removal",
144
- "object_replacement",
145
- "nested_addition",
146
- "nested_removal",
147
- "nested_replacement",
148
- "multiple_independent_changes",
149
- "deeply_nested_change",
150
- "unusual_object_keys",
151
- "unicode_keys_and_values",
152
- // null and missing
153
- "missing_to_value",
154
- "value_to_missing",
155
- "missing_to_null",
156
- "null_to_missing",
157
- "null_to_value",
158
- "value_to_null",
159
- // arrays
160
- "empty_to_populated_array",
161
- "array_append_one",
162
- "array_remove_tail",
163
- "array_index_replacement",
164
- "array_multiple_index_changes",
165
- "array_nested_change",
166
- "array_to_object",
167
- "object_to_array",
168
- "array_reorder_is_positional",
169
- // numbers
170
- "integer_change",
171
- "float_change",
172
- "zero_to_positive",
173
- "negative_zero_int_against_zero",
174
- "large_unsigned",
175
- "float_extremes",
176
- // structural
177
- "many_members",
178
- "repeated_nested_modifications",
179
- "changes_at_related_paths",
180
- "mixed_container_shapes_at_depth",
181
- // invalid
182
- "malformed_not_json",
183
- "malformed_duplicate_envelope_member",
184
- "malformed_leading_zero_number",
185
- "malformed_truncated_object",
186
- ] {
187
- assert!(
188
- names.contains(&required),
189
- "corpus lost coverage: {required}"
190
- );
191
- }
192
- }
193
-
194
- /// The committed canonical bytes are what this crate's encoder produces.
195
- ///
196
- /// Consumers outside Rust read `canonical_input` rather than re-canonicalizing,
197
- /// so this is the check that keeps those bytes honest: if canonicalization
198
- /// changed, every other reader of the corpus would silently be testing an
199
- /// encoding FeltDB no longer emits.
200
- #[test]
201
- fn committed_canonical_bytes_match_the_encoder() {
202
- for case in &corpus().cases {
203
- match (&case.input, &case.raw_input) {
204
- (Some(input), None) => assert_eq!(
205
- case.canonical_input.as_deref(),
206
- Some(
207
- String::from_utf8(canonical_input_bytes(input))
208
- .unwrap()
209
- .as_str()
210
- ),
211
- "{} has a stale canonical_input",
212
- case.name
213
- ),
214
- (None, Some(_)) => assert!(
215
- case.canonical_input.is_none(),
216
- "{} is a raw case and must not also record canonical_input",
217
- case.name
218
- ),
219
- _ => panic!(
220
- "case {} must have exactly one of input/raw_input",
221
- case.name
222
- ),
223
- }
224
- }
225
- }
226
-
227
- // ---------------------------------------------------------------------------
228
- // WASM host
229
- // ---------------------------------------------------------------------------
230
-
231
- const WASM_CRATE: &str = "crates/feltdb-state-diff-contract-wasm";
232
- const WASM_ARTIFACT: &str =
233
- "target/wasm32-unknown-unknown/release/feltdb_state_diff_contract_wasm.wasm";
234
-
235
- fn repository_root() -> &'static Path {
236
- Path::new(env!("CARGO_MANIFEST_DIR"))
237
- .parent()
238
- .and_then(Path::parent)
239
- .expect("the crate lives at <root>/crates/feltdb")
240
- }
241
-
242
- /// Whether this machine can compile the contract to WebAssembly at all.
243
- fn wasm_target_installed() -> bool {
244
- Command::new("rustc")
245
- .args([
246
- "--print",
247
- "target-libdir",
248
- "--target",
249
- "wasm32-unknown-unknown",
250
- ])
251
- .output()
252
- .is_ok_and(|output| output.status.success())
253
- }
254
-
255
- /// Compiles a WASM contract crate and returns the module bytes.
256
- ///
257
- /// A build failure is a defect and panics. A missing `wasm32-unknown-unknown`
258
- /// target is an environment fact, reported by the caller as a skip.
259
- fn build_module(manifest_dir: &Path) -> Vec<u8> {
260
- let output = Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
261
- .args(["build", "--release", "--target", "wasm32-unknown-unknown"])
262
- .current_dir(manifest_dir)
263
- // The contract crate is its own workspace; inheriting the outer
264
- // build's target directory or flags would defeat that.
265
- .env_remove("CARGO_TARGET_DIR")
266
- .env_remove("RUSTFLAGS")
267
- .output()
268
- .expect("cargo is runnable");
269
- assert!(
270
- output.status.success(),
271
- "building the WASM contract at {} failed:\n{}",
272
- manifest_dir.display(),
273
- String::from_utf8_lossy(&output.stderr)
274
- );
275
- std::fs::read(manifest_dir.join(WASM_ARTIFACT)).expect("the build emits one module")
276
- }
277
-
278
- /// The committed WASM contract, compiled once for the whole suite.
279
- fn contract_module() -> Option<&'static [u8]> {
280
- static MODULE: OnceLock<Option<Vec<u8>>> = OnceLock::new();
281
- MODULE
282
- .get_or_init(|| {
283
- if let Ok(path) = std::env::var("FELTDB_STATE_DIFF_CONTRACT_WASM") {
284
- return Some(
285
- std::fs::read(path).expect("FELTDB_STATE_DIFF_CONTRACT_WASM is readable"),
286
- );
287
- }
288
- if !wasm_target_installed() {
289
- return None;
290
- }
291
- Some(build_module(&repository_root().join(WASM_CRATE)))
292
- })
293
- .as_deref()
294
- }
295
-
296
- /// Announces a skip loudly, so an absent toolchain is never a silent pass.
297
- fn skipped(test: &str) {
298
- eprintln!(
299
- "SKIPPED {test}: the wasm32-unknown-unknown target is not installed, so the WASM \
300
- contract implementation could not be built. Native conformance against the committed \
301
- expectations still ran. Install the target, or set FELTDB_STATE_DIFF_CONTRACT_WASM to a \
302
- prebuilt module, to execute the cross-runtime proof."
303
- );
304
- }
305
-
306
- /// One instantiation of the WASM contract.
307
- ///
308
- /// The linker is empty and stays empty: the module declares no imports, so
309
- /// there is nothing to satisfy and nothing to grant.
310
- struct WasmContract {
311
- store: wasmi::Store<()>,
312
- memory: wasmi::Memory,
313
- alloc: wasmi::TypedFunc<u32, u32>,
314
- evaluate: wasmi::TypedFunc<(u32, u32), u64>,
315
- free: wasmi::TypedFunc<(u32, u32), ()>,
316
- }
317
-
318
- impl WasmContract {
319
- fn instantiate(bytes: &[u8]) -> Self {
320
- let engine = wasmi::Engine::default();
321
- let module = wasmi::Module::new(&engine, bytes).expect("the module validates");
322
- let mut store = wasmi::Store::new(&engine, ());
323
- let instance = wasmi::Linker::<()>::new(&engine)
324
- .instantiate_and_start(&mut store, &module)
325
- .expect("an empty linker satisfies a module with no imports");
326
- Self {
327
- memory: instance
328
- .get_memory(&store, "memory")
329
- .expect("the module exports its memory"),
330
- alloc: instance
331
- .get_typed_func(&store, "contract_alloc")
332
- .expect("contract_alloc"),
333
- evaluate: instance
334
- .get_typed_func(&store, "contract_evaluate")
335
- .expect("contract_evaluate"),
336
- free: instance
337
- .get_typed_func(&store, "contract_free")
338
- .expect("contract_free"),
339
- store,
340
- }
341
- }
342
-
343
- fn version(bytes: &[u8]) -> u32 {
344
- let engine = wasmi::Engine::default();
345
- let module = wasmi::Module::new(&engine, bytes).unwrap();
346
- let mut store = wasmi::Store::new(&engine, ());
347
- let instance = wasmi::Linker::<()>::new(&engine)
348
- .instantiate_and_start(&mut store, &module)
349
- .unwrap();
350
- instance
351
- .get_typed_func::<(), u32>(&store, "contract_version")
352
- .expect("contract_version")
353
- .call(&mut store, ())
354
- .expect("contract_version is total")
355
- }
356
-
357
- fn evaluate(&mut self, input: &[u8]) -> Vec<u8> {
358
- let len = input.len() as u32;
359
- let ptr = self.alloc.call(&mut self.store, len).expect("allocation");
360
- self.memory
361
- .write(&mut self.store, ptr as usize, input)
362
- .expect("input fits in linear memory");
363
- let packed = self
364
- .evaluate
365
- .call(&mut self.store, (ptr, len))
366
- .expect("the contract never traps on any input");
367
- let out_ptr = (packed >> 32) as usize;
368
- let out_len = (packed & 0xffff_ffff) as usize;
369
- let mut output = vec![0u8; out_len];
370
- self.memory
371
- .read(&self.store, out_ptr, &mut output)
372
- .expect("the output buffer is inside linear memory");
373
- self.free.call(&mut self.store, (ptr, len)).expect("free");
374
- self.free
375
- .call(&mut self.store, (out_ptr as u32, out_len as u32))
376
- .expect("free");
377
- output
378
- }
379
- }
380
-
381
- // ---------------------------------------------------------------------------
382
- // Conformance
383
- // ---------------------------------------------------------------------------
384
-
385
- /// Native evaluation reproduces the committed canonical output for every case.
386
- #[test]
387
- fn native_matches_the_committed_expectations() {
388
- let mut divergent = Vec::new();
389
- for case in &corpus().cases {
390
- let produced = String::from_utf8(evaluate_canonical(&case.bytes())).unwrap();
391
- if produced != case.expected() {
392
- divergent.push(format!(
393
- "{}\n expected: {}\n native: {}",
394
- case.name,
395
- case.expected(),
396
- produced
397
- ));
398
- }
399
- }
400
- assert!(
401
- divergent.is_empty(),
402
- "native contract diverged from the committed expectations:\n{}",
403
- divergent.join("\n")
404
- );
405
- }
406
-
407
- /// The proof: one corpus, two implementations, byte-identical canonical output.
408
- #[test]
409
- fn native_and_wasm_agree_on_every_fixture() {
410
- let Some(bytes) = contract_module() else {
411
- return skipped("native_and_wasm_agree_on_every_fixture");
412
- };
413
- let mut wasm = WasmContract::instantiate(bytes);
414
-
415
- println!(
416
- "{:<48} {:<9} {:<9} {}",
417
- "fixture", "native", "wasm", "equal"
418
- );
419
- let mut divergent = Vec::new();
420
- for case in &corpus().cases {
421
- let input = case.bytes();
422
- let native = evaluate_canonical(&input);
423
- let produced = wasm.evaluate(&input);
424
- let equal = native == produced;
425
- println!(
426
- "{:<48} {:<9} {:<9} {}",
427
- case.name,
428
- summarize(&native),
429
- summarize(&produced),
430
- if equal { "yes" } else { "NO" }
431
- );
432
- if !equal {
433
- divergent.push(format!(
434
- "{}\n input: {}\n native: {}\n wasm: {}",
435
- case.name,
436
- String::from_utf8_lossy(&input),
437
- String::from_utf8_lossy(&native),
438
- String::from_utf8_lossy(&produced)
439
- ));
440
- }
441
- }
442
- assert!(
443
- divergent.is_empty(),
444
- "native and WASM contract implementations diverged:\n{}",
445
- divergent.join("\n")
446
- );
447
- }
448
-
449
- /// The WASM implementation reproduces the committed canonical output too.
450
- ///
451
- /// Separate from the pairwise comparison on purpose: two implementations can
452
- /// agree with each other while both having moved.
453
- #[test]
454
- fn wasm_matches_the_committed_expectations() {
455
- let Some(bytes) = contract_module() else {
456
- return skipped("wasm_matches_the_committed_expectations");
457
- };
458
- let mut wasm = WasmContract::instantiate(bytes);
459
- for case in &corpus().cases {
460
- assert_eq!(
461
- String::from_utf8(wasm.evaluate(&case.bytes())).unwrap(),
462
- case.expected(),
463
- "WASM contract diverged from the committed expectation for {}",
464
- case.name
465
- );
466
- }
467
- }
468
-
469
- /// Both implementations answer for the same contract at the same version.
470
- #[test]
471
- fn both_implementations_declare_the_same_contract_version() {
472
- let Some(bytes) = contract_module() else {
473
- return skipped("both_implementations_declare_the_same_contract_version");
474
- };
475
- assert_eq!(WasmContract::version(bytes), STATE_DIFF_CONTRACT_VERSION);
476
- for case in &corpus().cases {
477
- let output: Value = serde_json::from_slice(&evaluate_canonical(&case.bytes())).unwrap();
478
- assert_eq!(output["contract"], STATE_DIFF_CONTRACT_ID);
479
- assert_eq!(output["version"], STATE_DIFF_CONTRACT_VERSION);
480
- }
481
- }
482
-
483
- /// The native adapter adds nothing to the existing implementation.
484
- ///
485
- /// The contract must be the diff FeltDB already computes, not a second one that
486
- /// happens to agree. This runs both entry points over the whole corpus.
487
- #[test]
488
- fn the_contract_is_the_existing_semantic_diff() {
489
- use feltdb::state_diff_contract::diff_states;
490
- use feltdb::state_model::SemanticDiff;
491
-
492
- let mut checked = 0;
493
- for case in &corpus().cases {
494
- let Some(input) = case.input.as_ref() else {
495
- continue;
496
- };
497
- let through_contract = diff_states(&input.state_a, &input.state_b);
498
- let direct = SemanticDiff::compute(&input.state_a, &input.state_b);
499
- assert_eq!(
500
- serde_json::to_string(&through_contract).unwrap(),
501
- serde_json::to_string(&direct).unwrap(),
502
- "the contract adapter changed the result for {}",
503
- case.name
504
- );
505
- checked += 1;
506
- }
507
- assert!(checked > 70, "most of the corpus is typed input");
508
- }
509
-
510
- /// A short rendering of an output, for the printed conformance table.
511
- fn summarize(output: &[u8]) -> String {
512
- let parsed: Value = match serde_json::from_slice(output) {
513
- Ok(value) => value,
514
- Err(_) => return "?".into(),
515
- };
516
- match parsed["diff"]["changes"].as_array() {
517
- Some(changes) => format!("{} chg", changes.len()),
518
- None => "error".into(),
519
- }
520
- }
521
-
522
- // ---------------------------------------------------------------------------
523
- // Float rendering
524
- // ---------------------------------------------------------------------------
525
-
526
- /// The WASM float writer reproduces `serde_json`'s, over far more values than
527
- /// the corpus can hold.
528
- ///
529
- /// This is the riskiest part of the second implementation. The contract's
530
- /// output embeds whole input values, so the WASM module has to render doubles
531
- /// exactly as `ryu` does — and neither Rust's `Display` nor its `Debug` will
532
- /// do it: they disagree with `ryu` on presentation (`1e16` against `1e+16`)
533
- /// and, on a fraction of values, on which shortest representation to pick.
534
- ///
535
- /// The sample is restricted to doubles `serde_json` parses back from its own
536
- /// output, for the reason
537
- /// [`serde_json_does_not_round_trip_every_double`] records: outside that set
538
- /// the two implementations disagree about which double the bytes denote,
539
- /// before any rendering happens, and this test would be measuring that instead.
540
- ///
541
- /// One diff carries the whole array as a single changed value, so this drives
542
- /// the real module over the whole sample in a handful of calls.
543
- #[test]
544
- fn wasm_renders_floats_exactly_as_serde_json_does() {
545
- let Some(bytes) = contract_module() else {
546
- return skipped("wasm_renders_floats_exactly_as_serde_json_does");
547
- };
548
- let mut wasm = WasmContract::instantiate(bytes);
549
-
550
- let mut sample: Vec<f64> = vec![
551
- 0.0,
552
- -0.0,
553
- 1.0,
554
- -1.0,
555
- 100.0,
556
- 0.5,
557
- 0.125,
558
- 3.141592653589793,
559
- 2.718281828459045,
560
- 1e15,
561
- 1e16,
562
- 1e17,
563
- 1e21,
564
- 1e100,
565
- 1e308,
566
- 1e-1,
567
- 1e-4,
568
- 1e-5,
569
- 1e-6,
570
- 1e-7,
571
- 1e-100,
572
- 5e-324,
573
- f64::MAX,
574
- f64::MIN,
575
- f64::MIN_POSITIVE,
576
- 0.1,
577
- 0.2,
578
- 0.30000000000000004,
579
- 9007199254740992.0,
580
- ];
581
- // A deterministic sweep over bit patterns, and over decimals of the kind
582
- // records actually hold.
583
- let mut seed: u64 = 0x243F6A8885A308D3;
584
- for _ in 0..12_000 {
585
- seed = seed
586
- .wrapping_mul(6364136223846793005)
587
- .wrapping_add(1442695040888963407);
588
- let value = f64::from_bits(seed);
589
- if value.is_finite() {
590
- sample.push(value);
591
- }
592
- }
593
- for step in -6_000i64..6_000 {
594
- sample.push(step as f64 / 1000.0);
595
- }
596
- let offered = sample.len();
597
- sample.retain(|value| serde_json_round_trips(*value));
598
- let checked = sample.len();
599
- assert!(
600
- checked > 12_000,
601
- "the sample should still be large after excluding what serde_json cannot round trip"
602
- );
603
-
604
- for chunk in sample.chunks(4_000) {
605
- let state_a = Value::Array(chunk.iter().map(|v| Value::from(*v)).collect());
606
- let input = canonical_input_bytes(&StateDiffInput::new(state_a, Value::from(0)));
607
- let native = evaluate_canonical(&input);
608
- let produced = wasm.evaluate(&input);
609
- if native != produced {
610
- // Report the first differing value rather than a 100 KB diff.
611
- let native_text = String::from_utf8_lossy(&native);
612
- let wasm_text = String::from_utf8_lossy(&produced);
613
- let at = native_text
614
- .char_indices()
615
- .zip(wasm_text.char_indices())
616
- .find(|((_, a), (_, b))| a != b)
617
- .map(|((index, _), _)| index)
618
- .unwrap_or(0);
619
- let from = at.saturating_sub(40);
620
- panic!(
621
- "the WASM float writer diverged from serde_json:\n native: …{}…\n wasm: …{}…",
622
- &native_text[from..(at + 40).min(native_text.len())],
623
- &wasm_text[from..(at + 40).min(wasm_text.len())]
624
- );
625
- }
626
- }
627
- println!("float rendering agrees on {checked} of {offered} sampled doubles");
628
- }
629
-
630
- /// Whether `serde_json` reads back the double it itself wrote.
631
- fn serde_json_round_trips(value: f64) -> bool {
632
- let text = serde_json::to_string(&Value::from(value)).expect("finite");
633
- serde_json::from_str::<Value>(&text)
634
- .ok()
635
- .and_then(|parsed| parsed.as_f64())
636
- .is_some_and(|parsed| parsed.to_bits() == value.to_bits())
637
- }
638
-
639
- /// `serde_json` does not parse back every double it writes, and that bounds
640
- /// what this contract can claim.
641
- ///
642
- /// `serde_json`'s float parser is accurate to within one unit in the last
643
- /// place but is not correctly rounded, which is its documented default: exact
644
- /// round-tripping is the opt-in `float_roundtrip` feature, at roughly twice the
645
- /// parsing cost. The WASM implementation uses `core`'s `dec2flt`, which *is*
646
- /// correctly rounded, so for the affected literals the two implementations
647
- /// disagree about which double the bytes denote — before either renders
648
- /// anything.
649
- ///
650
- /// This test exists so that boundary is measured and visible rather than
651
- /// assumed. It asserts only that short, ordinary decimals — the numbers real
652
- /// records hold — are unaffected. It deliberately does not assert a rate: that
653
- /// is a property of the dependency and may change when it is upgraded.
654
- #[test]
655
- fn serde_json_does_not_round_trip_every_double() {
656
- let mut lost = 0usize;
657
- let mut total = 0usize;
658
- let mut example = None;
659
- let mut seed: u64 = 0x243F6A8885A308D3;
660
- for _ in 0..200_000 {
661
- seed = seed
662
- .wrapping_mul(6364136223846793005)
663
- .wrapping_add(1442695040888963407);
664
- let value = f64::from_bits(seed);
665
- if !value.is_finite() {
666
- continue;
667
- }
668
- total += 1;
669
- if !serde_json_round_trips(value) {
670
- lost += 1;
671
- if example.is_none() {
672
- example = Some(serde_json::to_string(&Value::from(value)).unwrap());
673
- }
674
- }
675
- }
676
- println!(
677
- "serde_json {} of {total} random doubles do not survive its own \
678
- serialize/parse round trip (example: {})",
679
- lost,
680
- example.as_deref().unwrap_or("none")
681
- );
682
- assert!(
683
- lost > 0,
684
- "if serde_json now round trips every double, the restriction on the float rendering \
685
- test and the contract's stated input domain can both be removed"
686
- );
687
-
688
- // The values records actually hold are unaffected, which is what makes the
689
- // contract useful despite the restriction.
690
- for ordinary in [
691
- 0.0, -0.0, 0.5, 0.125, 1.0, 2.5, 42.0, 99.99, 1e-5, 1e15, 1e16, 1e-6, 3.14, 2.718,
692
- 1234.5678, -0.001, 100.0, 0.1, 0.2, 0.3,
693
- ] {
694
- assert!(
695
- serde_json_round_trips(ordinary),
696
- "an ordinary decimal must round trip: {ordinary}"
697
- );
698
- }
699
- }
700
-
701
- // ---------------------------------------------------------------------------
702
- // Determinism
703
- // ---------------------------------------------------------------------------
704
-
705
- /// The same input, in the same instance, produces the same output every time.
706
- #[test]
707
- fn wasm_is_deterministic_across_repeated_calls() {
708
- let Some(bytes) = contract_module() else {
709
- return skipped("wasm_is_deterministic_across_repeated_calls");
710
- };
711
- let mut wasm = WasmContract::instantiate(bytes);
712
- for case in &corpus().cases {
713
- let input = case.bytes();
714
- let first = wasm.evaluate(&input);
715
- for round in 1..16 {
716
- assert_eq!(
717
- wasm.evaluate(&input),
718
- first,
719
- "{} changed answer on repeat {round} within one instance",
720
- case.name
721
- );
722
- }
723
- }
724
- }
725
-
726
- /// Repeated native evaluation is also stable.
727
- ///
728
- /// Cheap, and it forecloses the reading in which only the WASM side was ever
729
- /// at risk of varying.
730
- #[test]
731
- fn native_is_deterministic_across_repeated_calls() {
732
- for case in &corpus().cases {
733
- let input = case.bytes();
734
- let first = evaluate_canonical(&input);
735
- for round in 1..16 {
736
- assert_eq!(
737
- evaluate_canonical(&input),
738
- first,
739
- "{} changed answer on native repeat {round}",
740
- case.name
741
- );
742
- }
743
- }
744
- }
745
-
746
- /// The same input produces the same output in a fresh instance.
747
- ///
748
- /// Each instance gets its own `Store`, so its linear memory, allocator state
749
- /// and call history all start over. An implementation that carried state
750
- /// between diffs would show it here.
751
- #[test]
752
- fn wasm_is_deterministic_across_fresh_instances() {
753
- let Some(bytes) = contract_module() else {
754
- return skipped("wasm_is_deterministic_across_fresh_instances");
755
- };
756
- let mut baseline = WasmContract::instantiate(bytes);
757
- let expected: Vec<Vec<u8>> = corpus()
758
- .cases
759
- .iter()
760
- .map(|case| baseline.evaluate(&case.bytes()))
761
- .collect();
762
-
763
- for round in 0..4 {
764
- let mut fresh = WasmContract::instantiate(bytes);
765
- for (case, want) in corpus().cases.iter().zip(&expected) {
766
- assert_eq!(
767
- &fresh.evaluate(&case.bytes()),
768
- want,
769
- "{} changed answer in fresh instance {round}",
770
- case.name
771
- );
772
- }
773
- }
774
- }
775
-
776
- /// Order of evaluation does not affect any answer.
777
- #[test]
778
- fn wasm_answers_do_not_depend_on_evaluation_order() {
779
- let Some(bytes) = contract_module() else {
780
- return skipped("wasm_answers_do_not_depend_on_evaluation_order");
781
- };
782
- let mut forwards = WasmContract::instantiate(bytes);
783
- let ordered: Vec<Vec<u8>> = corpus()
784
- .cases
785
- .iter()
786
- .map(|case| forwards.evaluate(&case.bytes()))
787
- .collect();
788
-
789
- let mut backwards = WasmContract::instantiate(bytes);
790
- for (index, case) in corpus().cases.iter().enumerate().rev() {
791
- assert_eq!(
792
- backwards.evaluate(&case.bytes()),
793
- ordered[index],
794
- "{} answered differently when the corpus ran in reverse",
795
- case.name
796
- );
797
- }
798
- }
799
-
800
- /// The same input produces the same output in a separate OS process.
801
- #[test]
802
- fn wasm_is_deterministic_across_process_boundaries() {
803
- let Some(bytes) = contract_module() else {
804
- return skipped("wasm_is_deterministic_across_process_boundaries");
805
- };
806
- let mut here = WasmContract::instantiate(bytes);
807
- let mine: Vec<Vec<u8>> = corpus()
808
- .cases
809
- .iter()
810
- .map(|case| here.evaluate(&case.bytes()))
811
- .collect();
812
-
813
- let executable = std::env::current_exe().expect("the test binary has a path");
814
- let output = Command::new(executable)
815
- .args([
816
- "--exact",
817
- "wasm_is_deterministic_across_fresh_instances",
818
- "--nocapture",
819
- ])
820
- .output()
821
- .expect("the test binary re-runs");
822
- assert!(
823
- output.status.success(),
824
- "the corpus did not reproduce in a separate process:\n{}",
825
- String::from_utf8_lossy(&output.stdout)
826
- );
827
-
828
- for (case, produced) in corpus().cases.iter().zip(&mine) {
829
- assert_eq!(String::from_utf8_lossy(produced), case.expected());
830
- }
831
- }
832
-
833
- // ---------------------------------------------------------------------------
834
- // Runtime restrictions
835
- // ---------------------------------------------------------------------------
836
-
837
- /// The module imports nothing.
838
- ///
839
- /// This is the whole capability argument and it is deliberately narrow. A
840
- /// WebAssembly module reaches the outside world only through its imports. With
841
- /// an empty import section there is no clock, no filesystem handle, no socket,
842
- /// no entropy source and no FeltDB authority state in scope — not because
843
- /// access was denied at call time, but because no such function is linked. It
844
- /// says nothing about hostile modules, which this suite does not test.
845
- #[test]
846
- fn wasm_module_declares_no_imports() {
847
- let Some(bytes) = contract_module() else {
848
- return skipped("wasm_module_declares_no_imports");
849
- };
850
- let engine = wasmi::Engine::default();
851
- let module = wasmi::Module::new(&engine, bytes).expect("the module validates");
852
- let imports: Vec<String> = module
853
- .imports()
854
- .map(|import: wasmi::ImportType| format!("{}::{}", import.module(), import.name()))
855
- .collect();
856
- assert!(
857
- imports.is_empty(),
858
- "the contract module must import nothing; it imports {imports:?}"
859
- );
860
- }
861
-
862
- /// An empty linker instantiates the module.
863
- #[test]
864
- fn wasm_module_instantiates_with_no_host_functions() {
865
- let Some(bytes) = contract_module() else {
866
- return skipped("wasm_module_instantiates_with_no_host_functions");
867
- };
868
- let engine = wasmi::Engine::default();
869
- let module = wasmi::Module::new(&engine, bytes).unwrap();
870
- let mut store = wasmi::Store::new(&engine, ());
871
- wasmi::Linker::<()>::new(&engine)
872
- .instantiate_and_start(&mut store, &module)
873
- .expect("no host function is required");
874
- }
875
-
876
- /// The module exports the contract ABI and nothing that could act on FeltDB.
877
- #[test]
878
- fn wasm_module_exports_only_the_contract_abi() {
879
- let Some(bytes) = contract_module() else {
880
- return skipped("wasm_module_exports_only_the_contract_abi");
881
- };
882
- let engine = wasmi::Engine::default();
883
- let module = wasmi::Module::new(&engine, bytes).unwrap();
884
- let mut functions: Vec<String> = module
885
- .exports()
886
- .filter(|export| export.ty().func().is_some())
887
- .map(|export| export.name().to_string())
888
- .collect();
889
- functions.sort();
890
- assert_eq!(
891
- functions,
892
- vec![
893
- "contract_alloc".to_string(),
894
- "contract_evaluate".to_string(),
895
- "contract_free".to_string(),
896
- "contract_version".to_string(),
897
- ]
898
- );
899
- }
900
-
901
- /// WASI is not present, under any of the names a runtime would look for.
902
- #[test]
903
- fn wasm_module_requires_no_wasi() {
904
- let Some(bytes) = contract_module() else {
905
- return skipped("wasm_module_requires_no_wasi");
906
- };
907
- let text = String::from_utf8_lossy(bytes);
908
- for forbidden in [
909
- "wasi_snapshot_preview1",
910
- "wasi_unstable",
911
- "clock_time_get",
912
- "random_get",
913
- "fd_read",
914
- "fd_write",
915
- "sock_recv",
916
- "path_open",
917
- ] {
918
- assert!(
919
- !text.contains(forbidden),
920
- "the contract module names {forbidden}, which it has no reason to"
921
- );
922
- }
923
- }
924
-
925
- /// The contract never traps, on any corpus input.
926
- #[test]
927
- fn wasm_contract_returns_a_result_for_every_input_including_malformed() {
928
- let Some(bytes) = contract_module() else {
929
- return skipped("wasm_contract_returns_a_result_for_every_input_including_malformed");
930
- };
931
- let mut wasm = WasmContract::instantiate(bytes);
932
- for case in &corpus().cases {
933
- let output = wasm.evaluate(&case.bytes());
934
- let parsed: Value =
935
- serde_json::from_slice(&output).expect("every output is canonical JSON");
936
- assert_eq!(parsed["contract"], STATE_DIFF_CONTRACT_ID);
937
- assert!(
938
- parsed.get("diff").is_some() ^ parsed.get("error").is_some(),
939
- "{} produced neither or both of diff and error",
940
- case.name
941
- );
942
- }
943
- // Inputs no fixture would contain, to show trap-freedom is not a property
944
- // of the corpus. Nesting past the shared recursion bound is included.
945
- for hostile in [
946
- vec![0u8; 0],
947
- vec![0xff; 64],
948
- b"{".repeat(4096),
949
- b"[".repeat(4096),
950
- vec![b'"'; 1024],
951
- format!(
952
- r#"{{"contract":"feltdb.state.diff","state_a":{}{},"state_b":1,"version":1}}"#,
953
- "[".repeat(300),
954
- "]".repeat(300)
955
- )
956
- .into_bytes(),
957
- ] {
958
- let output = wasm.evaluate(&hostile);
959
- assert!(serde_json::from_slice::<Value>(&output).is_ok());
960
- }
961
- }
962
-
963
- // ---------------------------------------------------------------------------
964
- // Mutation: does the suite actually detect divergence?
965
- // ---------------------------------------------------------------------------
966
-
967
- /// A deliberate change to the WASM implementation's source.
968
- struct Mutation {
969
- name: &'static str,
970
- file: &'static str,
971
- from: &'static str,
972
- to: &'static str,
973
- /// Whether the corpus must catch it.
974
- ///
975
- /// One mutation is expected *not* to be caught, and that expectation is
976
- /// part of the contract: it changes an ordering the contract does not
977
- /// make semantic.
978
- detected: bool,
979
- }
980
-
981
- const MUTATIONS: &[Mutation] = &[
982
- Mutation {
983
- name: "additions are reported as changes",
984
- file: "src/diff.rs",
985
- from: " kind: ChangeKind::Added,\n old_value: None,\n new_value: Some(new_value.clone()),\n });\n path.pop();",
986
- to: " kind: ChangeKind::Changed,\n old_value: None,\n new_value: Some(new_value.clone()),\n });\n path.pop();",
987
- detected: true,
988
- },
989
- Mutation {
990
- name: "object removals are not reported",
991
- file: "src/diff.rs",
992
- from: " None => changes.push(Change {\n path: path.clone(),\n kind: ChangeKind::Removed,",
993
- to: " None => changes.push(Change {\n path: path.clone(),\n kind: ChangeKind::Added,",
994
- detected: true,
995
- },
996
- Mutation {
997
- name: "unequal integers are not reported as changed",
998
- file: "src/diff.rs",
999
- from: " (Json::PosInt(a), Json::PosInt(b)) if a == b => {}",
1000
- to: " (Json::PosInt(_), Json::PosInt(_)) => {}",
1001
- detected: true,
1002
- },
1003
- Mutation {
1004
- name: "object path components are dropped",
1005
- file: "src/diff.rs",
1006
- from: " for (key, old_value) in a {\n path.push(PathComponent::Key(key.clone()));",
1007
- to: " for (key, old_value) in a {\n path.push(PathComponent::Key(alloc::string::String::from(\"x\")));",
1008
- detected: true,
1009
- },
1010
- Mutation {
1011
- name: "array indexes are off by one",
1012
- file: "src/diff.rs",
1013
- from: " for (index, (old_value, new_value)) in a.iter().zip(b.iter()).enumerate() {\n path.push(PathComponent::Index(index));",
1014
- to: " for (index, (old_value, new_value)) in a.iter().zip(b.iter()).enumerate() {\n path.push(PathComponent::Index(index + 1));",
1015
- detected: true,
1016
- },
1017
- Mutation {
1018
- name: "array removals start at the wrong index",
1019
- file: "src/diff.rs",
1020
- from: " for (index, old_value) in a.iter().enumerate().skip(b.len()) {",
1021
- to: " for (index, old_value) in a.iter().enumerate().skip(b.len() + 1) {",
1022
- detected: true,
1023
- },
1024
- Mutation {
1025
- name: "old values are not emitted",
1026
- file: "src/diff.rs",
1027
- from: " (_, _) => changes.push(Change {\n path: path.clone(),\n kind: ChangeKind::Changed,\n old_value: Some(old.clone()),",
1028
- to: " (_, _) => changes.push(Change {\n path: path.clone(),\n kind: ChangeKind::Changed,\n old_value: None,",
1029
- detected: true,
1030
- },
1031
- Mutation {
1032
- name: "new values are not emitted",
1033
- file: "src/diff.rs",
1034
- from: " kind: ChangeKind::Changed,\n old_value: Some(old.clone()),\n new_value: Some(new.clone()),",
1035
- to: " kind: ChangeKind::Changed,\n old_value: Some(old.clone()),\n new_value: None,",
1036
- detected: true,
1037
- },
1038
- Mutation {
1039
- name: "null against null is reported as a change",
1040
- file: "src/diff.rs",
1041
- from: " (Json::Null, Json::Null) => {}",
1042
- to: " (Json::Null, Json::Null) if false => {}",
1043
- detected: true,
1044
- },
1045
- Mutation {
1046
- name: "integers and floats compare equal",
1047
- file: "src/diff.rs",
1048
- from: " (Json::Float(a), Json::Float(b)) if a == b => {}",
1049
- to: " (Json::Float(a), Json::Float(b)) if a == b => {}\n (Json::PosInt(a), Json::Float(b)) if *a as f64 == *b => {}\n (Json::Float(a), Json::PosInt(b)) if *a == *b as f64 => {}",
1050
- detected: true,
1051
- },
1052
- Mutation {
1053
- name: "the result is not sorted by path",
1054
- file: "src/diff.rs",
1055
- from: " changes.sort_by(|a, b| a.path.cmp(&b.path).then_with(|| a.kind.cmp(&b.kind)));",
1056
- to: " changes.sort_by(|a, b| b.path.cmp(&a.path).then_with(|| a.kind.cmp(&b.kind)));",
1057
- detected: true,
1058
- },
1059
- Mutation {
1060
- name: "floats are rendered by Rust rather than by ryu's rule",
1061
- file: "src/json.rs",
1062
- from: " if (-5..=15).contains(&exponent) {",
1063
- to: " if (-4..=15).contains(&exponent) {",
1064
- detected: true,
1065
- },
1066
- Mutation {
1067
- name: "strings skip an escape serde_json emits",
1068
- file: "src/json.rs",
1069
- from: " '\\t' => out.extend_from_slice(b\"\\\\t\"),",
1070
- to: " '\\t' => out.extend_from_slice(b\"\\t\"),",
1071
- detected: true,
1072
- },
1073
- // The one mutation that must NOT be caught. Additions are emitted while
1074
- // walking the new object's members and the whole result is sorted
1075
- // afterwards; paths are unique within a diff, so the order they were
1076
- // emitted in cannot survive into the output. If this were ever detected,
1077
- // the contract would have accidentally made an internal traversal order
1078
- // part of its semantics.
1079
- Mutation {
1080
- name: "object additions are emitted in reverse order before sorting",
1081
- file: "src/diff.rs",
1082
- from: " for (key, new_value) in b {\n if old.get(key).is_none() {",
1083
- to: " for (key, new_value) in b.iter().rev() {\n if old.get(key).is_none() {",
1084
- detected: false,
1085
- },
1086
- ];
1087
-
1088
- /// Every deliberate mutation of the WASM contract lands where it should.
1089
- ///
1090
- /// Without this the conformance suite proves only that two implementations
1091
- /// agree, not that agreement was ever in question. Each mutant is built from a
1092
- /// copy of the real source with one edit applied. Twelve must fail against the
1093
- /// committed expectations; one must not, because it changes an order the
1094
- /// contract deliberately does not make semantic.
1095
- #[test]
1096
- fn deliberate_wasm_mutations_land_where_they_should() {
1097
- if contract_module().is_none() {
1098
- return skipped("deliberate_wasm_mutations_land_where_they_should");
1099
- }
1100
- let source = repository_root().join(WASM_CRATE);
1101
- let scratch = tempfile::tempdir().expect("a scratch directory");
1102
-
1103
- let mut wrong = Vec::new();
1104
- for mutation in MUTATIONS {
1105
- let root = scratch.path().join(mutation.name.replace(' ', "-"));
1106
- copy_crate(&source, &root);
1107
- let target = root.join(mutation.file);
1108
- let original = std::fs::read_to_string(&target).expect("the mutated file exists");
1109
- assert!(
1110
- original.matches(mutation.from).count() == 1,
1111
- "mutation {:?} no longer applies: its anchor is not present exactly once in {}. \
1112
- The mutation suite must be updated with the implementation it mutates.",
1113
- mutation.name,
1114
- mutation.file
1115
- );
1116
- std::fs::write(&target, original.replace(mutation.from, mutation.to)).unwrap();
1117
-
1118
- let mut mutant = WasmContract::instantiate(&build_module(&root));
1119
- let caught: Vec<&str> = corpus()
1120
- .cases
1121
- .iter()
1122
- .filter(|case| {
1123
- String::from_utf8_lossy(&mutant.evaluate(&case.bytes())) != case.expected()
1124
- })
1125
- .map(|case| case.name.as_str())
1126
- .collect();
1127
-
1128
- match (mutation.detected, caught.first()) {
1129
- (true, Some(first)) => println!(
1130
- "mutation {:?} caught by {} fixtures, first: {first}",
1131
- mutation.name,
1132
- caught.len()
1133
- ),
1134
- (false, None) => println!(
1135
- "mutation {:?} correctly NOT caught: the order it changes is not semantic",
1136
- mutation.name
1137
- ),
1138
- (true, None) => wrong.push(format!(
1139
- "{:?} was not caught, but the corpus should distinguish it",
1140
- mutation.name
1141
- )),
1142
- (false, Some(_)) => wrong.push(format!(
1143
- "{:?} was caught by {} fixtures, but it changes an order the contract does not \
1144
- make semantic; the contract has accidentally fixed an internal traversal order",
1145
- mutation.name,
1146
- caught.len()
1147
- )),
1148
- }
1149
- }
1150
- assert!(
1151
- wrong.is_empty(),
1152
- "mutation outcomes were wrong:\n{}",
1153
- wrong.join("\n")
1154
- );
1155
- }
1156
-
1157
- /// Copies the contract crate's source, without its build directory.
1158
- fn copy_crate(from: &Path, to: &Path) {
1159
- std::fs::create_dir_all(to.join("src")).unwrap();
1160
- std::fs::copy(from.join("Cargo.toml"), to.join("Cargo.toml")).unwrap();
1161
- for entry in std::fs::read_dir(from.join("src")).unwrap() {
1162
- let entry = entry.unwrap();
1163
- std::fs::copy(entry.path(), to.join("src").join(entry.file_name())).unwrap();
1164
- }
1165
- }
1166
-
1167
- /// Mutating the input encoding is detected where the encoding is semantically
1168
- /// load-bearing, and not where it is not.
1169
- ///
1170
- /// - Reordering members changes nothing, because a JSON object denotes a map
1171
- /// and both implementations decode it to one. The suite must *not* flag it.
1172
- /// - Swapping the two states changes the diff's direction, which is entirely
1173
- /// semantic. The suite must flag it.
1174
- /// - Dropping the contract identity turns every case into an input error.
1175
- #[test]
1176
- fn input_encoding_mutations_are_detected_where_they_matter() {
1177
- let typed: Vec<(&Case, Value)> = corpus()
1178
- .cases
1179
- .iter()
1180
- .filter_map(|case| {
1181
- let input = case.input.as_ref()?;
1182
- Some((case, serde_json::to_value(input).unwrap()))
1183
- })
1184
- .collect();
1185
- assert!(typed.len() > 70, "most of the corpus is typed input");
1186
-
1187
- // 1. Member order, which carries no meaning.
1188
- for (case, encoded) in &typed {
1189
- let produced =
1190
- String::from_utf8(evaluate_canonical(encode_reversed(encoded).as_bytes())).unwrap();
1191
- assert_eq!(
1192
- produced,
1193
- case.expected(),
1194
- "{} changed answer under a reordered encoding; object member order is not part of \
1195
- this contract's semantics",
1196
- case.name
1197
- );
1198
- }
1199
-
1200
- // 2. Direction, which carries all of it.
1201
- let mut direction_matters = 0;
1202
- for (case, encoded) in &typed {
1203
- let mut swapped = encoded.clone();
1204
- let object = swapped.as_object_mut().unwrap();
1205
- let a = object["state_a"].clone();
1206
- let b = object["state_b"].clone();
1207
- object["state_a"] = b;
1208
- object["state_b"] = a;
1209
- let produced = String::from_utf8(evaluate_canonical(
1210
- serde_json::to_vec(&swapped).unwrap().as_slice(),
1211
- ))
1212
- .unwrap();
1213
- if produced != case.expected() {
1214
- direction_matters += 1;
1215
- }
1216
- }
1217
- assert!(
1218
- direction_matters > 50,
1219
- "the diff is direction sensitive; swapping the states should change most results, \
1220
- but it changed only {direction_matters}"
1221
- );
1222
-
1223
- // 3. Contract identity dropped.
1224
- for (case, encoded) in typed.iter().take(1) {
1225
- let mut mutated = encoded.clone();
1226
- mutated.as_object_mut().unwrap().remove("contract");
1227
- let produced = String::from_utf8(evaluate_canonical(
1228
- serde_json::to_vec(&mutated).unwrap().as_slice(),
1229
- ))
1230
- .unwrap();
1231
- assert_ne!(produced, case.expected());
1232
- assert!(produced.contains("CONTRACT_INPUT_INVALID"));
1233
- }
1234
- }
1235
-
1236
- /// Encodes a value with every object's members in reverse sorted order.
1237
- ///
1238
- /// `serde_json::Map` re-sorts on insert, so a non-canonical member order has to
1239
- /// be produced as text rather than as a value.
1240
- fn encode_reversed(value: &Value) -> String {
1241
- match value {
1242
- Value::Object(members) => {
1243
- let mut parts: Vec<String> = members
1244
- .iter()
1245
- .map(|(key, value)| {
1246
- format!(
1247
- "{}:{}",
1248
- serde_json::to_string(key).unwrap(),
1249
- encode_reversed(value)
1250
- )
1251
- })
1252
- .collect();
1253
- parts.reverse();
1254
- format!("{{{}}}", parts.join(","))
1255
- }
1256
- Value::Array(items) => format!(
1257
- "[{}]",
1258
- items
1259
- .iter()
1260
- .map(encode_reversed)
1261
- .collect::<Vec<_>>()
1262
- .join(",")
1263
- ),
1264
- other => serde_json::to_string(other).unwrap(),
1265
- }
1266
- }
1267
-
1268
- // ---------------------------------------------------------------------------
1269
- // Corpus maintenance
1270
- // ---------------------------------------------------------------------------
1271
-
1272
- /// Records the canonical output of every case, after both implementations agree.
1273
- ///
1274
- /// Ignored by default: it writes the corpus. Run it deliberately with
1275
- /// `FELTDB_STATE_DIFF_CONTRACT_BLESS=1 cargo test -p feltdb --test
1276
- /// state_diff_contract_conformance -- bless --ignored`. It refuses to record
1277
- /// anything unless the WASM implementation is available and agrees, so an
1278
- /// expectation can never be blessed from one implementation alone.
1279
- #[test]
1280
- #[ignore = "rewrites the committed corpus"]
1281
- fn bless() {
1282
- assert_eq!(
1283
- std::env::var("FELTDB_STATE_DIFF_CONTRACT_BLESS").as_deref(),
1284
- Ok("1"),
1285
- "set FELTDB_STATE_DIFF_CONTRACT_BLESS=1 to rewrite the corpus"
1286
- );
1287
- let bytes = contract_module().expect(
1288
- "blessing requires the WASM implementation: an expectation recorded from the native \
1289
- implementation alone would prove nothing",
1290
- );
1291
- let mut wasm = WasmContract::instantiate(bytes);
1292
-
1293
- let mut document: Value =
1294
- serde_json::from_slice(&std::fs::read(corpus_path()).unwrap()).unwrap();
1295
- for (index, case) in corpus().cases.iter().enumerate() {
1296
- let input = case.bytes();
1297
- let native = evaluate_canonical(&input);
1298
- let produced = wasm.evaluate(&input);
1299
- assert_eq!(
1300
- String::from_utf8_lossy(&native),
1301
- String::from_utf8_lossy(&produced),
1302
- "refusing to record {}: the implementations disagree",
1303
- case.name
1304
- );
1305
- if case.input.is_some() {
1306
- document["cases"][index]["canonical_input"] =
1307
- Value::String(String::from_utf8(input).unwrap());
1308
- }
1309
- document["cases"][index]["expected_output"] =
1310
- Value::String(String::from_utf8(native).unwrap());
1311
- }
1312
- let mut text = serde_json::to_string_pretty(&document).unwrap();
1313
- text.push('\n');
1314
- std::fs::write(corpus_path(), text).unwrap();
1315
- println!("recorded {} expectations", corpus().cases.len());
1316
- }