@enbox/dwn-sdk-js 0.4.0 → 0.4.1
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.
- package/README.md +4 -4
- package/dist/browser.mjs +8 -8
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/generated/precompiled-validators.js +799 -885
- package/dist/esm/generated/precompiled-validators.js.map +1 -1
- package/dist/esm/src/core/dwn-constant.js +5 -0
- package/dist/esm/src/core/dwn-constant.js.map +1 -1
- package/dist/esm/src/core/dwn-error.js +12 -4
- package/dist/esm/src/core/dwn-error.js.map +1 -1
- package/dist/esm/src/core/grant-authorization.js +9 -18
- package/dist/esm/src/core/grant-authorization.js.map +1 -1
- package/dist/esm/src/core/message-reply.js.map +1 -1
- package/dist/esm/src/core/messages-grant-authorization.js +28 -45
- package/dist/esm/src/core/messages-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization-action.js +25 -27
- package/dist/esm/src/core/protocol-authorization-action.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization-validation.js +30 -68
- package/dist/esm/src/core/protocol-authorization-validation.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization.js +44 -118
- package/dist/esm/src/core/protocol-authorization.js.map +1 -1
- package/dist/esm/src/core/protocols-grant-authorization.js +5 -5
- package/dist/esm/src/core/protocols-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/recording-validation-state-reader.js +84 -0
- package/dist/esm/src/core/recording-validation-state-reader.js.map +1 -0
- package/dist/esm/src/core/records-grant-authorization.js +11 -11
- package/dist/esm/src/core/records-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/replication-apply.js +123 -28
- package/dist/esm/src/core/replication-apply.js.map +1 -1
- package/dist/esm/src/core/resumable-task-manager.js +5 -4
- package/dist/esm/src/core/resumable-task-manager.js.map +1 -1
- package/dist/esm/src/core/validation-state-reader.js +237 -0
- package/dist/esm/src/core/validation-state-reader.js.map +1 -0
- package/dist/esm/src/dwn.js +165 -132
- package/dist/esm/src/dwn.js.map +1 -1
- package/dist/esm/src/enums/dwn-interface-method.js +0 -1
- package/dist/esm/src/enums/dwn-interface-method.js.map +1 -1
- package/dist/esm/src/event-stream/durable-event-log.js +365 -0
- package/dist/esm/src/event-stream/durable-event-log.js.map +1 -0
- package/dist/esm/src/event-stream/event-emitter-wake-publisher.js +25 -0
- package/dist/esm/src/event-stream/event-emitter-wake-publisher.js.map +1 -0
- package/dist/esm/src/handlers/messages-query.js +159 -0
- package/dist/esm/src/handlers/messages-query.js.map +1 -0
- package/dist/esm/src/handlers/messages-read.js +5 -5
- package/dist/esm/src/handlers/messages-read.js.map +1 -1
- package/dist/esm/src/handlers/messages-subscribe.js +8 -8
- package/dist/esm/src/handlers/messages-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/protocols-configure.js +30 -49
- package/dist/esm/src/handlers/protocols-configure.js.map +1 -1
- package/dist/esm/src/handlers/protocols-query.js +1 -1
- package/dist/esm/src/handlers/protocols-query.js.map +1 -1
- package/dist/esm/src/handlers/records-count.js +20 -11
- package/dist/esm/src/handlers/records-count.js.map +1 -1
- package/dist/esm/src/handlers/records-delete.js +20 -16
- package/dist/esm/src/handlers/records-delete.js.map +1 -1
- package/dist/esm/src/handlers/records-query.js +35 -11
- package/dist/esm/src/handlers/records-query.js.map +1 -1
- package/dist/esm/src/handlers/records-read.js +52 -42
- package/dist/esm/src/handlers/records-read.js.map +1 -1
- package/dist/esm/src/handlers/records-subscribe.js +107 -11
- package/dist/esm/src/handlers/records-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/records-write.js +62 -116
- package/dist/esm/src/handlers/records-write.js.map +1 -1
- package/dist/esm/src/index.js +6 -7
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/interfaces/{messages-sync.js → messages-query.js} +21 -15
- package/dist/esm/src/interfaces/messages-query.js.map +1 -0
- package/dist/esm/src/interfaces/protocols-configure.js +7 -3
- package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
- package/dist/esm/src/interfaces/protocols-query.js +3 -4
- package/dist/esm/src/interfaces/protocols-query.js.map +1 -1
- package/dist/esm/src/interfaces/records-count.js +4 -3
- package/dist/esm/src/interfaces/records-count.js.map +1 -1
- package/dist/esm/src/interfaces/records-delete.js +21 -4
- package/dist/esm/src/interfaces/records-delete.js.map +1 -1
- package/dist/esm/src/interfaces/records-query.js +4 -3
- package/dist/esm/src/interfaces/records-query.js.map +1 -1
- package/dist/esm/src/interfaces/records-read.js +3 -3
- package/dist/esm/src/interfaces/records-read.js.map +1 -1
- package/dist/esm/src/interfaces/records-subscribe.js +4 -3
- package/dist/esm/src/interfaces/records-subscribe.js.map +1 -1
- package/dist/esm/src/interfaces/records-write.js +27 -13
- package/dist/esm/src/interfaces/records-write.js.map +1 -1
- package/dist/esm/src/protocols/permissions.js +27 -34
- package/dist/esm/src/protocols/permissions.js.map +1 -1
- package/dist/esm/src/store/index-level.js +24 -9
- package/dist/esm/src/store/index-level.js.map +1 -1
- package/dist/esm/src/store/level-wrapper.js +7 -0
- package/dist/esm/src/store/level-wrapper.js.map +1 -1
- package/dist/esm/src/store/message-store-level.js +536 -42
- package/dist/esm/src/store/message-store-level.js.map +1 -1
- package/dist/esm/src/store/storage-controller.js +58 -49
- package/dist/esm/src/store/storage-controller.js.map +1 -1
- package/dist/esm/src/types/message-types.js.map +1 -1
- package/dist/esm/src/types/validation-state-reader.js +2 -0
- package/dist/esm/src/types/validation-state-reader.js.map +1 -0
- package/dist/esm/src/utils/messages.js +17 -0
- package/dist/esm/src/utils/messages.js.map +1 -1
- package/dist/esm/src/utils/record-limit-occupancy.js +244 -0
- package/dist/esm/src/utils/record-limit-occupancy.js.map +1 -0
- package/dist/esm/src/utils/records.js +50 -14
- package/dist/esm/src/utils/records.js.map +1 -1
- package/dist/esm/src/utils/replication.js +85 -0
- package/dist/esm/src/utils/replication.js.map +1 -0
- package/dist/esm/tests/core/grant-authorization.spec.js +4 -4
- package/dist/esm/tests/core/grant-authorization.spec.js.map +1 -1
- package/dist/esm/tests/core/process-message-parity.spec.js +222 -0
- package/dist/esm/tests/core/process-message-parity.spec.js.map +1 -0
- package/dist/esm/tests/core/protocol-authorization.spec.js +5 -2
- package/dist/esm/tests/core/protocol-authorization.spec.js.map +1 -1
- package/dist/esm/tests/core/records-grant-authorization.spec.js +5 -5
- package/dist/esm/tests/core/records-grant-authorization.spec.js.map +1 -1
- package/dist/esm/tests/core/replication-apply.spec.js +55 -1
- package/dist/esm/tests/core/replication-apply.spec.js.map +1 -1
- package/dist/esm/tests/core/replication-replay-property.spec.js +350 -0
- package/dist/esm/tests/core/replication-replay-property.spec.js.map +1 -0
- package/dist/esm/tests/core/validation-read-closure.spec.js +469 -0
- package/dist/esm/tests/core/validation-read-closure.spec.js.map +1 -0
- package/dist/esm/tests/core/validation-state-reader.spec.js +716 -0
- package/dist/esm/tests/core/validation-state-reader.spec.js.map +1 -0
- package/dist/esm/tests/durable-event-log.spec.js +373 -0
- package/dist/esm/tests/durable-event-log.spec.js.map +1 -0
- package/dist/esm/tests/dwn.spec.js +504 -35
- package/dist/esm/tests/dwn.spec.js.map +1 -1
- package/dist/esm/tests/features/author-delegated-grant.spec.js +9 -6
- package/dist/esm/tests/features/author-delegated-grant.spec.js.map +1 -1
- package/dist/esm/tests/features/owner-delegated-grant.spec.js +1 -4
- package/dist/esm/tests/features/owner-delegated-grant.spec.js.map +1 -1
- package/dist/esm/tests/features/owner-signature.spec.js +1 -4
- package/dist/esm/tests/features/owner-signature.spec.js.map +1 -1
- package/dist/esm/tests/features/permissions.spec.js +165 -4
- package/dist/esm/tests/features/permissions.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-composition.spec.js +8 -11
- package/dist/esm/tests/features/protocol-composition.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-create-action.spec.js +1 -4
- package/dist/esm/tests/features/protocol-create-action.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-delete-action.spec.js +3 -5
- package/dist/esm/tests/features/protocol-delete-action.spec.js.map +1 -1
- package/dist/esm/tests/features/protocol-update-action.spec.js +3 -6
- package/dist/esm/tests/features/protocol-update-action.spec.js.map +1 -1
- package/dist/esm/tests/features/records-delivery.spec.js +1 -4
- package/dist/esm/tests/features/records-delivery.spec.js.map +1 -1
- package/dist/esm/tests/features/records-immutable.spec.js +1 -4
- package/dist/esm/tests/features/records-immutable.spec.js.map +1 -1
- package/dist/esm/tests/features/records-nested-query-scope.spec.js +281 -0
- package/dist/esm/tests/features/records-nested-query-scope.spec.js.map +1 -0
- package/dist/esm/tests/features/records-prune-cross-protocol.spec.js +3 -7
- package/dist/esm/tests/features/records-prune-cross-protocol.spec.js.map +1 -1
- package/dist/esm/tests/features/records-prune.spec.js +11 -22
- package/dist/esm/tests/features/records-prune.spec.js.map +1 -1
- package/dist/esm/tests/features/records-record-limit.spec.js +441 -231
- package/dist/esm/tests/features/records-record-limit.spec.js.map +1 -1
- package/dist/esm/tests/features/records-squash.spec.js +6 -4
- package/dist/esm/tests/features/records-squash.spec.js.map +1 -1
- package/dist/esm/tests/features/records-tags.spec.js +1 -4
- package/dist/esm/tests/features/records-tags.spec.js.map +1 -1
- package/dist/esm/tests/features/resumable-tasks.spec.js +3 -5
- package/dist/esm/tests/features/resumable-tasks.spec.js.map +1 -1
- package/dist/esm/tests/fuzz/message-store.fuzz.spec.js +1 -2
- package/dist/esm/tests/fuzz/message-store.fuzz.spec.js.map +1 -1
- package/dist/esm/tests/fuzz/process-message.fuzz.spec.js +2 -4
- package/dist/esm/tests/fuzz/process-message.fuzz.spec.js.map +1 -1
- package/dist/esm/tests/fuzz/schema-validation.fuzz.spec.js +1 -1
- package/dist/esm/tests/fuzz/schema-validation.fuzz.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-query.spec.js +246 -0
- package/dist/esm/tests/handlers/messages-query.spec.js.map +1 -0
- package/dist/esm/tests/handlers/messages-read.spec.js +2 -5
- package/dist/esm/tests/handlers/messages-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-subscribe.spec.js +3 -14
- package/dist/esm/tests/handlers/messages-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/protocols-configure.spec.js +27 -26
- package/dist/esm/tests/handlers/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/handlers/protocols-query.spec.js +1 -4
- package/dist/esm/tests/handlers/protocols-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-count.spec.js +1 -4
- package/dist/esm/tests/handlers/records-count.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-delete.spec.js +312 -30
- package/dist/esm/tests/handlers/records-delete.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-query.spec.js +32 -9
- package/dist/esm/tests/handlers/records-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-read.spec.js +4 -4
- package/dist/esm/tests/handlers/records-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-subscribe.spec.js +33 -14
- package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-write.spec.js +82 -36
- package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-delete.spec.js +69 -2
- package/dist/esm/tests/interfaces/records-delete.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-write.spec.js +4 -3
- package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
- package/dist/esm/tests/protocols/permissions.spec.js +55 -6
- package/dist/esm/tests/protocols/permissions.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/aggregator.spec.js +1 -4
- package/dist/esm/tests/scenarios/aggregator.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/deleted-record.spec.js +1 -4
- package/dist/esm/tests/scenarios/deleted-record.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js +1 -4
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/nested-roles.spec.js +1 -4
- package/dist/esm/tests/scenarios/nested-roles.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/subscriptions.spec.js +1 -4
- package/dist/esm/tests/scenarios/subscriptions.spec.js.map +1 -1
- package/dist/esm/tests/store/message-store-level.spec.js +361 -5
- package/dist/esm/tests/store/message-store-level.spec.js.map +1 -1
- package/dist/esm/tests/store/message-store.spec.js +60 -0
- package/dist/esm/tests/store/message-store.spec.js.map +1 -1
- package/dist/esm/tests/test-event-stream.js +7 -3
- package/dist/esm/tests/test-event-stream.js.map +1 -1
- package/dist/esm/tests/test-stores.js +19 -9
- package/dist/esm/tests/test-stores.js.map +1 -1
- package/dist/esm/tests/test-suite.js +4 -2
- package/dist/esm/tests/test-suite.js.map +1 -1
- package/dist/esm/tests/utils/test-data-generator.js +25 -0
- package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
- package/dist/esm/tests/utils/test-stub-generator.js.map +1 -1
- package/dist/esm/tests/utils/test-validation-state-reader.js +16 -0
- package/dist/esm/tests/utils/test-validation-state-reader.js.map +1 -0
- package/dist/types/generated/precompiled-validators.d.ts +6 -6
- package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
- package/dist/types/src/core/core-protocol.d.ts +3 -3
- package/dist/types/src/core/core-protocol.d.ts.map +1 -1
- package/dist/types/src/core/dwn-constant.d.ts +5 -0
- package/dist/types/src/core/dwn-constant.d.ts.map +1 -1
- package/dist/types/src/core/dwn-error.d.ts +12 -4
- package/dist/types/src/core/dwn-error.d.ts.map +1 -1
- package/dist/types/src/core/grant-authorization.d.ts +5 -5
- package/dist/types/src/core/grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/message-reply.d.ts +5 -4
- package/dist/types/src/core/message-reply.d.ts.map +1 -1
- package/dist/types/src/core/messages-grant-authorization.d.ts +12 -14
- package/dist/types/src/core/messages-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization-action.d.ts +4 -5
- package/dist/types/src/core/protocol-authorization-action.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization-validation.d.ts +13 -16
- package/dist/types/src/core/protocol-authorization-validation.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization.d.ts +8 -33
- package/dist/types/src/core/protocol-authorization.d.ts.map +1 -1
- package/dist/types/src/core/protocols-grant-authorization.d.ts +4 -4
- package/dist/types/src/core/protocols-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/recording-validation-state-reader.d.ts +75 -0
- package/dist/types/src/core/recording-validation-state-reader.d.ts.map +1 -0
- package/dist/types/src/core/records-grant-authorization.d.ts +8 -8
- package/dist/types/src/core/records-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/replication-apply.d.ts +36 -0
- package/dist/types/src/core/replication-apply.d.ts.map +1 -1
- package/dist/types/src/core/resumable-task-manager.d.ts +1 -1
- package/dist/types/src/core/resumable-task-manager.d.ts.map +1 -1
- package/dist/types/src/core/validation-state-reader.d.ts +79 -0
- package/dist/types/src/core/validation-state-reader.d.ts.map +1 -0
- package/dist/types/src/dwn.d.ts +33 -20
- package/dist/types/src/dwn.d.ts.map +1 -1
- package/dist/types/src/enums/dwn-interface-method.d.ts +0 -1
- package/dist/types/src/enums/dwn-interface-method.d.ts.map +1 -1
- package/dist/types/src/event-stream/durable-event-log.d.ts +69 -0
- package/dist/types/src/event-stream/durable-event-log.d.ts.map +1 -0
- package/dist/types/src/event-stream/event-emitter-wake-publisher.d.ts +13 -0
- package/dist/types/src/event-stream/event-emitter-wake-publisher.d.ts.map +1 -0
- package/dist/types/src/handlers/messages-query.d.ts +20 -0
- package/dist/types/src/handlers/messages-query.d.ts.map +1 -0
- package/dist/types/src/handlers/messages-read.d.ts +1 -1
- package/dist/types/src/handlers/messages-read.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/protocols-configure.d.ts +0 -5
- package/dist/types/src/handlers/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/handlers/records-count.d.ts +2 -1
- package/dist/types/src/handlers/records-count.d.ts.map +1 -1
- package/dist/types/src/handlers/records-delete.d.ts +2 -2
- package/dist/types/src/handlers/records-delete.d.ts.map +1 -1
- package/dist/types/src/handlers/records-query.d.ts +1 -1
- package/dist/types/src/handlers/records-query.d.ts.map +1 -1
- package/dist/types/src/handlers/records-read.d.ts +2 -1
- package/dist/types/src/handlers/records-read.d.ts.map +1 -1
- package/dist/types/src/handlers/records-subscribe.d.ts +4 -5
- package/dist/types/src/handlers/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/records-write.d.ts +3 -11
- package/dist/types/src/handlers/records-write.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +14 -16
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/interfaces/messages-query.d.ts +23 -0
- package/dist/types/src/interfaces/messages-query.d.ts.map +1 -0
- package/dist/types/src/interfaces/protocols-configure.d.ts +3 -3
- package/dist/types/src/interfaces/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/interfaces/protocols-query.d.ts +2 -2
- package/dist/types/src/interfaces/protocols-query.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-count.d.ts +3 -3
- package/dist/types/src/interfaces/records-count.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-delete.d.ts +11 -3
- package/dist/types/src/interfaces/records-delete.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-query.d.ts +3 -3
- package/dist/types/src/interfaces/records-query.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-read.d.ts +3 -3
- package/dist/types/src/interfaces/records-read.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-subscribe.d.ts +3 -3
- package/dist/types/src/interfaces/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-write.d.ts +15 -7
- package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
- package/dist/types/src/protocols/permissions.d.ts +9 -12
- package/dist/types/src/protocols/permissions.d.ts.map +1 -1
- package/dist/types/src/store/index-level.d.ts +10 -1
- package/dist/types/src/store/index-level.d.ts.map +1 -1
- package/dist/types/src/store/level-wrapper.d.ts +5 -0
- package/dist/types/src/store/level-wrapper.d.ts.map +1 -1
- package/dist/types/src/store/message-store-level.d.ts +94 -14
- package/dist/types/src/store/message-store-level.d.ts.map +1 -1
- package/dist/types/src/store/storage-controller.d.ts +17 -14
- package/dist/types/src/store/storage-controller.d.ts.map +1 -1
- package/dist/types/src/types/message-store.d.ts +29 -1
- package/dist/types/src/types/message-store.d.ts.map +1 -1
- package/dist/types/src/types/message-types.d.ts +2 -0
- package/dist/types/src/types/message-types.d.ts.map +1 -1
- package/dist/types/src/types/messages-types.d.ts +21 -37
- package/dist/types/src/types/messages-types.d.ts.map +1 -1
- package/dist/types/src/types/method-handler.d.ts +2 -2
- package/dist/types/src/types/method-handler.d.ts.map +1 -1
- package/dist/types/src/types/permission-types.d.ts +1 -1
- package/dist/types/src/types/subscriptions.d.ts +50 -39
- package/dist/types/src/types/subscriptions.d.ts.map +1 -1
- package/dist/types/src/types/validation-state-reader.d.ts +116 -0
- package/dist/types/src/types/validation-state-reader.d.ts.map +1 -0
- package/dist/types/src/utils/messages.d.ts +10 -0
- package/dist/types/src/utils/messages.d.ts.map +1 -1
- package/dist/types/src/utils/record-limit-occupancy.d.ts +40 -0
- package/dist/types/src/utils/record-limit-occupancy.d.ts.map +1 -0
- package/dist/types/src/utils/records.d.ts +25 -3
- package/dist/types/src/utils/records.d.ts.map +1 -1
- package/dist/types/src/utils/replication.d.ts +22 -0
- package/dist/types/src/utils/replication.d.ts.map +1 -0
- package/dist/types/tests/core/process-message-parity.spec.d.ts +2 -0
- package/dist/types/tests/core/process-message-parity.spec.d.ts.map +1 -0
- package/dist/types/tests/core/replication-replay-property.spec.d.ts +2 -0
- package/dist/types/tests/core/replication-replay-property.spec.d.ts.map +1 -0
- package/dist/types/tests/core/validation-read-closure.spec.d.ts +2 -0
- package/dist/types/tests/core/validation-read-closure.spec.d.ts.map +1 -0
- package/dist/types/tests/core/validation-state-reader.spec.d.ts +2 -0
- package/dist/types/tests/core/validation-state-reader.spec.d.ts.map +1 -0
- package/dist/types/tests/durable-event-log.spec.d.ts +2 -0
- package/dist/types/tests/durable-event-log.spec.d.ts.map +1 -0
- package/dist/types/tests/dwn.spec.d.ts.map +1 -1
- package/dist/types/tests/features/author-delegated-grant.spec.d.ts.map +1 -1
- package/dist/types/tests/features/owner-delegated-grant.spec.d.ts.map +1 -1
- package/dist/types/tests/features/owner-signature.spec.d.ts.map +1 -1
- package/dist/types/tests/features/permissions.spec.d.ts.map +1 -1
- package/dist/types/tests/features/protocol-composition.spec.d.ts.map +1 -1
- package/dist/types/tests/features/protocol-create-action.spec.d.ts.map +1 -1
- package/dist/types/tests/features/protocol-delete-action.spec.d.ts.map +1 -1
- package/dist/types/tests/features/protocol-update-action.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-delivery.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-immutable.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-nested-query-scope.spec.d.ts +2 -0
- package/dist/types/tests/features/records-nested-query-scope.spec.d.ts.map +1 -0
- package/dist/types/tests/features/records-prune-cross-protocol.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-prune.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-record-limit.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-squash.spec.d.ts.map +1 -1
- package/dist/types/tests/features/records-tags.spec.d.ts.map +1 -1
- package/dist/types/tests/features/resumable-tasks.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-query.spec.d.ts +2 -0
- package/dist/types/tests/handlers/messages-query.spec.d.ts.map +1 -0
- package/dist/types/tests/handlers/messages-read.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/protocols-configure.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/protocols-query.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-count.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-delete.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-query.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-read.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-write.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/deleted-record.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/end-to-end-tests.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/nested-roles.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/subscriptions.spec.d.ts.map +1 -1
- package/dist/types/tests/store/message-store.spec.d.ts.map +1 -1
- package/dist/types/tests/test-event-stream.d.ts +1 -1
- package/dist/types/tests/test-event-stream.d.ts.map +1 -1
- package/dist/types/tests/test-stores.d.ts +5 -4
- package/dist/types/tests/test-stores.d.ts.map +1 -1
- package/dist/types/tests/test-suite.d.ts +1 -2
- package/dist/types/tests/test-suite.d.ts.map +1 -1
- package/dist/types/tests/utils/test-data-generator.d.ts +20 -1
- package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
- package/dist/types/tests/utils/test-validation-state-reader.d.ts +15 -0
- package/dist/types/tests/utils/test-validation-state-reader.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/core/core-protocol.ts +3 -3
- package/src/core/dwn-constant.ts +7 -1
- package/src/core/dwn-error.ts +12 -4
- package/src/core/grant-authorization.ts +11 -20
- package/src/core/message-reply.ts +6 -5
- package/src/core/messages-grant-authorization.ts +37 -70
- package/src/core/protocol-authorization-action.ts +29 -38
- package/src/core/protocol-authorization-validation.ts +39 -96
- package/src/core/protocol-authorization.ts +56 -202
- package/src/core/protocols-grant-authorization.ts +9 -9
- package/src/core/recording-validation-state-reader.ts +130 -0
- package/src/core/records-grant-authorization.ts +16 -16
- package/src/core/replication-apply.ts +172 -32
- package/src/core/resumable-task-manager.ts +10 -8
- package/src/core/validation-state-reader.ts +350 -0
- package/src/dwn.ts +285 -192
- package/src/enums/dwn-interface-method.ts +0 -1
- package/src/event-stream/durable-event-log.ts +509 -0
- package/src/event-stream/event-emitter-wake-publisher.ts +34 -0
- package/src/handlers/messages-query.ts +203 -0
- package/src/handlers/messages-read.ts +9 -10
- package/src/handlers/messages-subscribe.ts +12 -13
- package/src/handlers/protocols-configure.ts +37 -58
- package/src/handlers/protocols-query.ts +1 -1
- package/src/handlers/records-count.ts +24 -17
- package/src/handlers/records-delete.ts +29 -27
- package/src/handlers/records-query.ts +38 -17
- package/src/handlers/records-read.ts +63 -50
- package/src/handlers/records-subscribe.ts +132 -19
- package/src/handlers/records-write.ts +77 -168
- package/src/index.ts +14 -17
- package/src/interfaces/messages-query.ts +70 -0
- package/src/interfaces/protocols-configure.ts +12 -4
- package/src/interfaces/protocols-query.ts +4 -5
- package/src/interfaces/records-count.ts +9 -4
- package/src/interfaces/records-delete.ts +25 -5
- package/src/interfaces/records-query.ts +9 -4
- package/src/interfaces/records-read.ts +4 -4
- package/src/interfaces/records-subscribe.ts +9 -4
- package/src/interfaces/records-write.ts +41 -13
- package/src/protocols/permissions.ts +32 -52
- package/src/store/index-level.ts +30 -9
- package/src/store/level-wrapper.ts +9 -1
- package/src/store/message-store-level.ts +757 -47
- package/src/store/storage-controller.ts +74 -63
- package/src/types/message-store.ts +45 -2
- package/src/types/message-types.ts +3 -1
- package/src/types/messages-types.ts +26 -45
- package/src/types/method-handler.ts +3 -3
- package/src/types/permission-types.ts +1 -1
- package/src/types/subscriptions.ts +53 -42
- package/src/types/validation-state-reader.ts +127 -0
- package/src/utils/messages.ts +25 -1
- package/src/utils/record-limit-occupancy.ts +377 -0
- package/src/utils/records.ts +69 -13
- package/src/utils/replication.ts +122 -0
- package/dist/esm/src/core/record-chain.js +0 -64
- package/dist/esm/src/core/record-chain.js.map +0 -1
- package/dist/esm/src/event-stream/event-emitter-event-log.js +0 -334
- package/dist/esm/src/event-stream/event-emitter-event-log.js.map +0 -1
- package/dist/esm/src/handlers/messages-sync.js +0 -278
- package/dist/esm/src/handlers/messages-sync.js.map +0 -1
- package/dist/esm/src/interfaces/messages-sync.js.map +0 -1
- package/dist/esm/src/smt/smt-store-level.js +0 -103
- package/dist/esm/src/smt/smt-store-level.js.map +0 -1
- package/dist/esm/src/smt/smt-store-memory.js +0 -41
- package/dist/esm/src/smt/smt-store-memory.js.map +0 -1
- package/dist/esm/src/smt/smt-utils.js +0 -129
- package/dist/esm/src/smt/smt-utils.js.map +0 -1
- package/dist/esm/src/smt/sparse-merkle-tree.js +0 -577
- package/dist/esm/src/smt/sparse-merkle-tree.js.map +0 -1
- package/dist/esm/src/state-index/state-index-level.js +0 -191
- package/dist/esm/src/state-index/state-index-level.js.map +0 -1
- package/dist/esm/src/types/smt-types.js +0 -5
- package/dist/esm/src/types/smt-types.js.map +0 -1
- package/dist/esm/src/types/state-index.js +0 -2
- package/dist/esm/src/types/state-index.js.map +0 -1
- package/dist/esm/tests/event-emitter-event-log.spec.js +0 -499
- package/dist/esm/tests/event-emitter-event-log.spec.js.map +0 -1
- package/dist/esm/tests/handlers/messages-sync.spec.js +0 -1088
- package/dist/esm/tests/handlers/messages-sync.spec.js.map +0 -1
- package/dist/esm/tests/smt/smt-store-level.spec.js +0 -132
- package/dist/esm/tests/smt/smt-store-level.spec.js.map +0 -1
- package/dist/esm/tests/smt/sparse-merkle-tree.spec.js +0 -732
- package/dist/esm/tests/smt/sparse-merkle-tree.spec.js.map +0 -1
- package/dist/esm/tests/state-index/state-index-level.spec.js +0 -245
- package/dist/esm/tests/state-index/state-index-level.spec.js.map +0 -1
- package/dist/types/src/core/record-chain.d.ts +0 -24
- package/dist/types/src/core/record-chain.d.ts.map +0 -1
- package/dist/types/src/event-stream/event-emitter-event-log.d.ts +0 -80
- package/dist/types/src/event-stream/event-emitter-event-log.d.ts.map +0 -1
- package/dist/types/src/handlers/messages-sync.d.ts +0 -39
- package/dist/types/src/handlers/messages-sync.d.ts.map +0 -1
- package/dist/types/src/interfaces/messages-sync.d.ts +0 -20
- package/dist/types/src/interfaces/messages-sync.d.ts.map +0 -1
- package/dist/types/src/smt/smt-store-level.d.ts +0 -32
- package/dist/types/src/smt/smt-store-level.d.ts.map +0 -1
- package/dist/types/src/smt/smt-store-memory.d.ts +0 -22
- package/dist/types/src/smt/smt-store-memory.d.ts.map +0 -1
- package/dist/types/src/smt/smt-utils.d.ts +0 -58
- package/dist/types/src/smt/smt-utils.d.ts.map +0 -1
- package/dist/types/src/smt/sparse-merkle-tree.d.ts +0 -124
- package/dist/types/src/smt/sparse-merkle-tree.d.ts.map +0 -1
- package/dist/types/src/state-index/state-index-level.d.ts +0 -83
- package/dist/types/src/state-index/state-index-level.d.ts.map +0 -1
- package/dist/types/src/types/smt-types.d.ts +0 -81
- package/dist/types/src/types/smt-types.d.ts.map +0 -1
- package/dist/types/src/types/state-index.d.ts +0 -90
- package/dist/types/src/types/state-index.d.ts.map +0 -1
- package/dist/types/tests/event-emitter-event-log.spec.d.ts +0 -2
- package/dist/types/tests/event-emitter-event-log.spec.d.ts.map +0 -1
- package/dist/types/tests/handlers/messages-sync.spec.d.ts +0 -2
- package/dist/types/tests/handlers/messages-sync.spec.d.ts.map +0 -1
- package/dist/types/tests/smt/smt-store-level.spec.d.ts +0 -2
- package/dist/types/tests/smt/smt-store-level.spec.d.ts.map +0 -1
- package/dist/types/tests/smt/sparse-merkle-tree.spec.d.ts +0 -2
- package/dist/types/tests/smt/sparse-merkle-tree.spec.d.ts.map +0 -1
- package/dist/types/tests/state-index/state-index-level.spec.d.ts +0 -2
- package/dist/types/tests/state-index/state-index-level.spec.d.ts.map +0 -1
- package/src/core/record-chain.ts +0 -99
- package/src/event-stream/event-emitter-event-log.ts +0 -430
- package/src/handlers/messages-sync.ts +0 -403
- package/src/interfaces/messages-sync.ts +0 -69
- package/src/smt/smt-store-level.ts +0 -143
- package/src/smt/smt-store-memory.ts +0 -53
- package/src/smt/smt-utils.ts +0 -149
- package/src/smt/sparse-merkle-tree.ts +0 -698
- package/src/state-index/state-index-level.ts +0 -239
- package/src/types/smt-types.ts +0 -95
- package/src/types/state-index.ts +0 -100
|
@@ -1,732 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
|
|
2
|
-
import { SMTStoreMemory } from '../../src/smt/smt-store-memory.js';
|
|
3
|
-
import { SparseMerkleTree } from '../../src/smt/sparse-merkle-tree.js';
|
|
4
|
-
import { getBit, getDefaultHashes, hashEquals, hashKey, hashLeaf, initDefaultHashes, resetDefaultHashesForTesting, SMT_DEPTH, ZERO_HASH } from '../../src/smt/smt-utils.js';
|
|
5
|
-
describe('SparseMerkleTree', () => {
|
|
6
|
-
let store;
|
|
7
|
-
let smt;
|
|
8
|
-
beforeEach(async () => {
|
|
9
|
-
store = new SMTStoreMemory();
|
|
10
|
-
smt = new SparseMerkleTree(store);
|
|
11
|
-
await smt.initialize();
|
|
12
|
-
});
|
|
13
|
-
afterEach(async () => {
|
|
14
|
-
await smt.close();
|
|
15
|
-
});
|
|
16
|
-
describe('initialization', () => {
|
|
17
|
-
it('should return the default empty root for a new tree', async () => {
|
|
18
|
-
const root = await smt.getRoot();
|
|
19
|
-
const defaultHashes = await initDefaultHashes();
|
|
20
|
-
expect(hashEquals(root, defaultHashes[0])).toBe(true);
|
|
21
|
-
});
|
|
22
|
-
it('should precompute default hashes for all 256 levels + leaf level', async () => {
|
|
23
|
-
const defaultHashes = await initDefaultHashes();
|
|
24
|
-
expect(defaultHashes.length).toBe(SMT_DEPTH + 1);
|
|
25
|
-
// Leaf level should be zero hash
|
|
26
|
-
expect(hashEquals(defaultHashes[SMT_DEPTH], ZERO_HASH)).toBe(true);
|
|
27
|
-
// Each level should be different from its neighbor
|
|
28
|
-
for (let i = 0; i < SMT_DEPTH; i++) {
|
|
29
|
-
expect(hashEquals(defaultHashes[i], defaultHashes[i + 1])).toBe(false);
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
describe('insert', () => {
|
|
34
|
-
it('should change the root hash after inserting a single element', async () => {
|
|
35
|
-
const emptyRoot = await smt.getRoot();
|
|
36
|
-
await smt.insert('bafyreigtest1');
|
|
37
|
-
const newRoot = await smt.getRoot();
|
|
38
|
-
expect(hashEquals(emptyRoot, newRoot)).toBe(false);
|
|
39
|
-
});
|
|
40
|
-
it('should produce different roots for different values', async () => {
|
|
41
|
-
const storeA = new SMTStoreMemory();
|
|
42
|
-
const smtA = new SparseMerkleTree(storeA);
|
|
43
|
-
await smtA.initialize();
|
|
44
|
-
const storeB = new SMTStoreMemory();
|
|
45
|
-
const smtB = new SparseMerkleTree(storeB);
|
|
46
|
-
await smtB.initialize();
|
|
47
|
-
await smtA.insert('bafyreigaaa');
|
|
48
|
-
await smtB.insert('bafyreigbbb');
|
|
49
|
-
const rootA = await smtA.getRoot();
|
|
50
|
-
const rootB = await smtB.getRoot();
|
|
51
|
-
expect(hashEquals(rootA, rootB)).toBe(false);
|
|
52
|
-
await smtA.close();
|
|
53
|
-
await smtB.close();
|
|
54
|
-
});
|
|
55
|
-
it('should produce the same root regardless of insertion order', async () => {
|
|
56
|
-
const cids = ['bafyreiA', 'bafyreiB', 'bafyreiC', 'bafyreiD', 'bafyreiE'];
|
|
57
|
-
// Insert in forward order
|
|
58
|
-
const storeA = new SMTStoreMemory();
|
|
59
|
-
const smtA = new SparseMerkleTree(storeA);
|
|
60
|
-
await smtA.initialize();
|
|
61
|
-
for (const cid of cids) {
|
|
62
|
-
await smtA.insert(cid);
|
|
63
|
-
}
|
|
64
|
-
// Insert in reverse order
|
|
65
|
-
const storeB = new SMTStoreMemory();
|
|
66
|
-
const smtB = new SparseMerkleTree(storeB);
|
|
67
|
-
await smtB.initialize();
|
|
68
|
-
for (const cid of [...cids].reverse()) {
|
|
69
|
-
await smtB.insert(cid);
|
|
70
|
-
}
|
|
71
|
-
const rootA = await smtA.getRoot();
|
|
72
|
-
const rootB = await smtB.getRoot();
|
|
73
|
-
expect(hashEquals(rootA, rootB)).toBe(true);
|
|
74
|
-
await smtA.close();
|
|
75
|
-
await smtB.close();
|
|
76
|
-
});
|
|
77
|
-
it('should handle inserting many elements', async () => {
|
|
78
|
-
const cids = [];
|
|
79
|
-
for (let i = 0; i < 100; i++) {
|
|
80
|
-
cids.push(`bafyreig-test-${i.toString().padStart(4, '0')}`);
|
|
81
|
-
}
|
|
82
|
-
for (const cid of cids) {
|
|
83
|
-
await smt.insert(cid);
|
|
84
|
-
}
|
|
85
|
-
// Verify all exist
|
|
86
|
-
for (const cid of cids) {
|
|
87
|
-
expect(await smt.has(cid)).toBe(true);
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
it('should handle re-inserting the same value (idempotent for same key)', async () => {
|
|
91
|
-
await smt.insert('bafyreigtest1');
|
|
92
|
-
const rootAfterFirst = await smt.getRoot();
|
|
93
|
-
await smt.insert('bafyreigtest1');
|
|
94
|
-
const rootAfterSecond = await smt.getRoot();
|
|
95
|
-
expect(hashEquals(rootAfterFirst, rootAfterSecond)).toBe(true);
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
describe('has', () => {
|
|
99
|
-
it('should return false for an empty tree', async () => {
|
|
100
|
-
expect(await smt.has('bafyreigtest1')).toBe(false);
|
|
101
|
-
});
|
|
102
|
-
it('should return true for an inserted element', async () => {
|
|
103
|
-
await smt.insert('bafyreigtest1');
|
|
104
|
-
expect(await smt.has('bafyreigtest1')).toBe(true);
|
|
105
|
-
});
|
|
106
|
-
it('should return false for an element not in the tree', async () => {
|
|
107
|
-
await smt.insert('bafyreigtest1');
|
|
108
|
-
expect(await smt.has('bafyreigtest2')).toBe(false);
|
|
109
|
-
});
|
|
110
|
-
it('should return false after deletion', async () => {
|
|
111
|
-
await smt.insert('bafyreigtest1');
|
|
112
|
-
expect(await smt.has('bafyreigtest1')).toBe(true);
|
|
113
|
-
await smt.delete('bafyreigtest1');
|
|
114
|
-
expect(await smt.has('bafyreigtest1')).toBe(false);
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
describe('delete', () => {
|
|
118
|
-
it('should return to empty root after inserting and deleting a single element', async () => {
|
|
119
|
-
const emptyRoot = await smt.getRoot();
|
|
120
|
-
await smt.insert('bafyreigtest1');
|
|
121
|
-
const nonEmptyRoot = await smt.getRoot();
|
|
122
|
-
expect(hashEquals(emptyRoot, nonEmptyRoot)).toBe(false);
|
|
123
|
-
await smt.delete('bafyreigtest1');
|
|
124
|
-
const rootAfterDelete = await smt.getRoot();
|
|
125
|
-
expect(hashEquals(emptyRoot, rootAfterDelete)).toBe(true);
|
|
126
|
-
});
|
|
127
|
-
it('should not change the root when deleting a non-existent element', async () => {
|
|
128
|
-
await smt.insert('bafyreigtest1');
|
|
129
|
-
const rootBefore = await smt.getRoot();
|
|
130
|
-
await smt.delete('bafyreigtest2');
|
|
131
|
-
const rootAfter = await smt.getRoot();
|
|
132
|
-
expect(hashEquals(rootBefore, rootAfter)).toBe(true);
|
|
133
|
-
});
|
|
134
|
-
it('should produce the same root regardless of insert-delete order', async () => {
|
|
135
|
-
// Insert A, B, C then delete B
|
|
136
|
-
const storeA = new SMTStoreMemory();
|
|
137
|
-
const smtA = new SparseMerkleTree(storeA);
|
|
138
|
-
await smtA.initialize();
|
|
139
|
-
await smtA.insert('bafyreiA');
|
|
140
|
-
await smtA.insert('bafyreiB');
|
|
141
|
-
await smtA.insert('bafyreiC');
|
|
142
|
-
await smtA.delete('bafyreiB');
|
|
143
|
-
// Insert A, C only (never insert B)
|
|
144
|
-
const storeB = new SMTStoreMemory();
|
|
145
|
-
const smtB = new SparseMerkleTree(storeB);
|
|
146
|
-
await smtB.initialize();
|
|
147
|
-
await smtB.insert('bafyreiA');
|
|
148
|
-
await smtB.insert('bafyreiC');
|
|
149
|
-
const rootA = await smtA.getRoot();
|
|
150
|
-
const rootB = await smtB.getRoot();
|
|
151
|
-
expect(hashEquals(rootA, rootB)).toBe(true);
|
|
152
|
-
await smtA.close();
|
|
153
|
-
await smtB.close();
|
|
154
|
-
});
|
|
155
|
-
it('should handle deleting all elements to return to empty state', async () => {
|
|
156
|
-
const emptyRoot = await smt.getRoot();
|
|
157
|
-
const cids = ['bafyreiA', 'bafyreiB', 'bafyreiC'];
|
|
158
|
-
for (const cid of cids) {
|
|
159
|
-
await smt.insert(cid);
|
|
160
|
-
}
|
|
161
|
-
for (const cid of cids) {
|
|
162
|
-
await smt.delete(cid);
|
|
163
|
-
}
|
|
164
|
-
const rootAfter = await smt.getRoot();
|
|
165
|
-
expect(hashEquals(emptyRoot, rootAfter)).toBe(true);
|
|
166
|
-
});
|
|
167
|
-
it('should handle interleaved inserts and deletes', async () => {
|
|
168
|
-
await smt.insert('bafyreiA');
|
|
169
|
-
await smt.insert('bafyreiB');
|
|
170
|
-
await smt.delete('bafyreiA');
|
|
171
|
-
await smt.insert('bafyreiC');
|
|
172
|
-
await smt.delete('bafyreiB');
|
|
173
|
-
await smt.insert('bafyreiD');
|
|
174
|
-
// Should have C and D
|
|
175
|
-
expect(await smt.has('bafyreiA')).toBe(false);
|
|
176
|
-
expect(await smt.has('bafyreiB')).toBe(false);
|
|
177
|
-
expect(await smt.has('bafyreiC')).toBe(true);
|
|
178
|
-
expect(await smt.has('bafyreiD')).toBe(true);
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
describe('proof', () => {
|
|
182
|
-
it('should generate a proof for an existing element (inclusion)', async () => {
|
|
183
|
-
await smt.insert('bafyreigtest1');
|
|
184
|
-
const proof = await smt.getProof('bafyreigtest1');
|
|
185
|
-
expect(proof.leafNode).toBeDefined();
|
|
186
|
-
expect(proof.leafNode.valueCid).toBe('bafyreigtest1');
|
|
187
|
-
expect(proof.leafNode.type).toBe('leaf');
|
|
188
|
-
});
|
|
189
|
-
it('should generate a proof for a non-existent element (non-inclusion)', async () => {
|
|
190
|
-
await smt.insert('bafyreigtest1');
|
|
191
|
-
const proof = await smt.getProof('bafyreigtest2');
|
|
192
|
-
// Either the leaf is undefined (empty slot) or it's a different key
|
|
193
|
-
if (proof.leafNode !== undefined) {
|
|
194
|
-
// The proof terminates at a leaf with a different key
|
|
195
|
-
const expectedKeyHash = await hashKey('bafyreigtest2');
|
|
196
|
-
expect(hashEquals(proof.leafNode.keyHash, expectedKeyHash)).toBe(false);
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
it('should generate an empty proof for an empty tree', async () => {
|
|
200
|
-
const proof = await smt.getProof('bafyreigtest1');
|
|
201
|
-
expect(proof.leafNode).toBeUndefined();
|
|
202
|
-
expect(proof.siblings.length).toBe(0);
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
describe('getSubtreeHash', () => {
|
|
206
|
-
it('should return default hash for empty subtree', async () => {
|
|
207
|
-
const defaultHashes = await initDefaultHashes();
|
|
208
|
-
const subtreeHash = await smt.getSubtreeHash([false]); // left child of root
|
|
209
|
-
expect(hashEquals(subtreeHash, defaultHashes[1])).toBe(true);
|
|
210
|
-
});
|
|
211
|
-
it('should return different hashes for subtrees with different contents', async () => {
|
|
212
|
-
await smt.insert('bafyreigtest1');
|
|
213
|
-
await smt.insert('bafyreigtest2');
|
|
214
|
-
const leftHash = await smt.getSubtreeHash([false]);
|
|
215
|
-
const rightHash = await smt.getSubtreeHash([true]);
|
|
216
|
-
// At least one should be non-default (elements distributed by hash)
|
|
217
|
-
const defaultHashes = await initDefaultHashes();
|
|
218
|
-
const leftIsDefault = hashEquals(leftHash, defaultHashes[1]);
|
|
219
|
-
const rightIsDefault = hashEquals(rightHash, defaultHashes[1]);
|
|
220
|
-
expect(leftIsDefault && rightIsDefault).toBe(false);
|
|
221
|
-
});
|
|
222
|
-
});
|
|
223
|
-
describe('getLeaves', () => {
|
|
224
|
-
it('should return empty array for empty tree', async () => {
|
|
225
|
-
const leaves = await smt.getLeaves([]);
|
|
226
|
-
expect(leaves).toEqual([]);
|
|
227
|
-
});
|
|
228
|
-
it('should return all leaves for empty prefix', async () => {
|
|
229
|
-
const cids = ['bafyreiA', 'bafyreiB', 'bafyreiC'];
|
|
230
|
-
for (const cid of cids) {
|
|
231
|
-
await smt.insert(cid);
|
|
232
|
-
}
|
|
233
|
-
const leaves = await smt.getLeaves([]);
|
|
234
|
-
expect(leaves.sort()).toEqual([...cids].sort());
|
|
235
|
-
});
|
|
236
|
-
it('should return only leaves under the specified prefix', async () => {
|
|
237
|
-
// Insert multiple elements and verify prefix filtering works
|
|
238
|
-
const cids = [];
|
|
239
|
-
for (let i = 0; i < 20; i++) {
|
|
240
|
-
cids.push(`bafyreig-prefix-test-${i}`);
|
|
241
|
-
}
|
|
242
|
-
for (const cid of cids) {
|
|
243
|
-
await smt.insert(cid);
|
|
244
|
-
}
|
|
245
|
-
// Get leaves under left and right subtrees
|
|
246
|
-
const leftLeaves = await smt.getLeaves([false]);
|
|
247
|
-
const rightLeaves = await smt.getLeaves([true]);
|
|
248
|
-
// Together they should contain all CIDs
|
|
249
|
-
const allLeaves = [...leftLeaves, ...rightLeaves].sort();
|
|
250
|
-
expect(allLeaves).toEqual([...cids].sort());
|
|
251
|
-
// Neither should be empty (with 20 random-ish hashes, both sides should have elements)
|
|
252
|
-
// (This is probabilistic but 20 elements should reliably split)
|
|
253
|
-
expect(leftLeaves.length).toBeGreaterThan(0);
|
|
254
|
-
expect(rightLeaves.length).toBeGreaterThan(0);
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
describe('diff', () => {
|
|
258
|
-
it('should return empty diff for identical trees', async () => {
|
|
259
|
-
const storeB = new SMTStoreMemory();
|
|
260
|
-
const smtB = new SparseMerkleTree(storeB);
|
|
261
|
-
await smtB.initialize();
|
|
262
|
-
const cids = ['bafyreiA', 'bafyreiB', 'bafyreiC'];
|
|
263
|
-
for (const cid of cids) {
|
|
264
|
-
await smt.insert(cid);
|
|
265
|
-
await smtB.insert(cid);
|
|
266
|
-
}
|
|
267
|
-
const diff = await smt.diff(smtB);
|
|
268
|
-
expect(diff.onlyLocal).toEqual([]);
|
|
269
|
-
expect(diff.onlyRemote).toEqual([]);
|
|
270
|
-
await smtB.close();
|
|
271
|
-
});
|
|
272
|
-
it('should return empty diff for two empty trees', async () => {
|
|
273
|
-
const storeB = new SMTStoreMemory();
|
|
274
|
-
const smtB = new SparseMerkleTree(storeB);
|
|
275
|
-
await smtB.initialize();
|
|
276
|
-
const diff = await smt.diff(smtB);
|
|
277
|
-
expect(diff.onlyLocal).toEqual([]);
|
|
278
|
-
expect(diff.onlyRemote).toEqual([]);
|
|
279
|
-
await smtB.close();
|
|
280
|
-
});
|
|
281
|
-
it('should detect elements only in the local tree', async () => {
|
|
282
|
-
const storeB = new SMTStoreMemory();
|
|
283
|
-
const smtB = new SparseMerkleTree(storeB);
|
|
284
|
-
await smtB.initialize();
|
|
285
|
-
await smt.insert('bafyreiA');
|
|
286
|
-
await smt.insert('bafyreiB');
|
|
287
|
-
await smtB.insert('bafyreiA');
|
|
288
|
-
const diff = await smt.diff(smtB);
|
|
289
|
-
expect(diff.onlyLocal.sort()).toEqual(['bafyreiB']);
|
|
290
|
-
expect(diff.onlyRemote).toEqual([]);
|
|
291
|
-
await smtB.close();
|
|
292
|
-
});
|
|
293
|
-
it('should detect elements only in the remote tree', async () => {
|
|
294
|
-
const storeB = new SMTStoreMemory();
|
|
295
|
-
const smtB = new SparseMerkleTree(storeB);
|
|
296
|
-
await smtB.initialize();
|
|
297
|
-
await smt.insert('bafyreiA');
|
|
298
|
-
await smtB.insert('bafyreiA');
|
|
299
|
-
await smtB.insert('bafyreiB');
|
|
300
|
-
const diff = await smt.diff(smtB);
|
|
301
|
-
expect(diff.onlyLocal).toEqual([]);
|
|
302
|
-
expect(diff.onlyRemote.sort()).toEqual(['bafyreiB']);
|
|
303
|
-
await smtB.close();
|
|
304
|
-
});
|
|
305
|
-
it('should detect elements unique to each tree', async () => {
|
|
306
|
-
const storeB = new SMTStoreMemory();
|
|
307
|
-
const smtB = new SparseMerkleTree(storeB);
|
|
308
|
-
await smtB.initialize();
|
|
309
|
-
// Shared
|
|
310
|
-
await smt.insert('bafyreiShared1');
|
|
311
|
-
await smtB.insert('bafyreiShared1');
|
|
312
|
-
await smt.insert('bafyreiShared2');
|
|
313
|
-
await smtB.insert('bafyreiShared2');
|
|
314
|
-
// Only local
|
|
315
|
-
await smt.insert('bafyreiLocalOnly1');
|
|
316
|
-
await smt.insert('bafyreiLocalOnly2');
|
|
317
|
-
// Only remote
|
|
318
|
-
await smtB.insert('bafyreiRemoteOnly1');
|
|
319
|
-
await smtB.insert('bafyreiRemoteOnly2');
|
|
320
|
-
await smtB.insert('bafyreiRemoteOnly3');
|
|
321
|
-
const diff = await smt.diff(smtB);
|
|
322
|
-
expect(diff.onlyLocal.sort()).toEqual(['bafyreiLocalOnly1', 'bafyreiLocalOnly2'].sort());
|
|
323
|
-
expect(diff.onlyRemote.sort()).toEqual(['bafyreiRemoteOnly1', 'bafyreiRemoteOnly2', 'bafyreiRemoteOnly3'].sort());
|
|
324
|
-
await smtB.close();
|
|
325
|
-
});
|
|
326
|
-
it('should detect all differences when trees are completely disjoint', async () => {
|
|
327
|
-
const storeB = new SMTStoreMemory();
|
|
328
|
-
const smtB = new SparseMerkleTree(storeB);
|
|
329
|
-
await smtB.initialize();
|
|
330
|
-
await smt.insert('bafyreiA');
|
|
331
|
-
await smt.insert('bafyreiB');
|
|
332
|
-
await smtB.insert('bafyreiC');
|
|
333
|
-
await smtB.insert('bafyreiD');
|
|
334
|
-
const diff = await smt.diff(smtB);
|
|
335
|
-
expect(diff.onlyLocal.sort()).toEqual(['bafyreiA', 'bafyreiB'].sort());
|
|
336
|
-
expect(diff.onlyRemote.sort()).toEqual(['bafyreiC', 'bafyreiD'].sort());
|
|
337
|
-
await smtB.close();
|
|
338
|
-
});
|
|
339
|
-
it('should detect differences in larger trees efficiently', async () => {
|
|
340
|
-
const storeB = new SMTStoreMemory();
|
|
341
|
-
const smtB = new SparseMerkleTree(storeB);
|
|
342
|
-
await smtB.initialize();
|
|
343
|
-
// Both trees share 50 elements
|
|
344
|
-
for (let i = 0; i < 50; i++) {
|
|
345
|
-
const cid = `bafyreig-shared-${i.toString().padStart(3, '0')}`;
|
|
346
|
-
await smt.insert(cid);
|
|
347
|
-
await smtB.insert(cid);
|
|
348
|
-
}
|
|
349
|
-
// Local has 5 unique elements
|
|
350
|
-
const localOnly = [];
|
|
351
|
-
for (let i = 0; i < 5; i++) {
|
|
352
|
-
const cid = `bafyreig-local-${i}`;
|
|
353
|
-
await smt.insert(cid);
|
|
354
|
-
localOnly.push(cid);
|
|
355
|
-
}
|
|
356
|
-
// Remote has 3 unique elements
|
|
357
|
-
const remoteOnly = [];
|
|
358
|
-
for (let i = 0; i < 3; i++) {
|
|
359
|
-
const cid = `bafyreig-remote-${i}`;
|
|
360
|
-
await smtB.insert(cid);
|
|
361
|
-
remoteOnly.push(cid);
|
|
362
|
-
}
|
|
363
|
-
const diff = await smt.diff(smtB);
|
|
364
|
-
expect(diff.onlyLocal.sort()).toEqual(localOnly.sort());
|
|
365
|
-
expect(diff.onlyRemote.sort()).toEqual(remoteOnly.sort());
|
|
366
|
-
await smtB.close();
|
|
367
|
-
});
|
|
368
|
-
it('should handle diff where local tree is empty', async () => {
|
|
369
|
-
const storeB = new SMTStoreMemory();
|
|
370
|
-
const smtB = new SparseMerkleTree(storeB);
|
|
371
|
-
await smtB.initialize();
|
|
372
|
-
await smtB.insert('bafyreiA');
|
|
373
|
-
await smtB.insert('bafyreiB');
|
|
374
|
-
const diff = await smt.diff(smtB);
|
|
375
|
-
expect(diff.onlyLocal).toEqual([]);
|
|
376
|
-
expect(diff.onlyRemote.sort()).toEqual(['bafyreiA', 'bafyreiB'].sort());
|
|
377
|
-
await smtB.close();
|
|
378
|
-
});
|
|
379
|
-
it('should handle diff where remote tree is empty', async () => {
|
|
380
|
-
const storeB = new SMTStoreMemory();
|
|
381
|
-
const smtB = new SparseMerkleTree(storeB);
|
|
382
|
-
await smtB.initialize();
|
|
383
|
-
await smt.insert('bafyreiA');
|
|
384
|
-
await smt.insert('bafyreiB');
|
|
385
|
-
const diff = await smt.diff(smtB);
|
|
386
|
-
expect(diff.onlyLocal.sort()).toEqual(['bafyreiA', 'bafyreiB'].sort());
|
|
387
|
-
expect(diff.onlyRemote).toEqual([]);
|
|
388
|
-
await smtB.close();
|
|
389
|
-
});
|
|
390
|
-
});
|
|
391
|
-
describe('clear', () => {
|
|
392
|
-
it('should reset the tree to empty state', async () => {
|
|
393
|
-
const emptyRoot = await smt.getRoot();
|
|
394
|
-
await smt.insert('bafyreiA');
|
|
395
|
-
await smt.insert('bafyreiB');
|
|
396
|
-
await smt.clear();
|
|
397
|
-
const rootAfterClear = await smt.getRoot();
|
|
398
|
-
expect(hashEquals(emptyRoot, rootAfterClear)).toBe(true);
|
|
399
|
-
expect(await smt.has('bafyreiA')).toBe(false);
|
|
400
|
-
expect(await smt.has('bafyreiB')).toBe(false);
|
|
401
|
-
});
|
|
402
|
-
});
|
|
403
|
-
describe('getSubtreeHash with leaf at shallow depth', () => {
|
|
404
|
-
it('should return leaf hash when leaf matches the prefix', async () => {
|
|
405
|
-
// Insert a single element — the tree stores it as a leaf near the root
|
|
406
|
-
await smt.insert('bafyreiA');
|
|
407
|
-
const keyHash = await hashKey('bafyreiA');
|
|
408
|
-
// Build a deep prefix that follows the leaf's keyHash bits
|
|
409
|
-
const matchingPrefix = [];
|
|
410
|
-
for (let i = 0; i < 8; i++) {
|
|
411
|
-
matchingPrefix.push(getBit(keyHash, i));
|
|
412
|
-
}
|
|
413
|
-
const subtreeHash = await smt.getSubtreeHash(matchingPrefix);
|
|
414
|
-
// Should be the leaf hash (non-default) since the leaf is under this prefix
|
|
415
|
-
const expectedLeafHash = await hashLeaf(keyHash, 'bafyreiA');
|
|
416
|
-
expect(hashEquals(subtreeHash, expectedLeafHash)).toBe(true);
|
|
417
|
-
});
|
|
418
|
-
it('should return default hash when leaf does not match the prefix', async () => {
|
|
419
|
-
await smt.insert('bafyreiA');
|
|
420
|
-
const keyHash = await hashKey('bafyreiA');
|
|
421
|
-
const defaultHashes = await initDefaultHashes();
|
|
422
|
-
// Build a prefix that diverges from the leaf's keyHash at bit 0
|
|
423
|
-
const firstBit = getBit(keyHash, 0);
|
|
424
|
-
const nonMatchingPrefix = [!firstBit]; // opposite of the first bit
|
|
425
|
-
const subtreeHash = await smt.getSubtreeHash(nonMatchingPrefix);
|
|
426
|
-
expect(hashEquals(subtreeHash, defaultHashes[1])).toBe(true);
|
|
427
|
-
});
|
|
428
|
-
});
|
|
429
|
-
describe('getLeaves with leaf at shallow depth', () => {
|
|
430
|
-
it('should return the leaf CID when leaf matches the prefix', async () => {
|
|
431
|
-
await smt.insert('bafyreiA');
|
|
432
|
-
const keyHash = await hashKey('bafyreiA');
|
|
433
|
-
// Build a prefix that matches the leaf's keyHash bits
|
|
434
|
-
const matchingPrefix = [];
|
|
435
|
-
for (let i = 0; i < 4; i++) {
|
|
436
|
-
matchingPrefix.push(getBit(keyHash, i));
|
|
437
|
-
}
|
|
438
|
-
const leaves = await smt.getLeaves(matchingPrefix);
|
|
439
|
-
expect(leaves).toEqual(['bafyreiA']);
|
|
440
|
-
});
|
|
441
|
-
it('should return empty when leaf does not match the prefix', async () => {
|
|
442
|
-
await smt.insert('bafyreiA');
|
|
443
|
-
const keyHash = await hashKey('bafyreiA');
|
|
444
|
-
// Build a prefix that diverges from the leaf's keyHash
|
|
445
|
-
const firstBit = getBit(keyHash, 0);
|
|
446
|
-
const nonMatchingPrefix = [!firstBit];
|
|
447
|
-
const leaves = await smt.getLeaves(nonMatchingPrefix);
|
|
448
|
-
expect(leaves).toEqual([]);
|
|
449
|
-
});
|
|
450
|
-
});
|
|
451
|
-
describe('proof', () => {
|
|
452
|
-
it('should generate a multi-level proof with sibling hashes', async () => {
|
|
453
|
-
// Insert two elements so the tree has internal nodes
|
|
454
|
-
await smt.insert('bafyreiA');
|
|
455
|
-
await smt.insert('bafyreiB');
|
|
456
|
-
const proof = await smt.getProof('bafyreiA');
|
|
457
|
-
expect(proof.leafNode).toBeDefined();
|
|
458
|
-
expect(proof.leafNode.valueCid).toBe('bafyreiA');
|
|
459
|
-
// With two elements, there should be at least one sibling hash
|
|
460
|
-
expect(proof.siblings.length).toBeGreaterThan(0);
|
|
461
|
-
});
|
|
462
|
-
it('should return empty proof when store node is missing (corrupted store)', async () => {
|
|
463
|
-
// Test lines 503-505 of sparse-merkle-tree.ts: getNode returns undefined
|
|
464
|
-
const realStore = new SMTStoreMemory();
|
|
465
|
-
let corruptDuringProof = false;
|
|
466
|
-
const corruptStore = {
|
|
467
|
-
open: () => realStore.open(),
|
|
468
|
-
close: () => realStore.close(),
|
|
469
|
-
clear: () => realStore.clear(),
|
|
470
|
-
getRoot: () => realStore.getRoot(),
|
|
471
|
-
setRoot: (hash) => realStore.setRoot(hash),
|
|
472
|
-
putNode: (hash, node) => realStore.putNode(hash, node),
|
|
473
|
-
deleteNode: (hash) => realStore.deleteNode(hash),
|
|
474
|
-
getNode: async (hash) => {
|
|
475
|
-
if (corruptDuringProof) {
|
|
476
|
-
return undefined;
|
|
477
|
-
}
|
|
478
|
-
return realStore.getNode(hash);
|
|
479
|
-
},
|
|
480
|
-
};
|
|
481
|
-
const corruptSmt = new SparseMerkleTree(corruptStore);
|
|
482
|
-
await corruptSmt.initialize();
|
|
483
|
-
await corruptSmt.insert('bafyreiProofCorrupt');
|
|
484
|
-
// Now corrupt the store so getNode returns undefined during proof generation
|
|
485
|
-
corruptDuringProof = true;
|
|
486
|
-
const proof = await corruptSmt.getProof('bafyreiProofCorrupt');
|
|
487
|
-
// Should return a proof without a leaf node since the node is missing
|
|
488
|
-
expect(proof.leafNode).toBeUndefined();
|
|
489
|
-
expect(proof.siblings).toHaveLength(0);
|
|
490
|
-
corruptDuringProof = false;
|
|
491
|
-
await corruptSmt.close();
|
|
492
|
-
});
|
|
493
|
-
});
|
|
494
|
-
describe('diff edge cases', () => {
|
|
495
|
-
it('should detect local leaf not present in remote subtree', async () => {
|
|
496
|
-
// We need a scenario where local has a single leaf at a node while remote
|
|
497
|
-
// has an internal subtree at the same position, and the local leaf is NOT
|
|
498
|
-
// in the remote set. This happens when:
|
|
499
|
-
// - Remote has 2+ elements that share a prefix
|
|
500
|
-
// - Local has 1 element with the same prefix but a different CID
|
|
501
|
-
const storeB = new SMTStoreMemory();
|
|
502
|
-
const smtB = new SparseMerkleTree(storeB);
|
|
503
|
-
await smtB.initialize();
|
|
504
|
-
// Insert many elements into both trees so they develop deep structure
|
|
505
|
-
const sharedCids = [];
|
|
506
|
-
for (let i = 0; i < 30; i++) {
|
|
507
|
-
const cid = `bafyreig-shared-${i}`;
|
|
508
|
-
sharedCids.push(cid);
|
|
509
|
-
await smt.insert(cid);
|
|
510
|
-
await smtB.insert(cid);
|
|
511
|
-
}
|
|
512
|
-
// Add unique elements to each side
|
|
513
|
-
const localOnly = 'bafyreig-local-unique-42';
|
|
514
|
-
await smt.insert(localOnly);
|
|
515
|
-
const remoteOnly = 'bafyreig-remote-unique-99';
|
|
516
|
-
await smtB.insert(remoteOnly);
|
|
517
|
-
const diff = await smt.diff(smtB);
|
|
518
|
-
expect(diff.onlyLocal).toContain(localOnly);
|
|
519
|
-
expect(diff.onlyRemote).toContain(remoteOnly);
|
|
520
|
-
await smtB.close();
|
|
521
|
-
});
|
|
522
|
-
it('should detect both-are-leaves diff when each tree has one unique element', async () => {
|
|
523
|
-
// When each tree has exactly one element, the root IS a leaf hash.
|
|
524
|
-
// diffAtNode at depth 0 encounters two leaf nodes → hits lines 632-643.
|
|
525
|
-
const storeB = new SMTStoreMemory();
|
|
526
|
-
const smtB = new SparseMerkleTree(storeB);
|
|
527
|
-
await smtB.initialize();
|
|
528
|
-
// Each tree has exactly one unique element (different keys → line 633-636)
|
|
529
|
-
await smt.insert('bafyreiOnlyInLocal');
|
|
530
|
-
await smtB.insert('bafyreiOnlyInRemote');
|
|
531
|
-
const diff = await smt.diff(smtB);
|
|
532
|
-
expect(diff.onlyLocal).toEqual(['bafyreiOnlyInLocal']);
|
|
533
|
-
expect(diff.onlyRemote).toEqual(['bafyreiOnlyInRemote']);
|
|
534
|
-
await smtB.close();
|
|
535
|
-
});
|
|
536
|
-
it('should detect both-are-leaves diff with same key but different value', async () => {
|
|
537
|
-
// Test lines 637-640: same keyHash, different valueCid.
|
|
538
|
-
// Since keyHash = hash(valueCid), this can't happen organically.
|
|
539
|
-
// We simulate it by directly placing leaf nodes in the store.
|
|
540
|
-
const storeA = new SMTStoreMemory();
|
|
541
|
-
const smtA = new SparseMerkleTree(storeA);
|
|
542
|
-
await smtA.initialize();
|
|
543
|
-
const storeB = new SMTStoreMemory();
|
|
544
|
-
const smtB = new SparseMerkleTree(storeB);
|
|
545
|
-
await smtB.initialize();
|
|
546
|
-
// Insert the same CID into both, then manually alter one tree's leaf to have a different valueCid
|
|
547
|
-
// but the same keyHash, by manipulating the store directly.
|
|
548
|
-
const sharedKeyHash = await hashKey('bafyreiSharedKey');
|
|
549
|
-
const leafHashA = await hashLeaf(sharedKeyHash, 'valueCidA');
|
|
550
|
-
const leafNodeA = { type: 'leaf', keyHash: sharedKeyHash, valueCid: 'valueCidA' };
|
|
551
|
-
await storeA.putNode(leafHashA, leafNodeA);
|
|
552
|
-
await storeA.setRoot(leafHashA);
|
|
553
|
-
const leafHashB = await hashLeaf(sharedKeyHash, 'valueCidB');
|
|
554
|
-
const leafNodeB = { type: 'leaf', keyHash: sharedKeyHash, valueCid: 'valueCidB' };
|
|
555
|
-
await storeB.putNode(leafHashB, leafNodeB);
|
|
556
|
-
await storeB.setRoot(leafHashB);
|
|
557
|
-
const diff = await smtA.diff(smtB);
|
|
558
|
-
// Same key, different value → both appear as unique
|
|
559
|
-
expect(diff.onlyLocal).toEqual(['valueCidA']);
|
|
560
|
-
expect(diff.onlyRemote).toEqual(['valueCidB']);
|
|
561
|
-
await smtA.close();
|
|
562
|
-
await smtB.close();
|
|
563
|
-
});
|
|
564
|
-
it('should handle diff with selectively corrupted nodes via fallback', async () => {
|
|
565
|
-
// To hit lines 687-696, we need both trees to have internal root nodes
|
|
566
|
-
// (so the recursion goes through line 682-684 into children), and then
|
|
567
|
-
// at the child level, one node is missing (getNode returns undefined).
|
|
568
|
-
const realStore = new SMTStoreMemory();
|
|
569
|
-
let corruptDuringDiff = false;
|
|
570
|
-
let lookupCount = 0;
|
|
571
|
-
const corruptStore = {
|
|
572
|
-
open: () => realStore.open(),
|
|
573
|
-
close: () => realStore.close(),
|
|
574
|
-
clear: () => realStore.clear(),
|
|
575
|
-
getRoot: () => realStore.getRoot(),
|
|
576
|
-
setRoot: (hash) => realStore.setRoot(hash),
|
|
577
|
-
putNode: (hash, node) => realStore.putNode(hash, node),
|
|
578
|
-
deleteNode: (hash) => realStore.deleteNode(hash),
|
|
579
|
-
getNode: async (hash) => {
|
|
580
|
-
if (corruptDuringDiff) {
|
|
581
|
-
lookupCount++;
|
|
582
|
-
// Return the root node (first lookup) but corrupt child nodes
|
|
583
|
-
if (lookupCount > 1) {
|
|
584
|
-
return undefined;
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
return realStore.getNode(hash);
|
|
588
|
-
},
|
|
589
|
-
};
|
|
590
|
-
// Build the corrupt tree with multiple elements (so root = internal node)
|
|
591
|
-
const corruptSmt = new SparseMerkleTree(corruptStore);
|
|
592
|
-
await corruptSmt.initialize();
|
|
593
|
-
await corruptSmt.insert('bafyreiCorrupt1');
|
|
594
|
-
await corruptSmt.insert('bafyreiCorrupt2');
|
|
595
|
-
await corruptSmt.insert('bafyreiCorrupt3');
|
|
596
|
-
// Build normal tree with multiple elements (so root = internal node too)
|
|
597
|
-
const normalStore = new SMTStoreMemory();
|
|
598
|
-
const normalSmt = new SparseMerkleTree(normalStore);
|
|
599
|
-
await normalSmt.initialize();
|
|
600
|
-
await normalSmt.insert('bafyreiNormal1');
|
|
601
|
-
await normalSmt.insert('bafyreiNormal2');
|
|
602
|
-
await normalSmt.insert('bafyreiNormal3');
|
|
603
|
-
// Corrupt the store so child nodes are missing during diff
|
|
604
|
-
corruptDuringDiff = true;
|
|
605
|
-
lookupCount = 0;
|
|
606
|
-
const diff = await normalSmt.diff(corruptSmt);
|
|
607
|
-
// The diff should still complete — fallback collects leaves from the other side
|
|
608
|
-
// Normal tree's elements should be in onlyLocal
|
|
609
|
-
expect(diff.onlyLocal.sort()).toEqual(['bafyreiNormal1', 'bafyreiNormal2', 'bafyreiNormal3'].sort());
|
|
610
|
-
corruptDuringDiff = false;
|
|
611
|
-
await corruptSmt.close();
|
|
612
|
-
await normalSmt.close();
|
|
613
|
-
});
|
|
614
|
-
it('should handle diff where local node is missing but remote exists', async () => {
|
|
615
|
-
// Test lines 689-692: localNode is undefined while remoteNode exists.
|
|
616
|
-
// Both trees need non-default hashes at the SAME child position so the
|
|
617
|
-
// recursion reaches line 628 (both load nodes). We achieve this by sharing
|
|
618
|
-
// elements so both trees have populated subtrees at the same positions.
|
|
619
|
-
const realStore = new SMTStoreMemory();
|
|
620
|
-
let corruptDuringDiff = false;
|
|
621
|
-
let lookupCount = 0;
|
|
622
|
-
const corruptStore = {
|
|
623
|
-
open: () => realStore.open(),
|
|
624
|
-
close: () => realStore.close(),
|
|
625
|
-
clear: () => realStore.clear(),
|
|
626
|
-
getRoot: () => realStore.getRoot(),
|
|
627
|
-
setRoot: (hash) => realStore.setRoot(hash),
|
|
628
|
-
putNode: (hash, node) => realStore.putNode(hash, node),
|
|
629
|
-
deleteNode: (hash) => realStore.deleteNode(hash),
|
|
630
|
-
getNode: async (hash) => {
|
|
631
|
-
if (corruptDuringDiff) {
|
|
632
|
-
lookupCount++;
|
|
633
|
-
// Return the root internal node (first lookup) but corrupt children
|
|
634
|
-
if (lookupCount > 1) {
|
|
635
|
-
return undefined;
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
return realStore.getNode(hash);
|
|
639
|
-
},
|
|
640
|
-
};
|
|
641
|
-
// Build corrupt tree (local) with shared + unique elements
|
|
642
|
-
const corruptSmt = new SparseMerkleTree(corruptStore);
|
|
643
|
-
await corruptSmt.initialize();
|
|
644
|
-
// Add many shared elements so both subtrees (left/right) are populated
|
|
645
|
-
for (let i = 0; i < 20; i++) {
|
|
646
|
-
await corruptSmt.insert(`bafyreiShared-${i}`);
|
|
647
|
-
}
|
|
648
|
-
await corruptSmt.insert('bafyreiCorruptOnly');
|
|
649
|
-
// Build normal tree (remote) with the same shared elements + different unique
|
|
650
|
-
const normalStore = new SMTStoreMemory();
|
|
651
|
-
const normalSmt = new SparseMerkleTree(normalStore);
|
|
652
|
-
await normalSmt.initialize();
|
|
653
|
-
for (let i = 0; i < 20; i++) {
|
|
654
|
-
await normalSmt.insert(`bafyreiShared-${i}`);
|
|
655
|
-
}
|
|
656
|
-
await normalSmt.insert('bafyreiNormalOnly');
|
|
657
|
-
// corruptSmt is local, normalSmt is remote
|
|
658
|
-
// The roots differ (different unique elements), both are internal nodes,
|
|
659
|
-
// and both have non-default children at the same positions (shared elements).
|
|
660
|
-
// When diffAtNode recurses into children, localNode will be undefined (corrupt)
|
|
661
|
-
// while remoteNode will be a real internal/leaf node → hits lines 689-692.
|
|
662
|
-
corruptDuringDiff = true;
|
|
663
|
-
lookupCount = 0;
|
|
664
|
-
const diff = await corruptSmt.diff(normalSmt);
|
|
665
|
-
// The diff should complete. Remote leaves should appear in onlyRemote
|
|
666
|
-
// since the corrupt local can't prove they exist locally.
|
|
667
|
-
expect(diff.onlyRemote.length).toBeGreaterThan(0);
|
|
668
|
-
corruptDuringDiff = false;
|
|
669
|
-
await corruptSmt.close();
|
|
670
|
-
await normalSmt.close();
|
|
671
|
-
});
|
|
672
|
-
});
|
|
673
|
-
describe('node storage efficiency', () => {
|
|
674
|
-
it('should clean up internal nodes after deletion collapses the tree', async () => {
|
|
675
|
-
await smt.insert('bafyreiA');
|
|
676
|
-
await smt.insert('bafyreiB');
|
|
677
|
-
const sizeAfterInserts = store.size;
|
|
678
|
-
await smt.delete('bafyreiA');
|
|
679
|
-
await smt.delete('bafyreiB');
|
|
680
|
-
const sizeAfterDeletes = store.size;
|
|
681
|
-
// After deleting everything, the store should have fewer nodes than after inserts
|
|
682
|
-
expect(sizeAfterDeletes).toBeLessThan(sizeAfterInserts);
|
|
683
|
-
});
|
|
684
|
-
});
|
|
685
|
-
});
|
|
686
|
-
describe('SMT Utility Functions', () => {
|
|
687
|
-
describe('getDefaultHashes', () => {
|
|
688
|
-
it('should return default hashes after initialization', async () => {
|
|
689
|
-
await initDefaultHashes();
|
|
690
|
-
const hashes = getDefaultHashes();
|
|
691
|
-
expect(hashes).toHaveLength(SMT_DEPTH + 1);
|
|
692
|
-
expect(hashEquals(hashes[SMT_DEPTH], ZERO_HASH)).toBe(true);
|
|
693
|
-
});
|
|
694
|
-
it('should throw when called before initDefaultHashes()', async () => {
|
|
695
|
-
// Reset the module-level cache
|
|
696
|
-
resetDefaultHashesForTesting();
|
|
697
|
-
try {
|
|
698
|
-
getDefaultHashes();
|
|
699
|
-
throw new Error('Expected an error');
|
|
700
|
-
}
|
|
701
|
-
catch (e) {
|
|
702
|
-
expect(e.message).toContain('Default hashes not initialized');
|
|
703
|
-
}
|
|
704
|
-
// Re-initialize so subsequent tests aren't affected
|
|
705
|
-
await initDefaultHashes();
|
|
706
|
-
});
|
|
707
|
-
});
|
|
708
|
-
describe('hashEquals', () => {
|
|
709
|
-
it('should return false for hashes of different lengths', () => {
|
|
710
|
-
const a = new Uint8Array(32);
|
|
711
|
-
const b = new Uint8Array(16);
|
|
712
|
-
expect(hashEquals(a, b)).toBe(false);
|
|
713
|
-
});
|
|
714
|
-
});
|
|
715
|
-
describe('hashKey', () => {
|
|
716
|
-
it('should produce consistent hashes for the same input', async () => {
|
|
717
|
-
const hash1 = await hashKey('bafyreigtest');
|
|
718
|
-
const hash2 = await hashKey('bafyreigtest');
|
|
719
|
-
expect(hashEquals(hash1, hash2)).toBe(true);
|
|
720
|
-
});
|
|
721
|
-
it('should produce different hashes for different inputs', async () => {
|
|
722
|
-
const hash1 = await hashKey('bafyreigtest1');
|
|
723
|
-
const hash2 = await hashKey('bafyreigtest2');
|
|
724
|
-
expect(hashEquals(hash1, hash2)).toBe(false);
|
|
725
|
-
});
|
|
726
|
-
it('should produce 32-byte hashes', async () => {
|
|
727
|
-
const hash = await hashKey('bafyreigtest');
|
|
728
|
-
expect(hash.length).toBe(32);
|
|
729
|
-
});
|
|
730
|
-
});
|
|
731
|
-
});
|
|
732
|
-
//# sourceMappingURL=sparse-merkle-tree.spec.js.map
|