@originals/sdk 1.2.0 → 1.4.3
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/package.json +4 -1
- package/.eslintrc.json +0 -33
- package/.turbo/turbo-build.log +0 -1
- package/dist/adapters/FeeOracleMock.d.ts +0 -6
- package/dist/adapters/FeeOracleMock.js +0 -8
- package/dist/adapters/index.d.ts +0 -4
- package/dist/adapters/index.js +0 -4
- package/dist/adapters/providers/OrdHttpProvider.d.ts +0 -56
- package/dist/adapters/providers/OrdHttpProvider.js +0 -110
- package/dist/adapters/providers/OrdMockProvider.d.ts +0 -70
- package/dist/adapters/providers/OrdMockProvider.js +0 -75
- package/dist/adapters/types.d.ts +0 -71
- package/dist/adapters/types.js +0 -1
- package/dist/bitcoin/BitcoinManager.d.ts +0 -15
- package/dist/bitcoin/BitcoinManager.js +0 -262
- package/dist/bitcoin/BroadcastClient.d.ts +0 -30
- package/dist/bitcoin/BroadcastClient.js +0 -35
- package/dist/bitcoin/OrdinalsClient.d.ts +0 -21
- package/dist/bitcoin/OrdinalsClient.js +0 -105
- package/dist/bitcoin/PSBTBuilder.d.ts +0 -24
- package/dist/bitcoin/PSBTBuilder.js +0 -80
- package/dist/bitcoin/fee-calculation.d.ts +0 -14
- package/dist/bitcoin/fee-calculation.js +0 -31
- package/dist/bitcoin/providers/OrdNodeProvider.d.ts +0 -38
- package/dist/bitcoin/providers/OrdNodeProvider.js +0 -67
- package/dist/bitcoin/providers/OrdinalsProvider.d.ts +0 -33
- package/dist/bitcoin/providers/OrdinalsProvider.js +0 -50
- package/dist/bitcoin/providers/types.d.ts +0 -63
- package/dist/bitcoin/providers/types.js +0 -1
- package/dist/bitcoin/transactions/commit.d.ts +0 -89
- package/dist/bitcoin/transactions/commit.js +0 -311
- package/dist/bitcoin/transactions/index.d.ts +0 -7
- package/dist/bitcoin/transactions/index.js +0 -8
- package/dist/bitcoin/transfer.d.ts +0 -9
- package/dist/bitcoin/transfer.js +0 -26
- package/dist/bitcoin/utxo-selection.d.ts +0 -78
- package/dist/bitcoin/utxo-selection.js +0 -237
- package/dist/bitcoin/utxo.d.ts +0 -26
- package/dist/bitcoin/utxo.js +0 -78
- package/dist/contexts/credentials-v1.json +0 -195
- package/dist/contexts/credentials-v2-examples.json +0 -5
- package/dist/contexts/credentials-v2.json +0 -301
- package/dist/contexts/credentials.json +0 -195
- package/dist/contexts/data-integrity-v2.json +0 -81
- package/dist/contexts/dids.json +0 -57
- package/dist/contexts/ed255192020.json +0 -93
- package/dist/contexts/ordinals-plus.json +0 -23
- package/dist/contexts/originals.json +0 -22
- package/dist/core/OriginalsSDK.d.ts +0 -158
- package/dist/core/OriginalsSDK.js +0 -274
- package/dist/crypto/Multikey.d.ts +0 -30
- package/dist/crypto/Multikey.js +0 -149
- package/dist/crypto/Signer.d.ts +0 -21
- package/dist/crypto/Signer.js +0 -196
- package/dist/crypto/noble-init.d.ts +0 -18
- package/dist/crypto/noble-init.js +0 -106
- package/dist/did/BtcoDidResolver.d.ts +0 -57
- package/dist/did/BtcoDidResolver.js +0 -166
- package/dist/did/DIDManager.d.ts +0 -101
- package/dist/did/DIDManager.js +0 -493
- package/dist/did/Ed25519Verifier.d.ts +0 -30
- package/dist/did/Ed25519Verifier.js +0 -59
- package/dist/did/KeyManager.d.ts +0 -17
- package/dist/did/KeyManager.js +0 -207
- package/dist/did/WebVHManager.d.ts +0 -100
- package/dist/did/WebVHManager.js +0 -304
- package/dist/did/createBtcoDidDocument.d.ts +0 -10
- package/dist/did/createBtcoDidDocument.js +0 -42
- package/dist/did/providers/OrdinalsClientProviderAdapter.d.ts +0 -23
- package/dist/did/providers/OrdinalsClientProviderAdapter.js +0 -51
- package/dist/events/EventEmitter.d.ts +0 -115
- package/dist/events/EventEmitter.js +0 -198
- package/dist/events/index.d.ts +0 -7
- package/dist/events/index.js +0 -6
- package/dist/events/types.d.ts +0 -286
- package/dist/events/types.js +0 -9
- package/dist/examples/basic-usage.d.ts +0 -3
- package/dist/examples/basic-usage.js +0 -62
- package/dist/examples/run.d.ts +0 -1
- package/dist/examples/run.js +0 -4
- package/dist/index.d.ts +0 -39
- package/dist/index.js +0 -47
- package/dist/lifecycle/BatchOperations.d.ts +0 -147
- package/dist/lifecycle/BatchOperations.js +0 -251
- package/dist/lifecycle/LifecycleManager.d.ts +0 -116
- package/dist/lifecycle/LifecycleManager.js +0 -971
- package/dist/lifecycle/OriginalsAsset.d.ts +0 -164
- package/dist/lifecycle/OriginalsAsset.js +0 -380
- package/dist/lifecycle/ProvenanceQuery.d.ts +0 -126
- package/dist/lifecycle/ProvenanceQuery.js +0 -220
- package/dist/lifecycle/ResourceVersioning.d.ts +0 -73
- package/dist/lifecycle/ResourceVersioning.js +0 -127
- package/dist/migration/MigrationManager.d.ts +0 -86
- package/dist/migration/MigrationManager.js +0 -412
- package/dist/migration/audit/AuditLogger.d.ts +0 -51
- package/dist/migration/audit/AuditLogger.js +0 -156
- package/dist/migration/checkpoint/CheckpointManager.d.ts +0 -31
- package/dist/migration/checkpoint/CheckpointManager.js +0 -96
- package/dist/migration/checkpoint/CheckpointStorage.d.ts +0 -26
- package/dist/migration/checkpoint/CheckpointStorage.js +0 -89
- package/dist/migration/index.d.ts +0 -22
- package/dist/migration/index.js +0 -27
- package/dist/migration/operations/BaseMigration.d.ts +0 -48
- package/dist/migration/operations/BaseMigration.js +0 -83
- package/dist/migration/operations/PeerToBtcoMigration.d.ts +0 -25
- package/dist/migration/operations/PeerToBtcoMigration.js +0 -67
- package/dist/migration/operations/PeerToWebvhMigration.d.ts +0 -19
- package/dist/migration/operations/PeerToWebvhMigration.js +0 -46
- package/dist/migration/operations/WebvhToBtcoMigration.d.ts +0 -25
- package/dist/migration/operations/WebvhToBtcoMigration.js +0 -67
- package/dist/migration/rollback/RollbackManager.d.ts +0 -29
- package/dist/migration/rollback/RollbackManager.js +0 -146
- package/dist/migration/state/StateMachine.d.ts +0 -25
- package/dist/migration/state/StateMachine.js +0 -76
- package/dist/migration/state/StateTracker.d.ts +0 -36
- package/dist/migration/state/StateTracker.js +0 -123
- package/dist/migration/types.d.ts +0 -306
- package/dist/migration/types.js +0 -33
- package/dist/migration/validation/BitcoinValidator.d.ts +0 -13
- package/dist/migration/validation/BitcoinValidator.js +0 -83
- package/dist/migration/validation/CredentialValidator.d.ts +0 -13
- package/dist/migration/validation/CredentialValidator.js +0 -46
- package/dist/migration/validation/DIDCompatibilityValidator.d.ts +0 -16
- package/dist/migration/validation/DIDCompatibilityValidator.js +0 -127
- package/dist/migration/validation/LifecycleValidator.d.ts +0 -10
- package/dist/migration/validation/LifecycleValidator.js +0 -52
- package/dist/migration/validation/StorageValidator.d.ts +0 -10
- package/dist/migration/validation/StorageValidator.js +0 -65
- package/dist/migration/validation/ValidationPipeline.d.ts +0 -29
- package/dist/migration/validation/ValidationPipeline.js +0 -180
- package/dist/storage/LocalStorageAdapter.d.ts +0 -11
- package/dist/storage/LocalStorageAdapter.js +0 -53
- package/dist/storage/MemoryStorageAdapter.d.ts +0 -6
- package/dist/storage/MemoryStorageAdapter.js +0 -21
- package/dist/storage/StorageAdapter.d.ts +0 -16
- package/dist/storage/StorageAdapter.js +0 -1
- package/dist/storage/index.d.ts +0 -2
- package/dist/storage/index.js +0 -2
- package/dist/types/bitcoin.d.ts +0 -84
- package/dist/types/bitcoin.js +0 -1
- package/dist/types/common.d.ts +0 -82
- package/dist/types/common.js +0 -1
- package/dist/types/credentials.d.ts +0 -75
- package/dist/types/credentials.js +0 -1
- package/dist/types/did.d.ts +0 -26
- package/dist/types/did.js +0 -1
- package/dist/types/index.d.ts +0 -5
- package/dist/types/index.js +0 -5
- package/dist/types/network.d.ts +0 -78
- package/dist/types/network.js +0 -145
- package/dist/utils/EventLogger.d.ts +0 -71
- package/dist/utils/EventLogger.js +0 -232
- package/dist/utils/Logger.d.ts +0 -106
- package/dist/utils/Logger.js +0 -257
- package/dist/utils/MetricsCollector.d.ts +0 -110
- package/dist/utils/MetricsCollector.js +0 -264
- package/dist/utils/bitcoin-address.d.ts +0 -38
- package/dist/utils/bitcoin-address.js +0 -113
- package/dist/utils/cbor.d.ts +0 -2
- package/dist/utils/cbor.js +0 -9
- package/dist/utils/encoding.d.ts +0 -37
- package/dist/utils/encoding.js +0 -120
- package/dist/utils/hash.d.ts +0 -1
- package/dist/utils/hash.js +0 -5
- package/dist/utils/retry.d.ts +0 -10
- package/dist/utils/retry.js +0 -35
- package/dist/utils/satoshi-validation.d.ts +0 -60
- package/dist/utils/satoshi-validation.js +0 -156
- package/dist/utils/serialization.d.ts +0 -14
- package/dist/utils/serialization.js +0 -76
- package/dist/utils/telemetry.d.ts +0 -17
- package/dist/utils/telemetry.js +0 -24
- package/dist/utils/validation.d.ts +0 -5
- package/dist/utils/validation.js +0 -98
- package/dist/vc/CredentialManager.d.ts +0 -22
- package/dist/vc/CredentialManager.js +0 -227
- package/dist/vc/Issuer.d.ts +0 -27
- package/dist/vc/Issuer.js +0 -70
- package/dist/vc/Verifier.d.ts +0 -16
- package/dist/vc/Verifier.js +0 -50
- package/dist/vc/cryptosuites/bbs.d.ts +0 -44
- package/dist/vc/cryptosuites/bbs.js +0 -213
- package/dist/vc/cryptosuites/bbsSimple.d.ts +0 -9
- package/dist/vc/cryptosuites/bbsSimple.js +0 -12
- package/dist/vc/cryptosuites/eddsa.d.ts +0 -30
- package/dist/vc/cryptosuites/eddsa.js +0 -81
- package/dist/vc/documentLoader.d.ts +0 -16
- package/dist/vc/documentLoader.js +0 -59
- package/dist/vc/proofs/data-integrity.d.ts +0 -21
- package/dist/vc/proofs/data-integrity.js +0 -15
- package/dist/vc/utils/jsonld.d.ts +0 -2
- package/dist/vc/utils/jsonld.js +0 -15
- package/src/adapters/FeeOracleMock.ts +0 -9
- package/src/adapters/index.ts +0 -5
- package/src/adapters/providers/OrdHttpProvider.ts +0 -126
- package/src/adapters/providers/OrdMockProvider.ts +0 -101
- package/src/adapters/types.ts +0 -66
- package/src/bitcoin/BitcoinManager.ts +0 -330
- package/src/bitcoin/BroadcastClient.ts +0 -54
- package/src/bitcoin/OrdinalsClient.ts +0 -119
- package/src/bitcoin/PSBTBuilder.ts +0 -106
- package/src/bitcoin/fee-calculation.ts +0 -38
- package/src/bitcoin/providers/OrdNodeProvider.ts +0 -92
- package/src/bitcoin/providers/OrdinalsProvider.ts +0 -56
- package/src/bitcoin/providers/types.ts +0 -59
- package/src/bitcoin/transactions/commit.ts +0 -465
- package/src/bitcoin/transactions/index.ts +0 -13
- package/src/bitcoin/transfer.ts +0 -43
- package/src/bitcoin/utxo-selection.ts +0 -322
- package/src/bitcoin/utxo.ts +0 -113
- package/src/contexts/credentials-v1.json +0 -237
- package/src/contexts/credentials-v2-examples.json +0 -5
- package/src/contexts/credentials-v2.json +0 -340
- package/src/contexts/credentials.json +0 -237
- package/src/contexts/data-integrity-v2.json +0 -81
- package/src/contexts/dids.json +0 -58
- package/src/contexts/ed255192020.json +0 -93
- package/src/contexts/ordinals-plus.json +0 -23
- package/src/contexts/originals.json +0 -22
- package/src/core/OriginalsSDK.ts +0 -416
- package/src/crypto/Multikey.ts +0 -194
- package/src/crypto/Signer.ts +0 -254
- package/src/crypto/noble-init.ts +0 -121
- package/src/did/BtcoDidResolver.ts +0 -227
- package/src/did/DIDManager.ts +0 -694
- package/src/did/Ed25519Verifier.ts +0 -68
- package/src/did/KeyManager.ts +0 -236
- package/src/did/WebVHManager.ts +0 -489
- package/src/did/createBtcoDidDocument.ts +0 -59
- package/src/did/providers/OrdinalsClientProviderAdapter.ts +0 -68
- package/src/events/EventEmitter.ts +0 -222
- package/src/events/index.ts +0 -19
- package/src/events/types.ts +0 -331
- package/src/examples/basic-usage.ts +0 -78
- package/src/examples/run.ts +0 -5
- package/src/index.ts +0 -84
- package/src/lifecycle/BatchOperations.ts +0 -373
- package/src/lifecycle/LifecycleManager.ts +0 -1218
- package/src/lifecycle/OriginalsAsset.ts +0 -524
- package/src/lifecycle/ProvenanceQuery.ts +0 -280
- package/src/lifecycle/ResourceVersioning.ts +0 -163
- package/src/migration/MigrationManager.ts +0 -527
- package/src/migration/audit/AuditLogger.ts +0 -176
- package/src/migration/checkpoint/CheckpointManager.ts +0 -112
- package/src/migration/checkpoint/CheckpointStorage.ts +0 -101
- package/src/migration/index.ts +0 -33
- package/src/migration/operations/BaseMigration.ts +0 -126
- package/src/migration/operations/PeerToBtcoMigration.ts +0 -105
- package/src/migration/operations/PeerToWebvhMigration.ts +0 -62
- package/src/migration/operations/WebvhToBtcoMigration.ts +0 -105
- package/src/migration/rollback/RollbackManager.ts +0 -170
- package/src/migration/state/StateMachine.ts +0 -92
- package/src/migration/state/StateTracker.ts +0 -156
- package/src/migration/types.ts +0 -344
- package/src/migration/validation/BitcoinValidator.ts +0 -107
- package/src/migration/validation/CredentialValidator.ts +0 -62
- package/src/migration/validation/DIDCompatibilityValidator.ts +0 -151
- package/src/migration/validation/LifecycleValidator.ts +0 -64
- package/src/migration/validation/StorageValidator.ts +0 -79
- package/src/migration/validation/ValidationPipeline.ts +0 -213
- package/src/storage/LocalStorageAdapter.ts +0 -61
- package/src/storage/MemoryStorageAdapter.ts +0 -29
- package/src/storage/StorageAdapter.ts +0 -25
- package/src/storage/index.ts +0 -3
- package/src/types/bitcoin.ts +0 -98
- package/src/types/common.ts +0 -92
- package/src/types/credentials.ts +0 -88
- package/src/types/did.ts +0 -31
- package/src/types/external-shims.d.ts +0 -53
- package/src/types/index.ts +0 -7
- package/src/types/network.ts +0 -175
- package/src/utils/EventLogger.ts +0 -298
- package/src/utils/Logger.ts +0 -322
- package/src/utils/MetricsCollector.ts +0 -358
- package/src/utils/bitcoin-address.ts +0 -130
- package/src/utils/cbor.ts +0 -12
- package/src/utils/encoding.ts +0 -127
- package/src/utils/hash.ts +0 -6
- package/src/utils/retry.ts +0 -46
- package/src/utils/satoshi-validation.ts +0 -196
- package/src/utils/serialization.ts +0 -96
- package/src/utils/telemetry.ts +0 -40
- package/src/utils/validation.ts +0 -119
- package/src/vc/CredentialManager.ts +0 -273
- package/src/vc/Issuer.ts +0 -100
- package/src/vc/Verifier.ts +0 -47
- package/src/vc/cryptosuites/bbs.ts +0 -253
- package/src/vc/cryptosuites/bbsSimple.ts +0 -21
- package/src/vc/cryptosuites/eddsa.ts +0 -99
- package/src/vc/documentLoader.ts +0 -67
- package/src/vc/proofs/data-integrity.ts +0 -33
- package/src/vc/utils/jsonld.ts +0 -18
- package/test/logs/did_webvh_QmNTn9Kkp8dQ75WrF9xqJ2kuDp9QhKc3aPiERRMj8XoTBN_example_com.jsonl +0 -1
- package/test/logs/did_webvh_QmNu4MNr8Lr5txx5gYNhuhZDchXsZEu3hJXKYuphpWTPDp_example_com_users_etc_passwd.jsonl +0 -1
- package/test/logs/did_webvh_QmR9MrGZACzjKETA8SBRNCKG11HxU85c4bVR2qN5eDCfsD_example_com.jsonl +0 -1
- package/test/logs/did_webvh_QmUc5suaqRM2P4nrXxZwqYMfqzhdMqjuL7oJaJbEpCQVCd_example_com_users_etc_passwd.jsonl +0 -1
- package/test/logs/did_webvh_QmUkiB2RCV2VZ1RTXsCebWN25Eiy9TLvpzDWAJNjhgvB4X_example_com_etc_passwd.jsonl +0 -1
- package/test/logs/did_webvh_QmUoRTe8UMwpAQXZSAW7pjAgZK1tq2X3C6Kfxq3UXGcaGy_example_com_secret.jsonl +0 -1
- package/test/logs/did_webvh_QmWWot3chx1t6KwTmcE5i2FeDZ5JMkQw3qXycsKDVmJ9Be_example_com_users_alice.jsonl +0 -1
- package/test/logs/did_webvh_QmWvVgALL5kjZdpgR7KZay7J8UiiUr834kkRmWeFAxjAuC_example_com_users_etc_passwd.jsonl +0 -1
- package/test/logs/did_webvh_QmWwaRQHUZAFcKihFC6xR6tRTTrQhHPTku6azf1egWbpy1_example_com_users_alice.jsonl +0 -1
- package/test/logs/did_webvh_QmXJLtkz23r7AozbtXsZMKWnVU6rd38CkVtjdWuATU3Yp6_example_com_users_alice123_profile.jsonl +0 -1
- package/test/logs/did_webvh_QmYsce448po14oDE1wXbyaP6wY9HQgHSKLwdezn1k577SF_example_com_my_org_user_name_test_123.jsonl +0 -1
- package/test/logs/did_webvh_QmZBeNzzqajxdfwcDUPZ4P8C5YSXyRztrAwmPiKuKUxmAK_example_com.jsonl +0 -1
- package/test/logs/did_webvh_QmZhJsqxizwVbRtqCUkmE6XQunSxtxMt3gbTYadVBNAaEq_example_com.jsonl +0 -1
- package/test/logs/did_webvh_QmZk7NHU2D57RzzbMq4tWW9gBa9AqtVTWfiRM6RFdwGVj2_example_com.jsonl +0 -1
- package/test/logs/did_webvh_QmZshSXp9w8ovH62zGGBS1b5pGGPsuYiu1VQ935sga2hWF_example_com_level1_level2.jsonl +0 -1
- package/test/logs/did_webvh_QmbWAmw7HQL7vKJyCsctZihXf1rmT4sGvggKCPKWcUWjw1_example_com.jsonl +0 -1
- package/test/logs/did_webvh_QmbdLUMbYs3juR39TLB6hhrFWLcNg45ybUzeBJCS1MhCh1_example_com_C_Windows_System32.jsonl +0 -1
- package/test/logs/did_webvh_QmcaQ1Ma4gkSbae85aCm8Mv4rvdT2Sb2RR3JzYwrm5XBq8_example_com_etc_passwd.jsonl +0 -1
- package/test/logs/did_webvh_QmcbA7WQhsBqZSoDpKJHjV8Q5o53h8vmgJhQfo6rqTY5ho_example_com.jsonl +0 -1
- package/test/logs/did_webvh_Qmdy8uWr2gkUJrXsThynAug3DASTWwb3onEj89LKmMGZYB_example_com.jsonl +0 -1
- package/tests/__mocks__/bbs-signatures.js +0 -17
- package/tests/__mocks__/mf-base58.js +0 -24
- package/tests/e2e/README.md +0 -97
- package/tests/e2e/example.spec.ts +0 -78
- package/tests/fixtures/did-documents.ts +0 -247
- package/tests/index.test.ts +0 -21
- package/tests/integration/BatchOperations.test.ts +0 -531
- package/tests/integration/CompleteLifecycle.e2e.test.ts +0 -735
- package/tests/integration/CredentialManager.test.ts +0 -42
- package/tests/integration/DIDManager.test.ts +0 -41
- package/tests/integration/DidPeerToWebVhFlow.test.ts +0 -351
- package/tests/integration/Events.test.ts +0 -435
- package/tests/integration/Lifecycle.transfer.btco.integration.test.ts +0 -25
- package/tests/integration/LifecycleManager.test.ts +0 -21
- package/tests/integration/MultikeyFlow.test.ts +0 -52
- package/tests/integration/TelemetryIntegration.test.ts +0 -395
- package/tests/integration/WebVhPublish.test.ts +0 -48
- package/tests/integration/migration/peer-to-webvh.test.ts +0 -172
- package/tests/manual/test-commit-creation.ts +0 -323
- package/tests/mocks/MockKeyStore.ts +0 -38
- package/tests/mocks/adapters/MemoryStorageAdapter.ts +0 -24
- package/tests/mocks/adapters/MockFeeOracle.ts +0 -11
- package/tests/mocks/adapters/MockOrdinalsProvider.ts +0 -76
- package/tests/mocks/adapters/OrdMockProvider.test.ts +0 -176
- package/tests/mocks/adapters/index.ts +0 -6
- package/tests/performance/BatchOperations.perf.test.ts +0 -403
- package/tests/performance/logging.perf.test.ts +0 -336
- package/tests/sdk.test.ts +0 -43
- package/tests/security/bitcoin-penetration-tests.test.ts +0 -622
- package/tests/setup.bun.ts +0 -69
- package/tests/setup.jest.ts +0 -23
- package/tests/stress/batch-operations-stress.test.ts +0 -571
- package/tests/unit/adapters/FeeOracleMock.test.ts +0 -40
- package/tests/unit/bitcoin/BitcoinManager.test.ts +0 -293
- package/tests/unit/bitcoin/BroadcastClient.test.ts +0 -52
- package/tests/unit/bitcoin/OrdNodeProvider.test.ts +0 -53
- package/tests/unit/bitcoin/OrdinalsClient.test.ts +0 -381
- package/tests/unit/bitcoin/OrdinalsClientProvider.test.ts +0 -102
- package/tests/unit/bitcoin/PSBTBuilder.test.ts +0 -84
- package/tests/unit/bitcoin/fee-calculation.test.ts +0 -261
- package/tests/unit/bitcoin/transactions/commit.test.ts +0 -649
- package/tests/unit/bitcoin/transfer.test.ts +0 -31
- package/tests/unit/bitcoin/utxo-selection-new.test.ts +0 -502
- package/tests/unit/bitcoin/utxo.more.test.ts +0 -39
- package/tests/unit/bitcoin/utxo.selection.test.ts +0 -38
- package/tests/unit/core/OriginalsSDK.test.ts +0 -152
- package/tests/unit/crypto/Multikey.test.ts +0 -206
- package/tests/unit/crypto/Signer.test.ts +0 -408
- package/tests/unit/did/BtcoDidResolver.test.ts +0 -611
- package/tests/unit/did/DIDManager.more.test.ts +0 -43
- package/tests/unit/did/DIDManager.test.ts +0 -185
- package/tests/unit/did/Ed25519Verifier.test.ts +0 -160
- package/tests/unit/did/KeyManager.test.ts +0 -452
- package/tests/unit/did/OrdinalsClientProviderAdapter.test.ts +0 -45
- package/tests/unit/did/WebVHManager.test.ts +0 -435
- package/tests/unit/did/createBtcoDidDocument.test.ts +0 -67
- package/tests/unit/did/providers/OrdinalsClientProviderAdapter.test.ts +0 -159
- package/tests/unit/events/EventEmitter.test.ts +0 -407
- package/tests/unit/lifecycle/BatchOperations.test.ts +0 -527
- package/tests/unit/lifecycle/LifecycleManager.keymanagement.test.ts +0 -312
- package/tests/unit/lifecycle/LifecycleManager.prov.test.ts +0 -18
- package/tests/unit/lifecycle/LifecycleManager.test.ts +0 -213
- package/tests/unit/lifecycle/LifecycleManager.transfer.unit.test.ts +0 -30
- package/tests/unit/lifecycle/OriginalsAsset.test.ts +0 -176
- package/tests/unit/lifecycle/ProvenanceQuery.test.ts +0 -577
- package/tests/unit/lifecycle/ResourceVersioning.test.ts +0 -651
- package/tests/unit/storage/MemoryStorageAdapter.test.ts +0 -93
- package/tests/unit/types/network.test.ts +0 -255
- package/tests/unit/utils/EventIntegration.test.ts +0 -384
- package/tests/unit/utils/Logger.test.ts +0 -473
- package/tests/unit/utils/MetricsCollector.test.ts +0 -358
- package/tests/unit/utils/bitcoin-address.test.ts +0 -250
- package/tests/unit/utils/cbor.test.ts +0 -35
- package/tests/unit/utils/encoding.test.ts +0 -318
- package/tests/unit/utils/hash.test.ts +0 -12
- package/tests/unit/utils/retry.test.ts +0 -100
- package/tests/unit/utils/satoshi-validation.test.ts +0 -354
- package/tests/unit/utils/serialization.test.ts +0 -124
- package/tests/unit/utils/telemetry.test.ts +0 -52
- package/tests/unit/utils/validation.test.ts +0 -141
- package/tests/unit/vc/CredentialManager.test.ts +0 -487
- package/tests/unit/vc/Issuer.test.ts +0 -107
- package/tests/unit/vc/Verifier.test.ts +0 -525
- package/tests/unit/vc/bbs.test.ts +0 -282
- package/tests/unit/vc/cryptosuites/eddsa.test.ts +0 -398
- package/tests/unit/vc/documentLoader.test.ts +0 -121
- package/tests/unit/vc/proofs/data-integrity.test.ts +0 -24
- package/tsconfig.json +0 -31
- package/tsconfig.test.json +0 -15
|
@@ -1,1218 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
OriginalsConfig,
|
|
3
|
-
AssetResource,
|
|
4
|
-
BitcoinTransaction,
|
|
5
|
-
KeyStore,
|
|
6
|
-
ExternalSigner,
|
|
7
|
-
VerifiableCredential
|
|
8
|
-
} from '../types';
|
|
9
|
-
import { BitcoinManager } from '../bitcoin/BitcoinManager';
|
|
10
|
-
import { DIDManager } from '../did/DIDManager';
|
|
11
|
-
import { CredentialManager } from '../vc/CredentialManager';
|
|
12
|
-
import { OriginalsAsset } from './OriginalsAsset';
|
|
13
|
-
import { MemoryStorageAdapter } from '../storage/MemoryStorageAdapter';
|
|
14
|
-
import { encodeBase64UrlMultibase, hexToBytes } from '../utils/encoding';
|
|
15
|
-
import { validateBitcoinAddress } from '../utils/bitcoin-address';
|
|
16
|
-
import { multikey } from '../crypto/Multikey';
|
|
17
|
-
import { EventEmitter } from '../events/EventEmitter';
|
|
18
|
-
import type { EventHandler, EventTypeMap } from '../events/types';
|
|
19
|
-
import { Logger } from '../utils/Logger';
|
|
20
|
-
import { MetricsCollector } from '../utils/MetricsCollector';
|
|
21
|
-
import {
|
|
22
|
-
BatchOperationExecutor,
|
|
23
|
-
BatchValidator,
|
|
24
|
-
BatchError,
|
|
25
|
-
type BatchResult,
|
|
26
|
-
type BatchOperationOptions,
|
|
27
|
-
type BatchInscriptionOptions,
|
|
28
|
-
} from './BatchOperations';
|
|
29
|
-
|
|
30
|
-
export class LifecycleManager {
|
|
31
|
-
private eventEmitter: EventEmitter;
|
|
32
|
-
private batchExecutor: BatchOperationExecutor;
|
|
33
|
-
private batchValidator: BatchValidator;
|
|
34
|
-
private logger: Logger;
|
|
35
|
-
private metrics: MetricsCollector;
|
|
36
|
-
|
|
37
|
-
constructor(
|
|
38
|
-
private config: OriginalsConfig,
|
|
39
|
-
private didManager: DIDManager,
|
|
40
|
-
private credentialManager: CredentialManager,
|
|
41
|
-
private deps?: { bitcoinManager?: BitcoinManager },
|
|
42
|
-
private keyStore?: KeyStore
|
|
43
|
-
) {
|
|
44
|
-
this.eventEmitter = new EventEmitter();
|
|
45
|
-
this.batchExecutor = new BatchOperationExecutor();
|
|
46
|
-
this.batchValidator = new BatchValidator();
|
|
47
|
-
this.logger = new Logger('LifecycleManager', config);
|
|
48
|
-
this.metrics = new MetricsCollector();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Subscribe to a lifecycle event
|
|
53
|
-
* @param eventType - The type of event to subscribe to
|
|
54
|
-
* @param handler - The handler function to call when the event is emitted
|
|
55
|
-
* @returns A function to unsubscribe from the event
|
|
56
|
-
*/
|
|
57
|
-
on<K extends keyof EventTypeMap>(eventType: K, handler: EventHandler<EventTypeMap[K]>): () => void {
|
|
58
|
-
return this.eventEmitter.on(eventType, handler);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Subscribe to a lifecycle event once
|
|
63
|
-
* @param eventType - The type of event to subscribe to
|
|
64
|
-
* @param handler - The handler function to call when the event is emitted (will only fire once)
|
|
65
|
-
* @returns A function to unsubscribe from the event
|
|
66
|
-
*/
|
|
67
|
-
once<K extends keyof EventTypeMap>(eventType: K, handler: EventHandler<EventTypeMap[K]>): () => void {
|
|
68
|
-
return this.eventEmitter.once(eventType, handler);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Unsubscribe from a lifecycle event
|
|
73
|
-
* @param eventType - The type of event to unsubscribe from
|
|
74
|
-
* @param handler - The handler function to remove
|
|
75
|
-
*/
|
|
76
|
-
off<K extends keyof EventTypeMap>(eventType: K, handler: EventHandler<EventTypeMap[K]>): void {
|
|
77
|
-
this.eventEmitter.off(eventType, handler);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async registerKey(verificationMethodId: string, privateKey: string): Promise<void> {
|
|
81
|
-
if (!this.keyStore) {
|
|
82
|
-
throw new Error('KeyStore not configured. Provide keyStore to LifecycleManager constructor.');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Validate verification method ID format
|
|
86
|
-
if (!verificationMethodId || typeof verificationMethodId !== 'string') {
|
|
87
|
-
throw new Error('Invalid verificationMethodId: must be a non-empty string');
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Validate private key format (should be multibase encoded)
|
|
91
|
-
if (!privateKey || typeof privateKey !== 'string') {
|
|
92
|
-
throw new Error('Invalid privateKey: must be a non-empty string');
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Validate that it's a valid multibase-encoded private key
|
|
96
|
-
try {
|
|
97
|
-
multikey.decodePrivateKey(privateKey);
|
|
98
|
-
} catch (err) {
|
|
99
|
-
throw new Error('Invalid privateKey format: must be a valid multibase-encoded private key');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
await this.keyStore.setPrivateKey(verificationMethodId, privateKey);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async createAsset(resources: AssetResource[]): Promise<OriginalsAsset> {
|
|
106
|
-
const stopTimer = this.logger.startTimer('createAsset');
|
|
107
|
-
this.logger.info('Creating asset', { resourceCount: resources.length });
|
|
108
|
-
|
|
109
|
-
try {
|
|
110
|
-
// Input validation
|
|
111
|
-
if (!Array.isArray(resources)) {
|
|
112
|
-
throw new Error('Resources must be an array');
|
|
113
|
-
}
|
|
114
|
-
if (resources.length === 0) {
|
|
115
|
-
throw new Error('At least one resource is required');
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Validate each resource
|
|
119
|
-
for (const resource of resources) {
|
|
120
|
-
if (!resource || typeof resource !== 'object') {
|
|
121
|
-
throw new Error('Invalid resource: must be an object');
|
|
122
|
-
}
|
|
123
|
-
if (!resource.id || typeof resource.id !== 'string') {
|
|
124
|
-
throw new Error('Invalid resource: missing or invalid id');
|
|
125
|
-
}
|
|
126
|
-
if (!resource.type || typeof resource.type !== 'string') {
|
|
127
|
-
throw new Error('Invalid resource: missing or invalid type');
|
|
128
|
-
}
|
|
129
|
-
if (!resource.contentType || typeof resource.contentType !== 'string') {
|
|
130
|
-
throw new Error('Invalid resource: missing or invalid contentType');
|
|
131
|
-
}
|
|
132
|
-
if (!resource.hash || typeof resource.hash !== 'string' || !/^[0-9a-fA-F]+$/.test(resource.hash)) {
|
|
133
|
-
throw new Error('Invalid resource: missing or invalid hash (must be hex string)');
|
|
134
|
-
}
|
|
135
|
-
// Validate contentType is a valid MIME type
|
|
136
|
-
if (!/^[a-zA-Z0-9][a-zA-Z0-9!#$&^_.+-]{0,126}\/[a-zA-Z0-9][a-zA-Z0-9!#$&^_.+-]{0,126}$/.test(resource.contentType)) {
|
|
137
|
-
throw new Error(`Invalid resource: invalid contentType MIME format: ${resource.contentType}`);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Create a proper DID:peer document with verification methods
|
|
142
|
-
// If keyStore is provided, request the key pair to be returned
|
|
143
|
-
if (this.keyStore) {
|
|
144
|
-
const result = await this.didManager.createDIDPeer(resources, true);
|
|
145
|
-
const didDoc = result.didDocument;
|
|
146
|
-
const keyPair = result.keyPair;
|
|
147
|
-
|
|
148
|
-
// Register the private key in the keyStore
|
|
149
|
-
if (didDoc.verificationMethod && didDoc.verificationMethod.length > 0) {
|
|
150
|
-
let verificationMethodId = didDoc.verificationMethod[0].id;
|
|
151
|
-
|
|
152
|
-
// Ensure VM ID is absolute (not just a fragment like #key-0)
|
|
153
|
-
if (verificationMethodId.startsWith('#')) {
|
|
154
|
-
verificationMethodId = `${didDoc.id}${verificationMethodId}`;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
await this.keyStore.setPrivateKey(verificationMethodId, keyPair.privateKey);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const asset = new OriginalsAsset(resources, didDoc, []);
|
|
161
|
-
|
|
162
|
-
// Defer asset:created event emission to next microtask so callers can subscribe first
|
|
163
|
-
queueMicrotask(() => {
|
|
164
|
-
const event = {
|
|
165
|
-
type: 'asset:created' as const,
|
|
166
|
-
timestamp: new Date().toISOString(),
|
|
167
|
-
asset: {
|
|
168
|
-
id: asset.id,
|
|
169
|
-
layer: asset.currentLayer,
|
|
170
|
-
resourceCount: resources.length,
|
|
171
|
-
createdAt: asset.getProvenance().createdAt
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
// Emit from both LifecycleManager and asset emitters
|
|
176
|
-
this.eventEmitter.emit(event);
|
|
177
|
-
(asset as any).eventEmitter.emit(event);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
stopTimer();
|
|
181
|
-
this.logger.info('Asset created successfully', { assetId: asset.id });
|
|
182
|
-
this.metrics.recordAssetCreated();
|
|
183
|
-
|
|
184
|
-
return asset;
|
|
185
|
-
} else {
|
|
186
|
-
// No keyStore, just create the DID document
|
|
187
|
-
const didDoc = await this.didManager.createDIDPeer(resources);
|
|
188
|
-
const asset = new OriginalsAsset(resources, didDoc, []);
|
|
189
|
-
|
|
190
|
-
// Defer asset:created event emission to next microtask so callers can subscribe first
|
|
191
|
-
queueMicrotask(() => {
|
|
192
|
-
const event = {
|
|
193
|
-
type: 'asset:created' as const,
|
|
194
|
-
timestamp: new Date().toISOString(),
|
|
195
|
-
asset: {
|
|
196
|
-
id: asset.id,
|
|
197
|
-
layer: asset.currentLayer,
|
|
198
|
-
resourceCount: resources.length,
|
|
199
|
-
createdAt: asset.getProvenance().createdAt
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
// Emit from both LifecycleManager and asset emitters
|
|
204
|
-
this.eventEmitter.emit(event);
|
|
205
|
-
(asset as any).eventEmitter.emit(event);
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
stopTimer();
|
|
209
|
-
this.logger.info('Asset created successfully', { assetId: asset.id });
|
|
210
|
-
this.metrics.recordAssetCreated();
|
|
211
|
-
|
|
212
|
-
return asset;
|
|
213
|
-
}
|
|
214
|
-
} catch (error) {
|
|
215
|
-
stopTimer();
|
|
216
|
-
this.logger.error('Asset creation failed', error as Error, { resourceCount: resources.length });
|
|
217
|
-
this.metrics.recordError('ASSET_CREATION_FAILED', 'createAsset');
|
|
218
|
-
throw error;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
async publishToWeb(
|
|
223
|
-
asset: OriginalsAsset,
|
|
224
|
-
publisherDidOrSigner: string | ExternalSigner
|
|
225
|
-
): Promise<OriginalsAsset> {
|
|
226
|
-
const stopTimer = this.logger.startTimer('publishToWeb');
|
|
227
|
-
|
|
228
|
-
try {
|
|
229
|
-
if (asset.currentLayer !== 'did:peer') {
|
|
230
|
-
throw new Error('Asset must be in did:peer layer to publish to web');
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
const { publisherDid, signer } = await this.extractPublisherInfo(publisherDidOrSigner);
|
|
234
|
-
const { domain, userPath } = this.parseWebVHDid(publisherDid);
|
|
235
|
-
|
|
236
|
-
this.logger.info('Publishing asset to web', { assetId: asset.id, publisherDid });
|
|
237
|
-
|
|
238
|
-
// Publish resources to storage
|
|
239
|
-
await this.publishResources(asset, publisherDid, domain, userPath);
|
|
240
|
-
|
|
241
|
-
// Store the original did:peer ID before migration
|
|
242
|
-
const originalPeerDid = asset.id;
|
|
243
|
-
|
|
244
|
-
// Migrate asset to did:webvh layer
|
|
245
|
-
await asset.migrate('did:webvh');
|
|
246
|
-
asset.bindings = { ...(asset as any).bindings, 'did:peer': originalPeerDid, 'did:webvh': publisherDid };
|
|
247
|
-
|
|
248
|
-
// Issue publication credential (best-effort)
|
|
249
|
-
await this.issuePublicationCredential(asset, publisherDid, signer);
|
|
250
|
-
|
|
251
|
-
stopTimer();
|
|
252
|
-
this.logger.info('Asset published to web successfully', {
|
|
253
|
-
assetId: asset.id,
|
|
254
|
-
publisherDid,
|
|
255
|
-
resourceCount: asset.resources.length
|
|
256
|
-
});
|
|
257
|
-
this.metrics.recordMigration('did:peer', 'did:webvh');
|
|
258
|
-
|
|
259
|
-
return asset;
|
|
260
|
-
} catch (error) {
|
|
261
|
-
stopTimer();
|
|
262
|
-
this.logger.error('Publish to web failed', error as Error, { assetId: asset.id });
|
|
263
|
-
this.metrics.recordError('PUBLISH_FAILED', 'publishToWeb');
|
|
264
|
-
throw error;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
private async extractPublisherInfo(publisherDidOrSigner: string | ExternalSigner): Promise<{
|
|
269
|
-
publisherDid: string;
|
|
270
|
-
signer?: ExternalSigner;
|
|
271
|
-
}> {
|
|
272
|
-
if (typeof publisherDidOrSigner === 'string') {
|
|
273
|
-
// If it's already a did:webvh DID, use it as-is
|
|
274
|
-
if (publisherDidOrSigner.startsWith('did:webvh:')) {
|
|
275
|
-
return { publisherDid: publisherDidOrSigner };
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Otherwise, treat it as a domain and construct a did:webvh DID
|
|
279
|
-
// Format: did:webvh:domain:user (use 'user' as default user path)
|
|
280
|
-
// Encode the domain to handle ports (e.g., localhost:5000 -> localhost%3A5000)
|
|
281
|
-
const domain = publisherDidOrSigner;
|
|
282
|
-
const encodedDomain = encodeURIComponent(domain);
|
|
283
|
-
const publisherDid = `did:webvh:${encodedDomain}:user`;
|
|
284
|
-
return { publisherDid };
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
const signer = publisherDidOrSigner;
|
|
288
|
-
const vmId = await signer.getVerificationMethodId();
|
|
289
|
-
const publisherDid = vmId.includes('#') ? vmId.split('#')[0] : vmId;
|
|
290
|
-
|
|
291
|
-
if (!publisherDid.startsWith('did:webvh:')) {
|
|
292
|
-
throw new Error('Signer must be associated with a did:webvh identifier');
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
return { publisherDid, signer };
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
private parseWebVHDid(did: string): { domain: string; userPath: string } {
|
|
299
|
-
const parts = did.split(':');
|
|
300
|
-
if (parts.length < 4) {
|
|
301
|
-
throw new Error('Invalid did:webvh format: must include domain and user path');
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
const domain = decodeURIComponent(parts[2]);
|
|
305
|
-
const userPath = parts.slice(3).join('/');
|
|
306
|
-
|
|
307
|
-
return { domain, userPath };
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
private async publishResources(
|
|
311
|
-
asset: OriginalsAsset,
|
|
312
|
-
publisherDid: string,
|
|
313
|
-
domain: string,
|
|
314
|
-
userPath: string
|
|
315
|
-
): Promise<void> {
|
|
316
|
-
const storage = (this.config as any).storageAdapter || new MemoryStorageAdapter();
|
|
317
|
-
|
|
318
|
-
for (const resource of asset.resources) {
|
|
319
|
-
const hashBytes = hexToBytes(resource.hash);
|
|
320
|
-
const multibase = encodeBase64UrlMultibase(hashBytes);
|
|
321
|
-
const resourceUrl = `${publisherDid}/resources/${multibase}`;
|
|
322
|
-
const relativePath = `${userPath}/resources/${multibase}`;
|
|
323
|
-
|
|
324
|
-
// Store resource content
|
|
325
|
-
const data = resource.content
|
|
326
|
-
? Buffer.from(resource.content)
|
|
327
|
-
: Buffer.from(resource.hash);
|
|
328
|
-
|
|
329
|
-
if (typeof storage.put === 'function') {
|
|
330
|
-
await storage.put(`${domain}/${relativePath}`, data, { contentType: resource.contentType });
|
|
331
|
-
} else {
|
|
332
|
-
const encoded = new TextEncoder().encode(resource.content || resource.hash);
|
|
333
|
-
await storage.putObject(domain, relativePath, encoded);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
(resource as any).url = resourceUrl;
|
|
337
|
-
|
|
338
|
-
await this.emitResourcePublishedEvent(asset, resource, resourceUrl, publisherDid, domain);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
private async emitResourcePublishedEvent(
|
|
343
|
-
asset: OriginalsAsset,
|
|
344
|
-
resource: AssetResource,
|
|
345
|
-
resourceUrl: string,
|
|
346
|
-
publisherDid: string,
|
|
347
|
-
domain: string
|
|
348
|
-
): Promise<void> {
|
|
349
|
-
const event = {
|
|
350
|
-
type: 'resource:published' as const,
|
|
351
|
-
timestamp: new Date().toISOString(),
|
|
352
|
-
asset: { id: asset.id },
|
|
353
|
-
resource: {
|
|
354
|
-
id: resource.id,
|
|
355
|
-
url: resourceUrl,
|
|
356
|
-
contentType: resource.contentType,
|
|
357
|
-
hash: resource.hash
|
|
358
|
-
},
|
|
359
|
-
publisherDid,
|
|
360
|
-
domain
|
|
361
|
-
};
|
|
362
|
-
|
|
363
|
-
try {
|
|
364
|
-
// Emit from both LifecycleManager and asset emitters
|
|
365
|
-
await this.eventEmitter.emit(event);
|
|
366
|
-
await (asset as any).eventEmitter.emit(event);
|
|
367
|
-
} catch (err) {
|
|
368
|
-
this.logger.error('Event handler error', err as Error, { event: event.type });
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
private async issuePublicationCredential(
|
|
373
|
-
asset: OriginalsAsset,
|
|
374
|
-
publisherDid: string,
|
|
375
|
-
signer?: ExternalSigner
|
|
376
|
-
): Promise<void> {
|
|
377
|
-
try {
|
|
378
|
-
const subject = {
|
|
379
|
-
id: asset.id,
|
|
380
|
-
publishedAs: publisherDid,
|
|
381
|
-
resourceId: asset.resources[0]?.id,
|
|
382
|
-
fromLayer: 'did:peer' as const,
|
|
383
|
-
toLayer: 'did:webvh' as const,
|
|
384
|
-
migratedAt: new Date().toISOString()
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
const unsigned = await this.credentialManager.createResourceCredential(
|
|
388
|
-
'ResourceMigrated',
|
|
389
|
-
subject,
|
|
390
|
-
publisherDid
|
|
391
|
-
);
|
|
392
|
-
|
|
393
|
-
const signed = signer
|
|
394
|
-
? await this.credentialManager.signCredentialWithExternalSigner(unsigned, signer)
|
|
395
|
-
: await this.signWithKeyStore(unsigned, publisherDid);
|
|
396
|
-
|
|
397
|
-
asset.credentials.push(signed);
|
|
398
|
-
|
|
399
|
-
const event = {
|
|
400
|
-
type: 'credential:issued' as const,
|
|
401
|
-
timestamp: new Date().toISOString(),
|
|
402
|
-
asset: { id: asset.id },
|
|
403
|
-
credential: {
|
|
404
|
-
type: signed.type,
|
|
405
|
-
issuer: typeof signed.issuer === 'string' ? signed.issuer : signed.issuer.id
|
|
406
|
-
}
|
|
407
|
-
};
|
|
408
|
-
|
|
409
|
-
// Emit from both LifecycleManager and asset emitters
|
|
410
|
-
await this.eventEmitter.emit(event);
|
|
411
|
-
await (asset as any).eventEmitter.emit(event);
|
|
412
|
-
} catch (err) {
|
|
413
|
-
this.logger.error('Failed to issue credential during publish', err as Error);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
private async signWithKeyStore(
|
|
418
|
-
credential: VerifiableCredential,
|
|
419
|
-
issuer: string
|
|
420
|
-
): Promise<VerifiableCredential> {
|
|
421
|
-
if (!this.keyStore) {
|
|
422
|
-
throw new Error('KeyStore required for signing. Provide keyStore or external signer.');
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// Try to find a key in the keyStore for this DID
|
|
426
|
-
// First try common verification method patterns: #key-0, #keys-1, etc.
|
|
427
|
-
const commonVmIds = [
|
|
428
|
-
`${issuer}#key-0`,
|
|
429
|
-
`${issuer}#keys-1`,
|
|
430
|
-
`${issuer}#authentication`,
|
|
431
|
-
];
|
|
432
|
-
|
|
433
|
-
let privateKey: string | null = null;
|
|
434
|
-
let vmId: string | null = null;
|
|
435
|
-
|
|
436
|
-
for (const testVmId of commonVmIds) {
|
|
437
|
-
const key = await this.keyStore.getPrivateKey(testVmId);
|
|
438
|
-
if (key) {
|
|
439
|
-
privateKey = key;
|
|
440
|
-
vmId = testVmId;
|
|
441
|
-
break;
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// If not found, try to find ANY key that starts with the issuer DID
|
|
446
|
-
if (!privateKey && typeof (this.keyStore as any).getAllVerificationMethodIds === 'function') {
|
|
447
|
-
const allVmIds = (this.keyStore as any).getAllVerificationMethodIds();
|
|
448
|
-
for (const testVmId of allVmIds) {
|
|
449
|
-
if (testVmId.startsWith(issuer)) {
|
|
450
|
-
const key = await this.keyStore.getPrivateKey(testVmId);
|
|
451
|
-
if (key) {
|
|
452
|
-
privateKey = key;
|
|
453
|
-
vmId = testVmId;
|
|
454
|
-
break;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
// If no key found in common patterns, try resolving the DID
|
|
461
|
-
if (!privateKey) {
|
|
462
|
-
const didDoc = await this.didManager.resolveDID(issuer);
|
|
463
|
-
if (!didDoc?.verificationMethod?.[0]) {
|
|
464
|
-
throw new Error('No verification method found in publisher DID document');
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
vmId = didDoc.verificationMethod[0].id;
|
|
468
|
-
if (vmId.startsWith('#')) {
|
|
469
|
-
vmId = `${issuer}${vmId}`;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
privateKey = await this.keyStore.getPrivateKey(vmId);
|
|
473
|
-
if (!privateKey) {
|
|
474
|
-
throw new Error('Private key not found in keyStore');
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
return this.credentialManager.signCredential(credential, privateKey!, vmId!);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
async inscribeOnBitcoin(
|
|
482
|
-
asset: OriginalsAsset,
|
|
483
|
-
feeRate?: number
|
|
484
|
-
): Promise<OriginalsAsset> {
|
|
485
|
-
const stopTimer = this.logger.startTimer('inscribeOnBitcoin');
|
|
486
|
-
this.logger.info('Inscribing asset on Bitcoin', { assetId: asset.id, feeRate });
|
|
487
|
-
|
|
488
|
-
try {
|
|
489
|
-
// Input validation
|
|
490
|
-
if (!asset || typeof asset !== 'object') {
|
|
491
|
-
throw new Error('Invalid asset: must be a valid OriginalsAsset');
|
|
492
|
-
}
|
|
493
|
-
if (feeRate !== undefined) {
|
|
494
|
-
if (typeof feeRate !== 'number' || feeRate <= 0 || !Number.isFinite(feeRate)) {
|
|
495
|
-
throw new Error('Invalid feeRate: must be a positive number');
|
|
496
|
-
}
|
|
497
|
-
if (feeRate < 1 || feeRate > 1000000) {
|
|
498
|
-
throw new Error('Invalid feeRate: must be between 1 and 1000000 sat/vB');
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
if (typeof (asset as any).migrate !== 'function') {
|
|
503
|
-
throw new Error('Not implemented');
|
|
504
|
-
}
|
|
505
|
-
if (asset.currentLayer !== 'did:webvh' && asset.currentLayer !== 'did:peer') {
|
|
506
|
-
throw new Error('Not implemented');
|
|
507
|
-
}
|
|
508
|
-
const bitcoinManager = this.deps?.bitcoinManager ?? new BitcoinManager(this.config);
|
|
509
|
-
const manifest = {
|
|
510
|
-
assetId: asset.id,
|
|
511
|
-
resources: asset.resources.map(res => ({ id: res.id, hash: res.hash, contentType: res.contentType, url: res.url })),
|
|
512
|
-
timestamp: new Date().toISOString()
|
|
513
|
-
};
|
|
514
|
-
const payload = Buffer.from(JSON.stringify(manifest));
|
|
515
|
-
const inscription: any = await bitcoinManager.inscribeData(payload, 'application/json', feeRate);
|
|
516
|
-
const revealTxId = inscription.revealTxId ?? inscription.txid;
|
|
517
|
-
const commitTxId = inscription.commitTxId;
|
|
518
|
-
const usedFeeRate = typeof inscription.feeRate === 'number' ? inscription.feeRate : feeRate;
|
|
519
|
-
|
|
520
|
-
// Capture the layer before migration for accurate metrics
|
|
521
|
-
const fromLayer = asset.currentLayer;
|
|
522
|
-
|
|
523
|
-
await asset.migrate('did:btco', {
|
|
524
|
-
transactionId: revealTxId,
|
|
525
|
-
inscriptionId: inscription.inscriptionId,
|
|
526
|
-
satoshi: inscription.satoshi,
|
|
527
|
-
commitTxId,
|
|
528
|
-
revealTxId,
|
|
529
|
-
feeRate: usedFeeRate
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
const bindingValue = inscription.satoshi
|
|
533
|
-
? `did:btco:${inscription.satoshi}`
|
|
534
|
-
: `did:btco:${inscription.inscriptionId}`;
|
|
535
|
-
(asset as any).bindings = Object.assign({}, (asset as any).bindings, { 'did:btco': bindingValue });
|
|
536
|
-
|
|
537
|
-
stopTimer();
|
|
538
|
-
this.logger.info('Asset inscribed on Bitcoin successfully', {
|
|
539
|
-
assetId: asset.id,
|
|
540
|
-
inscriptionId: inscription.inscriptionId,
|
|
541
|
-
transactionId: revealTxId
|
|
542
|
-
});
|
|
543
|
-
this.metrics.recordMigration(fromLayer, 'did:btco');
|
|
544
|
-
|
|
545
|
-
return asset;
|
|
546
|
-
} catch (error) {
|
|
547
|
-
stopTimer();
|
|
548
|
-
this.logger.error('Bitcoin inscription failed', error as Error, { assetId: asset.id, feeRate });
|
|
549
|
-
this.metrics.recordError('INSCRIPTION_FAILED', 'inscribeOnBitcoin');
|
|
550
|
-
throw error;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
async transferOwnership(
|
|
555
|
-
asset: OriginalsAsset,
|
|
556
|
-
newOwner: string
|
|
557
|
-
): Promise<BitcoinTransaction> {
|
|
558
|
-
const stopTimer = this.logger.startTimer('transferOwnership');
|
|
559
|
-
this.logger.info('Transferring asset ownership', { assetId: asset.id, newOwner });
|
|
560
|
-
|
|
561
|
-
try {
|
|
562
|
-
// Input validation
|
|
563
|
-
if (!asset || typeof asset !== 'object') {
|
|
564
|
-
throw new Error('Invalid asset: must be a valid OriginalsAsset');
|
|
565
|
-
}
|
|
566
|
-
if (!newOwner || typeof newOwner !== 'string') {
|
|
567
|
-
throw new Error('Invalid newOwner: must be a non-empty string');
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
// Validate Bitcoin address format and checksum
|
|
571
|
-
try {
|
|
572
|
-
validateBitcoinAddress(newOwner, this.config.network);
|
|
573
|
-
} catch (error) {
|
|
574
|
-
const message = error instanceof Error ? error.message : 'Invalid Bitcoin address';
|
|
575
|
-
throw new Error(`Invalid Bitcoin address for ownership transfer: ${message}`);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
// Transfer Bitcoin-anchored asset ownership
|
|
579
|
-
// Only works for assets in did:btco layer
|
|
580
|
-
if (asset.currentLayer !== 'did:btco') {
|
|
581
|
-
throw new Error('Asset must be inscribed on Bitcoin before transfer');
|
|
582
|
-
}
|
|
583
|
-
const bm = this.deps?.bitcoinManager ?? new BitcoinManager(this.config);
|
|
584
|
-
const provenance = asset.getProvenance();
|
|
585
|
-
const latestMigration = provenance.migrations[provenance.migrations.length - 1];
|
|
586
|
-
const satoshi = latestMigration?.satoshi ?? (asset.id.startsWith('did:btco:') ? asset.id.split(':')[2] : '');
|
|
587
|
-
const inscription = {
|
|
588
|
-
satoshi,
|
|
589
|
-
inscriptionId: latestMigration?.inscriptionId ?? `insc-${satoshi || 'unknown'}`,
|
|
590
|
-
content: Buffer.alloc(0),
|
|
591
|
-
contentType: 'application/octet-stream',
|
|
592
|
-
txid: latestMigration?.transactionId ?? 'unknown-tx',
|
|
593
|
-
vout: 0
|
|
594
|
-
};
|
|
595
|
-
const tx = await bm.transferInscription(inscription as any, newOwner);
|
|
596
|
-
await asset.recordTransfer(asset.id, newOwner, tx.txid);
|
|
597
|
-
|
|
598
|
-
stopTimer();
|
|
599
|
-
this.logger.info('Asset ownership transferred successfully', {
|
|
600
|
-
assetId: asset.id,
|
|
601
|
-
newOwner,
|
|
602
|
-
transactionId: tx.txid
|
|
603
|
-
});
|
|
604
|
-
this.metrics.recordTransfer();
|
|
605
|
-
|
|
606
|
-
return tx;
|
|
607
|
-
} catch (error) {
|
|
608
|
-
stopTimer();
|
|
609
|
-
this.logger.error('Ownership transfer failed', error as Error, { assetId: asset.id, newOwner });
|
|
610
|
-
this.metrics.recordError('TRANSFER_FAILED', 'transferOwnership');
|
|
611
|
-
throw error;
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
/**
|
|
616
|
-
* Create multiple assets in batch
|
|
617
|
-
*
|
|
618
|
-
* @param resourcesList - Array of resource arrays, one per asset to create
|
|
619
|
-
* @param options - Batch operation options
|
|
620
|
-
* @returns BatchResult with created assets
|
|
621
|
-
*/
|
|
622
|
-
async batchCreateAssets(
|
|
623
|
-
resourcesList: AssetResource[][],
|
|
624
|
-
options?: BatchOperationOptions
|
|
625
|
-
): Promise<BatchResult<OriginalsAsset>> {
|
|
626
|
-
const batchId = this.batchExecutor.generateBatchId();
|
|
627
|
-
|
|
628
|
-
// Validate first if requested
|
|
629
|
-
if (options?.validateFirst !== false) {
|
|
630
|
-
const validationResults = this.batchValidator.validateBatchCreate(resourcesList);
|
|
631
|
-
const invalid = validationResults.filter(r => !r.isValid);
|
|
632
|
-
if (invalid.length > 0) {
|
|
633
|
-
const errors = invalid.flatMap(r => r.errors).join('; ');
|
|
634
|
-
throw new Error(`Batch validation failed: ${errors}`);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
// Emit batch:started event
|
|
639
|
-
await this.eventEmitter.emit({
|
|
640
|
-
type: 'batch:started',
|
|
641
|
-
timestamp: new Date().toISOString(),
|
|
642
|
-
operation: 'create',
|
|
643
|
-
batchId,
|
|
644
|
-
itemCount: resourcesList.length
|
|
645
|
-
});
|
|
646
|
-
|
|
647
|
-
try {
|
|
648
|
-
// Use batch executor to process all asset creations
|
|
649
|
-
const result = await this.batchExecutor.execute(
|
|
650
|
-
resourcesList,
|
|
651
|
-
async (resources, index) => {
|
|
652
|
-
const asset = await this.createAsset(resources);
|
|
653
|
-
return asset;
|
|
654
|
-
},
|
|
655
|
-
options,
|
|
656
|
-
batchId // Pass the pre-generated batchId for event correlation
|
|
657
|
-
);
|
|
658
|
-
|
|
659
|
-
// Emit batch:completed event
|
|
660
|
-
await this.eventEmitter.emit({
|
|
661
|
-
type: 'batch:completed',
|
|
662
|
-
timestamp: new Date().toISOString(),
|
|
663
|
-
batchId,
|
|
664
|
-
operation: 'create',
|
|
665
|
-
results: {
|
|
666
|
-
successful: result.successful.length,
|
|
667
|
-
failed: result.failed.length,
|
|
668
|
-
totalDuration: result.totalDuration
|
|
669
|
-
}
|
|
670
|
-
});
|
|
671
|
-
|
|
672
|
-
return result;
|
|
673
|
-
} catch (error) {
|
|
674
|
-
// Emit batch:failed event
|
|
675
|
-
await this.eventEmitter.emit({
|
|
676
|
-
type: 'batch:failed',
|
|
677
|
-
timestamp: new Date().toISOString(),
|
|
678
|
-
batchId,
|
|
679
|
-
operation: 'create',
|
|
680
|
-
error: error instanceof Error ? error.message : String(error)
|
|
681
|
-
});
|
|
682
|
-
|
|
683
|
-
throw error;
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
/**
|
|
688
|
-
* Publish multiple assets to web storage in batch
|
|
689
|
-
*
|
|
690
|
-
* @param assets - Array of assets to publish
|
|
691
|
-
* @param domain - Domain to publish to
|
|
692
|
-
* @param options - Batch operation options
|
|
693
|
-
* @returns BatchResult with published assets
|
|
694
|
-
*/
|
|
695
|
-
async batchPublishToWeb(
|
|
696
|
-
assets: OriginalsAsset[],
|
|
697
|
-
domain: string,
|
|
698
|
-
options?: BatchOperationOptions
|
|
699
|
-
): Promise<BatchResult<OriginalsAsset>> {
|
|
700
|
-
const batchId = this.batchExecutor.generateBatchId();
|
|
701
|
-
|
|
702
|
-
// Validate domain once
|
|
703
|
-
if (!domain || typeof domain !== 'string') {
|
|
704
|
-
throw new Error('Invalid domain: must be a non-empty string');
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
const normalized = domain.trim().toLowerCase();
|
|
708
|
-
|
|
709
|
-
// Split domain and port if present
|
|
710
|
-
const [domainPart, portPart] = normalized.split(':');
|
|
711
|
-
|
|
712
|
-
// Validate port if present
|
|
713
|
-
if (portPart && (!/^\d+$/.test(portPart) || parseInt(portPart) < 1 || parseInt(portPart) > 65535)) {
|
|
714
|
-
throw new Error(`Invalid domain format: ${domain} - invalid port`);
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
// Allow localhost and IP addresses for development
|
|
718
|
-
const isLocalhost = domainPart === 'localhost';
|
|
719
|
-
const isIP = /^(\d{1,3}\.){3}\d{1,3}$/.test(domainPart);
|
|
720
|
-
|
|
721
|
-
if (!isLocalhost && !isIP) {
|
|
722
|
-
// For non-localhost domains, require proper domain format
|
|
723
|
-
const label = '[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?';
|
|
724
|
-
const domainRegex = new RegExp(`^(?=.{1,253}$)(?:${label})(?:\\.(?:${label}))+?$`, 'i');
|
|
725
|
-
if (!domainRegex.test(domainPart)) {
|
|
726
|
-
throw new Error(`Invalid domain format: ${domain}`);
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
// Emit batch:started event
|
|
731
|
-
await this.eventEmitter.emit({
|
|
732
|
-
type: 'batch:started',
|
|
733
|
-
timestamp: new Date().toISOString(),
|
|
734
|
-
operation: 'publish',
|
|
735
|
-
batchId,
|
|
736
|
-
itemCount: assets.length
|
|
737
|
-
});
|
|
738
|
-
|
|
739
|
-
try {
|
|
740
|
-
const result = await this.batchExecutor.execute(
|
|
741
|
-
assets,
|
|
742
|
-
async (asset, index) => {
|
|
743
|
-
return await this.publishToWeb(asset, domain);
|
|
744
|
-
},
|
|
745
|
-
options,
|
|
746
|
-
batchId // Pass the pre-generated batchId for event correlation
|
|
747
|
-
);
|
|
748
|
-
|
|
749
|
-
// Emit batch:completed event
|
|
750
|
-
await this.eventEmitter.emit({
|
|
751
|
-
type: 'batch:completed',
|
|
752
|
-
timestamp: new Date().toISOString(),
|
|
753
|
-
batchId,
|
|
754
|
-
operation: 'publish',
|
|
755
|
-
results: {
|
|
756
|
-
successful: result.successful.length,
|
|
757
|
-
failed: result.failed.length,
|
|
758
|
-
totalDuration: result.totalDuration
|
|
759
|
-
}
|
|
760
|
-
});
|
|
761
|
-
|
|
762
|
-
return result;
|
|
763
|
-
} catch (error) {
|
|
764
|
-
// Emit batch:failed event
|
|
765
|
-
await this.eventEmitter.emit({
|
|
766
|
-
type: 'batch:failed',
|
|
767
|
-
timestamp: new Date().toISOString(),
|
|
768
|
-
batchId,
|
|
769
|
-
operation: 'publish',
|
|
770
|
-
error: error instanceof Error ? error.message : String(error)
|
|
771
|
-
});
|
|
772
|
-
|
|
773
|
-
throw error;
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
/**
|
|
778
|
-
* Inscribe multiple assets on Bitcoin with cost optimization
|
|
779
|
-
* KEY FEATURE: singleTransaction option for 30%+ cost savings
|
|
780
|
-
*
|
|
781
|
-
* @param assets - Array of assets to inscribe
|
|
782
|
-
* @param options - Batch inscription options
|
|
783
|
-
* @returns BatchResult with inscribed assets
|
|
784
|
-
*/
|
|
785
|
-
async batchInscribeOnBitcoin(
|
|
786
|
-
assets: OriginalsAsset[],
|
|
787
|
-
options?: BatchInscriptionOptions
|
|
788
|
-
): Promise<BatchResult<OriginalsAsset>> {
|
|
789
|
-
// Validate first if requested
|
|
790
|
-
if (options?.validateFirst !== false) {
|
|
791
|
-
const validationResults = this.batchValidator.validateBatchInscription(assets);
|
|
792
|
-
const invalid = validationResults.filter(r => !r.isValid);
|
|
793
|
-
if (invalid.length > 0) {
|
|
794
|
-
const errors = invalid.flatMap(r => r.errors).join('; ');
|
|
795
|
-
throw new Error(`Batch validation failed: ${errors}`);
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
if (options?.singleTransaction) {
|
|
800
|
-
return this.batchInscribeSingleTransaction(assets, options);
|
|
801
|
-
} else {
|
|
802
|
-
return this.batchInscribeIndividualTransactions(assets, options);
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
/**
|
|
807
|
-
* CORE INNOVATION: Single-transaction batch inscription
|
|
808
|
-
* Combines multiple assets into one Bitcoin transaction for 30%+ cost savings
|
|
809
|
-
*
|
|
810
|
-
* @param assets - Array of assets to inscribe
|
|
811
|
-
* @param options - Batch inscription options
|
|
812
|
-
* @returns BatchResult with inscribed assets and cost savings data
|
|
813
|
-
*/
|
|
814
|
-
private async batchInscribeSingleTransaction(
|
|
815
|
-
assets: OriginalsAsset[],
|
|
816
|
-
options?: BatchInscriptionOptions
|
|
817
|
-
): Promise<BatchResult<OriginalsAsset>> {
|
|
818
|
-
const batchId = this.batchExecutor.generateBatchId();
|
|
819
|
-
const startTime = Date.now();
|
|
820
|
-
const startedAt = new Date().toISOString();
|
|
821
|
-
|
|
822
|
-
// Emit batch:started event
|
|
823
|
-
await this.eventEmitter.emit({
|
|
824
|
-
type: 'batch:started',
|
|
825
|
-
timestamp: startedAt,
|
|
826
|
-
operation: 'inscribe',
|
|
827
|
-
batchId,
|
|
828
|
-
itemCount: assets.length
|
|
829
|
-
});
|
|
830
|
-
|
|
831
|
-
try {
|
|
832
|
-
// Calculate total data size for all assets
|
|
833
|
-
const totalDataSize = this.calculateTotalDataSize(assets);
|
|
834
|
-
|
|
835
|
-
// Estimate savings from batch inscription
|
|
836
|
-
const estimatedSavings = await this.estimateBatchSavings(assets, options?.feeRate);
|
|
837
|
-
|
|
838
|
-
// Create manifests for all assets
|
|
839
|
-
const manifests = assets.map(asset => ({
|
|
840
|
-
assetId: asset.id,
|
|
841
|
-
resources: asset.resources.map(res => ({
|
|
842
|
-
id: res.id,
|
|
843
|
-
hash: res.hash,
|
|
844
|
-
contentType: res.contentType,
|
|
845
|
-
url: res.url
|
|
846
|
-
})),
|
|
847
|
-
timestamp: new Date().toISOString()
|
|
848
|
-
}));
|
|
849
|
-
|
|
850
|
-
// Combine all manifests into a single batch payload
|
|
851
|
-
const batchManifest = {
|
|
852
|
-
batchId,
|
|
853
|
-
assets: manifests,
|
|
854
|
-
timestamp: new Date().toISOString()
|
|
855
|
-
};
|
|
856
|
-
|
|
857
|
-
const payload = Buffer.from(JSON.stringify(batchManifest));
|
|
858
|
-
|
|
859
|
-
// Inscribe the batch manifest as a single transaction
|
|
860
|
-
const bitcoinManager = this.deps?.bitcoinManager ?? new BitcoinManager(this.config);
|
|
861
|
-
const inscription: any = await bitcoinManager.inscribeData(
|
|
862
|
-
payload,
|
|
863
|
-
'application/json',
|
|
864
|
-
options?.feeRate
|
|
865
|
-
);
|
|
866
|
-
|
|
867
|
-
const revealTxId = inscription.revealTxId ?? inscription.txid;
|
|
868
|
-
const commitTxId = inscription.commitTxId;
|
|
869
|
-
const usedFeeRate = typeof inscription.feeRate === 'number' ? inscription.feeRate : options?.feeRate;
|
|
870
|
-
|
|
871
|
-
// Calculate fee per asset (split proportionally by data size)
|
|
872
|
-
// Include both metadata and resource content size for accurate fee distribution
|
|
873
|
-
const assetSizes = assets.map(asset => {
|
|
874
|
-
// Calculate metadata size
|
|
875
|
-
const metadataSize = JSON.stringify({
|
|
876
|
-
assetId: asset.id,
|
|
877
|
-
resources: asset.resources.map(r => ({
|
|
878
|
-
id: r.id,
|
|
879
|
-
hash: r.hash,
|
|
880
|
-
contentType: r.contentType,
|
|
881
|
-
url: r.url
|
|
882
|
-
}))
|
|
883
|
-
}).length;
|
|
884
|
-
|
|
885
|
-
// Add resource content sizes
|
|
886
|
-
const contentSize = asset.resources.reduce((sum, r) => {
|
|
887
|
-
const content = (r as any).content;
|
|
888
|
-
if (content) {
|
|
889
|
-
return sum + (typeof content === 'string' ? Buffer.byteLength(content) : content.length || 0);
|
|
890
|
-
}
|
|
891
|
-
return sum;
|
|
892
|
-
}, 0);
|
|
893
|
-
|
|
894
|
-
return metadataSize + contentSize;
|
|
895
|
-
});
|
|
896
|
-
const totalSize = assetSizes.reduce((sum, size) => sum + size, 0);
|
|
897
|
-
|
|
898
|
-
// Calculate total fee from batch transaction size and fee rate
|
|
899
|
-
// Estimate transaction size: base overhead (200 bytes) + batch payload size
|
|
900
|
-
const batchTxSize = 200 + totalDataSize;
|
|
901
|
-
const effectiveFeeRate = usedFeeRate ?? 10;
|
|
902
|
-
const totalFee = batchTxSize * effectiveFeeRate;
|
|
903
|
-
|
|
904
|
-
// Split fees proportionally by asset data size
|
|
905
|
-
const feePerAsset = assetSizes.map(size =>
|
|
906
|
-
Math.floor(totalFee * (size / totalSize))
|
|
907
|
-
);
|
|
908
|
-
|
|
909
|
-
// Update all assets with batch inscription data
|
|
910
|
-
const individualInscriptionIds: string[] = [];
|
|
911
|
-
const successful: BatchResult<OriginalsAsset>['successful'] = [];
|
|
912
|
-
|
|
913
|
-
for (let i = 0; i < assets.length; i++) {
|
|
914
|
-
const asset = assets[i];
|
|
915
|
-
// For batch inscriptions, use the base inscription ID for all assets
|
|
916
|
-
// The batch index is stored as metadata, not in the ID
|
|
917
|
-
const individualInscriptionId = inscription.inscriptionId;
|
|
918
|
-
individualInscriptionIds.push(individualInscriptionId);
|
|
919
|
-
|
|
920
|
-
await asset.migrate('did:btco', {
|
|
921
|
-
transactionId: revealTxId,
|
|
922
|
-
inscriptionId: individualInscriptionId,
|
|
923
|
-
satoshi: inscription.satoshi,
|
|
924
|
-
commitTxId,
|
|
925
|
-
revealTxId,
|
|
926
|
-
feeRate: usedFeeRate
|
|
927
|
-
});
|
|
928
|
-
|
|
929
|
-
// Add batch metadata to provenance
|
|
930
|
-
const provenance = asset.getProvenance();
|
|
931
|
-
const latestMigration = provenance.migrations[provenance.migrations.length - 1];
|
|
932
|
-
(latestMigration as any).batchId = batchId;
|
|
933
|
-
(latestMigration as any).batchInscription = true;
|
|
934
|
-
(latestMigration as any).batchIndex = i; // Store index as metadata
|
|
935
|
-
(latestMigration as any).feePaid = feePerAsset[i];
|
|
936
|
-
|
|
937
|
-
const bindingValue = inscription.satoshi
|
|
938
|
-
? `did:btco:${inscription.satoshi}`
|
|
939
|
-
: `did:btco:${individualInscriptionId}`;
|
|
940
|
-
(asset as any).bindings = Object.assign({}, (asset as any).bindings, { 'did:btco': bindingValue });
|
|
941
|
-
|
|
942
|
-
successful.push({
|
|
943
|
-
index: i,
|
|
944
|
-
result: asset,
|
|
945
|
-
duration: Date.now() - startTime
|
|
946
|
-
});
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
const totalDuration = Date.now() - startTime;
|
|
950
|
-
const completedAt = new Date().toISOString();
|
|
951
|
-
|
|
952
|
-
// Emit batch:completed event with cost savings
|
|
953
|
-
await this.eventEmitter.emit({
|
|
954
|
-
type: 'batch:completed',
|
|
955
|
-
timestamp: completedAt,
|
|
956
|
-
batchId,
|
|
957
|
-
operation: 'inscribe',
|
|
958
|
-
results: {
|
|
959
|
-
successful: successful.length,
|
|
960
|
-
failed: 0,
|
|
961
|
-
totalDuration,
|
|
962
|
-
costSavings: {
|
|
963
|
-
amount: estimatedSavings.savings,
|
|
964
|
-
percentage: estimatedSavings.savingsPercentage
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
});
|
|
968
|
-
|
|
969
|
-
return {
|
|
970
|
-
successful,
|
|
971
|
-
failed: [],
|
|
972
|
-
totalProcessed: assets.length,
|
|
973
|
-
totalDuration,
|
|
974
|
-
batchId,
|
|
975
|
-
startedAt,
|
|
976
|
-
completedAt
|
|
977
|
-
};
|
|
978
|
-
} catch (error) {
|
|
979
|
-
// Emit batch:failed event
|
|
980
|
-
await this.eventEmitter.emit({
|
|
981
|
-
type: 'batch:failed',
|
|
982
|
-
timestamp: new Date().toISOString(),
|
|
983
|
-
batchId,
|
|
984
|
-
operation: 'inscribe',
|
|
985
|
-
error: error instanceof Error ? error.message : String(error)
|
|
986
|
-
});
|
|
987
|
-
|
|
988
|
-
throw new BatchError(
|
|
989
|
-
batchId,
|
|
990
|
-
'inscribe',
|
|
991
|
-
{ successful: 0, failed: assets.length },
|
|
992
|
-
error instanceof Error ? error.message : String(error)
|
|
993
|
-
);
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
/**
|
|
998
|
-
* Individual transaction batch inscription (fallback mode)
|
|
999
|
-
* Each asset is inscribed in its own transaction
|
|
1000
|
-
*
|
|
1001
|
-
* @param assets - Array of assets to inscribe
|
|
1002
|
-
* @param options - Batch inscription options
|
|
1003
|
-
* @returns BatchResult with inscribed assets
|
|
1004
|
-
*/
|
|
1005
|
-
private async batchInscribeIndividualTransactions(
|
|
1006
|
-
assets: OriginalsAsset[],
|
|
1007
|
-
options?: BatchInscriptionOptions
|
|
1008
|
-
): Promise<BatchResult<OriginalsAsset>> {
|
|
1009
|
-
const batchId = this.batchExecutor.generateBatchId();
|
|
1010
|
-
|
|
1011
|
-
// Emit batch:started event
|
|
1012
|
-
await this.eventEmitter.emit({
|
|
1013
|
-
type: 'batch:started',
|
|
1014
|
-
timestamp: new Date().toISOString(),
|
|
1015
|
-
operation: 'inscribe',
|
|
1016
|
-
batchId,
|
|
1017
|
-
itemCount: assets.length
|
|
1018
|
-
});
|
|
1019
|
-
|
|
1020
|
-
try {
|
|
1021
|
-
const result = await this.batchExecutor.execute(
|
|
1022
|
-
assets,
|
|
1023
|
-
async (asset, index) => {
|
|
1024
|
-
return await this.inscribeOnBitcoin(asset, options?.feeRate);
|
|
1025
|
-
},
|
|
1026
|
-
options,
|
|
1027
|
-
batchId // Pass the pre-generated batchId for event correlation
|
|
1028
|
-
);
|
|
1029
|
-
|
|
1030
|
-
// Emit batch:completed event
|
|
1031
|
-
await this.eventEmitter.emit({
|
|
1032
|
-
type: 'batch:completed',
|
|
1033
|
-
timestamp: new Date().toISOString(),
|
|
1034
|
-
batchId,
|
|
1035
|
-
operation: 'inscribe',
|
|
1036
|
-
results: {
|
|
1037
|
-
successful: result.successful.length,
|
|
1038
|
-
failed: result.failed.length,
|
|
1039
|
-
totalDuration: result.totalDuration
|
|
1040
|
-
}
|
|
1041
|
-
});
|
|
1042
|
-
|
|
1043
|
-
return result;
|
|
1044
|
-
} catch (error) {
|
|
1045
|
-
// Emit batch:failed event
|
|
1046
|
-
await this.eventEmitter.emit({
|
|
1047
|
-
type: 'batch:failed',
|
|
1048
|
-
timestamp: new Date().toISOString(),
|
|
1049
|
-
batchId,
|
|
1050
|
-
operation: 'inscribe',
|
|
1051
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1052
|
-
});
|
|
1053
|
-
|
|
1054
|
-
throw error;
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
/**
|
|
1059
|
-
* Transfer ownership of multiple assets in batch
|
|
1060
|
-
*
|
|
1061
|
-
* @param transfers - Array of transfer operations
|
|
1062
|
-
* @param options - Batch operation options
|
|
1063
|
-
* @returns BatchResult with transaction results
|
|
1064
|
-
*/
|
|
1065
|
-
async batchTransferOwnership(
|
|
1066
|
-
transfers: Array<{ asset: OriginalsAsset; to: string }>,
|
|
1067
|
-
options?: BatchOperationOptions
|
|
1068
|
-
): Promise<BatchResult<BitcoinTransaction>> {
|
|
1069
|
-
const batchId = this.batchExecutor.generateBatchId();
|
|
1070
|
-
|
|
1071
|
-
// Validate first if requested
|
|
1072
|
-
if (options?.validateFirst !== false) {
|
|
1073
|
-
const validationResults = this.batchValidator.validateBatchTransfer(transfers);
|
|
1074
|
-
const invalid = validationResults.filter(r => !r.isValid);
|
|
1075
|
-
if (invalid.length > 0) {
|
|
1076
|
-
const errors = invalid.flatMap(r => r.errors).join('; ');
|
|
1077
|
-
throw new Error(`Batch validation failed: ${errors}`);
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
// Validate all Bitcoin addresses
|
|
1081
|
-
for (let i = 0; i < transfers.length; i++) {
|
|
1082
|
-
try {
|
|
1083
|
-
validateBitcoinAddress(transfers[i].to, this.config.network);
|
|
1084
|
-
} catch (error) {
|
|
1085
|
-
const message = error instanceof Error ? error.message : 'Invalid Bitcoin address';
|
|
1086
|
-
throw new Error(`Transfer ${i}: Invalid Bitcoin address: ${message}`);
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
// Emit batch:started event
|
|
1092
|
-
await this.eventEmitter.emit({
|
|
1093
|
-
type: 'batch:started',
|
|
1094
|
-
timestamp: new Date().toISOString(),
|
|
1095
|
-
operation: 'transfer',
|
|
1096
|
-
batchId,
|
|
1097
|
-
itemCount: transfers.length
|
|
1098
|
-
});
|
|
1099
|
-
|
|
1100
|
-
try {
|
|
1101
|
-
const result = await this.batchExecutor.execute(
|
|
1102
|
-
transfers,
|
|
1103
|
-
async (transfer, index) => {
|
|
1104
|
-
return await this.transferOwnership(transfer.asset, transfer.to);
|
|
1105
|
-
},
|
|
1106
|
-
options,
|
|
1107
|
-
batchId // Pass the pre-generated batchId for event correlation
|
|
1108
|
-
);
|
|
1109
|
-
|
|
1110
|
-
// Emit batch:completed event
|
|
1111
|
-
await this.eventEmitter.emit({
|
|
1112
|
-
type: 'batch:completed',
|
|
1113
|
-
timestamp: new Date().toISOString(),
|
|
1114
|
-
batchId,
|
|
1115
|
-
operation: 'transfer',
|
|
1116
|
-
results: {
|
|
1117
|
-
successful: result.successful.length,
|
|
1118
|
-
failed: result.failed.length,
|
|
1119
|
-
totalDuration: result.totalDuration
|
|
1120
|
-
}
|
|
1121
|
-
});
|
|
1122
|
-
|
|
1123
|
-
return result;
|
|
1124
|
-
} catch (error) {
|
|
1125
|
-
// Emit batch:failed event
|
|
1126
|
-
await this.eventEmitter.emit({
|
|
1127
|
-
type: 'batch:failed',
|
|
1128
|
-
timestamp: new Date().toISOString(),
|
|
1129
|
-
batchId,
|
|
1130
|
-
operation: 'transfer',
|
|
1131
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1132
|
-
});
|
|
1133
|
-
|
|
1134
|
-
throw error;
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
/**
|
|
1139
|
-
* Calculate total data size for all assets in a batch
|
|
1140
|
-
*/
|
|
1141
|
-
private calculateTotalDataSize(assets: OriginalsAsset[]): number {
|
|
1142
|
-
return assets.reduce((total, asset) => {
|
|
1143
|
-
const manifest = {
|
|
1144
|
-
assetId: asset.id,
|
|
1145
|
-
resources: asset.resources.map(res => ({
|
|
1146
|
-
id: res.id,
|
|
1147
|
-
hash: res.hash,
|
|
1148
|
-
contentType: res.contentType,
|
|
1149
|
-
url: res.url
|
|
1150
|
-
})),
|
|
1151
|
-
timestamp: new Date().toISOString()
|
|
1152
|
-
};
|
|
1153
|
-
return total + JSON.stringify(manifest).length;
|
|
1154
|
-
}, 0);
|
|
1155
|
-
}
|
|
1156
|
-
|
|
1157
|
-
/**
|
|
1158
|
-
* Estimate cost savings from batch inscription vs individual inscriptions
|
|
1159
|
-
*/
|
|
1160
|
-
private async estimateBatchSavings(
|
|
1161
|
-
assets: OriginalsAsset[],
|
|
1162
|
-
feeRate?: number
|
|
1163
|
-
): Promise<{
|
|
1164
|
-
batchFee: number;
|
|
1165
|
-
individualFees: number;
|
|
1166
|
-
savings: number;
|
|
1167
|
-
savingsPercentage: number;
|
|
1168
|
-
}> {
|
|
1169
|
-
// Calculate total size for batch
|
|
1170
|
-
const batchSize = this.calculateTotalDataSize(assets);
|
|
1171
|
-
|
|
1172
|
-
// Estimate individual sizes
|
|
1173
|
-
const individualSizes = assets.map(asset =>
|
|
1174
|
-
JSON.stringify({
|
|
1175
|
-
assetId: asset.id,
|
|
1176
|
-
resources: asset.resources.map(r => ({
|
|
1177
|
-
id: r.id,
|
|
1178
|
-
hash: r.hash,
|
|
1179
|
-
contentType: r.contentType,
|
|
1180
|
-
url: r.url
|
|
1181
|
-
}))
|
|
1182
|
-
}).length
|
|
1183
|
-
);
|
|
1184
|
-
|
|
1185
|
-
// Realistic fee estimation based on Bitcoin transaction structure
|
|
1186
|
-
// Base transaction overhead: ~200 bytes (inputs, outputs, etc.)
|
|
1187
|
-
// Per inscription witness overhead: ~120 bytes (script, envelope, etc.)
|
|
1188
|
-
// In batch mode: shared transaction overhead + minimal per-asset overhead
|
|
1189
|
-
const effectiveFeeRate = feeRate ?? 10; // default 10 sat/vB
|
|
1190
|
-
|
|
1191
|
-
// Batch: one transaction overhead + batch data + minimal per-asset overhead
|
|
1192
|
-
// The batch manifest is more efficient as it shares structure
|
|
1193
|
-
const batchTxSize = 200 + batchSize + (assets.length * 5); // 5 bytes per asset for array/object overhead
|
|
1194
|
-
const batchFee = batchTxSize * effectiveFeeRate;
|
|
1195
|
-
|
|
1196
|
-
// Individual: each inscription needs full transaction overhead + witness overhead + data
|
|
1197
|
-
const individualFees = individualSizes.reduce((total, size) => {
|
|
1198
|
-
// Each individual inscription has:
|
|
1199
|
-
// - Full transaction overhead: 200 bytes
|
|
1200
|
-
// - Witness/inscription overhead: 122 bytes
|
|
1201
|
-
// - Asset data: size bytes
|
|
1202
|
-
const txSize = 200 + 122 + size;
|
|
1203
|
-
return total + (txSize * effectiveFeeRate);
|
|
1204
|
-
}, 0);
|
|
1205
|
-
|
|
1206
|
-
const savings = individualFees - batchFee;
|
|
1207
|
-
const savingsPercentage = (savings / individualFees) * 100;
|
|
1208
|
-
|
|
1209
|
-
return {
|
|
1210
|
-
batchFee,
|
|
1211
|
-
individualFees,
|
|
1212
|
-
savings,
|
|
1213
|
-
savingsPercentage
|
|
1214
|
-
};
|
|
1215
|
-
}
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
|