From 1aa55cfc64fd7e87db6e9d28969c50c2c7aac753 Mon Sep 17 00:00:00 2001 From: Kasper Ziemianek Date: Fri, 13 Dec 2024 12:31:17 +0100 Subject: [PATCH 1/2] remove bitacross --- .github/file-filter.yml | 15 - .github/workflows/ci.yml | 200 +- .github/workflows/create-release-draft.yml | 77 +- Makefile | 1 - common/primitives/core/src/teebag/types.rs | 3 +- local-setup/launch.py | 16 +- parachain/Cargo.toml | 2 - parachain/node/src/chain_specs/litentry.rs | 3 +- parachain/node/src/chain_specs/paseo.rs | 3 +- parachain/pallets/teebag/src/tests.rs | 22 +- parachain/runtime/litentry/Cargo.toml | 4 - parachain/runtime/litentry/src/lib.rs | 8 - parachain/runtime/paseo/Cargo.toml | 4 - parachain/runtime/paseo/src/lib.rs | 8 - tee-worker/Cargo.lock | 726 +-- tee-worker/Cargo.toml | 20 - tee-worker/bitacross/.dockerignore | 16 - tee-worker/bitacross/.editorconfig | 27 - tee-worker/bitacross/.env.dev | 11 - tee-worker/bitacross/.gitattributes.orig | 18 - tee-worker/bitacross/.githooks/pre-commit | 17 - tee-worker/bitacross/.gitignore | 78 - tee-worker/bitacross/DESIGN.md | 72 - tee-worker/bitacross/Dockerfile | 23 - tee-worker/bitacross/Jenkinsfile | 104 - tee-worker/bitacross/LICENSE | 201 - tee-worker/bitacross/Makefile | 289 - tee-worker/bitacross/README.md | 209 - tee-worker/bitacross/UpdateRustSGXSDK.mk | 33 - .../app-libs/parentchain-interface/Cargo.toml | 77 - .../src/event_subscriber.rs | 60 - .../src/integritee/event_filter.rs | 124 - .../src/integritee/event_handler.rs | 234 - .../src/integritee/mod.rs | 31 - .../app-libs/parentchain-interface/src/lib.rs | 54 - .../src/target_a/event_filter.rs | 122 - .../src/target_a/event_handler.rs | 68 - .../parentchain-interface/src/target_a/mod.rs | 21 - .../src/target_b/event_filter.rs | 122 - .../src/target_b/event_handler.rs | 68 - .../parentchain-interface/src/target_b/mod.rs | 22 - .../bitacross/app-libs/sgx-runtime/Cargo.toml | 57 - .../bitacross/app-libs/sgx-runtime/src/lib.rs | 314 - tee-worker/bitacross/app-libs/stf/Cargo.toml | 75 - .../bitacross/app-libs/stf/src/getter.rs | 215 - tee-worker/bitacross/app-libs/stf/src/hash.rs | 29 - .../bitacross/app-libs/stf/src/helpers.rs | 165 - tee-worker/bitacross/app-libs/stf/src/lib.rs | 51 - .../bitacross/app-libs/stf/src/stf_sgx.rs | 422 -- .../app-libs/stf/src/stf_sgx_primitives.rs | 29 - .../app-libs/stf/src/stf_sgx_tests.rs | 84 - .../app-libs/stf/src/test_genesis.rs | 95 - .../app-libs/stf/src/trusted_call.rs | 433 -- .../app-libs/stf/src/trusted_call_result.rs | 43 - .../bitacross/assets/teebag_registry.gif | Bin 1703551 -> 0 bytes tee-worker/bitacross/bin/README.md | 1 - .../core/bc-enclave-registry/Cargo.toml | 39 - .../core/bc-enclave-registry/src/lib.rs | 246 - .../core/bc-musig2-ceremony/Cargo.toml | 43 - .../core/bc-musig2-ceremony/src/lib.rs | 807 --- .../bitacross/core/bc-musig2-event/Cargo.toml | 64 - .../bitacross/core/bc-musig2-event/src/lib.rs | 274 - .../core/bc-relayer-registry/Cargo.toml | 38 - .../core/bc-relayer-registry/src/lib.rs | 206 - .../core/bc-signer-registry/Cargo.toml | 39 - .../core/bc-signer-registry/src/lib.rs | 253 - .../core/bc-task-processor/Cargo.toml | 90 - .../core/bc-task-processor/src/lib.rs | 769 --- .../bitacross/core/bc-task-sender/Cargo.toml | 30 - .../bitacross/core/bc-task-sender/src/lib.rs | 134 - tee-worker/bitacross/build.Dockerfile | 179 - tee-worker/bitacross/cli/Cargo.toml | 54 - tee-worker/bitacross/cli/README.md | 35 - tee-worker/bitacross/cli/benchmark.sh | 61 - .../bitacross/cli/lit_parentchain_nonce.sh | 69 - .../bitacross/cli/lit_ts_integration_test.sh | 59 - .../cli/src/attesteer/commands/mod.rs | 23 - .../src/attesteer/commands/send_dcap_quote.rs | 65 - .../commands/send_ias_attestation.rs | 66 - tee-worker/bitacross/cli/src/attesteer/mod.rs | 41 - .../cli/src/base_cli/commands/balance.rs | 39 - .../cli/src/base_cli/commands/faucet.rs | 65 - .../cli/src/base_cli/commands/listen.rs | 88 - .../cli/src/base_cli/commands/litentry/mod.rs | 15 - .../cli/src/base_cli/commands/mod.rs | 6 - .../base_cli/commands/register_tcb_info.rs | 146 - .../cli/src/base_cli/commands/transfer.rs | 61 - tee-worker/bitacross/cli/src/base_cli/mod.rs | 176 - tee-worker/bitacross/cli/src/benchmark/mod.rs | 378 -- tee-worker/bitacross/cli/src/command_utils.rs | 87 - tee-worker/bitacross/cli/src/commands.rs | 47 - tee-worker/bitacross/cli/src/lib.rs | 112 - tee-worker/bitacross/cli/src/main.rs | 29 - .../src/trusted_base_cli/commands/balance.rs | 34 - .../bitacross/direct_call_sign_bitcoin.rs | 55 - .../bitacross/direct_call_sign_ethereum.rs | 58 - .../commands/bitacross/mod.rs | 20 - .../commands/bitacross/utils.rs | 127 - .../trusted_base_cli/commands/get_shard.rs | 69 - .../cli/src/trusted_base_cli/commands/mod.rs | 7 - .../src/trusted_base_cli/commands/nonce.rs | 44 - .../trusted_base_cli/commands/set_balance.rs | 64 - .../src/trusted_base_cli/commands/transfer.rs | 72 - .../commands/unshield_funds.rs | 67 - .../bitacross/cli/src/trusted_base_cli/mod.rs | 122 - tee-worker/bitacross/cli/src/trusted_cli.rs | 60 - .../cli/src/trusted_command_utils.rs | 169 - .../bitacross/cli/src/trusted_operation.rs | 443 -- .../test_auto_shielding_with_transfer_bob.sh | 141 - ..._on_target_nodes_with_transfer_to_alice.sh | 159 - tee-worker/bitacross/cli/tests/basic_tests.rs | 24 - .../core-primitives/enclave-api/Cargo.toml | 34 - .../core-primitives/enclave-api/build.rs | 24 - .../enclave-api/ffi/Cargo.toml | 12 - .../core-primitives/enclave-api/ffi/build.rs | 44 - .../enclave-api/ffi/src/lib.rs | 270 - .../enclave-api/src/enclave_base.rs | 510 -- .../enclave-api/src/enclave_test.rs | 48 - .../core-primitives/enclave-api/src/error.rs | 14 - .../core-primitives/enclave-api/src/lib.rs | 47 - .../enclave-api/src/remote_attestation.rs | 870 --- .../enclave-api/src/sidechain.rs | 109 - .../core-primitives/enclave-api/src/utils.rs | 27 - .../core-primitives/stf-executor/Cargo.toml | 81 - .../stf-executor/src/enclave_signer.rs | 151 - .../core-primitives/stf-executor/src/error.rs | 88 - .../stf-executor/src/executor.rs | 393 -- .../stf-executor/src/executor_tests.rs | 279 - .../stf-executor/src/getter_executor.rs | 137 - .../core-primitives/stf-executor/src/lib.rs | 305 - .../core-primitives/stf-executor/src/mocks.rs | 168 - .../stf-executor/src/state_getter.rs | 85 - .../stf-executor/src/traits.rs | 87 - .../top-pool-author/Cargo.toml | 68 - .../top-pool-author/src/api.rs | 171 - .../top-pool-author/src/author.rs | 407 -- .../top-pool-author/src/author_tests.rs | 141 - .../top-pool-author/src/client_error.rs | 183 - .../top-pool-author/src/error.rs | 111 - .../top-pool-author/src/lib.rs | 51 - .../top-pool-author/src/mocks.rs | 306 - .../top-pool-author/src/test_fixtures.rs | 26 - .../top-pool-author/src/test_utils.rs | 49 - .../top-pool-author/src/top_filter.rs | 320 - .../top-pool-author/src/traits.rs | 102 - .../core-primitives/top-pool/Cargo.toml | 55 - .../core-primitives/top-pool/src/base_pool.rs | 1379 ----- .../top-pool/src/basic_pool.rs | 253 - .../core-primitives/top-pool/src/error.rs | 95 - .../core-primitives/top-pool/src/future.rs | 316 - .../core-primitives/top-pool/src/lib.rs | 47 - .../core-primitives/top-pool/src/listener.rs | 185 - .../core-primitives/top-pool/src/mocks/mod.rs | 22 - .../top-pool/src/mocks/rpc_responder_mock.rs | 85 - .../src/mocks/trusted_operation_pool_mock.rs | 225 - .../core-primitives/top-pool/src/pool.rs | 810 --- .../top-pool/src/primitives.rs | 346 -- .../core-primitives/top-pool/src/ready.rs | 800 --- .../core-primitives/top-pool/src/rotator.rs | 221 - .../top-pool/src/tracked_map.rs | 198 - .../top-pool/src/validated_pool.rs | 738 --- .../core-primitives/top-pool/src/watcher.rs | 176 - .../core/direct-rpc-client/Cargo.toml | 43 - .../core/direct-rpc-client/src/lib.rs | 198 - .../core/direct-rpc-server/Cargo.toml | 49 - .../direct-rpc-server/src/builders/mod.rs | 19 - .../src/builders/rpc_response_builder.rs | 64 - .../src/builders/rpc_return_value_builder.rs | 62 - .../core/direct-rpc-server/src/lib.rs | 165 - .../src/mocks/determine_watch_mock.rs | 52 - .../core/direct-rpc-server/src/mocks/mod.rs | 20 - .../src/mocks/response_channel_mock.rs | 55 - .../src/mocks/send_rpc_response_mock.rs | 83 - .../direct-rpc-server/src/response_channel.rs | 26 - .../src/rpc_connection_registry.rs | 140 - .../direct-rpc-server/src/rpc_responder.rs | 389 -- .../src/rpc_watch_extractor.rs | 133 - .../direct-rpc-server/src/rpc_ws_handler.rs | 241 - .../core/offchain-worker-executor/Cargo.toml | 61 - .../offchain-worker-executor/src/error.rs | 40 - .../offchain-worker-executor/src/executor.rs | 373 -- .../core/offchain-worker-executor/src/lib.rs | 33 - .../block-import-dispatcher/Cargo.toml | 38 - .../block-import-dispatcher/src/error.rs | 47 - .../src/immediate_dispatcher.rs | 107 - .../block-import-dispatcher/src/lib.rs | 125 - .../trigger_parentchain_block_import_mock.rs | 102 - .../src/triggered_dispatcher.rs | 374 -- .../parentchain/block-importer/Cargo.toml | 57 - .../block-importer/src/block_importer.rs | 221 - .../block-importer/src/block_importer_mock.rs | 65 - .../parentchain/block-importer/src/error.rs | 51 - .../parentchain/block-importer/src/lib.rs | 61 - .../indirect-calls-executor/Cargo.toml | 88 - .../indirect-calls-executor/src/error.rs | 81 - .../src/event_filter.rs | 33 - .../indirect-calls-executor/src/executor.rs | 436 -- .../src/filter_metadata.rs | 118 - .../indirect-calls-executor/src/lib.rs | 52 - .../indirect-calls-executor/src/mock.rs | 317 - .../indirect-calls-executor/src/traits.rs | 65 - .../parentchain/parentchain-crate/Cargo.toml | 42 - .../parentchain/parentchain-crate/src/lib.rs | 31 - tee-worker/bitacross/docker/README.md | 106 - .../bitacross/docker/docker-compose.yml | 42 - tee-worker/bitacross/docker/entrypoint.sh | 19 - tee-worker/bitacross/docker/fork.Dockerfile | 26 - .../docker/lit-parentchain-nonce.yml | 22 - .../bitacross/docker/lit-sign-bitcoin.yml | 21 - .../docker/multiworker-docker-compose.yml | 102 - tee-worker/bitacross/docker/ping.Dockerfile | 19 - .../bitacross/docker/sidechain-benchmark.yml | 25 - tee-worker/bitacross/docs/README.md | 25 - .../docs/diagramms/block_import_sequence.svg | 4 - .../bitacross/enclave-runtime/Cargo.lock | 5313 ----------------- .../bitacross/enclave-runtime/Cargo.toml | 186 - .../Enclave.config.production.xml | 12 - .../enclave-runtime/Enclave.config.xml | 12 - .../bitacross/enclave-runtime/Enclave.edl | 253 - .../bitacross/enclave-runtime/Enclave.lds | 9 - .../enclave-runtime/Enclave_private.pem | 39 - tee-worker/bitacross/enclave-runtime/Makefile | 68 - .../bitacross/enclave-runtime/README.md | 2 - .../enclave-runtime/rust-toolchain.toml | 4 - .../bitacross/enclave-runtime/rustfmt.toml | 18 - .../enclave-runtime/src/attestation.rs | 583 -- .../enclave-runtime/src/empty_impls.rs | 24 - .../bitacross/enclave-runtime/src/error.rs | 87 - .../src/initialization/global_components.rs | 459 -- .../enclave-runtime/src/initialization/mod.rs | 517 -- .../src/initialization/parentchain/common.rs | 291 - .../parentchain/integritee_parachain.rs | 113 - .../parentchain/integritee_solochain.rs | 112 - .../src/initialization/parentchain/mod.rs | 136 - .../parentchain/target_a_parachain.rs | 117 - .../parentchain/target_a_solochain.rs | 110 - .../parentchain/target_b_parachain.rs | 117 - .../parentchain/target_b_solochain.rs | 110 - .../bitacross/enclave-runtime/src/ipfs.rs | 105 - .../bitacross/enclave-runtime/src/lib.rs | 772 --- .../src/ocall/attestation_ocall.rs | 275 - .../enclave-runtime/src/ocall/ffi.rs | 111 - .../enclave-runtime/src/ocall/ipfs_ocall.rs | 57 - .../src/ocall/metrics_ocall.rs | 42 - .../enclave-runtime/src/ocall/mod.rs | 25 - .../src/ocall/on_chain_ocall.rs | 226 - .../bitacross/enclave-runtime/src/rpc/mod.rs | 19 - .../src/rpc/rpc_response_channel.rs | 40 - .../src/rpc/worker_api_direct.rs | 603 -- .../enclave-runtime/src/shard_config.rs | 24 - .../src/shard_creation_info.rs | 144 - .../enclave-runtime/src/stf_task_handler.rs | 1 - .../bitacross/enclave-runtime/src/sync.rs | 104 - .../enclave-runtime/src/test/Counter.sol | 31 - .../enclave-runtime/src/test/cert_tests.rs | 72 - .../src/test/direct_rpc_tests.rs | 176 - .../src/test/enclave_signer_tests.rs | 172 - .../src/test/fixtures/components.rs | 63 - .../test/fixtures/initialize_test_state.rs | 43 - .../enclave-runtime/src/test/fixtures/mod.rs | 21 - .../src/test/fixtures/ra_dump_cert_TEST4.der | Bin 3234 -> 0 bytes ...st_ra_signer_attn_MRSIGNER1_MRENCLAVE1.bin | Bin 64 -> 0 bytes .../src/test/fixtures/test_setup.rs | 116 - .../enclave-runtime/src/test/ipfs_tests.rs | 42 - .../src/test/mocks/attestation_ocall_mock.rs | 101 - .../enclave-runtime/src/test/mocks/mod.rs | 21 - .../src/test/mocks/rpc_responder_mock.rs | 84 - .../enclave-runtime/src/test/mocks/types.rs | 84 - .../bitacross/enclave-runtime/src/test/mod.rs | 27 - .../src/test/state_getter_tests.rs | 53 - .../enclave-runtime/src/test/tests_main.rs | 625 -- .../src/test/top_pool_tests.rs | 210 - .../enclave-runtime/src/tls_ra/README.md | 33 - .../src/tls_ra/authentication.rs | 158 - .../enclave-runtime/src/tls_ra/mocks.rs | 103 - .../enclave-runtime/src/tls_ra/mod.rs | 85 - .../src/tls_ra/seal_handler.rs | 382 -- .../enclave-runtime/src/tls_ra/tests.rs | 201 - .../src/tls_ra/tls_ra_client.rs | 357 -- .../src/tls_ra/tls_ra_server.rs | 340 -- .../bitacross/enclave-runtime/src/utils.rs | 126 - .../enclave-runtime/src/vc_issuance_task.rs | 0 .../x86_64-unknown-linux-sgx.json | 31 - tee-worker/bitacross/entrypoint.sh | 37 - tee-worker/bitacross/example/README.md | 9 - .../bitacross/example/client/definitions.json | 256 - .../bitacross/example/client/example.go | 405 -- tee-worker/bitacross/example/client/go.mod | 20 - tee-worker/bitacross/example/client/go.sum | 70 - tee-worker/bitacross/extract_identity | 28 - tee-worker/bitacross/lib/readme.txt | 1 - tee-worker/bitacross/license_header_scs.txt | 16 - .../litentry/core/direct-call/Cargo.toml | 59 - .../direct-call/src/handler/kill_ceremony.rs | 43 - .../core/direct-call/src/handler/mod.rs | 22 - .../direct-call/src/handler/nonce_share.rs | 126 - .../src/handler/partial_signature_share.rs | 123 - .../direct-call/src/handler/sign_bitcoin.rs | 188 - .../direct-call/src/handler/sign_ethereum.rs | 116 - .../core/direct-call/src/handler/sign_ton.rs | 103 - .../litentry/core/direct-call/src/lib.rs | 132 - tee-worker/bitacross/rust-sgx-sdk/Readme.md | 5 - tee-worker/bitacross/rust-sgx-sdk/buildenv.mk | 179 - .../rust-sgx-sdk/common/inc/assert.h | 63 - .../rust-sgx-sdk/common/inc/complex.h | 134 - .../bitacross/rust-sgx-sdk/common/inc/ctype.h | 65 - .../rust-sgx-sdk/common/inc/dirent.h | 48 - .../rust-sgx-sdk/common/inc/endian.h | 33 - .../bitacross/rust-sgx-sdk/common/inc/errno.h | 187 - .../bitacross/rust-sgx-sdk/common/inc/fenv.h | 139 - .../bitacross/rust-sgx-sdk/common/inc/float.h | 84 - .../rust-sgx-sdk/common/inc/inttypes.h | 330 - .../rust-sgx-sdk/common/inc/iso646.h | 26 - .../rust-sgx-sdk/common/inc/limits.h | 41 - .../bitacross/rust-sgx-sdk/common/inc/math.h | 430 -- .../rust-sgx-sdk/common/inc/mbusafecrt.h | 85 - .../bitacross/rust-sgx-sdk/common/inc/netdb.h | 41 - .../bitacross/rust-sgx-sdk/common/inc/poll.h | 38 - .../rust-sgx-sdk/common/inc/pthread.h | 34 - .../bitacross/rust-sgx-sdk/common/inc/pwd.h | 40 - .../bitacross/rust-sgx-sdk/common/inc/sched.h | 62 - .../rust-sgx-sdk/common/inc/setjmp.h | 65 - .../rust-sgx-sdk/common/inc/signal.h | 104 - .../rust-sgx-sdk/common/inc/stdalign.h | 15 - .../rust-sgx-sdk/common/inc/stdarg.h | 48 - .../rust-sgx-sdk/common/inc/stdbool.h | 44 - .../rust-sgx-sdk/common/inc/stddef.h | 70 - .../rust-sgx-sdk/common/inc/stdint.h | 24 - .../bitacross/rust-sgx-sdk/common/inc/stdio.h | 95 - .../rust-sgx-sdk/common/inc/stdlib.h | 159 - .../rust-sgx-sdk/common/inc/string.h | 130 - .../rust-sgx-sdk/common/inc/sys/_types.h | 168 - .../rust-sgx-sdk/common/inc/sys/cdefs.h | 132 - .../rust-sgx-sdk/common/inc/sys/endian.h | 54 - .../rust-sgx-sdk/common/inc/sys/epoll.h | 42 - .../rust-sgx-sdk/common/inc/sys/fpu.h | 99 - .../rust-sgx-sdk/common/inc/sys/ieee.h | 170 - .../rust-sgx-sdk/common/inc/sys/limits.h | 77 - .../rust-sgx-sdk/common/inc/sys/sockaddr.h | 32 - .../rust-sgx-sdk/common/inc/sys/socket.h | 54 - .../rust-sgx-sdk/common/inc/sys/stat.h | 127 - .../rust-sgx-sdk/common/inc/sys/stdint.h | 260 - .../common/inc/sys/struct_timespec.h | 37 - .../rust-sgx-sdk/common/inc/sys/types.h | 129 - .../rust-sgx-sdk/common/inc/sys/uio.h | 35 - .../bitacross/rust-sgx-sdk/common/inc/time.h | 105 - .../rust-sgx-sdk/common/inc/unistd.h | 59 - .../bitacross/rust-sgx-sdk/common/inc/wchar.h | 143 - .../rust-sgx-sdk/common/inc/wctype.h | 80 - .../bitacross/rust-sgx-sdk/edl/inc/dirent.h | 39 - .../bitacross/rust-sgx-sdk/edl/inc/stat.h | 65 - .../rust-sgx-sdk/edl/intel/sgx_dcap_tvl.edl | 73 - .../rust-sgx-sdk/edl/intel/sgx_pthread.edl | 38 - .../edl/intel/sgx_tkey_exchange.edl | 49 - .../edl/intel/sgx_tprotected_fs.edl | 47 - .../rust-sgx-sdk/edl/intel/sgx_tstdc.edl | 48 - .../edl/intel/sgx_tswitchless.edl | 39 - .../rust-sgx-sdk/edl/intel/sgx_ttls.edl | 62 - .../rust-sgx-sdk/edl/sgx_asyncio.edl | 33 - .../rust-sgx-sdk/edl/sgx_backtrace.edl | 31 - .../bitacross/rust-sgx-sdk/edl/sgx_env.edl | 40 - .../bitacross/rust-sgx-sdk/edl/sgx_fd.edl | 57 - .../bitacross/rust-sgx-sdk/edl/sgx_file.edl | 66 - .../bitacross/rust-sgx-sdk/edl/sgx_fs.edl | 31 - .../bitacross/rust-sgx-sdk/edl/sgx_mem.edl | 40 - .../bitacross/rust-sgx-sdk/edl/sgx_net.edl | 41 - .../rust-sgx-sdk/edl/sgx_net_switchless.edl | 92 - .../bitacross/rust-sgx-sdk/edl/sgx_pipe.edl | 31 - .../rust-sgx-sdk/edl/sgx_process.edl | 28 - .../bitacross/rust-sgx-sdk/edl/sgx_signal.edl | 43 - .../bitacross/rust-sgx-sdk/edl/sgx_socket.edl | 111 - .../bitacross/rust-sgx-sdk/edl/sgx_stdio.edl | 29 - .../bitacross/rust-sgx-sdk/edl/sgx_sys.edl | 32 - .../bitacross/rust-sgx-sdk/edl/sgx_thread.edl | 32 - .../bitacross/rust-sgx-sdk/edl/sgx_time.edl | 29 - .../bitacross/rust-sgx-sdk/edl/sgx_tstd.edl | 38 - tee-worker/bitacross/rust-sgx-sdk/version | 1 - .../scripts/benchmark_local-setup.sh | 31 - .../bitacross/scripts/changelog/.gitignore | 4 - .../bitacross/scripts/changelog/Gemfile | 21 - .../bitacross/scripts/changelog/Gemfile.lock | 82 - .../bitacross/scripts/changelog/README.md | 3 - .../bitacross/scripts/changelog/bin/changelog | 84 - .../scripts/changelog/digests/.gitignore | 1 - .../scripts/changelog/digests/.gitkeep | 0 .../scripts/changelog/lib/changelog.rb | 38 - .../changelog/templates/_free_notes.md.tera | 10 - .../templates/challenge_level.md.tera | 37 - .../changelog/templates/change.md.tera | 42 - .../changelog/templates/changes.md.tera | 23 - .../templates/changes_applibs.md.tera | 17 - .../templates/changes_client.md.tera | 17 - .../changelog/templates/changes_core.md.tera | 17 - .../changelog/templates/changes_evm.md.tera | 17 - .../changelog/templates/changes_misc.md.tera | 37 - .../templates/changes_offchain.md.tera | 17 - .../templates/changes_sidechain.md.tera | 17 - .../scripts/changelog/templates/debug.md.tera | 8 - .../templates/global_challenge_level.md.tera | 26 - .../templates/global_priority.md.tera | 27 - .../changelog/templates/high_priority.md.tera | 38 - .../changelog/templates/pre_release.md.tera | 11 - .../changelog/templates/template.md.tera | 33 - tee-worker/bitacross/scripts/init_env.sh | 15 - tee-worker/bitacross/scripts/launch.sh | 90 - .../bitacross/scripts/launch_local_worker.sh | 138 - .../scripts/litentry/release/ReadMe.md | 106 - .../scripts/litentry/release/build.sh | 50 - .../scripts/litentry/release/deploy.sh | 559 -- .../scripts/litentry/release/prepare.sh | 50 - .../release/template/para-alice.service | 15 - .../release/template/relay-alice.service | 15 - .../release/template/relay-bob.service | 15 - .../litentry/release/template/worker.service | 14 - .../scripts/litentry/ubuntu_setup.sh | 71 - tee-worker/bitacross/scripts/m6.sh | 23 - tee-worker/bitacross/scripts/m8.sh | 21 - .../bitacross/scripts/polkadot_update.sh | 97 - tee-worker/bitacross/scripts/sidechain.sh | 17 - .../bitacross/scripts/test_transfer/README.md | 6 - .../scripts/test_transfer/package-lock.json | 1322 ---- .../scripts/test_transfer/package.json | 16 - .../scripts/test_transfer/transfer.js | 53 - tee-worker/bitacross/service/Cargo.toml | 94 - tee-worker/bitacross/service/build.rs | 31 - .../bitacross/service/src/account_funding.rs | 182 - tee-worker/bitacross/service/src/cli.yml | 212 - tee-worker/bitacross/service/src/config.rs | 617 -- .../bitacross/service/src/enclave/api.rs | 116 - .../bitacross/service/src/enclave/mod.rs | 20 - .../bitacross/service/src/enclave/tls_ra.rs | 117 - tee-worker/bitacross/service/src/error.rs | 65 - .../bitacross/service/src/globals/mod.rs | 19 - .../service/src/globals/tokio_handle.rs | 108 - .../service/src/initialized_service.rs | 109 - tee-worker/bitacross/service/src/main.rs | 48 - tee-worker/bitacross/service/src/main_impl.rs | 942 --- .../service/src/ocall_bridge/bridge_api.rs | 236 - .../src/ocall_bridge/component_factory.rs | 117 - .../src/ocall_bridge/ffi/get_ias_socket.rs | 86 - .../service/src/ocall_bridge/ffi/get_quote.rs | 140 - .../ffi/get_qve_report_on_quote.rs | 100 - .../src/ocall_bridge/ffi/get_update_info.rs | 61 - .../src/ocall_bridge/ffi/init_quote.rs | 85 - .../service/src/ocall_bridge/ffi/ipfs.rs | 76 - .../service/src/ocall_bridge/ffi/mod.rs | 32 - .../ocall_bridge/ffi/send_to_parentchain.rs | 86 - .../src/ocall_bridge/ffi/update_metric.rs | 50 - .../src/ocall_bridge/ffi/worker_request.rs | 77 - .../service/src/ocall_bridge/ipfs_ocall.rs | 112 - .../service/src/ocall_bridge/metrics_ocall.rs | 51 - .../bitacross/service/src/ocall_bridge/mod.rs | 28 - .../ocall_bridge/remote_attestation_ocall.rs | 150 - .../src/ocall_bridge/worker_on_chain_ocall.rs | 297 - .../service/src/parentchain_handler.rs | 350 -- .../service/src/prometheus_metrics.rs | 210 - tee-worker/bitacross/service/src/setup.rs | 276 - .../bitacross/service/src/sync_state.rs | 92 - .../bitacross/service/src/tests/commons.rs | 63 - .../bitacross/service/src/tests/mock.rs | 69 - .../src/tests/mocks/enclave_api_mock.rs | 147 - .../mocks/initialization_handler_mock.rs | 34 - .../bitacross/service/src/tests/mocks/mod.rs | 21 - .../src/tests/mocks/parentchain_api_mock.rs | 111 - .../tests/mocks/update_worker_peers_mock.rs | 34 - tee-worker/bitacross/service/src/tests/mod.rs | 48 - .../src/tests/parentchain_handler_test.rs | 51 - tee-worker/bitacross/service/src/utils.rs | 53 - tee-worker/bitacross/service/src/wasm.rs | 62 - tee-worker/bitacross/service/src/worker.rs | 160 - .../service/src/worker_peers_updater.rs | 57 - tee-worker/bitacross/ts-tests/.editorconfig | 6 - tee-worker/bitacross/ts-tests/.gitignore | 1 - tee-worker/bitacross/ts-tests/.prettierrc | 7 - tee-worker/bitacross/ts-tests/README.md | 23 - .../integration-tests/.env.local.example | 5 - .../ts-tests/integration-tests/.env.staging | 5 - .../ts-tests/integration-tests/.eslintrc.json | 40 - .../ts-tests/integration-tests/package.json | 24 - .../integration-tests/sign_bitcoin.test.ts | 83 - .../ts-tests/integration-tests/tsconfig.json | 20 - tee-worker/bitacross/ts-tests/package.json | 9 - tee-worker/bitacross/ts-tests/pnpm-lock.yaml | 706 --- .../bitacross/ts-tests/pnpm-workspace.yaml | 0 tee-worker/bitacross/upstream_commit | 1 - .../enclave-metrics/src/lib.rs | 5 - .../node-api/metadata/src/lib.rs | 5 +- .../node-api/metadata/src/metadata_mocks.rs | 38 +- .../node-api/metadata/src/pallet_bitacross.rs | 50 - .../core-primitives/settings/src/lib.rs | 7 - .../types/src/parentchain/events.rs | 65 +- .../types/src/parentchain/mod.rs | 6 - .../src/integritee/event_filter.rs | 12 - .../src/target_a/event_filter.rs | 12 - .../src/target_b/event_filter.rs | 12 - .../indirect-calls-executor/src/mock.rs | 12 - 496 files changed, 38 insertions(+), 62137 deletions(-) delete mode 100644 tee-worker/bitacross/.dockerignore delete mode 100644 tee-worker/bitacross/.editorconfig delete mode 100644 tee-worker/bitacross/.env.dev delete mode 100644 tee-worker/bitacross/.gitattributes.orig delete mode 100755 tee-worker/bitacross/.githooks/pre-commit delete mode 100644 tee-worker/bitacross/.gitignore delete mode 100644 tee-worker/bitacross/DESIGN.md delete mode 100644 tee-worker/bitacross/Dockerfile delete mode 100755 tee-worker/bitacross/Jenkinsfile delete mode 100644 tee-worker/bitacross/LICENSE delete mode 100755 tee-worker/bitacross/Makefile delete mode 100755 tee-worker/bitacross/README.md delete mode 100755 tee-worker/bitacross/UpdateRustSGXSDK.mk delete mode 100644 tee-worker/bitacross/app-libs/parentchain-interface/Cargo.toml delete mode 100644 tee-worker/bitacross/app-libs/parentchain-interface/src/event_subscriber.rs delete mode 100644 tee-worker/bitacross/app-libs/parentchain-interface/src/integritee/event_filter.rs delete mode 100644 tee-worker/bitacross/app-libs/parentchain-interface/src/integritee/event_handler.rs delete mode 100644 tee-worker/bitacross/app-libs/parentchain-interface/src/integritee/mod.rs delete mode 100644 tee-worker/bitacross/app-libs/parentchain-interface/src/lib.rs delete mode 100644 tee-worker/bitacross/app-libs/parentchain-interface/src/target_a/event_filter.rs delete mode 100644 tee-worker/bitacross/app-libs/parentchain-interface/src/target_a/event_handler.rs delete mode 100644 tee-worker/bitacross/app-libs/parentchain-interface/src/target_a/mod.rs delete mode 100644 tee-worker/bitacross/app-libs/parentchain-interface/src/target_b/event_filter.rs delete mode 100644 tee-worker/bitacross/app-libs/parentchain-interface/src/target_b/event_handler.rs delete mode 100644 tee-worker/bitacross/app-libs/parentchain-interface/src/target_b/mod.rs delete mode 100644 tee-worker/bitacross/app-libs/sgx-runtime/Cargo.toml delete mode 100644 tee-worker/bitacross/app-libs/sgx-runtime/src/lib.rs delete mode 100644 tee-worker/bitacross/app-libs/stf/Cargo.toml delete mode 100644 tee-worker/bitacross/app-libs/stf/src/getter.rs delete mode 100644 tee-worker/bitacross/app-libs/stf/src/hash.rs delete mode 100644 tee-worker/bitacross/app-libs/stf/src/helpers.rs delete mode 100644 tee-worker/bitacross/app-libs/stf/src/lib.rs delete mode 100644 tee-worker/bitacross/app-libs/stf/src/stf_sgx.rs delete mode 100644 tee-worker/bitacross/app-libs/stf/src/stf_sgx_primitives.rs delete mode 100644 tee-worker/bitacross/app-libs/stf/src/stf_sgx_tests.rs delete mode 100644 tee-worker/bitacross/app-libs/stf/src/test_genesis.rs delete mode 100644 tee-worker/bitacross/app-libs/stf/src/trusted_call.rs delete mode 100644 tee-worker/bitacross/app-libs/stf/src/trusted_call_result.rs delete mode 100644 tee-worker/bitacross/assets/teebag_registry.gif delete mode 100644 tee-worker/bitacross/bin/README.md delete mode 100644 tee-worker/bitacross/bitacross/core/bc-enclave-registry/Cargo.toml delete mode 100644 tee-worker/bitacross/bitacross/core/bc-enclave-registry/src/lib.rs delete mode 100644 tee-worker/bitacross/bitacross/core/bc-musig2-ceremony/Cargo.toml delete mode 100644 tee-worker/bitacross/bitacross/core/bc-musig2-ceremony/src/lib.rs delete mode 100644 tee-worker/bitacross/bitacross/core/bc-musig2-event/Cargo.toml delete mode 100644 tee-worker/bitacross/bitacross/core/bc-musig2-event/src/lib.rs delete mode 100644 tee-worker/bitacross/bitacross/core/bc-relayer-registry/Cargo.toml delete mode 100644 tee-worker/bitacross/bitacross/core/bc-relayer-registry/src/lib.rs delete mode 100644 tee-worker/bitacross/bitacross/core/bc-signer-registry/Cargo.toml delete mode 100644 tee-worker/bitacross/bitacross/core/bc-signer-registry/src/lib.rs delete mode 100644 tee-worker/bitacross/bitacross/core/bc-task-processor/Cargo.toml delete mode 100644 tee-worker/bitacross/bitacross/core/bc-task-processor/src/lib.rs delete mode 100644 tee-worker/bitacross/bitacross/core/bc-task-sender/Cargo.toml delete mode 100644 tee-worker/bitacross/bitacross/core/bc-task-sender/src/lib.rs delete mode 100644 tee-worker/bitacross/build.Dockerfile delete mode 100644 tee-worker/bitacross/cli/Cargo.toml delete mode 100644 tee-worker/bitacross/cli/README.md delete mode 100755 tee-worker/bitacross/cli/benchmark.sh delete mode 100755 tee-worker/bitacross/cli/lit_parentchain_nonce.sh delete mode 100755 tee-worker/bitacross/cli/lit_ts_integration_test.sh delete mode 100644 tee-worker/bitacross/cli/src/attesteer/commands/mod.rs delete mode 100644 tee-worker/bitacross/cli/src/attesteer/commands/send_dcap_quote.rs delete mode 100644 tee-worker/bitacross/cli/src/attesteer/commands/send_ias_attestation.rs delete mode 100644 tee-worker/bitacross/cli/src/attesteer/mod.rs delete mode 100644 tee-worker/bitacross/cli/src/base_cli/commands/balance.rs delete mode 100644 tee-worker/bitacross/cli/src/base_cli/commands/faucet.rs delete mode 100644 tee-worker/bitacross/cli/src/base_cli/commands/listen.rs delete mode 100644 tee-worker/bitacross/cli/src/base_cli/commands/litentry/mod.rs delete mode 100644 tee-worker/bitacross/cli/src/base_cli/commands/mod.rs delete mode 100644 tee-worker/bitacross/cli/src/base_cli/commands/register_tcb_info.rs delete mode 100644 tee-worker/bitacross/cli/src/base_cli/commands/transfer.rs delete mode 100644 tee-worker/bitacross/cli/src/base_cli/mod.rs delete mode 100644 tee-worker/bitacross/cli/src/benchmark/mod.rs delete mode 100644 tee-worker/bitacross/cli/src/command_utils.rs delete mode 100644 tee-worker/bitacross/cli/src/commands.rs delete mode 100644 tee-worker/bitacross/cli/src/lib.rs delete mode 100644 tee-worker/bitacross/cli/src/main.rs delete mode 100644 tee-worker/bitacross/cli/src/trusted_base_cli/commands/balance.rs delete mode 100644 tee-worker/bitacross/cli/src/trusted_base_cli/commands/bitacross/direct_call_sign_bitcoin.rs delete mode 100644 tee-worker/bitacross/cli/src/trusted_base_cli/commands/bitacross/direct_call_sign_ethereum.rs delete mode 100644 tee-worker/bitacross/cli/src/trusted_base_cli/commands/bitacross/mod.rs delete mode 100644 tee-worker/bitacross/cli/src/trusted_base_cli/commands/bitacross/utils.rs delete mode 100644 tee-worker/bitacross/cli/src/trusted_base_cli/commands/get_shard.rs delete mode 100644 tee-worker/bitacross/cli/src/trusted_base_cli/commands/mod.rs delete mode 100644 tee-worker/bitacross/cli/src/trusted_base_cli/commands/nonce.rs delete mode 100644 tee-worker/bitacross/cli/src/trusted_base_cli/commands/set_balance.rs delete mode 100644 tee-worker/bitacross/cli/src/trusted_base_cli/commands/transfer.rs delete mode 100644 tee-worker/bitacross/cli/src/trusted_base_cli/commands/unshield_funds.rs delete mode 100644 tee-worker/bitacross/cli/src/trusted_base_cli/mod.rs delete mode 100644 tee-worker/bitacross/cli/src/trusted_cli.rs delete mode 100644 tee-worker/bitacross/cli/src/trusted_command_utils.rs delete mode 100644 tee-worker/bitacross/cli/src/trusted_operation.rs delete mode 100644 tee-worker/bitacross/cli/test_auto_shielding_with_transfer_bob.sh delete mode 100755 tee-worker/bitacross/cli/test_shield_on_target_nodes_with_transfer_to_alice.sh delete mode 100644 tee-worker/bitacross/cli/tests/basic_tests.rs delete mode 100644 tee-worker/bitacross/core-primitives/enclave-api/Cargo.toml delete mode 100644 tee-worker/bitacross/core-primitives/enclave-api/build.rs delete mode 100644 tee-worker/bitacross/core-primitives/enclave-api/ffi/Cargo.toml delete mode 100644 tee-worker/bitacross/core-primitives/enclave-api/ffi/build.rs delete mode 100644 tee-worker/bitacross/core-primitives/enclave-api/ffi/src/lib.rs delete mode 100644 tee-worker/bitacross/core-primitives/enclave-api/src/enclave_base.rs delete mode 100644 tee-worker/bitacross/core-primitives/enclave-api/src/enclave_test.rs delete mode 100644 tee-worker/bitacross/core-primitives/enclave-api/src/error.rs delete mode 100644 tee-worker/bitacross/core-primitives/enclave-api/src/lib.rs delete mode 100644 tee-worker/bitacross/core-primitives/enclave-api/src/remote_attestation.rs delete mode 100644 tee-worker/bitacross/core-primitives/enclave-api/src/sidechain.rs delete mode 100644 tee-worker/bitacross/core-primitives/enclave-api/src/utils.rs delete mode 100644 tee-worker/bitacross/core-primitives/stf-executor/Cargo.toml delete mode 100644 tee-worker/bitacross/core-primitives/stf-executor/src/enclave_signer.rs delete mode 100644 tee-worker/bitacross/core-primitives/stf-executor/src/error.rs delete mode 100644 tee-worker/bitacross/core-primitives/stf-executor/src/executor.rs delete mode 100644 tee-worker/bitacross/core-primitives/stf-executor/src/executor_tests.rs delete mode 100644 tee-worker/bitacross/core-primitives/stf-executor/src/getter_executor.rs delete mode 100644 tee-worker/bitacross/core-primitives/stf-executor/src/lib.rs delete mode 100644 tee-worker/bitacross/core-primitives/stf-executor/src/mocks.rs delete mode 100644 tee-worker/bitacross/core-primitives/stf-executor/src/state_getter.rs delete mode 100644 tee-worker/bitacross/core-primitives/stf-executor/src/traits.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool-author/Cargo.toml delete mode 100644 tee-worker/bitacross/core-primitives/top-pool-author/src/api.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool-author/src/author.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool-author/src/author_tests.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool-author/src/client_error.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool-author/src/error.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool-author/src/lib.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool-author/src/mocks.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool-author/src/test_fixtures.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool-author/src/test_utils.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool-author/src/top_filter.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool-author/src/traits.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool/Cargo.toml delete mode 100644 tee-worker/bitacross/core-primitives/top-pool/src/base_pool.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool/src/basic_pool.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool/src/error.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool/src/future.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool/src/lib.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool/src/listener.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool/src/mocks/mod.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool/src/mocks/rpc_responder_mock.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool/src/mocks/trusted_operation_pool_mock.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool/src/pool.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool/src/primitives.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool/src/ready.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool/src/rotator.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool/src/tracked_map.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool/src/validated_pool.rs delete mode 100644 tee-worker/bitacross/core-primitives/top-pool/src/watcher.rs delete mode 100644 tee-worker/bitacross/core/direct-rpc-client/Cargo.toml delete mode 100644 tee-worker/bitacross/core/direct-rpc-client/src/lib.rs delete mode 100644 tee-worker/bitacross/core/direct-rpc-server/Cargo.toml delete mode 100644 tee-worker/bitacross/core/direct-rpc-server/src/builders/mod.rs delete mode 100644 tee-worker/bitacross/core/direct-rpc-server/src/builders/rpc_response_builder.rs delete mode 100644 tee-worker/bitacross/core/direct-rpc-server/src/builders/rpc_return_value_builder.rs delete mode 100644 tee-worker/bitacross/core/direct-rpc-server/src/lib.rs delete mode 100644 tee-worker/bitacross/core/direct-rpc-server/src/mocks/determine_watch_mock.rs delete mode 100644 tee-worker/bitacross/core/direct-rpc-server/src/mocks/mod.rs delete mode 100644 tee-worker/bitacross/core/direct-rpc-server/src/mocks/response_channel_mock.rs delete mode 100644 tee-worker/bitacross/core/direct-rpc-server/src/mocks/send_rpc_response_mock.rs delete mode 100644 tee-worker/bitacross/core/direct-rpc-server/src/response_channel.rs delete mode 100644 tee-worker/bitacross/core/direct-rpc-server/src/rpc_connection_registry.rs delete mode 100644 tee-worker/bitacross/core/direct-rpc-server/src/rpc_responder.rs delete mode 100644 tee-worker/bitacross/core/direct-rpc-server/src/rpc_watch_extractor.rs delete mode 100644 tee-worker/bitacross/core/direct-rpc-server/src/rpc_ws_handler.rs delete mode 100644 tee-worker/bitacross/core/offchain-worker-executor/Cargo.toml delete mode 100644 tee-worker/bitacross/core/offchain-worker-executor/src/error.rs delete mode 100644 tee-worker/bitacross/core/offchain-worker-executor/src/executor.rs delete mode 100644 tee-worker/bitacross/core/offchain-worker-executor/src/lib.rs delete mode 100644 tee-worker/bitacross/core/parentchain/block-import-dispatcher/Cargo.toml delete mode 100644 tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/error.rs delete mode 100644 tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/immediate_dispatcher.rs delete mode 100644 tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/lib.rs delete mode 100644 tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/trigger_parentchain_block_import_mock.rs delete mode 100644 tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/triggered_dispatcher.rs delete mode 100644 tee-worker/bitacross/core/parentchain/block-importer/Cargo.toml delete mode 100644 tee-worker/bitacross/core/parentchain/block-importer/src/block_importer.rs delete mode 100644 tee-worker/bitacross/core/parentchain/block-importer/src/block_importer_mock.rs delete mode 100644 tee-worker/bitacross/core/parentchain/block-importer/src/error.rs delete mode 100644 tee-worker/bitacross/core/parentchain/block-importer/src/lib.rs delete mode 100644 tee-worker/bitacross/core/parentchain/indirect-calls-executor/Cargo.toml delete mode 100644 tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/error.rs delete mode 100644 tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/event_filter.rs delete mode 100644 tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/executor.rs delete mode 100644 tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/filter_metadata.rs delete mode 100644 tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/lib.rs delete mode 100644 tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/mock.rs delete mode 100644 tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/traits.rs delete mode 100644 tee-worker/bitacross/core/parentchain/parentchain-crate/Cargo.toml delete mode 100644 tee-worker/bitacross/core/parentchain/parentchain-crate/src/lib.rs delete mode 100644 tee-worker/bitacross/docker/README.md delete mode 100644 tee-worker/bitacross/docker/docker-compose.yml delete mode 100755 tee-worker/bitacross/docker/entrypoint.sh delete mode 100644 tee-worker/bitacross/docker/fork.Dockerfile delete mode 100644 tee-worker/bitacross/docker/lit-parentchain-nonce.yml delete mode 100644 tee-worker/bitacross/docker/lit-sign-bitcoin.yml delete mode 100644 tee-worker/bitacross/docker/multiworker-docker-compose.yml delete mode 100644 tee-worker/bitacross/docker/ping.Dockerfile delete mode 100644 tee-worker/bitacross/docker/sidechain-benchmark.yml delete mode 100644 tee-worker/bitacross/docs/README.md delete mode 100644 tee-worker/bitacross/docs/diagramms/block_import_sequence.svg delete mode 100644 tee-worker/bitacross/enclave-runtime/Cargo.lock delete mode 100644 tee-worker/bitacross/enclave-runtime/Cargo.toml delete mode 100644 tee-worker/bitacross/enclave-runtime/Enclave.config.production.xml delete mode 100644 tee-worker/bitacross/enclave-runtime/Enclave.config.xml delete mode 100644 tee-worker/bitacross/enclave-runtime/Enclave.edl delete mode 100644 tee-worker/bitacross/enclave-runtime/Enclave.lds delete mode 100644 tee-worker/bitacross/enclave-runtime/Enclave_private.pem delete mode 100644 tee-worker/bitacross/enclave-runtime/Makefile delete mode 100644 tee-worker/bitacross/enclave-runtime/README.md delete mode 100644 tee-worker/bitacross/enclave-runtime/rust-toolchain.toml delete mode 100644 tee-worker/bitacross/enclave-runtime/rustfmt.toml delete mode 100644 tee-worker/bitacross/enclave-runtime/src/attestation.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/empty_impls.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/error.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/initialization/global_components.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/initialization/mod.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/common.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/integritee_parachain.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/integritee_solochain.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/mod.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/target_a_parachain.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/target_a_solochain.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/target_b_parachain.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/target_b_solochain.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/ipfs.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/lib.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/ocall/attestation_ocall.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/ocall/ffi.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/ocall/ipfs_ocall.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/ocall/metrics_ocall.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/ocall/mod.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/ocall/on_chain_ocall.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/rpc/mod.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/rpc/rpc_response_channel.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/rpc/worker_api_direct.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/shard_config.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/shard_creation_info.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/stf_task_handler.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/sync.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/Counter.sol delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/cert_tests.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/direct_rpc_tests.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/enclave_signer_tests.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/fixtures/components.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/fixtures/initialize_test_state.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/fixtures/mod.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/fixtures/ra_dump_cert_TEST4.der delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/fixtures/test_ra_signer_attn_MRSIGNER1_MRENCLAVE1.bin delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/fixtures/test_setup.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/ipfs_tests.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/mocks/attestation_ocall_mock.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/mocks/mod.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/mocks/rpc_responder_mock.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/mocks/types.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/mod.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/state_getter_tests.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/tests_main.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/test/top_pool_tests.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/tls_ra/README.md delete mode 100644 tee-worker/bitacross/enclave-runtime/src/tls_ra/authentication.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/tls_ra/mocks.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/tls_ra/mod.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/tls_ra/seal_handler.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/tls_ra/tests.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/tls_ra/tls_ra_client.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/tls_ra/tls_ra_server.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/utils.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/src/vc_issuance_task.rs delete mode 100644 tee-worker/bitacross/enclave-runtime/x86_64-unknown-linux-sgx.json delete mode 100755 tee-worker/bitacross/entrypoint.sh delete mode 100644 tee-worker/bitacross/example/README.md delete mode 100644 tee-worker/bitacross/example/client/definitions.json delete mode 100644 tee-worker/bitacross/example/client/example.go delete mode 100644 tee-worker/bitacross/example/client/go.mod delete mode 100644 tee-worker/bitacross/example/client/go.sum delete mode 100755 tee-worker/bitacross/extract_identity delete mode 100644 tee-worker/bitacross/lib/readme.txt delete mode 100644 tee-worker/bitacross/license_header_scs.txt delete mode 100644 tee-worker/bitacross/litentry/core/direct-call/Cargo.toml delete mode 100644 tee-worker/bitacross/litentry/core/direct-call/src/handler/kill_ceremony.rs delete mode 100644 tee-worker/bitacross/litentry/core/direct-call/src/handler/mod.rs delete mode 100644 tee-worker/bitacross/litentry/core/direct-call/src/handler/nonce_share.rs delete mode 100644 tee-worker/bitacross/litentry/core/direct-call/src/handler/partial_signature_share.rs delete mode 100644 tee-worker/bitacross/litentry/core/direct-call/src/handler/sign_bitcoin.rs delete mode 100644 tee-worker/bitacross/litentry/core/direct-call/src/handler/sign_ethereum.rs delete mode 100644 tee-worker/bitacross/litentry/core/direct-call/src/handler/sign_ton.rs delete mode 100644 tee-worker/bitacross/litentry/core/direct-call/src/lib.rs delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/Readme.md delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/buildenv.mk delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/assert.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/complex.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/ctype.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/dirent.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/endian.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/errno.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/fenv.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/float.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/inttypes.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/iso646.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/limits.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/math.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/mbusafecrt.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/netdb.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/poll.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/pthread.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/pwd.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/sched.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/setjmp.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/signal.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/stdalign.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/stdarg.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/stdbool.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/stddef.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/stdint.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/stdio.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/stdlib.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/string.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/_types.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/cdefs.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/endian.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/epoll.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/fpu.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/ieee.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/limits.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/sockaddr.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/socket.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/stat.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/stdint.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/struct_timespec.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/types.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/uio.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/time.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/unistd.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/wchar.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/common/inc/wctype.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/inc/dirent.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/inc/stat.h delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_dcap_tvl.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_pthread.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_tkey_exchange.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_tprotected_fs.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_tstdc.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_tswitchless.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_ttls.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_asyncio.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_backtrace.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_env.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_fd.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_file.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_fs.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_mem.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_net.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_net_switchless.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_pipe.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_process.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_signal.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_socket.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_stdio.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_sys.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_thread.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_time.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/edl/sgx_tstd.edl delete mode 100644 tee-worker/bitacross/rust-sgx-sdk/version delete mode 100644 tee-worker/bitacross/scripts/benchmark_local-setup.sh delete mode 100644 tee-worker/bitacross/scripts/changelog/.gitignore delete mode 100644 tee-worker/bitacross/scripts/changelog/Gemfile delete mode 100644 tee-worker/bitacross/scripts/changelog/Gemfile.lock delete mode 100644 tee-worker/bitacross/scripts/changelog/README.md delete mode 100755 tee-worker/bitacross/scripts/changelog/bin/changelog delete mode 100644 tee-worker/bitacross/scripts/changelog/digests/.gitignore delete mode 100644 tee-worker/bitacross/scripts/changelog/digests/.gitkeep delete mode 100644 tee-worker/bitacross/scripts/changelog/lib/changelog.rb delete mode 100644 tee-worker/bitacross/scripts/changelog/templates/_free_notes.md.tera delete mode 100644 tee-worker/bitacross/scripts/changelog/templates/challenge_level.md.tera delete mode 100644 tee-worker/bitacross/scripts/changelog/templates/change.md.tera delete mode 100644 tee-worker/bitacross/scripts/changelog/templates/changes.md.tera delete mode 100644 tee-worker/bitacross/scripts/changelog/templates/changes_applibs.md.tera delete mode 100644 tee-worker/bitacross/scripts/changelog/templates/changes_client.md.tera delete mode 100644 tee-worker/bitacross/scripts/changelog/templates/changes_core.md.tera delete mode 100644 tee-worker/bitacross/scripts/changelog/templates/changes_evm.md.tera delete mode 100644 tee-worker/bitacross/scripts/changelog/templates/changes_misc.md.tera delete mode 100644 tee-worker/bitacross/scripts/changelog/templates/changes_offchain.md.tera delete mode 100644 tee-worker/bitacross/scripts/changelog/templates/changes_sidechain.md.tera delete mode 100644 tee-worker/bitacross/scripts/changelog/templates/debug.md.tera delete mode 100644 tee-worker/bitacross/scripts/changelog/templates/global_challenge_level.md.tera delete mode 100644 tee-worker/bitacross/scripts/changelog/templates/global_priority.md.tera delete mode 100644 tee-worker/bitacross/scripts/changelog/templates/high_priority.md.tera delete mode 100644 tee-worker/bitacross/scripts/changelog/templates/pre_release.md.tera delete mode 100644 tee-worker/bitacross/scripts/changelog/templates/template.md.tera delete mode 100755 tee-worker/bitacross/scripts/init_env.sh delete mode 100755 tee-worker/bitacross/scripts/launch.sh delete mode 100755 tee-worker/bitacross/scripts/launch_local_worker.sh delete mode 100644 tee-worker/bitacross/scripts/litentry/release/ReadMe.md delete mode 100755 tee-worker/bitacross/scripts/litentry/release/build.sh delete mode 100755 tee-worker/bitacross/scripts/litentry/release/deploy.sh delete mode 100755 tee-worker/bitacross/scripts/litentry/release/prepare.sh delete mode 100644 tee-worker/bitacross/scripts/litentry/release/template/para-alice.service delete mode 100644 tee-worker/bitacross/scripts/litentry/release/template/relay-alice.service delete mode 100644 tee-worker/bitacross/scripts/litentry/release/template/relay-bob.service delete mode 100644 tee-worker/bitacross/scripts/litentry/release/template/worker.service delete mode 100755 tee-worker/bitacross/scripts/litentry/ubuntu_setup.sh delete mode 100755 tee-worker/bitacross/scripts/m6.sh delete mode 100755 tee-worker/bitacross/scripts/m8.sh delete mode 100755 tee-worker/bitacross/scripts/polkadot_update.sh delete mode 100755 tee-worker/bitacross/scripts/sidechain.sh delete mode 100644 tee-worker/bitacross/scripts/test_transfer/README.md delete mode 100644 tee-worker/bitacross/scripts/test_transfer/package-lock.json delete mode 100644 tee-worker/bitacross/scripts/test_transfer/package.json delete mode 100644 tee-worker/bitacross/scripts/test_transfer/transfer.js delete mode 100644 tee-worker/bitacross/service/Cargo.toml delete mode 100644 tee-worker/bitacross/service/build.rs delete mode 100644 tee-worker/bitacross/service/src/account_funding.rs delete mode 100644 tee-worker/bitacross/service/src/cli.yml delete mode 100644 tee-worker/bitacross/service/src/config.rs delete mode 100644 tee-worker/bitacross/service/src/enclave/api.rs delete mode 100644 tee-worker/bitacross/service/src/enclave/mod.rs delete mode 100644 tee-worker/bitacross/service/src/enclave/tls_ra.rs delete mode 100644 tee-worker/bitacross/service/src/error.rs delete mode 100644 tee-worker/bitacross/service/src/globals/mod.rs delete mode 100644 tee-worker/bitacross/service/src/globals/tokio_handle.rs delete mode 100644 tee-worker/bitacross/service/src/initialized_service.rs delete mode 100644 tee-worker/bitacross/service/src/main.rs delete mode 100644 tee-worker/bitacross/service/src/main_impl.rs delete mode 100644 tee-worker/bitacross/service/src/ocall_bridge/bridge_api.rs delete mode 100644 tee-worker/bitacross/service/src/ocall_bridge/component_factory.rs delete mode 100644 tee-worker/bitacross/service/src/ocall_bridge/ffi/get_ias_socket.rs delete mode 100644 tee-worker/bitacross/service/src/ocall_bridge/ffi/get_quote.rs delete mode 100755 tee-worker/bitacross/service/src/ocall_bridge/ffi/get_qve_report_on_quote.rs delete mode 100644 tee-worker/bitacross/service/src/ocall_bridge/ffi/get_update_info.rs delete mode 100644 tee-worker/bitacross/service/src/ocall_bridge/ffi/init_quote.rs delete mode 100644 tee-worker/bitacross/service/src/ocall_bridge/ffi/ipfs.rs delete mode 100644 tee-worker/bitacross/service/src/ocall_bridge/ffi/mod.rs delete mode 100644 tee-worker/bitacross/service/src/ocall_bridge/ffi/send_to_parentchain.rs delete mode 100644 tee-worker/bitacross/service/src/ocall_bridge/ffi/update_metric.rs delete mode 100644 tee-worker/bitacross/service/src/ocall_bridge/ffi/worker_request.rs delete mode 100644 tee-worker/bitacross/service/src/ocall_bridge/ipfs_ocall.rs delete mode 100644 tee-worker/bitacross/service/src/ocall_bridge/metrics_ocall.rs delete mode 100644 tee-worker/bitacross/service/src/ocall_bridge/mod.rs delete mode 100644 tee-worker/bitacross/service/src/ocall_bridge/remote_attestation_ocall.rs delete mode 100644 tee-worker/bitacross/service/src/ocall_bridge/worker_on_chain_ocall.rs delete mode 100644 tee-worker/bitacross/service/src/parentchain_handler.rs delete mode 100644 tee-worker/bitacross/service/src/prometheus_metrics.rs delete mode 100644 tee-worker/bitacross/service/src/setup.rs delete mode 100644 tee-worker/bitacross/service/src/sync_state.rs delete mode 100644 tee-worker/bitacross/service/src/tests/commons.rs delete mode 100644 tee-worker/bitacross/service/src/tests/mock.rs delete mode 100644 tee-worker/bitacross/service/src/tests/mocks/enclave_api_mock.rs delete mode 100644 tee-worker/bitacross/service/src/tests/mocks/initialization_handler_mock.rs delete mode 100644 tee-worker/bitacross/service/src/tests/mocks/mod.rs delete mode 100644 tee-worker/bitacross/service/src/tests/mocks/parentchain_api_mock.rs delete mode 100644 tee-worker/bitacross/service/src/tests/mocks/update_worker_peers_mock.rs delete mode 100644 tee-worker/bitacross/service/src/tests/mod.rs delete mode 100644 tee-worker/bitacross/service/src/tests/parentchain_handler_test.rs delete mode 100644 tee-worker/bitacross/service/src/utils.rs delete mode 100644 tee-worker/bitacross/service/src/wasm.rs delete mode 100644 tee-worker/bitacross/service/src/worker.rs delete mode 100644 tee-worker/bitacross/service/src/worker_peers_updater.rs delete mode 100644 tee-worker/bitacross/ts-tests/.editorconfig delete mode 100644 tee-worker/bitacross/ts-tests/.gitignore delete mode 100644 tee-worker/bitacross/ts-tests/.prettierrc delete mode 100644 tee-worker/bitacross/ts-tests/README.md delete mode 100644 tee-worker/bitacross/ts-tests/integration-tests/.env.local.example delete mode 100644 tee-worker/bitacross/ts-tests/integration-tests/.env.staging delete mode 100644 tee-worker/bitacross/ts-tests/integration-tests/.eslintrc.json delete mode 100644 tee-worker/bitacross/ts-tests/integration-tests/package.json delete mode 100644 tee-worker/bitacross/ts-tests/integration-tests/sign_bitcoin.test.ts delete mode 100644 tee-worker/bitacross/ts-tests/integration-tests/tsconfig.json delete mode 100644 tee-worker/bitacross/ts-tests/package.json delete mode 100644 tee-worker/bitacross/ts-tests/pnpm-lock.yaml delete mode 100644 tee-worker/bitacross/ts-tests/pnpm-workspace.yaml delete mode 100644 tee-worker/bitacross/upstream_commit delete mode 100644 tee-worker/common/core-primitives/node-api/metadata/src/pallet_bitacross.rs diff --git a/.github/file-filter.yml b/.github/file-filter.yml index 0961419a08..e71da10e40 100644 --- a/.github/file-filter.yml +++ b/.github/file-filter.yml @@ -31,21 +31,6 @@ identity_src: &identity_src - 'tee-worker/identity/build.Dockerfile' - 'tee-worker/identity/enclave-runtime/**' -bitacross_src: &bitacross_src - - 'common/**' - - 'tee-worker/Cargo.toml' - - 'tee-worker/Cargo.lock' - - 'tee-worker/bitacross/**/*.rs' - - 'tee-worker/bitacross/**/rust-toolchain.toml' - - 'tee-worker/bitacross/build.Dockerfile' - - 'tee-worker/bitacross/enclave-runtime/**' - -bitacross_test: &bitacross_test - - 'tee-worker/bitacross/ts-tests/**' - - 'tee-worker/bitacross/cli/*.sh' - - 'parachain/docker/**' - - 'tee-worker/bitacross/docker/*.yml' - omni_executor_src: &omni_executor_src - 'tee-worker/omni-executor/Cargo.toml' - 'tee-worker/omni-executor/Cargo.lock' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 35e0e9cbbc..6958009ede 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,11 +55,6 @@ on: description: rebuild-identity required: true default: true - rebuild-bitacross: - type: boolean - description: rebuild-bitacross - required: true - default: true rebuild-omni-executor: type: boolean description: rebuild-omni-executor @@ -99,12 +94,10 @@ jobs: outputs: rebuild_parachain: ${{ steps.env.outputs.rebuild_parachain }} rebuild_identity: ${{ steps.env.outputs.rebuild_identity }} - rebuild_bitacross: ${{ steps.env.outputs.rebuild_bitacross }} rebuild_omni_executor: ${{ steps.env.outputs.rebuild_omni_executor }} push_docker: ${{ steps.env.outputs.push_docker }} run_parachain_test: ${{ steps.env.outputs.run_parachain_test }} run_identity_test: ${{ steps.env.outputs.run_identity_test }} - run_bitacross_test: ${{ steps.env.outputs.run_bitacross_test }} run_omni_executor_test: ${{ steps.env.outputs.run_omni_executor_test }} steps: - uses: actions/checkout@v4 @@ -124,12 +117,10 @@ jobs: run: | rebuild_parachain=false rebuild_identity=false - rebuild_bitacross=false rebuild_omni_executor=false push_docker=false run_parachain_test=false run_identity_test=false - run_bitacross_test=false run_omni_executor_test=false if [ "${{ github.event.inputs.rebuild-parachain }}" = "true" ] || [ "${{ steps.filter.outputs.parachain_src }}" = "true" ]; then rebuild_parachain=true @@ -137,9 +128,6 @@ jobs: if [ "${{ github.event.inputs.rebuild-identity }}" = "true" ] || [ "${{ steps.filter.outputs.identity_src }}" = "true" ]; then rebuild_identity=true fi - if [ "${{ github.event.inputs.rebuild-bitacross }}" = "true" ] || [ "${{ steps.filter.outputs.bitacross_src }}" = "true" ]; then - rebuild_bitacross=true - fi if [ "${{ github.event.inputs.rebuild-omni-executor }}" = "true" ] || [ "${{ steps.filter.outputs.omni_executor_src }}" = "true" ]; then rebuild_omni_executor=true fi @@ -154,20 +142,15 @@ jobs: if [ "${{ steps.filter.outputs.identity_test }}" = "true" ] || [ "$rebuild_parachain" = "true" ] || [ "$rebuild_identity" = "true" ]; then run_identity_test=true fi - if [ "${{ steps.filter.outputs.bitacross_test }}" = "true" ] || [ "$rebuild_parachain" = "true" ] || [ "$rebuild_bitacross" = "true" ]; then - run_bitacross_test=true - fi if [ "${{ steps.filter.outputs.omni_executor_test }}" = "true" ] || [ "$rebuild_parachain" = "true" ] || [ "$rebuild_omni_executor" = "true" ]; then run_omni_executor_test=true fi echo "rebuild_parachain=$rebuild_parachain" | tee -a $GITHUB_OUTPUT echo "rebuild_identity=$rebuild_identity" | tee -a $GITHUB_OUTPUT - echo "rebuild_bitacross=$rebuild_bitacross" | tee -a $GITHUB_OUTPUT echo "rebuild_omni_executor=$rebuild_omni_executor" | tee -a $GITHUB_OUTPUT echo "push_docker=$push_docker" | tee -a $GITHUB_OUTPUT echo "run_parachain_test=$run_parachain_test" | tee -a $GITHUB_OUTPUT echo "run_identity_test=$run_identity_test" | tee -a $GITHUB_OUTPUT - echo "run_bitacross_test=$run_bitacross_test" | tee -a $GITHUB_OUTPUT echo "run_omni_executor_test=$run_omni_executor_test" | tee -a $GITHUB_OUTPUT fmt: @@ -204,11 +187,6 @@ jobs: run: | cargo fmt --all -- --check - - name: bitacross-worker enclave-runtime fmt check - working-directory: ./tee-worker/bitacross/enclave-runtime - run: | - cargo fmt --all -- --check - - name: omni-executor fmt check working-directory: ./tee-worker/omni-executor run: | @@ -292,7 +270,7 @@ jobs: working-directory: ./tee-worker shell: bash run: | - for d in . identity/enclave-runtime bitacross/enclave-runtime; do + for d in . identity/enclave-runtime; do pushd "$d" echo "::group::cargo clippy all" cargo clippy --release -- -D warnings @@ -539,84 +517,6 @@ jobs: if: failure() uses: andymckay/cancel-action@0.5 - bitacross-build: - runs-on: ubuntu-22.04 - needs: - - fmt - - set-condition - - sequentialise - steps: - - uses: actions/checkout@v4 - - - name: Free up disk space - if: startsWith(runner.name, 'GitHub Actions') - uses: ./.github/actions/disk-cleanup - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - # use the docker driver to access the local image - # we don't need external caches or multi platforms here - # see https://docs.docker.com/build/drivers/ - driver: docker - - - name: Build local builder - if: needs.set-condition.outputs.rebuild_bitacross == 'true' - uses: docker/build-push-action@v6 - with: - context: . - file: tee-worker/bitacross/build.Dockerfile - tags: local-builder:latest - target: builder - build-args: | - WORKER_MODE_ARG=offchain-worker - ADDITIONAL_FEATURES_ARG= - - - name: Build worker - if: needs.set-condition.outputs.rebuild_bitacross == 'true' - uses: docker/build-push-action@v6 - with: - context: . - file: tee-worker/bitacross/build.Dockerfile - tags: litentry/bitacross-worker:latest - target: deployed-worker - - - name: Build cli - if: needs.set-condition.outputs.rebuild_bitacross == 'true' - uses: docker/build-push-action@v6 - with: - context: . - file: tee-worker/bitacross/build.Dockerfile - tags: litentry/bitacross-cli:latest - target: deployed-client - - - name: Pull and tag worker and cli image optionally - if: needs.set-condition.outputs.rebuild_bitacross == 'false' - run: | - docker pull litentry/bitacross-worker:latest - docker pull litentry/bitacross-cli:latest - - - run: docker images --all - - - name: Test enclave - if: needs.set-condition.outputs.rebuild_bitacross == 'true' - # cargo test is not supported in the enclave - # see https://github.com/apache/incubator-teaclave-sgx-sdk/issues/232 - run: docker run litentry/bitacross-worker:latest test --all - - - name: Save docker images - run: docker save litentry/bitacross-worker:latest litentry/bitacross-cli:latest | gzip > litentry-bitacross.tar.gz - - - name: Upload docker images - uses: actions/upload-artifact@v4 - with: - name: litentry-bitacross - path: litentry-bitacross.tar.gz - if-no-files-found: error - - name: Fail early - if: failure() - uses: andymckay/cancel-action@0.5 - omni-executor-build: runs-on: ubuntu-22.04 needs: @@ -964,86 +864,6 @@ jobs: if-no-files-found: ignore retention-days: 3 - bitacross-worker-test: - runs-on: ubuntu-22.04 - needs: - - set-condition - - parachain-build-dev - - bitacross-build - strategy: - fail-fast: false - matrix: - include: - - test_name: lit-sign-bitcoin - name: ${{ matrix.test_name }} - steps: - - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 - with: - name: litentry-parachain-dev - - - uses: actions/download-artifact@v4 - with: - name: litentry-bitacross - - - name: Load docker image - run: | - docker load < litentry-parachain-dev.tar.gz - docker load < litentry-bitacross.tar.gz - docker images - - - name: Enable corepack and pnpm - run: corepack enable && corepack enable pnpm - - - name: Launch parachain network - run: | - make launch-network-litentry - - - name: Integration bitacross worker test ${{ matrix.test_name }} - working-directory: ./tee-worker/bitacross/docker - if: needs.set-condition.outputs.run_bitacross_test == 'true' - timeout-minutes: 40 - run: | - docker compose -f multiworker-docker-compose.yml -f ${{ matrix.test_name }}.yml up --no-build --exit-code-from ${{ matrix.test_name }} ${{ matrix.test_name }} - - - name: Stop integration multi worker docker containers - working-directory: ./tee-worker/bitacross/docker - if: needs.set-condition.outputs.run_bitacross_test == 'true' - run: | - docker compose -f multiworker-docker-compose.yml -f ${{ matrix.test_name }}.yml stop - - - name: Upload zombienet logs if test fails - continue-on-error: true - uses: actions/upload-artifact@v4 - if: failure() - with: - name: ${{ matrix.test_name }}-zombienet-logs - path: | - /tmp/parachain_dev - !/tmp/parachain_dev/polkadot* - !/tmp/parachain_dev/zombienet* - !/tmp/parachain_dev/litentry-collator - if-no-files-found: ignore - retention-days: 3 - - - name: Collect docker logs if test fails - continue-on-error: true - uses: jwalton/gh-docker-logs@v2 - if: failure() - with: - tail: all - dest: docker-logs - - - name: Upload docker logs if test fails - uses: actions/upload-artifact@v4 - if: failure() - with: - name: ${{ matrix.test_name }}-docker-logs - path: docker-logs - if-no-files-found: ignore - retention-days: 3 - omni-executor-test: runs-on: ubuntu-22.04 needs: @@ -1080,12 +900,6 @@ jobs: # run: | # docker compose -f docker-compose.yml -f ${{ matrix.test_name }}.yml up --no-build --exit-code-from ${{ matrix.test_name }} ${{ matrix.test_name }} - # - name: Stop integration omni executor docker containers - # working-directory: ./tee-worker/bitacross/docker - # if: needs.set-condition.outputs.run_omni_executor_test == 'true' - # run: | - # docker compose -f docker-compose.yml -f ${{ matrix.test_name }}.yml stop - - name: Collect docker logs if test fails continue-on-error: true uses: jwalton/gh-docker-logs@v2 @@ -1123,7 +937,6 @@ jobs: - set-condition - parachain-ts-test - identity-single-worker-test - - bitacross-worker-test - omni-executor-test if: ${{ !failure() && needs.set-condition.outputs.push_docker == 'true' }} steps: @@ -1135,10 +948,6 @@ jobs: with: name: litentry-identity - - uses: actions/download-artifact@v4 - with: - name: litentry-bitacross - - uses: actions/download-artifact@v4 with: name: litentry-omni @@ -1163,13 +972,6 @@ jobs: docker push litentry/identity-worker docker push litentry/identity-cli - - name: Push bitacross-worker image - if: needs.set-condition.outputs.rebuild_bitacross == 'true' - run: | - docker load < litentry-bitacross.tar.gz - docker push litentry/bitacross-worker - docker push litentry/bitacross-cli - - name: Push omni-executor image if: needs.set-condition.outputs.rebuild_omni_executor == 'true' run: | diff --git a/.github/workflows/create-release-draft.yml b/.github/workflows/create-release-draft.yml index fe5ebbd5f2..5023b6d5b8 100644 --- a/.github/workflows/create-release-draft.yml +++ b/.github/workflows/create-release-draft.yml @@ -18,11 +18,6 @@ on: description: identity-worker required: true default: true - bitacross_worker: - type: boolean - description: bitacross-worker - required: true - default: true release_tag: description: an existing tag for creating release (e.g. v1.2.0) required: true @@ -57,7 +52,6 @@ jobs: [ "${{ github.event.inputs.parachain_client }}" = "true" ] && t="${t:0:0}1${t:1}" [ "${{ github.event.inputs.parachain_runtime }}" = "true" ] && t="${t:0:1}1${t:2}" [ "${{ github.event.inputs.identity_worker }}" = "true" ] && t="${t:0:2}1${t:3}" - [ "${{ github.event.inputs.bitacross_worker }}" = "true" ] && t="${t:0:3}1${t:4}" if [ $t = "0000"]; then echo "::error::Please select at least one release type." exit 1 @@ -227,74 +221,6 @@ jobs: run: | docker push litentry/identity-worker:${{ env.RELEASE_TAG }} - build-bitacross-worker: - if: ${{ github.event.inputs.bitacross_worker == 'true' }} - runs-on: ubuntu-22.04 - environment: production - steps: - - name: Checkout codes on ${{ env.RELEASE_TAG }} - uses: actions/checkout@v4 - with: - ref: ${{ env.RELEASE_TAG }} - fetch-depth: 0 - - - name: Free up disk space - if: startsWith(runner.name, 'GitHub Actions') - uses: ./.github/actions/disk-cleanup - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - # use the docker driver to access the local image - # we don't need external caches or multi platforms here - # see https://docs.docker.com/build/drivers/ - driver: docker - - - name: Write enclave signing key - run: | - cat << EOF > tee-worker/bitacross/enclave_key.pem - ${{ secrets.BITACROSS_ENCLAVE_PROD_SIGNING_KEY }} - EOF - - - name: Build local builder - uses: docker/build-push-action@v6 - env: - DOCKER_BUILD_RECORD_UPLOAD: false - with: - context: . - file: tee-worker/bitacross/build.Dockerfile - tags: local-builder:latest - target: builder - build-args: | - WORKER_MODE_ARG=offchain-worker - ADDITIONAL_FEATURES_ARG= - SGX_PRODUCTION=1 - SGX_MODE=HW - IMAGE_FOR_RELEASE=true - SGX_COMMERCIAL_KEY=enclave_key.pem - - - name: Build worker - uses: docker/build-push-action@v6 - env: - DOCKER_BUILD_RECORD_UPLOAD: false - with: - context: . - file: tee-worker/bitacross/build.Dockerfile - tags: litentry/bitacross-worker:${{ env.RELEASE_TAG }} - target: worker-release - - - run: docker images --all - - - name: Dockerhub login - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_PASSWORD }} - - - name: Push worker image - run: | - docker push litentry/bitacross-worker:${{ env.RELEASE_TAG }} - parachain-ts-tests: runs-on: ubuntu-22.04 needs: build-parachain-client @@ -361,11 +287,10 @@ jobs: - set-release-type - build-parachain-runtime - build-identity-worker - - build-bitacross-worker - parachain-ts-tests if: | !failure() && - (success('build-parachain-runtime') || success('parachain-ts-tests') || success('build-identity-worker') || success('build-bitacross-worker')) + (success('build-parachain-runtime') || success('parachain-ts-tests') || success('build-identity-worker')) steps: - name: Checkout codes on ${{ env.RELEASE_TAG }} uses: actions/checkout@v4 diff --git a/Makefile b/Makefile index 2fce7c9943..c37805042e 100644 --- a/Makefile +++ b/Makefile @@ -105,7 +105,6 @@ fmt-cargo: @cd parachain && cargo fmt --all @cd tee-worker && cargo fmt --all @cd tee-worker/identity/enclave-runtime && cargo fmt --all - @cd tee-worker/bitacross/enclave-runtime && cargo fmt --all .PHONY: fmt-taplo ## taplo fmt fmt-taplo: diff --git a/common/primitives/core/src/teebag/types.rs b/common/primitives/core/src/teebag/types.rs index ec65014c3b..91c947f7a5 100644 --- a/common/primitives/core/src/teebag/types.rs +++ b/common/primitives/core/src/teebag/types.rs @@ -70,8 +70,9 @@ pub enum AttestationType { #[derive(Encode, Decode, Clone, Copy, Default, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub enum WorkerType { #[default] + #[codec(index = 0)] Identity, - BitAcross, + #[codec(index = 2)] OmniExecutor, } diff --git a/local-setup/launch.py b/local-setup/launch.py index 4d6a278c8e..a3275687c1 100755 --- a/local-setup/launch.py +++ b/local-setup/launch.py @@ -195,13 +195,13 @@ def get_flags(index, worker): return list(filter(None, [ "--clean-reset", - "-T", "wss://localhost" if worker == "bitacross" else "ws://localhost", + "-T", "ws://localhost", "-P", ports['trusted_worker_port'], "-w", ports['untrusted_worker_port'], "-r", ports['mura_port'], "-h", ports['untrusted_http_port'], "-p", ports['collator_ws_port'], - "--enable-mock-server" if worker == "identity" else "", + "--enable-mock-server", "--parentchain-start-block", "0", "--enable-metrics" if index == 0 else None ])) @@ -222,14 +222,8 @@ def add_collator_ports(): def main(processes, worker, workers_number, parachain_type, log_config_path, offset, parachain_dir): # Litentry - if worker == "identity": - worker_dir = "tee-worker/identity" - worker_bin = "litentry-worker" - elif worker == "bitacross": - worker_dir = "tee-worker/bitacross" - worker_bin = "bitacross-worker" - else: - sys.exit("Unsupported worker") + worker_dir = "tee-worker/identity" + worker_bin = "litentry-worker" print("Starting litentry parachain in background ...") if parachain_type == "standalone": @@ -306,7 +300,7 @@ def main(processes, worker, workers_number, parachain_type, log_config_path, off parser = argparse.ArgumentParser( description="Run a setup consisting of a node and some workers" ) - parser.add_argument("-w", "--worker", type=str, default="identity", help="Worker to run: identity / bitacross") + parser.add_argument("-w", "--worker", type=str, default="identity", help="Worker to run: identity") parser.add_argument("-wn", "--workers-number", type=int, default=1, help="Number of workers to run") parser.add_argument( "-p", diff --git a/parachain/Cargo.toml b/parachain/Cargo.toml index 953e18e2c5..ab13139eda 100644 --- a/parachain/Cargo.toml +++ b/parachain/Cargo.toml @@ -3,7 +3,6 @@ resolver = "2" members = [ 'node', 'pallets/account-fix', - 'pallets/bitacross', 'pallets/bridge/assets-handler', 'pallets/bridge/chain-bridge', 'pallets/bridge/bridge-transfer', @@ -270,7 +269,6 @@ paseo-parachain-runtime = { path = "runtime/paseo", default-features = false } pallet-account-fix = { path = "pallets/account-fix", default-features = false } pallet-asset-manager = { path = "pallets/xcm-asset-manager", default-features = false } pallet-assets-handler = { path = "pallets/bridge/assets-handler", default-features = false } -pallet-bitacross = { path = "pallets/bitacross", default-features = false } pallet-chain-bridge = { path = "pallets/bridge/chain-bridge", default-features = false } pallet-bridge-common = { path = "pallets/bridge/common", default-features = false } pallet-bridge-transfer = { path = "pallets/bridge/bridge-transfer", default-features = false } diff --git a/parachain/node/src/chain_specs/litentry.rs b/parachain/node/src/chain_specs/litentry.rs index 354fbef94c..7b06b1ad5e 100644 --- a/parachain/node/src/chain_specs/litentry.rs +++ b/parachain/node/src/chain_specs/litentry.rs @@ -17,7 +17,7 @@ use super::*; use cumulus_primitives_core::ParaId; use litentry_parachain_runtime::{ - AccountId, AuraId, Balance, BalancesConfig, BitacrossConfig, CouncilMembershipConfig, + AccountId, AuraId, Balance, BalancesConfig, CouncilMembershipConfig, DeveloperCommitteeMembershipConfig, ParachainInfoConfig, ParachainStakingConfig, PolkadotXcmConfig, RuntimeGenesisConfig, SessionConfig, TechnicalCommitteeMembershipConfig, TeebagConfig, TeebagOperationalMode, VCManagementConfig, WASM_BINARY, @@ -241,7 +241,6 @@ fn generate_genesis( admin: None, mode: TeebagOperationalMode::Development, }, - bitacross: BitacrossConfig { admin: None }, score_staking: Default::default(), }; diff --git a/parachain/node/src/chain_specs/paseo.rs b/parachain/node/src/chain_specs/paseo.rs index 09cbefcff4..7345cc7982 100644 --- a/parachain/node/src/chain_specs/paseo.rs +++ b/parachain/node/src/chain_specs/paseo.rs @@ -18,7 +18,7 @@ use super::*; use core_primitives::PASEO_PARA_ID; use cumulus_primitives_core::ParaId; use paseo_parachain_runtime::{ - AccountId, AuraId, Balance, BalancesConfig, BitacrossConfig, CouncilMembershipConfig, + AccountId, AuraId, Balance, BalancesConfig, CouncilMembershipConfig, DeveloperCommitteeMembershipConfig, ParachainInfoConfig, ParachainStakingConfig, PolkadotXcmConfig, RuntimeGenesisConfig, SessionConfig, SudoConfig, TechnicalCommitteeMembershipConfig, TeebagConfig, TeebagOperationalMode, VCManagementConfig, @@ -230,7 +230,6 @@ fn generate_genesis( admin: Some(root_key.clone()), mode: TeebagOperationalMode::Development, }, - bitacross: BitacrossConfig { admin: Some(root_key) }, score_staking: Default::default(), }; diff --git a/parachain/pallets/teebag/src/tests.rs b/parachain/pallets/teebag/src/tests.rs index 9e7a04ca29..fe3e2399b3 100644 --- a/parachain/pallets/teebag/src/tests.rs +++ b/parachain/pallets/teebag/src/tests.rs @@ -104,7 +104,6 @@ fn register_enclave_dev_works_with_no_authorized_enclave() { let enclave = default_enclave().with_mrenclave(TEST4_MRENCLAVE); assert_eq!(Teebag::enclave_count(WorkerType::Identity), 1); - assert_eq!(Teebag::enclave_count(WorkerType::BitAcross), 0); assert_eq!(EnclaveRegistry::::get(alice()).unwrap(), enclave); let authorized_enclave = AuthorizedEnclave::::get(WorkerType::default()); assert_eq!(authorized_enclave.len(), 1); @@ -136,7 +135,6 @@ fn register_enclave_dev_works_with_sgx_build_mode_debug() { .with_attestation_type(AttestationType::Ias); assert_eq!(Teebag::enclave_count(WorkerType::Identity), 1); - assert_eq!(Teebag::enclave_count(WorkerType::BitAcross), 0); assert_eq!(EnclaveRegistry::::get(signer4).unwrap(), enclave); }) } @@ -159,7 +157,7 @@ fn parentchain_block_processed_works() { // Ensure that enclave is registered assert_ok!(Teebag::register_enclave( RuntimeOrigin::signed(signer7.clone()), - WorkerType::BitAcross, + WorkerType::Identity, Default::default(), TEST7_CERT.to_vec(), URL.to_vec(), @@ -167,7 +165,7 @@ fn parentchain_block_processed_works() { None, AttestationType::Ias, )); - assert_eq!(Teebag::enclave_count(WorkerType::BitAcross), 1); + assert_eq!(Teebag::enclave_count(WorkerType::Identity), 1); run_to_block(3); Timestamp::set_timestamp(TEST7_TIMESTAMP + 24 * 1000); @@ -309,7 +307,6 @@ fn register_enclave_prod_works_with_sgx_build_mode_debug() { .with_attestation_type(AttestationType::Ias); assert_eq!(Teebag::enclave_count(WorkerType::Identity), 1); - assert_eq!(Teebag::enclave_count(WorkerType::BitAcross), 0); assert_eq!(EnclaveRegistry::::get(signer4).unwrap(), enclave); }) } @@ -350,7 +347,6 @@ fn register_enclave_prod_works_with_sgx_build_mode_production() { .with_attestation_type(AttestationType::Ias); assert_eq!(Teebag::enclave_count(WorkerType::Identity), 1); - assert_eq!(Teebag::enclave_count(WorkerType::BitAcross), 0); assert_eq!(EnclaveRegistry::::get(signer8).unwrap(), enclave); // remove authorized enclave should remove enclave too @@ -419,12 +415,12 @@ fn register_enclave_prod_fails_with_max_limit_reached() { )); assert_ok!(Teebag::force_add_authorized_enclave( RuntimeOrigin::signed(alice()), - WorkerType::BitAcross, + WorkerType::OmniExecutor, TEST4_MRENCLAVE )); assert_ok!(Teebag::force_add_authorized_enclave( RuntimeOrigin::signed(alice()), - WorkerType::BitAcross, + WorkerType::OmniExecutor, TEST6_MRENCLAVE )); @@ -433,7 +429,7 @@ fn register_enclave_prod_fails_with_max_limit_reached() { let signer6: AccountId32 = get_signer(TEST6_SIGNER_PUB); assert_ok!(Teebag::add_enclave_identifier( RuntimeOrigin::signed(admin.clone()), - WorkerType::BitAcross, + WorkerType::OmniExecutor, signer4.clone(), )); assert_ok!(Teebag::add_enclave_identifier( @@ -443,7 +439,7 @@ fn register_enclave_prod_fails_with_max_limit_reached() { )); assert_ok!(Teebag::add_enclave_identifier( RuntimeOrigin::signed(admin.clone()), - WorkerType::BitAcross, + WorkerType::OmniExecutor, signer6.clone(), )); assert_ok!(Teebag::add_enclave_identifier( @@ -455,7 +451,7 @@ fn register_enclave_prod_fails_with_max_limit_reached() { Timestamp::set_timestamp(TEST4_TIMESTAMP); assert_ok!(Teebag::register_enclave( RuntimeOrigin::signed(signer4.clone()), - WorkerType::BitAcross, + WorkerType::OmniExecutor, Default::default(), TEST4_CERT.to_vec(), URL.to_vec(), @@ -467,7 +463,7 @@ fn register_enclave_prod_fails_with_max_limit_reached() { Timestamp::set_timestamp(TEST6_TIMESTAMP); assert_ok!(Teebag::register_enclave( RuntimeOrigin::signed(signer6.clone()), - WorkerType::BitAcross, + WorkerType::OmniExecutor, Default::default(), TEST6_CERT.to_vec(), URL.to_vec(), @@ -522,7 +518,7 @@ fn register_enclave_prod_fails_with_max_limit_reached() { ); assert_eq!(Teebag::enclave_count(WorkerType::Identity), 2); - assert_eq!(Teebag::enclave_count(WorkerType::BitAcross), 1); + assert_eq!(Teebag::enclave_count(WorkerType::OmniExecutor), 1); }) } diff --git a/parachain/runtime/litentry/Cargo.toml b/parachain/runtime/litentry/Cargo.toml index 945fad61a4..d1f8b10039 100644 --- a/parachain/runtime/litentry/Cargo.toml +++ b/parachain/runtime/litentry/Cargo.toml @@ -82,7 +82,6 @@ frame-system-benchmarking = { workspace = true, optional = true } core-primitives = { workspace = true } pallet-account-fix = { workspace = true } pallet-asset-manager = { workspace = true } -pallet-bitacross = { workspace = true } pallet-bridge-transfer = { workspace = true } pallet-chain-bridge = { workspace = true } pallet-evm-assertions = { workspace = true } @@ -151,7 +150,6 @@ runtime-benchmarks = [ "pallet-assets-handler/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", - "pallet-bitacross/runtime-benchmarks", "pallet-bounties/runtime-benchmarks", "pallet-bridge-transfer/runtime-benchmarks", "pallet-chain-bridge/runtime-benchmarks", @@ -227,7 +225,6 @@ std = [ "pallet-aura/std", "pallet-authorship/std", "pallet-balances/std", - "pallet-bitacross/std", "pallet-bounties/std", "pallet-bridge-transfer/std", "pallet-chain-bridge/std", @@ -320,7 +317,6 @@ try-runtime = [ "pallet-aura/try-runtime", "pallet-authorship/try-runtime", "pallet-balances/try-runtime", - "pallet-bitacross/try-runtime", "pallet-bounties/try-runtime", "pallet-bridge-transfer/try-runtime", "pallet-chain-bridge/try-runtime", diff --git a/parachain/runtime/litentry/src/lib.rs b/parachain/runtime/litentry/src/lib.rs index b85b78a6f3..fb5f6d0c32 100644 --- a/parachain/runtime/litentry/src/lib.rs +++ b/parachain/runtime/litentry/src/lib.rs @@ -1126,12 +1126,6 @@ impl pallet_omni_account::Config for Runtime { type OmniAccountConverter = DefaultOmniAccountConverter; } -impl pallet_bitacross::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type TEECallOrigin = EnsureEnclaveSigner; - type SetAdminOrigin = EnsureRootOrHalfCouncil; -} - impl pallet_evm_assertions::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AssertionId = H160; @@ -1250,7 +1244,6 @@ construct_runtime! { ExtrinsicFilter: pallet_extrinsic_filter = 63, AssetManager: pallet_asset_manager = 64, Teebag: pallet_teebag = 65, - Bitacross: pallet_bitacross = 66, AssetsHandler: pallet_assets_handler = 68, EvmAssertions: pallet_evm_assertions = 71, @@ -1354,7 +1347,6 @@ impl Contains for NormalModeFilter { // AccountFix RuntimeCall::AccountFix(_) | RuntimeCall::AssetsHandler(_) | - RuntimeCall::Bitacross(_) | RuntimeCall::EvmAssertions(_) | RuntimeCall::ScoreStaking(_) | RuntimeCall::OmniAccount(_) diff --git a/parachain/runtime/paseo/Cargo.toml b/parachain/runtime/paseo/Cargo.toml index b08d5db88f..3f0cb5a128 100644 --- a/parachain/runtime/paseo/Cargo.toml +++ b/parachain/runtime/paseo/Cargo.toml @@ -82,7 +82,6 @@ pallet-account-fix = { workspace = true } pallet-asset-manager = { workspace = true } pallet-assets = { workspace = true } pallet-assets-handler = { workspace = true } -pallet-bitacross = { workspace = true } pallet-bridge-transfer = { workspace = true } pallet-chain-bridge = { workspace = true } pallet-evm-assertions = { workspace = true } @@ -164,7 +163,6 @@ runtime-benchmarks = [ "pallet-assets-handler/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", - "pallet-bitacross/runtime-benchmarks", "pallet-bounties/runtime-benchmarks", "pallet-bridge-transfer/runtime-benchmarks", "pallet-chain-bridge/runtime-benchmarks", @@ -248,7 +246,6 @@ std = [ "pallet-aura/std", "pallet-authorship/std", "pallet-balances/std", - "pallet-bitacross/std", "pallet-bounties/std", "pallet-bridge-transfer/std", "pallet-chain-bridge/std", @@ -352,7 +349,6 @@ try-runtime = [ "pallet-aura/try-runtime", "pallet-authorship/try-runtime", "pallet-balances/try-runtime", - "pallet-bitacross/try-runtime", "pallet-bounties/try-runtime", "pallet-bridge-transfer/try-runtime", "pallet-chain-bridge/try-runtime", diff --git a/parachain/runtime/paseo/src/lib.rs b/parachain/runtime/paseo/src/lib.rs index 35c166c7e1..29277e28f4 100644 --- a/parachain/runtime/paseo/src/lib.rs +++ b/parachain/runtime/paseo/src/lib.rs @@ -1169,12 +1169,6 @@ impl pallet_omni_account::Config for Runtime { type OmniAccountConverter = DefaultOmniAccountConverter; } -impl pallet_bitacross::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type TEECallOrigin = EnsureEnclaveSigner; - type SetAdminOrigin = EnsureRootOrAllCouncil; -} - impl pallet_evm_assertions::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AssertionId = H160; @@ -1376,7 +1370,6 @@ construct_runtime! { VCManagement: pallet_vc_management = 66, IMPExtrinsicWhitelist: pallet_group:: = 67, VCMPExtrinsicWhitelist: pallet_group:: = 68, - Bitacross: pallet_bitacross = 70, EvmAssertions: pallet_evm_assertions = 71, // Developer council @@ -1500,7 +1493,6 @@ impl Contains for NormalModeFilter { // AccountFix RuntimeCall::AccountFix(_) | RuntimeCall::AssetsHandler(_) | - RuntimeCall::Bitacross(_) | RuntimeCall::EvmAssertions(_) | RuntimeCall::ScoreStaking(_) | RuntimeCall::OmniAccount(_) | diff --git a/tee-worker/Cargo.lock b/tee-worker/Cargo.lock index 355ef7ab86..8ce9c9b013 100644 --- a/tee-worker/Cargo.lock +++ b/tee-worker/Cargo.lock @@ -366,480 +366,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "bc-enclave-registry" -version = "0.1.0" -dependencies = [ - "base64 0.13.1", - "itp-settings", - "itp-sgx-io", - "litentry-primitives", - "log 0.4.20", - "parity-scale-codec", - "sgx_tstd", - "sp-std", - "thiserror 1.0.44", - "thiserror 1.0.9", -] - -[[package]] -name = "bc-ita-parentchain-interface" -version = "0.1.0" -dependencies = [ - "bc-enclave-registry", - "bc-ita-sgx-runtime", - "bc-ita-stf", - "bc-itc-parentchain-indirect-calls-executor", - "bc-itp-stf-executor", - "bc-itp-top-pool-author", - "bc-relayer-registry", - "bc-signer-registry", - "env_logger 0.10.2", - "itc-parentchain-test", - "itp-api-client-types", - "itp-node-api", - "itp-sgx-crypto", - "itp-stf-primitives", - "itp-test", - "itp-types", - "litentry-primitives", - "log 0.4.20", - "parity-scale-codec", - "sgx_tstd", - "sp-core", - "sp-runtime", - "sp-std", - "substrate-api-client", -] - -[[package]] -name = "bc-ita-sgx-runtime" -version = "0.1.0" -dependencies = [ - "frame-executive", - "frame-support", - "frame-system", - "itp-sgx-runtime-primitives", - "pallet-balances", - "pallet-parentchain", - "pallet-sudo", - "pallet-timestamp", - "pallet-transaction-payment", - "parity-scale-codec", - "scale-info", - "sp-api", - "sp-core", - "sp-runtime", - "sp-std", - "sp-version", -] - -[[package]] -name = "bc-ita-stf" -version = "0.1.0" -dependencies = [ - "bc-ita-sgx-runtime", - "frame-support", - "frame-system", - "hex", - "hex-literal", - "itp-hashing", - "itp-node-api", - "itp-sgx-externalities", - "itp-stf-interface", - "itp-stf-primitives", - "itp-storage", - "itp-types", - "itp-utils", - "litentry-macros", - "litentry-primitives", - "log 0.4.20", - "pallet-balances", - "pallet-parentchain", - "pallet-sudo", - "parity-scale-codec", - "sgx_tstd", - "sp-core", - "sp-io 7.0.0", - "sp-keyring", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "bc-itc-direct-rpc-client" -version = "0.1.0" -dependencies = [ - "itp-rpc", - "itp-types", - "itp-utils", - "log 0.4.20", - "rustls 0.19.0 (git+https://github.com/mesalock-linux/rustls?tag=sgx_1.1.3)", - "rustls 0.19.1", - "serde_json 1.0.120", - "sgx_tstd", - "tungstenite 0.14.0", - "tungstenite 0.15.0", - "url 2.5.0 (git+https://github.com/domenukk/rust-url?rev=316c868)", - "webpki 0.21.4 (registry+https://github.com/rust-lang/crates.io-index)", - "webpki 0.21.4 (git+https://github.com/mesalock-linux/webpki?branch=mesalock_sgx)", -] - -[[package]] -name = "bc-itc-direct-rpc-server" -version = "0.1.0" -dependencies = [ - "itc-tls-websocket-server", - "itp-rpc", - "itp-types", - "itp-utils", - "jsonrpc-core 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 18.0.0 (git+https://github.com/scs/jsonrpc?branch=no_std_v18)", - "log 0.4.20", - "parity-scale-codec", - "serde_json 1.0.120", - "sgx_tstd", - "sp-runtime", - "thiserror 1.0.44", - "thiserror 1.0.9", -] - -[[package]] -name = "bc-itc-offchain-worker-executor" -version = "0.1.0" -dependencies = [ - "bc-itp-stf-executor", - "bc-itp-top-pool-author", - "itc-parentchain-light-client", - "itp-extrinsics-factory", - "itp-sgx-externalities", - "itp-stf-interface", - "itp-stf-primitives", - "itp-stf-state-handler", - "itp-test", - "itp-types", - "log 0.4.20", - "parity-scale-codec", - "sgx_tstd", - "sp-core", - "sp-runtime", - "thiserror 1.0.44", - "thiserror 1.0.9", -] - -[[package]] -name = "bc-itc-parentchain" -version = "0.1.0" -dependencies = [ - "bc-itc-parentchain-block-import-dispatcher", - "bc-itc-parentchain-block-importer", - "bc-itc-parentchain-indirect-calls-executor", - "itc-parentchain-light-client", - "itp-types", - "parity-scale-codec", - "sp-runtime", -] - -[[package]] -name = "bc-itc-parentchain-block-import-dispatcher" -version = "0.1.0" -dependencies = [ - "bc-itc-parentchain-block-importer", - "itp-import-queue", - "log 0.4.20", - "sgx_tstd", - "sgx_types", - "thiserror 1.0.44", - "thiserror 1.0.9", -] - -[[package]] -name = "bc-itc-parentchain-block-importer" -version = "0.1.0" -dependencies = [ - "bc-ita-stf", - "bc-itc-parentchain-indirect-calls-executor", - "bc-itp-stf-executor", - "itc-parentchain-light-client", - "itp-enclave-metrics", - "itp-extrinsics-factory", - "itp-ocall-api", - "itp-stf-interface", - "itp-types", - "log 0.4.20", - "parity-scale-codec", - "sgx_tstd", - "sgx_types", - "sp-runtime", - "thiserror 1.0.44", - "thiserror 1.0.9", -] - -[[package]] -name = "bc-itc-parentchain-indirect-calls-executor" -version = "0.1.0" -dependencies = [ - "bc-enclave-registry", - "bc-itp-stf-executor", - "bc-itp-top-pool-author", - "bc-relayer-registry", - "bc-signer-registry", - "binary-merkle-tree", - "bs58", - "env_logger 0.10.2", - "futures 0.3.28", - "futures 0.3.8", - "itc-parentchain-test", - "itp-api-client-types", - "itp-node-api", - "itp-sgx-crypto", - "itp-sgx-runtime-primitives", - "itp-stf-primitives", - "itp-test", - "itp-types", - "litentry-primitives", - "log 0.4.20", - "parity-scale-codec", - "sgx_tstd", - "sgx_types", - "sp-runtime", - "thiserror 1.0.44", - "thiserror 1.0.9", -] - -[[package]] -name = "bc-itp-enclave-api" -version = "0.1.0" -dependencies = [ - "bc-itp-enclave-api-ffi", - "frame-support", - "hex", - "itp-settings", - "itp-sgx-crypto", - "itp-stf-interface", - "itp-storage", - "itp-types", - "log 0.4.20", - "parity-scale-codec", - "serde_json 1.0.120", - "sgx_crypto_helper", - "sgx_types", - "sgx_urts", - "sp-core", - "sp-runtime", - "thiserror 1.0.44", -] - -[[package]] -name = "bc-itp-enclave-api-ffi" -version = "0.1.0" -dependencies = [ - "sgx_types", -] - -[[package]] -name = "bc-itp-stf-executor" -version = "0.1.0" -dependencies = [ - "bc-itp-top-pool", - "bc-itp-top-pool-author", - "hex", - "itc-parentchain-test", - "itp-enclave-metrics", - "itp-node-api", - "itp-ocall-api", - "itp-sgx-crypto", - "itp-sgx-externalities", - "itp-stf-interface", - "itp-stf-primitives", - "itp-stf-state-handler", - "itp-stf-state-observer", - "itp-test", - "itp-time-utils", - "itp-types", - "log 0.4.20", - "parity-scale-codec", - "sgx_tstd", - "sgx_types", - "sp-core", - "sp-runtime", - "thiserror 1.0.44", - "thiserror 1.0.9", -] - -[[package]] -name = "bc-itp-top-pool" -version = "0.1.0" -dependencies = [ - "bc-itc-direct-rpc-server", - "byteorder 1.4.3", - "derive_more", - "itp-stf-primitives", - "itp-test", - "itp-types", - "jsonrpc-core 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 18.0.0 (git+https://github.com/scs/jsonrpc?branch=no_std_v18)", - "linked-hash-map 0.5.2", - "linked-hash-map 0.5.6", - "litentry-primitives", - "log 0.4.20", - "parity-scale-codec", - "parity-util-mem", - "serde 1.0.210", - "sgx_tstd", - "sp-application-crypto", - "sp-core", - "sp-runtime", -] - -[[package]] -name = "bc-itp-top-pool-author" -version = "0.1.0" -dependencies = [ - "bc-itp-top-pool", - "derive_more", - "futures 0.3.28", - "itp-enclave-metrics", - "itp-ocall-api", - "itp-sgx-crypto", - "itp-stf-primitives", - "itp-stf-state-handler", - "itp-test", - "itp-types", - "itp-utils", - "jsonrpc-core 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 18.0.0 (git+https://github.com/scs/jsonrpc?branch=no_std_v18)", - "lazy_static", - "litentry-primitives", - "log 0.4.20", - "parity-scale-codec", - "sgx_crypto_helper", - "sgx_tstd", - "sp-core", - "sp-keyring", - "sp-runtime", -] - -[[package]] -name = "bc-musig2-ceremony" -version = "0.1.0" -dependencies = [ - "itp-sgx-crypto", - "k256", - "litentry-primitives", - "log 0.4.20", - "musig2 0.0.8 (git+https://github.com/kziemianek/musig2?branch=master)", - "musig2 0.0.8 (git+https://github.com/kailai-wang/musig2?branch=use-sha2-0.8)", - "parity-scale-codec", - "rand 0.8.5", - "sgx_rand", - "sgx_tstd", - "signature 2.1.0", -] - -[[package]] -name = "bc-musig2-event" -version = "0.1.0" -dependencies = [ - "bc-enclave-registry", - "bc-itc-direct-rpc-client", - "bc-itc-direct-rpc-server", - "bc-musig2-ceremony", - "itp-ocall-api", - "itp-rpc", - "itp-sgx-crypto", - "itp-types", - "itp-utils", - "lc-direct-call", - "litentry-primitives", - "log 0.4.20", - "parity-scale-codec", - "rand 0.8.5", - "sgx_rand", - "sgx_tstd", - "sp-core", - "threadpool 1.8.0", - "threadpool 1.8.1", -] - -[[package]] -name = "bc-relayer-registry" -version = "0.1.0" -dependencies = [ - "base64 0.13.1", - "itp-settings", - "itp-sgx-io", - "litentry-primitives", - "log 0.4.20", - "parity-scale-codec", - "sgx_tstd", - "sp-std", - "thiserror 1.0.44", - "thiserror 1.0.9", -] - -[[package]] -name = "bc-signer-registry" -version = "0.1.0" -dependencies = [ - "base64 0.13.1", - "itp-settings", - "itp-sgx-io", - "litentry-primitives", - "log 0.4.20", - "parity-scale-codec", - "sgx_tstd", - "sp-std", - "thiserror 1.0.44", - "thiserror 1.0.9", -] - -[[package]] -name = "bc-task-processor" -version = "0.1.0" -dependencies = [ - "bc-enclave-registry", - "bc-ita-stf", - "bc-itc-direct-rpc-client", - "bc-itc-direct-rpc-server", - "bc-itp-stf-executor", - "bc-musig2-ceremony", - "bc-musig2-event", - "bc-relayer-registry", - "bc-signer-registry", - "bc-task-sender", - "frame-support", - "futures 0.3.8", - "itp-enclave-metrics", - "itp-ocall-api", - "itp-sgx-crypto", - "itp-sgx-externalities", - "itp-stf-state-handler", - "lc-direct-call", - "litentry-primitives", - "log 0.4.20", - "parity-scale-codec", - "sgx_crypto_helper", - "sgx_tstd", - "sp-core", - "thiserror 1.0.44", - "thiserror 1.0.9", - "threadpool 1.8.0", - "threadpool 1.8.1", -] - -[[package]] -name = "bc-task-sender" -version = "0.1.0" -dependencies = [ - "futures 0.3.28", - "futures 0.3.8", - "lazy_static", - "litentry-primitives", - "parity-scale-codec", - "sgx_tstd", -] - [[package]] name = "bech32" version = "0.10.0-beta" @@ -899,108 +425,6 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" -[[package]] -name = "bitacross-cli" -version = "0.0.1" -dependencies = [ - "base58", - "bc-ita-parentchain-interface", - "bc-ita-stf", - "bc-musig2-ceremony", - "chrono 0.4.26", - "clap 4.1.0", - "env_logger 0.10.2", - "hdrhistogram", - "hex", - "itc-rpc-client", - "itp-node-api", - "itp-rpc", - "itp-sgx-crypto", - "itp-stf-primitives", - "itp-types", - "itp-utils", - "lc-direct-call", - "litentry-primitives", - "log 0.4.20", - "parity-scale-codec", - "rand 0.8.5", - "rayon", - "regex 1.9.5", - "reqwest", - "serde 1.0.210", - "serde_json 1.0.120", - "sgx_crypto_helper", - "sp-application-crypto", - "sp-core", - "sp-keyring", - "sp-keystore", - "sp-runtime", - "substrate-api-client", - "substrate-client-keystore", - "thiserror 1.0.44", - "urlencoding", -] - -[[package]] -name = "bitacross-worker" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "base58", - "bc-ita-parentchain-interface", - "bc-itc-parentchain", - "bc-itp-enclave-api", - "clap 2.34.0", - "config", - "dirs", - "env_logger 0.10.2", - "frame-support", - "futures 0.3.28", - "hex", - "humantime", - "ipfs-api", - "itc-parentchain-test", - "itc-rest-client", - "itc-rpc-client", - "itp-api-client-types", - "itp-enclave-metrics", - "itp-node-api", - "itp-settings", - "itp-sgx-crypto", - "itp-stf-interface", - "itp-storage", - "itp-time-utils", - "itp-types", - "itp-utils", - "jsonrpsee", - "lazy_static", - "litentry-primitives", - "log 0.4.20", - "mockall", - "parity-scale-codec", - "parking_lot 0.12.1", - "parse_duration", - "prometheus", - "rayon", - "regex 1.9.5", - "scale-info", - "serde 1.0.210", - "serde_derive 1.0.210", - "serde_json 1.0.120", - "sgx_crypto_helper", - "sgx_types", - "sp-consensus-grandpa", - "sp-core", - "sp-keyring", - "sp-runtime", - "substrate-api-client", - "thiserror 1.0.44", - "tokio", - "url 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "warp", -] - [[package]] name = "bitcoin" version = "0.31.0" @@ -1584,7 +1008,7 @@ checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", - "subtle 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", "zeroize", ] @@ -1605,7 +1029,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ "generic-array 0.14.7", - "subtle 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", ] [[package]] @@ -1615,7 +1039,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25fab6889090c8133f3deb8f73ba3c65a7f456f66436fc012a1b1e272b1e103e" dependencies = [ "generic-array 0.14.7", - "subtle 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", ] [[package]] @@ -1636,7 +1060,7 @@ dependencies = [ "byteorder 1.4.3", "digest 0.8.1", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", "zeroize", ] @@ -1649,7 +1073,7 @@ dependencies = [ "byteorder 1.4.3", "digest 0.9.0", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", "zeroize", ] @@ -1784,7 +1208,7 @@ dependencies = [ "block-buffer 0.10.4", "const-oid", "crypto-common", - "subtle 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", ] [[package]] @@ -1922,7 +1346,7 @@ dependencies = [ "pkcs8", "rand_core 0.6.4", "sec1", - "subtle 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", "zeroize", ] @@ -2249,7 +1673,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ "rand_core 0.6.4", - "subtle 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", ] [[package]] @@ -2708,7 +2132,7 @@ name = "futures-task" version = "0.3.8" source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" dependencies = [ - "once_cell 1.4.0 (git+https://github.com/mesalock-linux/once_cell-sgx)", + "once_cell 1.4.0", "sgx_tstd", ] @@ -2858,7 +2282,7 @@ checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", "rand_core 0.6.4", - "subtle 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", ] [[package]] @@ -5080,27 +4504,6 @@ dependencies = [ "url 2.5.0 (git+https://github.com/domenukk/rust-url?rev=316c868)", ] -[[package]] -name = "lc-direct-call" -version = "0.1.0" -dependencies = [ - "bc-enclave-registry", - "bc-musig2-ceremony", - "bc-relayer-registry", - "bc-signer-registry", - "hex", - "itp-sgx-crypto", - "itp-stf-primitives", - "k256", - "litentry-primitives", - "log 0.4.20", - "parity-scale-codec", - "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "sgx_tstd", - "sp-core", - "sp-io 7.0.0 (git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42)", -] - [[package]] name = "lc-dynamic-assertion" version = "0.1.0" @@ -5465,7 +4868,7 @@ checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" dependencies = [ "crunchy", "digest 0.9.0", - "subtle 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", ] [[package]] @@ -6047,38 +5450,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "musig2" -version = "0.0.8" -source = "git+https://github.com/kziemianek/musig2?branch=master#cd5e61ac9ecdf842da58605ac7b07b6e359f08c5" -dependencies = [ - "base16ct", - "hmac 0.12.1", - "k256", - "once_cell 1.19.0", - "secp 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "secp256k1 0.28.0", - "sha2 0.10.8", - "subtle 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "musig2" -version = "0.0.8" -source = "git+https://github.com/kailai-wang/musig2?branch=use-sha2-0.8#93857e52abbe8f9898c9ec743eecb1380132abcb" -dependencies = [ - "base16ct", - "hmac 0.12.1", - "k256", - "once_cell 1.4.0 (git+https://github.com/mesalock-linux/once_cell-sgx?branch=master)", - "secp 0.2.3 (git+https://github.com/kziemianek/secp.git?branch=sgx)", - "secp256k1 0.28.0", - "sgx_tstd", - "sha2 0.10.8", - "sha2_v08_wrapper", - "subtle 2.5.0 (git+https://github.com/kziemianek/subtle-sgx.git?branch=2.5.0-update)", -] - [[package]] name = "nalgebra" version = "0.32.3" @@ -6435,14 +5806,6 @@ dependencies = [ "cipher", ] -[[package]] -name = "once_cell" -version = "1.4.0" -source = "git+https://github.com/mesalock-linux/once_cell-sgx?branch=master#cefcaa03fed4d85276b3235d875f1b45d399cc3c" -dependencies = [ - "sgx_tstd", -] - [[package]] name = "once_cell" version = "1.4.0" @@ -6841,7 +6204,7 @@ version = "0.8.2" source = "git+https://github.com/mesalock-linux/pem-rs-sgx#fdfef4f24a9fb3fa72e8a71bb28bd8ff15feff2f" dependencies = [ "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx)", - "once_cell 1.4.0 (git+https://github.com/mesalock-linux/once_cell-sgx)", + "once_cell 1.4.0", "regex 1.3.1", "sgx_tstd", ] @@ -7474,7 +6837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ "hmac 0.12.1", - "subtle 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", ] [[package]] @@ -7953,7 +7316,7 @@ dependencies = [ "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.2", - "subtle 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", "zeroize", ] @@ -7999,36 +7362,10 @@ dependencies = [ "der 0.7.8", "generic-array 0.14.7", "pkcs8", - "subtle 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle", "zeroize", ] -[[package]] -name = "secp" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1507279bb0404bb566f85523e48fcf37a158daa5380577ee0d93f3ef4df39ccc" -dependencies = [ - "base16ct", - "k256", - "once_cell 1.19.0", - "secp256k1 0.28.0", - "subtle 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "secp" -version = "0.2.3" -source = "git+https://github.com/kziemianek/secp.git?branch=sgx#0479a3b12fc204015cdb63c138078fefe7e32341" -dependencies = [ - "base16ct", - "k256", - "once_cell 1.4.0 (git+https://github.com/mesalock-linux/once_cell-sgx?branch=master)", - "secp256k1 0.28.0", - "sgx_tstd", - "subtle 2.5.0 (git+https://github.com/kziemianek/subtle-sgx.git?branch=2.5.0-update)", -] - [[package]] name = "secp256k1" version = "0.24.3" @@ -8443,14 +7780,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sha2_v08_wrapper" -version = "0.1.0" -source = "git+https://github.com/kailai-wang/sha2_v08#c41176becc675e84cd708e8b18ba2cd0c9cf8eb0" -dependencies = [ - "sha2 0.8.2", -] - [[package]] name = "sha3" version = "0.10.8" @@ -9265,14 +8594,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" -[[package]] -name = "subtle" -version = "2.5.0" -source = "git+https://github.com/kziemianek/subtle-sgx.git?branch=2.5.0-update#57c424bdb6b98cbf9cfe19879748f20c3525c80e" -dependencies = [ - "sgx_tstd", -] - [[package]] name = "syn" version = "1.0.109" @@ -9411,23 +8732,6 @@ dependencies = [ "once_cell 1.19.0", ] -[[package]] -name = "threadpool" -version = "1.8.0" -source = "git+https://github.com/mesalock-linux/rust-threadpool-sgx?tag=sgx_1.1.3#098d98a85b7e2b02e2bb451a3dec0b027017ff4c" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus 1.16.0", -] - [[package]] name = "time" version = "0.1.45" diff --git a/tee-worker/Cargo.toml b/tee-worker/Cargo.toml index dc3d1d616a..4ea108a167 100644 --- a/tee-worker/Cargo.toml +++ b/tee-worker/Cargo.toml @@ -69,25 +69,6 @@ members = [ "identity/sidechain/consensus/slots", "identity/sidechain/consensus/common", "identity/sidechain/consensus/aura", - - # bitacross-worker - "bitacross/app-libs/*", - "bitacross/cli", - "bitacross/core/parentchain/block-import-dispatcher", - "bitacross/core/parentchain/block-importer", - "bitacross/core/parentchain/indirect-calls-executor", - "bitacross/core/parentchain/parentchain-crate", - "bitacross/core/direct-rpc-client", - "bitacross/core/direct-rpc-server", - "bitacross/core/offchain-worker-executor", - "bitacross/core-primitives/stf-executor", - "bitacross/core-primitives/top-pool", - "bitacross/core-primitives/top-pool-author", - "bitacross/core-primitives/enclave-api", - "bitacross/core-primitives/enclave-api/ffi", - "bitacross/service", - "bitacross/litentry/core/direct-call", - "bitacross/bitacross/core/*", ] exclude = [ @@ -97,7 +78,6 @@ exclude = [ # enclave-runtime needs to have its own workspace root for patching "identity/enclave-runtime", - "bitacross/enclave-runtime", ] [workspace.dependencies] diff --git a/tee-worker/bitacross/.dockerignore b/tee-worker/bitacross/.dockerignore deleted file mode 100644 index 10a8164af1..0000000000 --- a/tee-worker/bitacross/.dockerignore +++ /dev/null @@ -1,16 +0,0 @@ -# Litentry note: this file is unused -# Please edit the ../.dockerignore directly -.git -.githooks -.github -.idea -ci/ -docker/*yml -docs/ -local-setup/ -scripts/ -target/ -enclave-runtime/target/ -tmp/ -*.Dockerfile -Dockerfile \ No newline at end of file diff --git a/tee-worker/bitacross/.editorconfig b/tee-worker/bitacross/.editorconfig deleted file mode 100644 index de2a30a350..0000000000 --- a/tee-worker/bitacross/.editorconfig +++ /dev/null @@ -1,27 +0,0 @@ -root = true - -[*] -indent_style = tab -indent_size = 4 -tab_width = 4 -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -max_line_length = 100 -insert_final_newline = true - -[*.yml] -indent_style = space -indent_size = 4 -tab_width = 4 -end_of_line = lf - -[*.ts] -indent_style = space -indent_size = 4 -tab_width = 4 -end_of_line = lf - - -[*.toml] -indent_style = space \ No newline at end of file diff --git a/tee-worker/bitacross/.env.dev b/tee-worker/bitacross/.env.dev deleted file mode 100644 index 728ec336c3..0000000000 --- a/tee-worker/bitacross/.env.dev +++ /dev/null @@ -1,11 +0,0 @@ -AliceWSPort=9946 -AlicePort=30336 -BobWSPort=9947 -BobPort=30337 -CollatorWSPort=9944 -CollatorPort=30333 -TrustedWorkerPort=2000 -UntrustedWorkerPort=2001 -MuRaPort=3443 -UntrustedHttpPort=4545 -NODE_ENV=local \ No newline at end of file diff --git a/tee-worker/bitacross/.gitattributes.orig b/tee-worker/bitacross/.gitattributes.orig deleted file mode 100644 index 00c1715114..0000000000 --- a/tee-worker/bitacross/.gitattributes.orig +++ /dev/null @@ -1,18 +0,0 @@ -# TODO: why do we need binary mode for Cargo.lock? -# Cargo.lock linguist-generated=true -diff - -[attr]rust text eol=lf whitespace=tab-in-indent,trailing-space,tabwidth=4 - -* text=auto eol=lf -*.cpp rust -*.h rust -*.rs rust -*.fixed linguist-language=Rust -src/etc/installer/gfx/* binary -*.woff binary -src/vendor/** -text -Cargo.lock -merge linguist-generated=false - -# Older git versions try to fix line endings on images, this prevents it. -*.png binary -*.ico binary diff --git a/tee-worker/bitacross/.githooks/pre-commit b/tee-worker/bitacross/.githooks/pre-commit deleted file mode 100755 index 399188a65d..0000000000 --- a/tee-worker/bitacross/.githooks/pre-commit +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -# This pre-commit hook uses cargo fmt to check the code style -# Install it either with `make githooks` or copy the file to .git/hooks - -echo '+cargo fmt -- --check' -cargo fmt -- --check -result=$? - -if [[ ${result} -ne 0 ]] ; then - cat <<\EOF -There are some code style issues, run `cargo fmt` first. -EOF - exit 1 -fi - -exit 0 \ No newline at end of file diff --git a/tee-worker/bitacross/.gitignore b/tee-worker/bitacross/.gitignore deleted file mode 100644 index a14498c6eb..0000000000 --- a/tee-worker/bitacross/.gitignore +++ /dev/null @@ -1,78 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -**/target/ - -**/__pycache__/ -/log/* -log* -**/tmp/* - -**/node_modules/* - -# These are backup files generated by rustfmt -**/*.rs.bk - -# binaries -bin/*.so -bin/bitacross-* -bin/*.wasm - -# sealed data -bin/*.bin - -# public RSA key -bin/rsa_pubkey.txt -bin/ecc_pubkey.txt - -# VS Code settings -.vscode - -#intelliJ -.idea/ -*.iml - -*.log - -# vim -*.swp - -# keystores -my_keystore/* -my_trusted_keystore/* - -# generated enclave files -service/Enclave_u.* -service/libEnclave_u.* -enclave-runtime/Enclave_t.* -enclave-runtime/enclave.so -lib/libEnclave_u.* -lib/libcompiler-rt-patch.a -lib/libenclave.a - -# certificate, key, spid and generated report for remote attestation -bin/client.crt -bin/client.key -bin/spid.txt -bin/spid_production.txt -bin/key.txt -bin/key_production.txt -bin/attestation_report.json -bin/shards -bin/*.der -bin/enclave-shielding-pubkey.json -bin/sidechain_db -bin/my_trusted_keystore - -# client -cli/my_keystore -cli/my_trusted_keystore -bin/light_client_db.bin.1 - -# generated upstream patch -upstream.patch - -# backup log files -log-backup - -# env files and configs -.env diff --git a/tee-worker/bitacross/DESIGN.md b/tee-worker/bitacross/DESIGN.md deleted file mode 100644 index c49194f57f..0000000000 --- a/tee-worker/bitacross/DESIGN.md +++ /dev/null @@ -1,72 +0,0 @@ -# sidechain startup internal view - -```mermaid -sequenceDiagram - participant integritee_network - participant service - participant slotworker - participant parentsync - participant enclave - participant enclave_rpc - participant provisioningserver - participant isinitializedserver - participant metrics - service ->> enclave: EnclaveBase.get_mrenclave - service ->> provisioningserver: spawn (`--mu-ra-port` | 3443) - activate provisioningserver - service ->> enclave: get_ecc_signing_pubkey - service ->> isinitializedserver: spawn (`--untrusted-http-port | 4545) - activate isinitializedserver - service ->> metrics: spawn (`--metrics-port`| 8787) - activate metrics - service ->> enclave_rpc: spawn (`--trusted-worker-port`| 2000) - activate enclave_rpc - - service ->> enclave: generate_dcap_ra_extrinsic - service ->> integritee_network: send register_sgx_enclave extrinsic - service ->> integritee_network: get ShardStatus - service ->> isinitializedserver: registered_on_parentchain -# schedule teeracle re-registration and updates - loop while blocks to sync - service ->> integritee_network: get_block - service ->> enclave: sync_parentchain(blocks, events, proofs) - end - service ->> slotworker: spawn - loop forever - slotworker ->> enclave: execute_trusted_calls - activate enclave - enclave ->> enclave: propose_sidechain_block - enclave ->> integritee_network: send_extrinsics - deactivate enclave - end - service ->> parentsync: spawn - loop forever - parentsync ->> integritee_network: subscribe new headers - parentsync ->> enclave: sync_parentchain - end - service ->> service: poll worker_for_shard - service ->> isinitializedserver: worker_for_shard_registered - - deactivate enclave_rpc - deactivate metrics - deactivate isinitializedserver - deactivate provisioningserver -``` - -# sidechain lifetime external view - -```mermaid -sequenceDiagram - participant integritee_network - participant validateer_1 - participant validateer_2 - actor alice - - validateer_1 ->> integritee_network: register_sgx_enclave() - - validateer_2 ->> integritee_network: register_sgx_enclave() - - validateer_2 ->> validateer_1: sidechain_fetchBlocksFromPeer() - - validateer_1 ->> validateer_2: sidechain_importBlock() -``` diff --git a/tee-worker/bitacross/Dockerfile b/tee-worker/bitacross/Dockerfile deleted file mode 100644 index 95bd8a9d60..0000000000 --- a/tee-worker/bitacross/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -FROM integritee/integritee-dev:0.2.2 -LABEL maintainer="zoltan@integritee.network" - -# By default we warp the service -ARG BINARY_FILE=integritee-service - -COPY bin/enclave.signed.so /usr/local/bin/ -COPY bin/${BINARY_FILE} /usr/local/bin/integritee - -RUN chmod +x /usr/local/bin/integritee - -WORKDIR /usr/local/bin -RUN touch spid.txt key.txt -RUN if [[ "x$BINARY_FILE" != "xintegritee-client" ]] ; then ./integritee init-shard; fi -RUN if [[ "x$BINARY_FILE" != "xintegritee-client" ]] ; then ./integritee shielding-key; fi -RUN if [[ "x$BINARY_FILE" != "xintegritee-client" ]] ; then ./integritee signing-key; fi -RUN if [[ "x$BINARY_FILE" != "xintegritee-client" ]] ; then ./integritee mrenclave > ~/mrenclave.b58; fi - -# checks -RUN ldd /usr/local/bin/integritee && \ - /usr/local/bin/integritee --version - -ENTRYPOINT ["/usr/local/bin/integritee"] diff --git a/tee-worker/bitacross/Jenkinsfile b/tee-worker/bitacross/Jenkinsfile deleted file mode 100755 index 62c9197d68..0000000000 --- a/tee-worker/bitacross/Jenkinsfile +++ /dev/null @@ -1,104 +0,0 @@ -pipeline { - agent { - docker { - image 'integritee/integritee-dev:0.2.2' - args ''' - -u root - --privileged - ''' - } - } - options { - timeout(time: 2, unit: 'HOURS') - buildDiscarder(logRotator(numToKeepStr: '14')) - } - stages { - stage('Init rust') { - steps { - sh 'cargo --version' - sh 'rustup show' - sh 'env' - } - } - stage('Build') { - steps { - sh 'export SGX_SDK=/opt/intel/sgxsdk' - sh 'make' - } - } - stage('Archive build output') { - steps { - archiveArtifacts artifacts: 'bin/enclave.signed.so, bin/integritee-*', caseSensitive: false, fingerprint: true, onlyIfSuccessful: true - } - } - stage('Test') { - steps { - sh 'cd cli && cargo test 2>&1 | tee ${WORKSPACE}/test_client.log' - sh 'cd service && cargo test 2>&1 | tee ${WORKSPACE}/test_server.log' - sh 'cd enclave-runtime && cargo test 2>&1 | tee ${WORKSPACE}/test_enclave.log' - } - } - stage('Clippy') { - steps { - sh 'cargo clean' - catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { - sh 'cd cli && cargo clippy 2>&1 | tee ${WORKSPACE}/clippy_client.log' - } - catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { - sh 'cd worker && cargo clippy 2>&1 | tee ${WORKSPACE}/clippy_worker.log' - } - catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') { - sh 'cd enclave && cargo clippy 2>&1 | tee ${WORKSPACE}/clippy_enclave.log' - } - } - } - stage('Formatter') { - steps { - catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') { - sh 'cargo fmt -- --check > ${WORKSPACE}/fmt.log' - } - } - } - stage('Results') { - steps { - recordIssues( - aggregatingResults: true, - enabledForFailure: true, - qualityGates: [[threshold: 1, type: 'TOTAL', unstable: true]], - tools: [ - groovyScript( - parserId:'clippy-warnings', - pattern: 'clippy_*.log', - reportEncoding: 'UTF-8' - ), - groovyScript( - parserId:'clippy-errors', - pattern: 'clippy_*.log', - reportEncoding: 'UTF-8' - ) - ] - ) - catchError(buildResult: 'SUCCESS', stageResult: 'SUCCESS') { - sh './ci/check_fmt_log.sh' - } - } - } - stage('Archive logs') { - steps { - archiveArtifacts artifacts: '*.log' - } - } - } - post { - unsuccessful { - emailext ( - subject: "Jenkins Build '${env.JOB_NAME} [${env.BUILD_NUMBER}]' is ${currentBuild.currentResult}", - body: "${env.JOB_NAME} build ${env.BUILD_NUMBER} is ${currentBuild.currentResult}\n\nMore info at: ${env.BUILD_URL}", - to: "${env.RECIPIENTS_SUBSTRATEE}" - ) - } - always { - cleanWs() - } - } -} diff --git a/tee-worker/bitacross/LICENSE b/tee-worker/bitacross/LICENSE deleted file mode 100644 index 261eeb9e9f..0000000000 --- a/tee-worker/bitacross/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/tee-worker/bitacross/Makefile b/tee-worker/bitacross/Makefile deleted file mode 100755 index e3f2901f6c..0000000000 --- a/tee-worker/bitacross/Makefile +++ /dev/null @@ -1,289 +0,0 @@ -# Copyright 2021 Integritee AG and Supercomputing Systems AG -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -######## Update SGX SDK ######## -# use this manually to update sdk -#include UpdateRustSGXSDK.mk - -######## SGX SDK Settings ######## -SGX_SDK ?= /opt/intel/sgxsdk -SGX_MODE ?= HW -SGX_ARCH ?= x64 -SGX_DEBUG ?= 0 -SGX_PRERELEASE ?= 0 -SGX_PRODUCTION ?= 0 - -######## Worker Feature Settings ######## -# Set offchain-worker as default feature mode -WORKER_MODE ?= offchain-worker -RA_METHOD ?= dcap - -SKIP_WASM_BUILD = 1 -# include the build settings from rust-sgx-sdk -include rust-sgx-sdk/buildenv.mk - -ifeq ($(shell getconf LONG_BIT), 32) - SGX_ARCH := x86 -else ifeq ($(findstring -m32, $(CXXFLAGS)), -m32) - SGX_ARCH := x86 -endif - -ifeq ($(SGX_ARCH), x86) - SGX_COMMON_CFLAGS := -m32 - SGX_LIBRARY_PATH := $(SGX_SDK)/lib - SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x86/sgx_sign - SGX_EDGER8R := $(SGX_SDK)/bin/x86/sgx_edger8r -else - SGX_COMMON_CFLAGS := -m64 - SGX_LIBRARY_PATH := $(SGX_SDK)/lib64 - SGX_ENCLAVE_SIGNER := $(SGX_SDK)/bin/x64/sgx_sign - SGX_EDGER8R := $(SGX_SDK)/bin/x64/sgx_edger8r -endif - -ifeq ($(SGX_DEBUG), 1) -ifeq ($(SGX_PRERELEASE), 1) -$(error Cannot set SGX_DEBUG and SGX_PRERELEASE at the same time!!) -endif -ifeq ($(SGX_PRODUCTION), 1) -$(error Cannot set SGX_DEBUG and SGX_PRODUCTION at the same time!!) -endif -endif - -ifeq ($(SGX_DEBUG), 1) - SGX_COMMON_CFLAGS += -O0 -g -ggdb - OUTPUT_PATH := debug - CARGO_TARGET := -else - SGX_COMMON_CFLAGS += -O2 - OUTPUT_PATH := release - CARGO_TARGET := --release -endif - -SGX_COMMON_CFLAGS += -fstack-protector - -ifeq ($(SGX_PRODUCTION), 1) - SGX_ENCLAVE_MODE = "Production Mode" - SGX_ENCLAVE_CONFIG = "enclave-runtime/Enclave.config.production.xml" - SGX_SIGN_KEY = $(SGX_COMMERCIAL_KEY) - SGX_SIGN_PASSFILE = $(SGX_PASSFILE) - WORKER_FEATURES := --features=link-binary,$(WORKER_MODE),$(RA_METHOD),$(WORKER_FEATURES),$(ADDITIONAL_FEATURES) -else - SGX_ENCLAVE_MODE = "Development Mode" - SGX_ENCLAVE_CONFIG = "enclave-runtime/Enclave.config.xml" - SGX_SIGN_KEY = "enclave-runtime/Enclave_private.pem" - SGX_SIGN_PASSFILE = "" - WORKER_FEATURES := --features=default,development,link-binary,$(WORKER_MODE),$(RA_METHOD),$(WORKER_FEATURES),$(ADDITIONAL_FEATURES) - ADDITIONAL_FEATURES := development -endif - -CLIENT_FEATURES = --features=$(WORKER_MODE),$(RA_METHOD),$(ADDITIONAL_FEATURES) - -# check if running on Jenkins -ifdef BUILD_ID - CARGO_TARGET += --verbose -endif - -######## CUSTOM settings ######## -CUSTOM_LIBRARY_PATH := ./lib -CUSTOM_BIN_PATH := ./bin -CUSTOM_EDL_PATH := ./rust-sgx-sdk/edl -CUSTOM_COMMON_PATH := ./rust-sgx-sdk/common - -######## EDL settings ######## -Enclave_EDL_Files := enclave-runtime/Enclave_t.c enclave-runtime/Enclave_t.h service/Enclave_u.c service/Enclave_u.h - -######## bitacross-worker settings ######## -SRC_Files := $(shell find . -type f -name '*.rs') $(shell find . -type f -name 'Cargo.toml') -Worker_Rust_Flags := $(CARGO_TARGET) $(WORKER_FEATURES) -Worker_Include_Paths := -I ./service -I./include -I$(SGX_SDK)/include -I$(CUSTOM_EDL_PATH) -Worker_C_Flags := $(SGX_COMMON_CFLAGS) -fPIC -Wno-attributes $(Worker_Include_Paths) - -Worker_Rust_Path := ../target/$(OUTPUT_PATH) -Worker_Enclave_u_Object :=service/libEnclave_u.a -Worker_Name := bin/app - -######## bitacross-cli settings ######## -Client_Rust_Flags := $(CARGO_TARGET) $(CLIENT_FEATURES) - -Client_Rust_Path := ../target/$(OUTPUT_PATH) -Client_Path := bin -Client_Binary := bitacross-cli -Client_Name := $(Client_Path)/$(Client_Binary) - -######## Enclave settings ######## -ifneq ($(SGX_MODE), HW) - Trts_Library_Name := sgx_trts_sim - Service_Library_Name := sgx_tservice_sim -else - Trts_Library_Name := sgx_trts - Service_Library_Name := sgx_tservice -endif -Crypto_Library_Name := sgx_tcrypto -KeyExchange_Library_Name := sgx_tkey_exchange -ProtectedFs_Library_Name := sgx_tprotected_fs - -RustEnclave_C_Files := $(wildcard ./enclave-runtime/*.c) -RustEnclave_C_Objects := $(RustEnclave_C_Files:.c=.o) -RustEnclave_Include_Paths := -I$(CUSTOM_COMMON_PATH)/inc -I$(CUSTOM_EDL_PATH) -I$(SGX_SDK)/include -I$(SGX_SDK)/include/tlibc -I$(SGX_SDK)/include/stlport -I$(SGX_SDK)/include/epid -I ./enclave-runtime -I./include - -RustEnclave_Link_Libs := -L$(CUSTOM_LIBRARY_PATH) -lenclave -RustEnclave_Compile_Flags := $(SGX_COMMON_CFLAGS) $(ENCLAVE_CFLAGS) $(RustEnclave_Include_Paths) -RustEnclave_Link_Flags := -Wl,--no-undefined -nostdlib -nodefaultlibs -nostartfiles -L$(SGX_LIBRARY_PATH) \ - -Wl,--whole-archive -l$(Trts_Library_Name) -Wl,--no-whole-archive \ - -Wl,--start-group -lsgx_tstdc -lsgx_tcxx -lsgx_dcap_tvl -l$(Crypto_Library_Name) -l$(Service_Library_Name) -l$(ProtectedFs_Library_Name) $(RustEnclave_Link_Libs) -Wl,--end-group \ - -Wl,--version-script=enclave-runtime/Enclave.lds \ - $(ENCLAVE_LDFLAGS) - -RustEnclave_Name := enclave-runtime/enclave.so -Signed_RustEnclave_Name := bin/enclave.signed.so - -######## Targets ######## -.PHONY: all -all: $(Worker_Name) $(Client_Name) $(Signed_RustEnclave_Name) -service: $(Worker_Name) -client: $(Client_Name) -githooks: .git/hooks/pre-commit - -######## EDL objects ######## -$(Enclave_EDL_Files): $(SGX_EDGER8R) enclave-runtime/Enclave.edl - $(SGX_EDGER8R) --trusted enclave-runtime/Enclave.edl --search-path $(SGX_SDK)/include --search-path $(CUSTOM_EDL_PATH) --trusted-dir enclave-runtime - $(SGX_EDGER8R) --untrusted enclave-runtime/Enclave.edl --search-path $(SGX_SDK)/include --search-path $(CUSTOM_EDL_PATH) --untrusted-dir service - @echo "GEN => $(Enclave_EDL_Files)" - -######## bitacross-worker objects ######## -service/Enclave_u.o: $(Enclave_EDL_Files) - @$(CC) $(Worker_C_Flags) -c service/Enclave_u.c -o $@ - @echo "CC <= $<" - -$(Worker_Enclave_u_Object): service/Enclave_u.o - $(AR) rcsD $@ $^ - cp $(Worker_Enclave_u_Object) ./lib - -$(Worker_Name): $(Worker_Enclave_u_Object) $(SRC_Files) - @echo - @echo "Building the bitacross-worker: $(Worker_Rust_Flags)" - @SGX_SDK=$(SGX_SDK) SGX_MODE=$(SGX_MODE) cargo build -p bitacross-worker $(Worker_Rust_Flags) - @echo "Cargo => $@" - cp $(Worker_Rust_Path)/bitacross-worker ./bin - -######## bitacross-client objects ######## -$(Client_Name): $(SRC_Files) - @echo - @echo "Building the bitacross-cli $(Client_Rust_Flags)" - @cargo build -p bitacross-cli $(Client_Rust_Flags) - @echo "Cargo => $@" - cp $(Client_Rust_Path)/$(Client_Binary) ./bin - -######## Enclave objects ######## -enclave-runtime/Enclave_t.o: $(Enclave_EDL_Files) - @$(CC) $(RustEnclave_Compile_Flags) -c enclave-runtime/Enclave_t.c -o $@ - @echo "CC <= $<" - -$(RustEnclave_Name): enclave enclave-runtime/Enclave_t.o - @echo Compiling $(RustEnclave_Name) - @$(CXX) enclave-runtime/Enclave_t.o -o $@ $(RustEnclave_Link_Flags) - @echo "LINK => $@" - -$(Signed_RustEnclave_Name): $(RustEnclave_Name) - @echo - @echo "Signing the enclave: $(SGX_ENCLAVE_MODE)" - @echo "SGX_ENCLAVE_SIGNER: $(SGX_ENCLAVE_SIGNER)" - @echo "RustEnclave_Name: $(RustEnclave_Name)" - @echo "SGX_ENCLAVE_CONFIG: $(SGX_ENCLAVE_CONFIG)" - @echo "SGX_SIGN_PASSFILE: $(SGX_SIGN_PASSFILE)" - @echo "SGX_SIGN_KEY: $(SGX_SIGN_KEY)" - - -# TODO: figure out if/how to use the passphrase file in PROD -ifeq ($(SGX_PRODUCTION), 1) - $(SGX_ENCLAVE_SIGNER) gendata -enclave $(RustEnclave_Name) -out enclave_sig.dat -config $(SGX_ENCLAVE_CONFIG) - openssl rsa -pubout -in $(SGX_SIGN_KEY) -out intel_sgx.pub - openssl dgst -sha256 -sign $(SGX_SIGN_KEY) -out signature.dat enclave_sig.dat - openssl dgst -sha256 -verify intel_sgx.pub -signature signature.dat enclave_sig.dat - $(SGX_ENCLAVE_SIGNER) catsig -enclave $(RustEnclave_Name) -config $(SGX_ENCLAVE_CONFIG) -out $@ -key intel_sgx.pub -sig signature.dat -unsigned enclave_sig.dat -else - $(SGX_ENCLAVE_SIGNER) sign -key $(SGX_SIGN_KEY) -enclave $(RustEnclave_Name) -out $@ -config $(SGX_ENCLAVE_CONFIG) -endif - @echo "SIGN => $@" - @echo - @echo "Enclave is in $(SGX_ENCLAVE_MODE)" - -.PHONY: enclave -enclave: - @echo - @echo "Building the enclave" - $(MAKE) -C ./enclave-runtime/ RA_METHOD=$(RA_METHOD) WORKER_DEV=$(WORKER_DEV) - -.git/hooks/pre-commit: .githooks/pre-commit - @echo "Installing git hooks" - cp .githooks/pre-commit .git/hooks - -.PHONY: clean -clean: - @echo "Removing the compiled files" - @rm -f $(Client_Name) $(Worker_Name) $(RustEnclave_Name) $(Signed_RustEnclave_Name) \ - enclave-runtime/*_t.* \ - service/*_u.* \ - lib/*.a \ - bin/*.bin - @echo "cargo clean in enclave directory" - @cd enclave-runtime && cargo clean - @echo "cargo clean in root directory" - @cargo clean - -.PHONY: fmt -fmt: - @echo "Cargo format all ..." - @cargo fmt --all - @cd enclave-runtime && cargo fmt --all - -.PHONY: pin-sgx -pin-sgx: - @echo "Pin sgx dependencies to 594806f827b57e6c4c9a0611fa4cbf2d83aabd2e" - @cd enclave-runtime && cargo update -p sgx_tstd --precise 594806f827b57e6c4c9a0611fa4cbf2d83aabd2e - @cargo update -p sgx_tstd --precise 594806f827b57e6c4c9a0611fa4cbf2d83aabd2e - -mrenclave: - @$(SGX_ENCLAVE_SIGNER) dump -enclave ./bin/enclave.signed.so -dumpfile df.out && ./extract_identity < df.out && rm df.out - -mrsigner: - @$(SGX_ENCLAVE_SIGNER) dump -enclave ./bin/enclave.signed.so -dumpfile df.out && ./extract_identity --mrsigner < df.out && rm df.out - -.PHONY: identity -identity: mrenclave mrsigner - -.PHONY: release-pkg -release-pkg: - @./scripts/litentry/release/generate_release_pkg.sh - -.PHONY: help -help: - @echo "Available targets" - @echo " all - builds all targets (default)" - @echo " service - builds the bitacross-worker" - @echo " client - builds the bitacross-cli" - @echo " githooks - installs the git hooks (copy .githooks/pre-commit to .git/hooks)" - @echo "" - @echo " clean - cleanup" - @echo "" - @echo "Compilation options. Prepend them to the make command. Example: 'SGX_MODE=SW make'" - @echo " SGX_MODE" - @echo " HW (default): Use SGX hardware" - @echo " SW: Simulation mode" - @echo " SGX_DEBUG" - @echo " 0 (default): No debug information, optimization level 2, cargo release build" - @echo " 1: Debug information, optimization level 0, cargo debug build" - @echo " SGX_PRODUCTION" - @echo " 0 (default): Using SGX development environment" - @echo " 1: Using SGX production environment" diff --git a/tee-worker/bitacross/README.md b/tee-worker/bitacross/README.md deleted file mode 100755 index e614d4be86..0000000000 --- a/tee-worker/bitacross/README.md +++ /dev/null @@ -1,209 +0,0 @@ -# BitAcross worker - -This repository contains code for BitAcross offchain worker. The main responsibility of the worker is to -store custodian wallets and sign transactions submitted by relayers. - -## Wallets - -Supported wallets: -* ethereum (ecdsa based on secp256k1) -* bitcoin (schnorr based on secp256k1) - -Wallets (private keys) are generated during the initialization (on first startup) and sealed to encrypted file using Intel Protected File System while public keys are published on parachain's bitacross pallet in compressed SEC1-encoded format. - - -## Transaction signing - -Signing requests are processed by a dedicated JSON-RPC `bitacross_submitRequest` method and results in raw signature bytes. Only requests signed by registered relayers are permitted. - -Typescript code related to the RPC integration and can be found in [tee-worker's ts-tests](https://github.com/litentry/litentry-parachain/blob/a6b78ed68396280655271f9cd30e17535d54da81/tee-worker/ts-tests/integration-tests/common/di-utils.ts). - -Rust code used in CLI module can also be used as a reference and can be found [here](https://github.com/litentry/litentry-parachain/blob/a6b78ed68396280655271f9cd30e17535d54da81/bitacross-worker/cli/src/trusted_base_cli/commands/bitacross/utils.rs). - - -## Local launch - -```bash -make SGX_MODE=SW -cd ../.. && local-setup/launch.py -w bitacross -wn 3 -p standalone -``` - -### Step by step guide for request preparing/sending and response handling. - -1. Prepare `DirectCall`, for example `SignBitcoin` variant which will reflect bitcoin's transaction signing request. Generate 256-bit AES-GCM as request enc/dec key. The first parameter is relayer identity, second generated aes key and third is transaction payload to sign. - -```rust -pub enum DirectCall { - SignBitcoin(Identity, RequestAesKey, Vec), - SignEthereum(Identity, RequestAesKey, Vec), -} -``` - -2. Prepare `DirectCallSigned`. Scale encode created direct call from step 1, append scale encoded mrenclave and shard identifier (use mrenclave) to it, do a Blake2 256-bit hash of it and sign it using relayer's private key, then prepare struct containing direct call and signature. Mrenclave can be obtained from parachain's teebag pallet enclave registry storage. - -```Rust -#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] -pub struct DirectCallSigned { - pub call: DirectCall, - pub signature: LitentryMultiSignature, -} -``` - -3. Prepare `AesRequest`. Fill payload property with aes encrypted scale encoded `DirectCallSigned` prepared in previous step. Get worker's shielding key and use it to encrypt aes key from step 1 and fill `key` property with it. Fill shard identifier (use mrenclave again). Shielding key can be obtained from parachain's teebag pallet enclave registry storage. - -```rust -pub struct AesRequest { - pub shard: ShardIdentifier, - pub key: Vec, - pub payload: AesOutput, -} -``` - -4. Prepare `RpcRequest`. Scale encode `AesRequest` prepared in previous step, turn the bytes into hex representation and put in `params` vec. Generate locally unique `id`. Use following consts for `jsonrpc` and `method`: `"2.0"`, `bitacross_submitRequest` - -```rust -pub struct RpcRequest { - pub jsonrpc: String, - pub method: String, - pub params: Vec, - pub id: Id, -} -``` - -5. Send prepared request to worker's jsonrpc endpoint, in the following example `websocat` is used to send request to locally run bitacross worker. - -```bash -echo '{"jsonrpc":"2.0","method":"bitacross_submitRequest","params":["0x6565c4529cd2af40f89e5d526c6e890019a2fd33cfdc9ee3cd14a0bf1427a61601065c22cde40abe4ad0550a4beba5d05a55380117a57824a57c5949a472fb0639d1ebb1baff0f5453e222418844044ed75352f9a76b4f3fd57f8db4deabf4074eb552784b32c1a881ac27d143148e06a3607455ebafb7dd3ab1669013502bfd7b840d6698363015f55fede5275dfe7d05827315301772e4b75bf745f74b71c443b97b7d22010d54b89fcc1105cbfc72a58dfbd4c10e34ef6019dad859abafdb4f82118f5f339255cb5d2400243bc2e982b4c60341572b6253e0815ed90de74b64145aef8d8304a576ba11c73421b9c86a053619908c475be5d223acc942460afb7e248836f58d2e639d3e32365bbc7ba9fe838b3329db6432fce3427569523f513e7cc82098db4ccaf024a286ad94e6be775ba1f9e918f0867e20a8dbb409232ba297878eff52740e705f59dab2a1c5827d1f8bf7adfa7cdf9e345c16fda757016337f398201af14c820782dac82bc9c5f8df93c917cba29f89e5a1e323dafcf2465e258f1d6dcf9808e5202e6fa3766433981f619c580b831c0d49eed759a0ca1555021c688b72490ffd3f4391c60c04ba904d83aa9497cce62eb6d0e55124692c5124fabfabd70ab366ba81d152f2299ba99021a3705754d64d2b9455229d6ecd730a120a1003abe432a060e40931ad9eb3199cbb09a6b2c84af35735b51628d80210369c0f902905f7e7902d6787673691f2e923b6bc001cfa56f3568e95a95f1f084cd69e658e42c96e317cebc17d54de13f08a0fb007008777e7510d0aa8d124271afe"],"id":1}' | websocat -n1 -k -B 99999999 wss://localhost:2000 -{"jsonrpc":"2.0","result":"0x7d014101d9197274039df1280452819ede02d0867aa57185251d19c9e2c74bd22d1f3b8e1db031b068e3ee7229631a804c1d03e2d9af7055851ac9609dae5e8c7c8dccf4961b31a5cc98bfa356fa262a7376525300a2c03d6f2b59e28578fdcee00000","id":1} -``` - -6. Get result from response `result` field. It's a hex representation of scale encoded `RpcReturnValue`. In case of success, the signature can be obtained from `value` property (it's aes encrypted). - -Types definitions: - -```rust -pub type RequestAesKey = [u8; 32]; - -#[derive( -Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo, MaxEncodedLen, EnumIter, Ord, PartialOrd, -)] -pub enum Identity { - // web2 - #[codec(index = 0)] - Twitter(IdentityString), - #[codec(index = 1)] - Discord(IdentityString), - #[codec(index = 2)] - Github(IdentityString), - - // web3 - #[codec(index = 3)] - Substrate(Address32), - #[codec(index = 4)] - Evm(Address20), - // bitcoin addresses are derived (one-way hash) from the pubkey - // by using `Address33` as the Identity handle, it requires that pubkey - // is retrievable by the wallet API when verifying the bitcoin account. - // e.g. unisat-wallet: https://docs.unisat.io/dev/unisat-developer-service/unisat-wallet#getpublickey - #[codec(index = 5)] - Bitcoin(Address33), -} - -pub enum LitentryMultiSignature { - /// An Ed25519 signature. - #[codec(index = 0)] - Ed25519(ed25519::Signature), - /// An Sr25519 signature. - #[codec(index = 1)] - Sr25519(sr25519::Signature), - /// An ECDSA/SECP256k1 signature. - #[codec(index = 2)] - Ecdsa(ecdsa::Signature), - /// An ECDSA/keccak256 signature. An Ethereum signature. hash message with keccak256 - #[codec(index = 3)] - Ethereum(EthereumSignature), - /// Same as above, but the payload bytes are prepended with a readable prefix and `0x` - #[codec(index = 4)] - EthereumPrettified(EthereumSignature), - /// Bitcoin signed message, a hex-encoded string of original &[u8] message, without `0x` prefix - #[codec(index = 5)] - Bitcoin(BitcoinSignature), - /// Same as above, but the payload bytes are prepended with a readable prefix and `0x` - #[codec(index = 6)] - BitcoinPrettified(BitcoinSignature), -} - -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Eq, Clone, Debug)] -pub struct EthereumSignature(pub [u8; 65]); - -#[derive(Encode, Decode, MaxEncodedLen, TypeInfo, PartialEq, Eq, Clone, Debug)] -pub struct BitcoinSignature(pub [u8; 65]); - -#[derive( -Encode, Decode, Copy, Clone, Default, PartialEq, Eq, TypeInfo, MaxEncodedLen, Ord, PartialOrd, -)] -pub struct Address20([u8; 20]); - -#[derive( -Encode, Decode, Copy, Clone, Default, PartialEq, Eq, TypeInfo, MaxEncodedLen, Ord, PartialOrd, -)] -pub struct Address32([u8; 32]); - -#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, TypeInfo, MaxEncodedLen, PartialOrd, Ord)] -pub struct Address33([u8; 33]); - -#[derive(Debug, Default, Clone, Eq, PartialEq, Encode, Decode)] -pub struct AesOutput { - pub ciphertext: Vec, - pub aad: Vec, - pub nonce: RequestAesKeyNonce, // IV -} - -#[derive(Encode, Decode, Debug, Eq, PartialEq)] -pub struct RpcReturnValue { - pub value: Vec, - pub do_watch: bool, - pub status: DirectRequestStatus, -} - -#[derive(Debug, Clone, PartialEq, Encode, Decode, Eq)] -pub enum DirectRequestStatus { - /// Direct request was successfully executed - #[codec(index = 0)] - Ok, - /// Trusted Call Status - /// Litentry: embed the top hash here - TODO - use generic type? - #[codec(index = 1)] - TrustedOperationStatus(TrustedOperationStatus, H256), - /// Direct request could not be executed - #[codec(index = 2)] - Error, - #[codec(index = 3)] - Processing -} -``` - -### Using CLI - -There are two commands related to transaction signing: - -* request-direct-call-sign-bitcoin -* request-direct-call-sign-ethereum - -They take single argument representing raw payload bytes to sign. - -#### Example usage - -```bash -./bitacross-cli trusted -m 7ppBUcnjGir4szRHCG59p2dTnbtRwKRbLZPpR32ACjbK request-direct-call-sign-bitcoin 00 -``` - -### Obtaining data from parachain's teebag pallet - -Mrencalve, worker's url and public shielding key can be obtained during the runtime from parachain's teebag pallet registry. - -The following gif ilustrates how it can be done manually: - -![demo](./assets/teebag_registry.gif) - -These values can also be obtained programmatically using substrate's `state_getStorage` RPC method. See [this](https://docs.substrate.io/build/remote-procedure-calls/) documentation for more information. \ No newline at end of file diff --git a/tee-worker/bitacross/UpdateRustSGXSDK.mk b/tee-worker/bitacross/UpdateRustSGXSDK.mk deleted file mode 100755 index 88c95d5dc6..0000000000 --- a/tee-worker/bitacross/UpdateRustSGXSDK.mk +++ /dev/null @@ -1,33 +0,0 @@ -# helper script to update the files in rust-sgx-sdk to the lastest version - -GIT = git -CP = cp - -REPO = https://github.com/apache/incubator-teaclave-sgx-sdk -SDK_PATH_GIT = rust-sgx-sdk-github -SDK_PATH = rust-sgx-sdk -VERSION_FILE = rust-sgx-sdk/version -LOCAL_VERSION = $(shell cat $(VERSION_FILE)) -COMMAND = git ls-remote $(REPO) HEAD | awk '{ print $$1 }' -REMOTE_VERSION = $(shell $(COMMAND)) -# or specify the exact hash if you need a non-default branch / tag / commit etc. -#REMOTE_VERSION = 9c1bbd52f188f600a212b57c916124245da1b7fd - -# update the SDK files -all: updatesdk - -updatesdk: -# check for already updated version -ifneq ('$(LOCAL_VERSION)','$(REMOTE_VERSION)') - @echo Local version = $(LOCAL_VERSION) - @echo Remote version = $(REMOTE_VERSION) - - @rm -rf $(SDK_PATH_GIT) - @$(GIT) clone $(REPO) $(SDK_PATH_GIT) - @$(GIT) -C $(SDK_PATH_GIT) checkout $(REMOTE_VERSION) - rsync -a $(SDK_PATH_GIT)/edl $(SDK_PATH) - rsync -a $(SDK_PATH_GIT)/common $(SDK_PATH) - rm -rf $(SDK_PATH_GIT) - @echo $(REMOTE_VERSION) > $(VERSION_FILE) - -endif diff --git a/tee-worker/bitacross/app-libs/parentchain-interface/Cargo.toml b/tee-worker/bitacross/app-libs/parentchain-interface/Cargo.toml deleted file mode 100644 index d4b0ccea49..0000000000 --- a/tee-worker/bitacross/app-libs/parentchain-interface/Cargo.toml +++ /dev/null @@ -1,77 +0,0 @@ -[package] -name = "bc-ita-parentchain-interface" -version = "0.1.0" -authors = ["Integritee AG "] -edition = "2021" - -[dependencies] -sgx_tstd = { workspace = true, optional = true } - -ita-sgx-runtime = { package = "bc-ita-sgx-runtime", path = "../sgx-runtime", default-features = false } -ita-stf = { package = "bc-ita-stf", path = "../stf", default-features = false } -itc-parentchain-indirect-calls-executor = { package = "bc-itc-parentchain-indirect-calls-executor", path = "../../core/parentchain/indirect-calls-executor", default-features = false } -itp-api-client-types = { workspace = true } -itp-node-api = { workspace = true } -itp-stf-primitives = { workspace = true } -itp-types = { workspace = true } - -codec = { package = "parity-scale-codec", workspace = true } -log = { workspace = true } - -sp-core = { workspace = true, features = ["full_crypto"] } -sp-runtime = { workspace = true } -substrate-api-client = { workspace = true, optional = true } - -# litentry -bc-enclave-registry = { path = "../../bitacross/core/bc-enclave-registry", default-features = false } -bc-relayer-registry = { path = "../../bitacross/core/bc-relayer-registry", default-features = false } -bc-signer-registry = { path = "../../bitacross/core/bc-signer-registry", default-features = false } -litentry-primitives = { workspace = true } -sp-std = { workspace = true } - -[dev-dependencies] -env_logger = { workspace = true } -itp-node-api = { workspace = true, features = ["std", "mocks"] } -itp-sgx-crypto = { workspace = true, features = ["std", "mocks"] } -itp-test = { workspace = true, features = ["std"] } -itp-stf-executor = { package = "bc-itp-stf-executor", path = "../../core-primitives/stf-executor", features = ["std", "mocks"] } -itp-top-pool-author = { package = "bc-itp-top-pool-author", path = "../../core-primitives/top-pool-author", features = ["std", "mocks"] } -itc-parentchain-test = { workspace = true, features = ["std"] } - -[features] -default = ["std"] -std = [ - "codec/std", - "ita-sgx-runtime/std", - "ita-stf/std", - "itc-parentchain-indirect-calls-executor/std", - "itp-api-client-types/std", - "itp-node-api/std", - "itp-sgx-crypto/std", - "itp-stf-executor/std", - "itp-stf-primitives/std", - "itp-top-pool-author/std", - "itp-types/std", - "log/std", - "sp-core/std", - "sp-runtime/std", - "substrate-api-client", - "litentry-primitives/std", - "sp-std/std", - "bc-enclave-registry/std", - "bc-relayer-registry/std", - "bc-signer-registry/std", -] -sgx = [ - "sgx_tstd", - "ita-stf/sgx", - "itc-parentchain-indirect-calls-executor/sgx", - "itp-node-api/sgx", - "itp-sgx-crypto/sgx", - "itp-stf-executor/sgx", - "itp-top-pool-author/sgx", - "litentry-primitives/sgx", - "bc-enclave-registry/sgx", - "bc-relayer-registry/sgx", - "bc-signer-registry/sgx", -] diff --git a/tee-worker/bitacross/app-libs/parentchain-interface/src/event_subscriber.rs b/tee-worker/bitacross/app-libs/parentchain-interface/src/event_subscriber.rs deleted file mode 100644 index 237a678f06..0000000000 --- a/tee-worker/bitacross/app-libs/parentchain-interface/src/event_subscriber.rs +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright 2021 Integritee AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use itp_api_client_types::ParentchainApi; -use itp_types::parentchain::{events::BalanceTransfer, ParentchainId}; -use substrate_api_client::SubscribeEvents; - -pub fn subscribe_to_parentchain_events(api: &ParentchainApi, parentchain_id: ParentchainId) { - println!("[L1Event:{}] Subscribing to selected events", parentchain_id); - let mut subscription = api.subscribe_events().unwrap(); - loop { - let events = subscription.next_events_from_metadata().unwrap().unwrap(); - - for event in events.iter() { - let event = event.unwrap(); - match event.pallet_name() { - "System" => continue, - "ParaInclusion" => continue, - "MessageQueue" => continue, - "TransactionPayment" => continue, - "Treasury" => continue, - "Balances" => match event.variant_name() { - "Deposit" => continue, - "Withdraw" => continue, - "Transfer" => - if let Ok(Some(ev)) = event.as_event::() { - println!("[L1Event:{}] {:?}", parentchain_id, ev); - }, - _ => println!( - "[L1Event:{}] {}::{}", - parentchain_id, - event.pallet_name(), - event.variant_name() - ), - }, - // TODO(Litentry): add important teebag events? - _ => println!( - "[L1Event:{}] {}::{}", - parentchain_id, - event.pallet_name(), - event.variant_name() - ), - } - } - } -} diff --git a/tee-worker/bitacross/app-libs/parentchain-interface/src/integritee/event_filter.rs b/tee-worker/bitacross/app-libs/parentchain-interface/src/integritee/event_filter.rs deleted file mode 100644 index b7ceeb561a..0000000000 --- a/tee-worker/bitacross/app-libs/parentchain-interface/src/integritee/event_filter.rs +++ /dev/null @@ -1,124 +0,0 @@ -/* - Copyright 2021 Integritee AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -//! Various way to filter Parentchain events - -use itc_parentchain_indirect_calls_executor::event_filter::ToEvents; -use itp_api_client_types::Events; -use itp_node_api::api_client::StaticEvent; - -use itp_types::{ - parentchain::{events::*, FilterEvents}, - H256, -}; -use std::vec::Vec; - -#[derive(Clone)] -pub struct FilterableEvents(pub Events); - -impl FilterableEvents { - fn filter(&self) -> Result, E> { - Ok(self - .to_events() - .iter() - .flatten() - .filter_map(|ev| match ev.as_event::() { - Ok(maybe_event) => maybe_event, - Err(e) => { - log::error!("Could not decode event: {:?}", e); - None - }, - }) - .collect()) - } -} - -// todo: improve: https://github.com/integritee-network/worker/pull/1378#discussion_r1393933766 -impl ToEvents> for FilterableEvents { - fn to_events(&self) -> &Events { - &self.0 - } -} - -impl From> for FilterableEvents { - fn from(ev: Events) -> Self { - Self(ev) - } -} - -impl FilterEvents for FilterableEvents { - type Error = itc_parentchain_indirect_calls_executor::Error; - - fn get_link_identity_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_vc_requested_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_deactivate_identity_events( - &self, - ) -> Result, Self::Error> { - self.filter() - } - - fn get_activate_identity_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_enclave_unauthorized_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_opaque_task_posted_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_assertion_created_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_parentchain_block_proccessed_events( - &self, - ) -> Result, Self::Error> { - self.filter() - } - - fn get_relayer_added_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_relayers_removed_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_enclave_added_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_enclave_removed_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_btc_wallet_generated_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_account_store_updated_events(&self) -> Result, Self::Error> { - self.filter() - } -} diff --git a/tee-worker/bitacross/app-libs/parentchain-interface/src/integritee/event_handler.rs b/tee-worker/bitacross/app-libs/parentchain-interface/src/integritee/event_handler.rs deleted file mode 100644 index e26c697c32..0000000000 --- a/tee-worker/bitacross/app-libs/parentchain-interface/src/integritee/event_handler.rs +++ /dev/null @@ -1,234 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pub use ita_sgx_runtime::{Balance, Index}; - -use bc_enclave_registry::{EnclaveRegistry, EnclaveRegistryUpdater}; -use bc_relayer_registry::{RelayerRegistry, RelayerRegistryUpdater}; -use bc_signer_registry::{SignerRegistry, SignerRegistryUpdater}; -use codec::Encode; -use core::str::from_utf8; -use ita_stf::TrustedCallSigned; -use itc_parentchain_indirect_calls_executor::error::Error; -use itp_stf_primitives::traits::IndirectExecutor; -use itp_types::{ - parentchain::{FilterEvents, HandleParentchainEvents, ParentchainEventProcessingError}, - WorkerType, -}; -use litentry_primitives::{Address32, Identity}; -use log::*; -use sp_core::{blake2_256, H256}; -use sp_runtime::traits::{Block as ParentchainBlock, Header as ParentchainHeader}; -use sp_std::vec::Vec; -use std::string::ToString; - -pub struct ParentchainEventHandler {} - -impl ParentchainEventHandler { - fn add_relayer(relayer_registry: &RelayerRegistry, account: Identity) -> Result<(), Error> { - info!("Adding Relayer Account to Registry: {:?}", account); - relayer_registry.update(account).map_err(|e| { - error!("Error adding relayer: {:?}", e); - Error::Other("Error adding relayer".into()) - })?; - - Ok(()) - } - - fn remove_relayer(relayer_registry: &RelayerRegistry, account: Identity) -> Result<(), Error> { - info!("Remove Relayer Account from Registry: {:?}", account); - relayer_registry.remove(account).map_err(|e| { - error!("Error removing relayer: {:?}", e); - Error::Other("Error removing relayer".into()) - })?; - - Ok(()) - } - - fn add_enclave( - enclave_registry: &EnclaveRegistry, - account_id: Address32, - url: Vec, - worker_type: WorkerType, - ) -> Result<(), Error> { - info!("Adding Enclave Account to Registry: {:?}", account_id); - if worker_type != WorkerType::BitAcross { - warn!("Ignore AddEnclave due to wrong worker_type"); - return Ok(()) - } - - let url = from_utf8(&url) - .map_err(|_| Error::Other("Invalid enclave URL".into()))? - .to_string(); - enclave_registry.update(account_id, url).map_err(|e| { - error!("Error adding enclave: {:?}", e); - Error::Other("Error adding enclave".into()) - })?; - - Ok(()) - } - - fn remove_enclave( - enclave_registry: &EnclaveRegistry, - account_id: Address32, - ) -> Result<(), Error> { - info!("Remove Enclave Account from Registry: {:?}", account_id); - enclave_registry.remove(account_id).map_err(|e| { - error!("Error removing enclave: {:?}", e); - Error::Other("Error removing enclave".into()) - })?; - - Ok(()) - } - - fn save_signer( - signer_registry: &SignerRegistry, - account_id: Address32, - pub_key: [u8; 33], - ) -> Result<(), Error> { - info!("Saving Signer Account to Registry: {:?}", account_id); - signer_registry.update(account_id, pub_key).map_err(|e| { - error!("Error saving signer: {:?}", e); - Error::Other("Error saving signer".into()) - })?; - - Ok(()) - } -} - -impl - HandleParentchainEvents< - Executor, - TrustedCallSigned, - Error, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, - > for ParentchainEventHandler -where - Executor: IndirectExecutor< - TrustedCallSigned, - Error, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, - >, -{ - type Output = Vec; - - fn handle_events( - &self, - executor: &Executor, - events: impl FilterEvents, - _block_number: <::Header as ParentchainHeader>::Number, - ) -> Result, Error> - where - Block: ParentchainBlock, - { - let mut handled_events: Vec = Vec::new(); - - if let Ok(events) = events.get_relayer_added_events() { - debug!("Handling RelayerAdded events"); - let relayer_registry = executor.get_relayer_registry_updater(); - events - .iter() - .try_for_each(|event| { - debug!("found RelayerAdded event: {:?}", event); - let result = Self::add_relayer(relayer_registry, event.who.clone()); - handled_events.push(hash_of(&event)); - - result - }) - .map_err(|_| ParentchainEventProcessingError::RelayerAddFailure)?; - } - - if let Ok(events) = events.get_relayers_removed_events() { - debug!("Handling RelayerRemoved events"); - let relayer_registry = executor.get_relayer_registry_updater(); - events - .iter() - .try_for_each(|event| { - debug!("found RelayerRemoved event: {:?}", event); - let result = Self::remove_relayer(relayer_registry, event.who.clone()); - handled_events.push(hash_of(&event)); - - result - }) - .map_err(|_| ParentchainEventProcessingError::RelayerRemoveFailure)?; - } - - if let Ok(events) = events.get_enclave_added_events() { - debug!("Handling EnclaveAdded events"); - let enclave_registry = executor.get_enclave_registry_updater(); - events - .iter() - .try_for_each(|event| { - debug!("found EnclaveAdded event: {:?}", event); - let result = Self::add_enclave( - enclave_registry, - event.who, - event.url.clone(), - event.worker_type, - ); - handled_events.push(hash_of(&event)); - - result - }) - .map_err(|_| ParentchainEventProcessingError::EnclaveAddFailure)?; - } - - if let Ok(events) = events.get_enclave_removed_events() { - debug!("Handling EnclaveRemoved events"); - let enclave_registry = executor.get_enclave_registry_updater(); - events - .iter() - .try_for_each(|event| { - debug!("found EnclaveRemoved event: {:?}", event); - let result = Self::remove_enclave(enclave_registry, event.who); - handled_events.push(hash_of(&event)); - - result - }) - .map_err(|_| ParentchainEventProcessingError::EnclaveRemoveFailure)?; - } - - if let Ok(events) = events.get_btc_wallet_generated_events() { - debug!("Handling BtcWalletGenerated events"); - let signer_registry = executor.get_signer_registry_updater(); - events - .iter() - .try_for_each(|event| { - debug!("found BtcWalletGenerated event: {:?}", event); - let result = Self::save_signer( - signer_registry, - event.account_id.clone().into(), - event.pub_key, - ); - handled_events.push(hash_of(&event)); - - result - }) - .map_err(|_| ParentchainEventProcessingError::BtcWalletGeneratedFailure)?; - } - - Ok(handled_events) - } -} - -fn hash_of(ev: &T) -> H256 { - blake2_256(&ev.encode()).into() -} diff --git a/tee-worker/bitacross/app-libs/parentchain-interface/src/integritee/mod.rs b/tee-worker/bitacross/app-libs/parentchain-interface/src/integritee/mod.rs deleted file mode 100644 index ff2bbea146..0000000000 --- a/tee-worker/bitacross/app-libs/parentchain-interface/src/integritee/mod.rs +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright 2021 Integritee AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -mod event_filter; -mod event_handler; - -pub use event_filter::FilterableEvents; -pub use event_handler::ParentchainEventHandler; -pub use itp_types::{ - parentchain::{AccountId, Balance, Hash}, - CallIndex, H256, -}; -use sp_runtime::traits::BlakeTwo256; - -pub type BlockNumber = u32; -pub type Header = sp_runtime::generic::Header; -pub type Signature = sp_runtime::MultiSignature; diff --git a/tee-worker/bitacross/app-libs/parentchain-interface/src/lib.rs b/tee-worker/bitacross/app-libs/parentchain-interface/src/lib.rs deleted file mode 100644 index ccaeac9266..0000000000 --- a/tee-worker/bitacross/app-libs/parentchain-interface/src/lib.rs +++ /dev/null @@ -1,54 +0,0 @@ -/* - Copyright 2021 Integritee AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#![cfg_attr(all(not(target_env = "sgx"), not(feature = "std")), no_std)] -#![cfg_attr(target_env = "sgx", feature(rustc_private))] - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -use codec::{Decode, Encode}; - -#[cfg(feature = "std")] -pub mod event_subscriber; -pub mod integritee; -pub mod target_a; -pub mod target_b; - -pub trait ParentchainInstance {} - -#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] -pub struct Litentry; -impl ParentchainInstance for Litentry {} - -#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] -pub struct TargetA; -impl ParentchainInstance for TargetA {} - -#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] -pub struct TargetB; -impl ParentchainInstance for TargetB {} - -pub fn decode_and_log_error(encoded: &mut &[u8]) -> Option { - match V::decode(encoded) { - Ok(v) => Some(v), - Err(e) => { - log::warn!("Could not decode. {:?}: raw: {:?}", e, encoded); - None - }, - } -} diff --git a/tee-worker/bitacross/app-libs/parentchain-interface/src/target_a/event_filter.rs b/tee-worker/bitacross/app-libs/parentchain-interface/src/target_a/event_filter.rs deleted file mode 100644 index 33c0278536..0000000000 --- a/tee-worker/bitacross/app-libs/parentchain-interface/src/target_a/event_filter.rs +++ /dev/null @@ -1,122 +0,0 @@ -/* - Copyright 2021 Integritee AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -//! Various way to filter Parentchain events - -use itc_parentchain_indirect_calls_executor::event_filter::ToEvents; -use itp_api_client_types::Events; -use itp_node_api::api_client::StaticEvent; -use itp_types::{ - parentchain::{events::*, FilterEvents}, - H256, -}; -use std::vec::Vec; - -#[derive(Clone)] -pub struct FilterableEvents(pub Events); - -impl FilterableEvents { - fn filter(&self) -> Result, E> { - Ok(self - .to_events() - .iter() - .flatten() - .filter_map(|ev| match ev.as_event::() { - Ok(maybe_event) => maybe_event, - Err(e) => { - log::error!("Could not decode event: {:?}", e); - None - }, - }) - .collect()) - } -} - -impl ToEvents> for FilterableEvents { - fn to_events(&self) -> &Events { - &self.0 - } -} - -impl From> for FilterableEvents { - fn from(ev: Events) -> Self { - Self(ev) - } -} - -impl FilterEvents for FilterableEvents { - type Error = itc_parentchain_indirect_calls_executor::Error; - - fn get_link_identity_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_vc_requested_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_deactivate_identity_events( - &self, - ) -> Result, Self::Error> { - self.filter() - } - - fn get_activate_identity_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_enclave_unauthorized_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_opaque_task_posted_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_assertion_created_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_parentchain_block_proccessed_events( - &self, - ) -> Result, Self::Error> { - self.filter() - } - - fn get_relayer_added_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_relayers_removed_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_enclave_added_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_enclave_removed_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_btc_wallet_generated_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_account_store_updated_events(&self) -> Result, Self::Error> { - self.filter() - } -} diff --git a/tee-worker/bitacross/app-libs/parentchain-interface/src/target_a/event_handler.rs b/tee-worker/bitacross/app-libs/parentchain-interface/src/target_a/event_handler.rs deleted file mode 100644 index 12b6eb40a7..0000000000 --- a/tee-worker/bitacross/app-libs/parentchain-interface/src/target_a/event_handler.rs +++ /dev/null @@ -1,68 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pub use ita_sgx_runtime::{Balance, Index}; - -use bc_enclave_registry::EnclaveRegistry; -use bc_relayer_registry::RelayerRegistry; -use bc_signer_registry::SignerRegistry; -use ita_stf::TrustedCallSigned; -use itc_parentchain_indirect_calls_executor::error::Error; -use itp_stf_primitives::traits::IndirectExecutor; -use itp_types::{ - parentchain::{FilterEvents, HandleParentchainEvents}, - H256, -}; -use log::*; -use sp_runtime::traits::{Block as ParentchainBlock, Header as ParentchainHeader}; -use std::vec::Vec; - -pub struct ParentchainEventHandler {} - -impl - HandleParentchainEvents< - Executor, - TrustedCallSigned, - Error, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, - > for ParentchainEventHandler -where - Executor: IndirectExecutor< - TrustedCallSigned, - Error, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, - >, -{ - type Output = Vec; - - fn handle_events( - &self, - _executor: &Executor, - _events: impl FilterEvents, - _block_number: <::Header as ParentchainHeader>::Number, - ) -> Result, Error> - where - Block: ParentchainBlock, - { - debug!("not handling any events for target a"); - Ok(Vec::new()) - } -} diff --git a/tee-worker/bitacross/app-libs/parentchain-interface/src/target_a/mod.rs b/tee-worker/bitacross/app-libs/parentchain-interface/src/target_a/mod.rs deleted file mode 100644 index 0458b6c9ee..0000000000 --- a/tee-worker/bitacross/app-libs/parentchain-interface/src/target_a/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -/* - Copyright 2021 Integritee AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -mod event_filter; -mod event_handler; - -pub use event_filter::FilterableEvents; -pub use event_handler::ParentchainEventHandler; diff --git a/tee-worker/bitacross/app-libs/parentchain-interface/src/target_b/event_filter.rs b/tee-worker/bitacross/app-libs/parentchain-interface/src/target_b/event_filter.rs deleted file mode 100644 index 33c0278536..0000000000 --- a/tee-worker/bitacross/app-libs/parentchain-interface/src/target_b/event_filter.rs +++ /dev/null @@ -1,122 +0,0 @@ -/* - Copyright 2021 Integritee AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -//! Various way to filter Parentchain events - -use itc_parentchain_indirect_calls_executor::event_filter::ToEvents; -use itp_api_client_types::Events; -use itp_node_api::api_client::StaticEvent; -use itp_types::{ - parentchain::{events::*, FilterEvents}, - H256, -}; -use std::vec::Vec; - -#[derive(Clone)] -pub struct FilterableEvents(pub Events); - -impl FilterableEvents { - fn filter(&self) -> Result, E> { - Ok(self - .to_events() - .iter() - .flatten() - .filter_map(|ev| match ev.as_event::() { - Ok(maybe_event) => maybe_event, - Err(e) => { - log::error!("Could not decode event: {:?}", e); - None - }, - }) - .collect()) - } -} - -impl ToEvents> for FilterableEvents { - fn to_events(&self) -> &Events { - &self.0 - } -} - -impl From> for FilterableEvents { - fn from(ev: Events) -> Self { - Self(ev) - } -} - -impl FilterEvents for FilterableEvents { - type Error = itc_parentchain_indirect_calls_executor::Error; - - fn get_link_identity_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_vc_requested_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_deactivate_identity_events( - &self, - ) -> Result, Self::Error> { - self.filter() - } - - fn get_activate_identity_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_enclave_unauthorized_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_opaque_task_posted_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_assertion_created_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_parentchain_block_proccessed_events( - &self, - ) -> Result, Self::Error> { - self.filter() - } - - fn get_relayer_added_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_relayers_removed_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_enclave_added_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_enclave_removed_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_btc_wallet_generated_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_account_store_updated_events(&self) -> Result, Self::Error> { - self.filter() - } -} diff --git a/tee-worker/bitacross/app-libs/parentchain-interface/src/target_b/event_handler.rs b/tee-worker/bitacross/app-libs/parentchain-interface/src/target_b/event_handler.rs deleted file mode 100644 index f3ab23f2a1..0000000000 --- a/tee-worker/bitacross/app-libs/parentchain-interface/src/target_b/event_handler.rs +++ /dev/null @@ -1,68 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pub use ita_sgx_runtime::{Balance, Index}; - -use bc_enclave_registry::EnclaveRegistry; -use bc_relayer_registry::RelayerRegistry; -use bc_signer_registry::SignerRegistry; -use ita_stf::TrustedCallSigned; -use itc_parentchain_indirect_calls_executor::error::Error; -use itp_stf_primitives::traits::IndirectExecutor; -use itp_types::{ - parentchain::{FilterEvents, HandleParentchainEvents}, - H256, -}; -use log::*; -use sp_runtime::traits::{Block as ParentchainBlock, Header as ParentchainHeader}; -use std::vec::Vec; - -pub struct ParentchainEventHandler {} - -impl - HandleParentchainEvents< - Executor, - TrustedCallSigned, - Error, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, - > for ParentchainEventHandler -where - Executor: IndirectExecutor< - TrustedCallSigned, - Error, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, - >, -{ - type Output = Vec; - - fn handle_events( - &self, - _executor: &Executor, - _events: impl FilterEvents, - _block_number: <::Header as ParentchainHeader>::Number, - ) -> Result, Error> - where - Block: ParentchainBlock, - { - debug!("not handling any events for target B"); - Ok(Vec::new()) - } -} diff --git a/tee-worker/bitacross/app-libs/parentchain-interface/src/target_b/mod.rs b/tee-worker/bitacross/app-libs/parentchain-interface/src/target_b/mod.rs deleted file mode 100644 index 14bedb28cb..0000000000 --- a/tee-worker/bitacross/app-libs/parentchain-interface/src/target_b/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -/* - Copyright 2021 Integritee AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -mod event_filter; -mod event_handler; - -pub use event_filter::FilterableEvents; -pub use event_handler::ParentchainEventHandler; diff --git a/tee-worker/bitacross/app-libs/sgx-runtime/Cargo.toml b/tee-worker/bitacross/app-libs/sgx-runtime/Cargo.toml deleted file mode 100644 index f49678f57e..0000000000 --- a/tee-worker/bitacross/app-libs/sgx-runtime/Cargo.toml +++ /dev/null @@ -1,57 +0,0 @@ -[package] -name = "bc-ita-sgx-runtime" -version = "0.1.0" -authors = ['Trust Computing GmbH ', 'Integritee AG '] -edition = "2021" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", workspace = true } -scale-info = { workspace = true } - -itp-sgx-runtime-primitives = { workspace = true } - -frame-executive = { workspace = true } -frame-support = { workspace = true } -frame-system = { workspace = true } -pallet-balances = { workspace = true } -pallet-sudo = { workspace = true } -pallet-timestamp = { workspace = true } -pallet-transaction-payment = { workspace = true } -sp-api = { workspace = true } -sp-core = { workspace = true } -sp-runtime = { workspace = true } -sp-std = { workspace = true } -sp-version = { workspace = true } - -pallet-parentchain = { workspace = true } - -[features] -default = ["std"] -runtime-benchmarks = [ - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-timestamp/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -std = [ - "codec/std", - "scale-info/std", - "itp-sgx-runtime-primitives/std", - "frame-executive/std", - "frame-support/std", - "frame-system/std", - "pallet-balances/std", - "pallet-sudo/std", - "pallet-timestamp/std", - "pallet-transaction-payment/std", - "pallet-parentchain/std", - "sp-api/std", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", - "sp-version/std", -] diff --git a/tee-worker/bitacross/app-libs/sgx-runtime/src/lib.rs b/tee-worker/bitacross/app-libs/sgx-runtime/src/lib.rs deleted file mode 100644 index c08002263a..0000000000 --- a/tee-worker/bitacross/app-libs/sgx-runtime/src/lib.rs +++ /dev/null @@ -1,314 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -//! The Substrate Node Template sgx-runtime for SGX. -//! This is only meant to be used inside an SGX enclave with `#[no_std]` -//! -//! you should assemble your sgx-runtime to be used with your STF here -//! and get all your needed pallets in - -#![cfg_attr(not(feature = "std"), no_std)] -#![feature(prelude_import)] -#![feature(structural_match)] -#![feature(core_intrinsics)] -#![feature(derive_eq)] -// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256. -#![recursion_limit = "256"] - -use core::convert::{TryFrom, TryInto}; -use frame_support::{traits::ConstU32, weights::ConstantMultiplier}; -use pallet_transaction_payment::CurrencyAdapter; -use sp_api::impl_runtime_apis; -use sp_core::OpaqueMetadata; -use sp_runtime::{ - create_runtime_str, generic, - traits::{AccountIdLookup, BlakeTwo256, Block as BlockT}, -}; -use sp_std::prelude::*; -use sp_version::RuntimeVersion; - -// Re-exports from itp-sgx-runtime-primitives. -pub use itp_sgx_runtime_primitives::{ - constants::SLOT_DURATION, - types::{ - AccountData, AccountId, Address, Balance, BlockNumber, Hash, Header, Index, Signature, - }, -}; - -// A few exports that help ease life for downstream crates. -pub use frame_support::{ - construct_runtime, parameter_types, - traits::{KeyOwnerProofSystem, Randomness}, - weights::{ - constants::{ - BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, - }, - IdentityFee, Weight, - }, - StorageValue, -}; -use itp_sgx_runtime_primitives::types::Moment; -pub use pallet_balances::Call as BalancesCall; -pub use pallet_timestamp::Call as TimestampCall; -#[cfg(any(feature = "std", test))] -pub use sp_runtime::BuildStorage; -pub use sp_runtime::{Perbill, Permill}; - -/// Block type as expected by this sgx-runtime. -pub type Block = generic::Block; -/// A Block signed with a Justification -pub type SignedBlock = generic::SignedBlock; -/// BlockId type as expected by this sgx-runtime. -pub type BlockId = generic::BlockId; - -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( - frame_system::CheckNonZeroSender, - frame_system::CheckSpecVersion, - frame_system::CheckTxVersion, - frame_system::CheckGenesis, - frame_system::CheckEra, - frame_system::CheckNonce, - frame_system::CheckWeight, - pallet_transaction_payment::ChargeTransactionPayment, -); -/// Unchecked extrinsic type as expected by this sgx-runtime. -pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; -/// Extrinsic type that has already been checked. -pub type CheckedExtrinsic = generic::CheckedExtrinsic; - -/// Executive: handles dispatch to the various modules. -pub type Executive = frame_executive::Executive< - Runtime, - Block, - frame_system::ChainContext, - Runtime, - AllPalletsWithSystem, ->; - -/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know -/// the specifics of the sgx-runtime. They can then be made to be agnostic over specific formats -/// of data like extrinsics, allowing for them to continue syncing the network through upgrades -/// to even the core data structures. -pub mod opaque { - - use sp_runtime::generic; - pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; - - /// Opaque block header type. - pub type Header = itp_sgx_runtime_primitives::types::Header; - /// Opaque block type. - pub type Block = super::Block; - /// Opaque block identifier type. - pub type BlockId = generic::BlockId; -} - -pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: create_runtime_str!("node-template"), - impl_name: create_runtime_str!("node-template"), - authoring_version: 1, - spec_version: 106, - impl_version: 1, - apis: RUNTIME_API_VERSIONS, - transaction_version: 1, - state_version: 0, -}; - -const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); - -parameter_types! { - pub const Version: RuntimeVersion = VERSION; - pub const BlockHashCount: BlockNumber = 2400; - /// We allow for 2 seconds of compute with a 6 second average block time. - pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights - ::with_sensible_defaults(Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, u64::MAX), NORMAL_DISPATCH_RATIO); - pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength - ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); - pub const SS58Prefix: u8 = 42; -} - -// Configure FRAME pallets to include in sgx-runtime. - -impl frame_system::Config for Runtime { - /// The basic call filter to use in dispatchable. - type BaseCallFilter = frame_support::traits::Everything; - /// Block & extrinsics weights: base values and limits. - type BlockWeights = BlockWeights; - /// The maximum length of a block (in bytes). - type BlockLength = BlockLength; - /// The identifier used to distinguish between accounts. - type AccountId = AccountId; - /// The aggregated dispatch type that is available for extrinsics. - type RuntimeCall = RuntimeCall; - /// The lookup mechanism to get account ID from whatever is passed in dispatchers. - type Lookup = AccountIdLookup; - /// The index type for storing how many extrinsics an account has signed. - type Index = Index; - /// The index type for blocks. - type BlockNumber = BlockNumber; - /// The type for hashing blocks and tries. - type Hash = Hash; - /// The hashing algorithm used. - type Hashing = BlakeTwo256; - /// The header type. - type Header = Header; - /// The ubiquitous event type. - type RuntimeEvent = RuntimeEvent; - /// The ubiquitous origin type. - type RuntimeOrigin = RuntimeOrigin; - /// Maximum number of block number to block hash mappings to keep (oldest pruned first). - type BlockHashCount = BlockHashCount; - /// The weight of database operations that the sgx-runtime can invoke. - type DbWeight = RocksDbWeight; - /// Version of the sgx-runtime. - type Version = Version; - /// Converts a module to the index of the module in `construct_runtime!`. - /// - /// This type is being generated by `construct_runtime!`. - type PalletInfo = PalletInfo; - /// What to do if a new account is created. - type OnNewAccount = (); - /// What to do if an account is fully reaped from the system. - type OnKilledAccount = (); - /// The data to be stored in an account. - type AccountData = AccountData; - /// Weight information for the extrinsics of this pallet. - type SystemWeightInfo = (); - /// This is used as an identifier of the chain. 42 is the generic substrate prefix. - type SS58Prefix = SS58Prefix; - /// The set code logic, just the default since we're not a parachain. - type OnSetCode = (); - /// The maximum number of consumers allowed on a single account. - type MaxConsumers = frame_support::traits::ConstU32<16>; -} - -parameter_types! { - pub const MinimumPeriod: u64 = SLOT_DURATION / 2; -} - -impl pallet_timestamp::Config for Runtime { - /// A timestamp: milliseconds since the unix epoch. - type Moment = Moment; - type OnTimestampSet = (); - type MinimumPeriod = MinimumPeriod; - type WeightInfo = (); -} - -parameter_types! { - pub const ExistentialDeposit: u128 = 500; - pub const MaxLocks: u32 = 50; -} - -impl pallet_balances::Config for Runtime { - type MaxLocks = MaxLocks; - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - /// The type for recording an account's balance. - type Balance = Balance; - /// The ubiquitous event type. - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); - type HoldIdentifier = (); - type FreezeIdentifier = (); - type MaxHolds = ConstU32<0>; - type MaxFreezes = ConstU32<0>; -} - -parameter_types! { - pub const TransactionByteFee: Balance = 1; - pub const OperationalFeeMultiplier: u8 = 5; -} - -impl pallet_transaction_payment::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type OnChargeTransaction = CurrencyAdapter; - type OperationalFeeMultiplier = OperationalFeeMultiplier; - type WeightToFee = IdentityFee; - type LengthToFee = ConstantMultiplier; - type FeeMultiplierUpdate = (); -} - -impl pallet_sudo::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; -} - -pub type ParentchainInstanceLitentry = pallet_parentchain::Instance1; -impl pallet_parentchain::Config for Runtime { - type WeightInfo = (); -} - -pub type ParentchainInstanceTargetA = pallet_parentchain::Instance2; -impl pallet_parentchain::Config for Runtime { - type WeightInfo = (); -} - -pub type ParentchainInstanceTargetB = pallet_parentchain::Instance3; -impl pallet_parentchain::Config for Runtime { - type WeightInfo = (); -} - -construct_runtime!( - pub enum Runtime where - Block = Block, - NodeBlock = opaque::Block, - UncheckedExtrinsic = UncheckedExtrinsic - { - System: frame_system, - Timestamp: pallet_timestamp, - Balances: pallet_balances, - TransactionPayment: pallet_transaction_payment, - Sudo: pallet_sudo, - ParentchainLitentry: pallet_parentchain::, - ParentchainTargetA: pallet_parentchain::, - ParentchainTargetB: pallet_parentchain::, - } -); - -impl_runtime_apis! { - impl sp_api::Core for Runtime { - fn version() -> RuntimeVersion { - VERSION - } - - fn execute_block(block: Block) { - Executive::execute_block(block); - } - - fn initialize_block(header: &::Header) { - Executive::initialize_block(header) - } - } - - impl sp_api::Metadata for Runtime { - fn metadata() -> OpaqueMetadata { - OpaqueMetadata::new(Runtime::metadata().into()) - } - - fn metadata_at_version(version: u32) -> Option { - Runtime::metadata_at_version(version) - } - - fn metadata_versions() -> sp_std::vec::Vec { - Runtime::metadata_versions() - } - } - -} diff --git a/tee-worker/bitacross/app-libs/stf/Cargo.toml b/tee-worker/bitacross/app-libs/stf/Cargo.toml deleted file mode 100644 index d3016c539c..0000000000 --- a/tee-worker/bitacross/app-libs/stf/Cargo.toml +++ /dev/null @@ -1,75 +0,0 @@ -[package] -name = "bc-ita-stf" -version = "0.1.0" -authors = ['Trust Computing GmbH ', 'Integritee AG '] -edition = "2021" - -[dependencies] -codec = { package = "parity-scale-codec", workspace = true } -hex = { workspace = true } -hex-literal = { workspace = true } -log = { workspace = true } - -sgx_tstd = { workspace = true, features = ["untrusted_fs", "net", "backtrace"], optional = true } - -itp-hashing = { workspace = true } -itp-node-api = { workspace = true } -itp-sgx-externalities = { workspace = true } -itp-stf-interface = { workspace = true } -itp-stf-primitives = { workspace = true } -itp-storage = { workspace = true } -itp-types = { workspace = true } -itp-utils = { workspace = true } - -ita-sgx-runtime = { package = "bc-ita-sgx-runtime", path = "../sgx-runtime", default-features = false } -sp-io = { path = "../../../common/core-primitives/substrate-sgx/sp-io", default-features = false, features = ["disable_oom", "disable_panic_handler", "disable_allocator"] } - -frame-support = { workspace = true } -frame-system = { workspace = true } -pallet-balances = { workspace = true } -pallet-sudo = { workspace = true } -sp-core = { workspace = true, features = ["full_crypto"] } -sp-runtime = { workspace = true } -sp-std = { workspace = true } - -litentry-macros = { workspace = true } -litentry-primitives = { workspace = true } -pallet-parentchain = { workspace = true } - -[dev-dependencies] -sp-keyring = { workspace = true } - -[features] -default = ["std"] -sgx = [ - "sgx_tstd", - "itp-sgx-externalities/sgx", - "sp-io/sgx", - "itp-node-api/sgx", - "litentry-primitives/sgx", -] -std = [ - # crates.io - "codec/std", - "log/std", - "ita-sgx-runtime/std", - "itp-hashing/std", - "itp-sgx-externalities/std", - "itp-stf-interface/std", - "itp-storage/std", - "itp-types/std", - "itp-node-api/std", - "sp-core/std", - "pallet-balances/std", - "pallet-sudo/std", - "frame-system/std", - "frame-support/std", - "sp-runtime/std", - "pallet-parentchain/std", - "sp-io/std", - "litentry-primitives/std", -] -test = [] -development = [ - "litentry-macros/development", -] diff --git a/tee-worker/bitacross/app-libs/stf/src/getter.rs b/tee-worker/bitacross/app-libs/stf/src/getter.rs deleted file mode 100644 index 662fa355e5..0000000000 --- a/tee-worker/bitacross/app-libs/stf/src/getter.rs +++ /dev/null @@ -1,215 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use codec::{Decode, Encode}; -use ita_sgx_runtime::System; -use itp_stf_interface::ExecuteGetter; -use itp_stf_primitives::{traits::GetterAuthorization, types::KeyPair}; -use itp_utils::stringify::account_id_to_string; -use litentry_macros::if_development_or; -use litentry_primitives::{Identity, LitentryMultiSignature}; -use log::*; -use sp_std::vec; -use std::prelude::v1::*; - -use itp_stf_primitives::traits::PoolTransactionValidation; -use sp_runtime::transaction_validity::{ - TransactionValidityError, UnknownTransaction, ValidTransaction, -}; - -#[cfg(feature = "development")] -use crate::helpers::ALICE_ACCOUNTID32; - -#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] -#[allow(non_camel_case_types)] -pub enum Getter { - #[codec(index = 0)] - public(PublicGetter), - #[codec(index = 1)] - trusted(TrustedGetterSigned), -} - -impl Default for Getter { - fn default() -> Self { - Getter::public(PublicGetter::some_value) - } -} -impl From for Getter { - fn from(item: PublicGetter) -> Self { - Getter::public(item) - } -} - -impl From for Getter { - fn from(item: TrustedGetterSigned) -> Self { - Getter::trusted(item) - } -} - -impl GetterAuthorization for Getter { - fn is_authorized(&self) -> bool { - match self { - Self::trusted(ref getter) => getter.verify_signature(), - Self::public(_) => true, - } - } -} - -impl PoolTransactionValidation for Getter { - fn validate(&self) -> Result { - match self { - Self::public(_) => - Err(TransactionValidityError::Unknown(UnknownTransaction::CannotLookup)), - Self::trusted(trusted_getter_signed) => Ok(ValidTransaction { - priority: 1 << 20, - requires: vec![], - provides: vec![trusted_getter_signed.signature.encode()], - longevity: 64, - propagate: true, - }), - } - } -} - -#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] -#[allow(non_camel_case_types)] -pub enum PublicGetter { - #[codec(index = 0)] - some_value, - #[codec(index = 1)] - nonce(Identity), -} - -#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] -#[allow(non_camel_case_types)] -pub enum TrustedGetter { - #[codec(index = 0)] - free_balance(Identity), - #[codec(index = 1)] - reserved_balance(Identity), -} - -impl TrustedGetter { - pub fn sender_identity(&self) -> &Identity { - match self { - TrustedGetter::free_balance(sender_identity) => sender_identity, - TrustedGetter::reserved_balance(sender_identity) => sender_identity, - } - } - - pub fn sign(&self, pair: &KeyPair) -> TrustedGetterSigned { - let signature = pair.sign(self.encode().as_slice()); - TrustedGetterSigned { getter: self.clone(), signature } - } -} - -#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] -pub struct TrustedGetterSigned { - pub getter: TrustedGetter, - pub signature: LitentryMultiSignature, -} - -impl TrustedGetterSigned { - pub fn new(getter: TrustedGetter, signature: LitentryMultiSignature) -> Self { - TrustedGetterSigned { getter, signature } - } - - pub fn verify_signature(&self) -> bool { - // in non-prod, we accept signature from Alice too - if_development_or!( - { - self.signature - .verify(self.getter.encode().as_slice(), self.getter.sender_identity()) - || self - .signature - .verify(self.getter.encode().as_slice(), &ALICE_ACCOUNTID32.into()) - }, - { - self.signature - .verify(self.getter.encode().as_slice(), self.getter.sender_identity()) - } - ) - } -} - -impl ExecuteGetter for Getter { - fn execute(self) -> Option> { - match self { - Getter::trusted(g) => g.execute(), - Getter::public(g) => g.execute(), - } - } - - fn get_storage_hashes_to_update(self) -> Vec> { - match self { - Getter::trusted(g) => g.get_storage_hashes_to_update(), - Getter::public(g) => g.get_storage_hashes_to_update(), - } - } -} - -impl ExecuteGetter for TrustedGetterSigned { - fn execute(self) -> Option> { - match self.getter { - TrustedGetter::free_balance(who) => - if let Some(account_id) = who.to_native_account() { - let info = System::account(&account_id); - debug!("TrustedGetter free_balance"); - debug!("AccountInfo for {} is {:?}", account_id_to_string(&who), info); - std::println!("⣿STF⣿ 🔍 TrustedGetter query: free balance for ⣿⣿⣿ is ⣿⣿⣿",); - Some(info.data.free.encode()) - } else { - None - }, - TrustedGetter::reserved_balance(who) => - if let Some(account_id) = who.to_native_account() { - let info = System::account(&account_id); - debug!("TrustedGetter reserved_balance"); - debug!("AccountInfo for {} is {:?}", account_id_to_string(&who), info); - debug!("Account reserved balance is {}", info.data.reserved); - Some(info.data.reserved.encode()) - } else { - None - }, - } - } - - fn get_storage_hashes_to_update(self) -> Vec> { - Vec::new() - } -} - -impl ExecuteGetter for PublicGetter { - fn execute(self) -> Option> { - match self { - PublicGetter::some_value => Some(42u32.encode()), - PublicGetter::nonce(identity) => - if let Some(account_id) = identity.to_native_account() { - let nonce = System::account_nonce(&account_id); - debug!("PublicGetter nonce"); - debug!("Account nonce is {}", nonce); - Some(nonce.encode()) - } else { - None - }, - } - } - - fn get_storage_hashes_to_update(self) -> Vec> { - Vec::new() - } -} diff --git a/tee-worker/bitacross/app-libs/stf/src/hash.rs b/tee-worker/bitacross/app-libs/stf/src/hash.rs deleted file mode 100644 index f3cde9fa32..0000000000 --- a/tee-worker/bitacross/app-libs/stf/src/hash.rs +++ /dev/null @@ -1,29 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::TrustedGetter; -use codec::Encode; -pub use itp_hashing::Hash; - -use itp_types::H256; -use sp_core::blake2_256; - -impl Hash for TrustedGetter { - fn hash(&self) -> H256 { - blake2_256(&self.encode()).into() - } -} diff --git a/tee-worker/bitacross/app-libs/stf/src/helpers.rs b/tee-worker/bitacross/app-libs/stf/src/helpers.rs deleted file mode 100644 index 24256aa507..0000000000 --- a/tee-worker/bitacross/app-libs/stf/src/helpers.rs +++ /dev/null @@ -1,165 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -use crate::ENCLAVE_ACCOUNT_KEY; -use codec::{Decode, Encode}; -use ita_sgx_runtime::{ParentchainLitentry, ParentchainTargetA, ParentchainTargetB}; -use itp_stf_interface::{BlockMetadata, ShardCreationInfo}; -use itp_stf_primitives::error::{StfError, StfResult}; -use itp_storage::{storage_double_map_key, storage_map_key, storage_value_key, StorageHasher}; - -use itp_utils::stringify::account_id_to_string; -use log::*; -use std::prelude::v1::*; - -#[cfg(feature = "development")] -pub use non_prod::*; - -pub fn get_storage_value( - storage_prefix: &'static str, - storage_key_name: &'static str, -) -> Option { - let key = storage_value_key(storage_prefix, storage_key_name); - get_storage_by_key_hash(key) -} - -pub fn get_storage_map( - storage_prefix: &'static str, - storage_key_name: &'static str, - map_key: &K, - hasher: &StorageHasher, -) -> Option { - let key = storage_map_key::(storage_prefix, storage_key_name, map_key, hasher); - get_storage_by_key_hash(key) -} - -pub fn get_storage_double_map( - storage_prefix: &'static str, - storage_key_name: &'static str, - first: &K, - first_hasher: &StorageHasher, - second: &Q, - second_hasher: &StorageHasher, -) -> Option { - let key = storage_double_map_key::( - storage_prefix, - storage_key_name, - first, - first_hasher, - second, - second_hasher, - ); - get_storage_by_key_hash(key) -} - -/// Get value in storage. -pub fn get_storage_by_key_hash(key: Vec) -> Option { - if let Some(value_encoded) = sp_io::storage::get(&key) { - if let Ok(value) = Decode::decode(&mut value_encoded.as_slice()) { - Some(value) - } else { - error!("could not decode state for key {:?}", hex::encode(&key)); - None - } - } else { - info!("key not found in state {:?}", hex::encode(key)); - None - } -} - -/// Get the AccountInfo key where the account is stored. -pub fn account_key_hash(account: &AccountId) -> Vec { - storage_map_key("System", "Account", account, &StorageHasher::Blake2_128Concat) -} - -pub fn enclave_signer_account() -> AccountId { - get_storage_value("Sudo", ENCLAVE_ACCOUNT_KEY).expect("No enclave account") -} - -/// Ensures an account is a registered enclave account. -pub fn ensure_enclave_signer_account( - account: &AccountId, -) -> StfResult<()> { - let expected_enclave_account: AccountId = enclave_signer_account(); - if &expected_enclave_account == account { - Ok(()) - } else { - error!( - "Expected enclave account {}, but found {}", - account_id_to_string(&expected_enclave_account), - account_id_to_string(account) - ); - Err(StfError::RequireEnclaveSignerAccount) - } -} - -pub fn set_block_number(block_number: u32) { - sp_io::storage::set(&storage_value_key("System", "Number"), &block_number.encode()); -} - -pub fn ensure_self(signer: &AccountId, who: &AccountId) -> bool { - signer == who -} - -pub fn ensure_enclave_signer_or_self( - signer: &AccountId, - who: Option, -) -> bool { - match who { - Some(ref who) => - signer == &enclave_signer_account::() || ensure_self(signer, who), - None => false, - } -} - -pub fn shard_creation_info() -> ShardCreationInfo { - let maybe_litentry_info: Option = ParentchainLitentry::creation_block_number() - .and_then(|number| { - ParentchainLitentry::creation_block_hash().map(|hash| BlockMetadata { number, hash }) - }); - let maybe_target_a_info: Option = ParentchainTargetA::creation_block_number() - .and_then(|number| { - ParentchainTargetA::creation_block_hash().map(|hash| BlockMetadata { number, hash }) - }); - let maybe_target_b_info: Option = ParentchainTargetB::creation_block_number() - .and_then(|number| { - ParentchainTargetB::creation_block_hash().map(|hash| BlockMetadata { number, hash }) - }); - - ShardCreationInfo { - litentry: maybe_litentry_info, - target_a: maybe_target_a_info, - target_b: maybe_target_b_info, - } -} - -#[cfg(feature = "development")] -mod non_prod { - use super::*; - use hex_literal::hex; - use sp_runtime::AccountId32; - - pub const ALICE_ACCOUNTID32: AccountId32 = - AccountId32::new(hex!["d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"]); - - pub fn ensure_alice(signer: &AccountId32) -> bool { - signer == &ALICE_ACCOUNTID32 - } - - pub fn ensure_enclave_signer_or_alice(signer: &AccountId32) -> bool { - signer == &enclave_signer_account::() || ensure_alice(signer) - } -} diff --git a/tee-worker/bitacross/app-libs/stf/src/lib.rs b/tee-worker/bitacross/app-libs/stf/src/lib.rs deleted file mode 100644 index 4aa3ff1db4..0000000000 --- a/tee-worker/bitacross/app-libs/stf/src/lib.rs +++ /dev/null @@ -1,51 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -///////////////////////////////////////////////////////////////////////////// -#![feature(structural_match)] -#![feature(rustc_attrs)] -#![feature(core_intrinsics)] -#![feature(derive_eq)] -#![cfg_attr(all(not(target_env = "sgx"), not(feature = "std")), no_std)] -#![cfg_attr(target_env = "sgx", feature(rustc_private))] -#![allow(clippy::large_enum_variant)] -#![allow(clippy::result_large_err)] - -extern crate core; -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -pub use getter::*; -pub use stf_sgx_primitives::{types::*, Stf}; -pub use trusted_call::*; - -pub mod getter; -pub mod hash; -pub mod helpers; -pub mod stf_sgx; -pub mod stf_sgx_primitives; -#[cfg(all(feature = "test", feature = "sgx"))] -pub mod stf_sgx_tests; -#[cfg(all(feature = "test", feature = "sgx"))] -pub mod test_genesis; -pub mod trusted_call; -pub mod trusted_call_result; - -pub(crate) const ENCLAVE_ACCOUNT_KEY: &str = "Enclave_Account_Key"; - -// fixme: this if a temporary hack only -pub const STF_TX_FEE: Balance = 100000000; diff --git a/tee-worker/bitacross/app-libs/stf/src/stf_sgx.rs b/tee-worker/bitacross/app-libs/stf/src/stf_sgx.rs deleted file mode 100644 index 6412b5cf78..0000000000 --- a/tee-worker/bitacross/app-libs/stf/src/stf_sgx.rs +++ /dev/null @@ -1,422 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(feature = "test")] -use crate::test_genesis::test_genesis_setup; -use crate::{ - helpers::{enclave_signer_account, shard_creation_info}, - Stf, ENCLAVE_ACCOUNT_KEY, -}; -use codec::{Decode, Encode}; -use frame_support::traits::{OriginTrait, UnfilteredDispatchable}; -use ita_sgx_runtime::{ - Executive, ParentchainInstanceLitentry, ParentchainInstanceTargetA, ParentchainInstanceTargetB, -}; -use itp_node_api::metadata::{provider::AccessNodeMetadata, NodeMetadataTrait}; -use itp_sgx_externalities::SgxExternalitiesTrait; -use itp_stf_interface::{ - parentchain_pallet::ParentchainPalletInstancesInterface, - runtime_upgrade::RuntimeUpgradeInterface, - sudo_pallet::SudoPalletInterface, - system_pallet::{SystemPalletAccountInterface, SystemPalletEventInterface}, - ExecuteCall, ExecuteGetter, InitState, ShardCreationInfo, ShardCreationQuery, - StateCallInterface, StateGetterInterface, UpdateState, -}; -use itp_stf_primitives::{ - error::StfError, traits::TrustedCallVerification, types::ShardIdentifier, -}; -use itp_storage::storage_value_key; -use itp_types::{ - parentchain::{AccountId, ParentchainCall, ParentchainId}, - H256, -}; -use itp_utils::stringify::account_id_to_string; -use log::*; -use sp_runtime::traits::StaticLookup; -use std::{fmt::Debug, format, prelude::v1::*, sync::Arc, vec}; - -impl InitState for Stf -where - State: SgxExternalitiesTrait + Debug, - ::SgxExternalitiesType: core::default::Default, - Runtime: frame_system::Config + pallet_balances::Config, - <::Lookup as StaticLookup>::Source: - std::convert::From, - AccountId: Encode, -{ - fn init_state(enclave_account: AccountId) -> State { - debug!("initializing stf state, account id {}", account_id_to_string(&enclave_account)); - let mut state = State::new(Default::default()); - - state.execute_with(|| { - // Do not set genesis for pallets that are meant to be on-chain - // use get_storage_hashes_to_update instead. - - sp_io::storage::set(&storage_value_key("Balances", "TotalIssuance"), &11u128.encode()); - sp_io::storage::set(&storage_value_key("Balances", "CreationFee"), &1u128.encode()); - sp_io::storage::set(&storage_value_key("Balances", "TransferFee"), &1u128.encode()); - sp_io::storage::set( - &storage_value_key("Balances", "TransactionBaseFee"), - &1u128.encode(), - ); - sp_io::storage::set( - &storage_value_key("Balances", "TransactionByteFee"), - &1u128.encode(), - ); - sp_io::storage::set( - &storage_value_key("Balances", "ExistentialDeposit"), - &1u128.encode(), - ); - }); - - #[cfg(feature = "test")] - test_genesis_setup(&mut state); - - state.execute_with(|| { - sp_io::storage::set( - &storage_value_key("Sudo", ENCLAVE_ACCOUNT_KEY), - &enclave_account.encode(), - ); - - if let Err(e) = create_enclave_self_account::(enclave_account) { - error!("Failed to initialize the enclave signer account: {:?}", e); - } - }); - - trace!("Returning updated state: {:?}", state); - state - } -} - -impl - UpdateState::SgxExternalitiesDiffType> - for Stf -where - State: SgxExternalitiesTrait + Debug, - ::SgxExternalitiesType: core::default::Default, - ::SgxExternalitiesDiffType: - IntoIterator, Option>)>, -{ - fn apply_state_diff( - state: &mut State, - map_update: ::SgxExternalitiesDiffType, - ) { - state.execute_with(|| { - map_update.into_iter().for_each(|(k, v)| { - match v { - Some(value) => sp_io::storage::set(&k, &value), - None => sp_io::storage::clear(&k), - }; - }); - }); - } - - fn storage_hashes_to_update_on_block(parentchain_id: &ParentchainId) -> Vec> { - // Get all shards that are currently registered. - match parentchain_id { - ParentchainId::Litentry => vec![], // shards_key_hash() moved to stf_executor and is currently unused - ParentchainId::TargetA => vec![], - ParentchainId::TargetB => vec![], - } - } -} - -impl - StateCallInterface for Stf -where - TCS: PartialEq - + ExecuteCall - + Encode - + Decode - + Debug - + Clone - + Sync - + Send - + TrustedCallVerification, - State: SgxExternalitiesTrait + Debug, - NodeMetadataRepository: AccessNodeMetadata, - NodeMetadataRepository::MetadataType: NodeMetadataTrait, -{ - type Error = TCS::Error; - type Result = TCS::Result; - - fn execute_call( - state: &mut State, - shard: &ShardIdentifier, - call: TCS, - top_hash: H256, - calls: &mut Vec, - node_metadata_repo: Arc, - ) -> Result { - state.execute_with(|| call.execute(shard, top_hash, calls, node_metadata_repo)) - } -} - -impl StateGetterInterface for Stf -where - G: PartialEq + ExecuteGetter, - State: SgxExternalitiesTrait + Debug, -{ - fn execute_getter(state: &mut State, getter: G) -> Option> { - state.execute_with(|| getter.execute()) - } -} - -impl ShardCreationQuery for Stf -where - State: SgxExternalitiesTrait + Debug, -{ - fn get_shard_creation_info(state: &mut State) -> ShardCreationInfo { - state.execute_with(shard_creation_info) - } -} - -impl SudoPalletInterface for Stf -where - State: SgxExternalitiesTrait, - Runtime: frame_system::Config + pallet_sudo::Config, -{ - type AccountId = Runtime::AccountId; - - fn get_root(state: &mut State) -> Self::AccountId { - state.execute_with(|| pallet_sudo::Pallet::::key().expect("No root account")) - } - - fn get_enclave_account(state: &mut State) -> Self::AccountId { - state.execute_with(enclave_signer_account::) - } -} - -impl SystemPalletAccountInterface - for Stf -where - State: SgxExternalitiesTrait, - Runtime: frame_system::Config, - AccountId: Encode, -{ - type Index = Runtime::Index; - type AccountData = Runtime::AccountData; - - fn get_account_nonce(state: &mut State, account: &AccountId) -> Self::Index { - state.execute_with(|| { - let nonce = frame_system::Pallet::::account_nonce(account); - debug!("Account {} nonce is {:?}", account_id_to_string(account), nonce); - nonce - }) - } - - fn get_account_data(state: &mut State, account: &AccountId) -> Self::AccountData { - state.execute_with(|| frame_system::Pallet::::account(account).data) - } -} - -impl SystemPalletEventInterface for Stf -where - State: SgxExternalitiesTrait, - Runtime: frame_system::Config, -{ - type EventRecord = frame_system::EventRecord; - type EventIndex = u32; // For some reason this is not a pub type in frame_system - type BlockNumber = Runtime::BlockNumber; - type Hash = Runtime::Hash; - - fn get_events(state: &mut State) -> Vec> { - // Fixme: Not nice to have to call collect here, but we can't use impl Iterator<..> - // in trait method return types yet, see: - // https://rust-lang.github.io/impl-trait-initiative/RFCs/rpit-in-traits.html - state.execute_with(|| frame_system::Pallet::::read_events_no_consensus().collect()) - } - - fn get_event_count(state: &mut State) -> Self::EventIndex { - state.execute_with(|| frame_system::Pallet::::event_count()) - } - - fn get_event_topics( - state: &mut State, - topic: &Self::Hash, - ) -> Vec<(Self::BlockNumber, Self::EventIndex)> { - state.execute_with(|| frame_system::Pallet::::event_topics(topic)) - } - - fn reset_events(state: &mut State) { - state.execute_with(|| frame_system::Pallet::::reset_events()) - } -} - -impl - ParentchainPalletInstancesInterface for Stf -where - State: SgxExternalitiesTrait, - Runtime: frame_system::Config
- + pallet_parentchain::Config - + pallet_parentchain::Config - + pallet_parentchain::Config, - <::Lookup as StaticLookup>::Source: From, - ParentchainHeader: Debug, -{ - type Error = StfError; - - fn update_parentchain_litentry_block( - state: &mut State, - header: ParentchainHeader, - ) -> Result<(), Self::Error> { - trace!("updating litentry parentchain block : {:?}", header); - state.execute_with(|| { - pallet_parentchain::Call::::set_block { header } - .dispatch_bypass_filter(Runtime::RuntimeOrigin::root()) - .map_err(|e| { - Self::Error::Dispatch(format!( - "Update parentchain litentry block error: {:?}", - e.error - )) - }) - })?; - Ok(()) - } - - fn update_parentchain_target_a_block( - state: &mut State, - header: ParentchainHeader, - ) -> Result<(), Self::Error> { - trace!("updating target_a parentchain block: {:?}", header); - state.execute_with(|| { - pallet_parentchain::Call::::set_block { header } - .dispatch_bypass_filter(Runtime::RuntimeOrigin::root()) - .map_err(|e| { - Self::Error::Dispatch(format!( - "Update parentchain target_a block error: {:?}", - e.error - )) - }) - })?; - Ok(()) - } - - fn update_parentchain_target_b_block( - state: &mut State, - header: ParentchainHeader, - ) -> Result<(), Self::Error> { - trace!("updating target_b parentchain block: {:?}", header); - state.execute_with(|| { - pallet_parentchain::Call::::set_block { header } - .dispatch_bypass_filter(Runtime::RuntimeOrigin::root()) - .map_err(|e| { - Self::Error::Dispatch(format!( - "Update parentchain target_b block error: {:?}", - e.error - )) - }) - })?; - Ok(()) - } - - fn set_creation_block( - state: &mut State, - header: ParentchainHeader, - parentchain_id: ParentchainId, - ) -> Result<(), Self::Error> { - state.execute_with(|| match parentchain_id { - ParentchainId::Litentry => pallet_parentchain::Call::< - Runtime, - ParentchainInstanceLitentry, - >::set_creation_block { - header, - } - .dispatch_bypass_filter(Runtime::RuntimeOrigin::root()) - .map_err(|e| { - Self::Error::Dispatch(format!("Init shard vault account error: {:?}", e.error)) - }), - ParentchainId::TargetA => pallet_parentchain::Call::< - Runtime, - ParentchainInstanceTargetA, - >::set_creation_block { - header, - } - .dispatch_bypass_filter(Runtime::RuntimeOrigin::root()) - .map_err(|e| { - Self::Error::Dispatch(format!("Init shard vault account error: {:?}", e.error)) - }), - ParentchainId::TargetB => pallet_parentchain::Call::< - Runtime, - ParentchainInstanceTargetB, - >::set_creation_block { - header, - } - .dispatch_bypass_filter(Runtime::RuntimeOrigin::root()) - .map_err(|e| { - Self::Error::Dispatch(format!("Init shard vault account error: {:?}", e.error)) - }), - })?; - Ok(()) - } -} - -impl RuntimeUpgradeInterface for Stf -where - State: SgxExternalitiesTrait, - Runtime: frame_system::Config, -{ - type Error = StfError; - - fn on_runtime_upgrade(state: &mut State) -> Result<(), Self::Error> { - // Returns if the runtime was upgraded since the last time this function was called. - let runtime_upgraded = || -> bool { - let last = frame_system::LastRuntimeUpgrade::::get(); - let current = - <::Version as frame_support::traits::Get<_>>::get( - ); - - if last.as_ref().map(|v| v.was_upgraded(¤t)).unwrap_or(true) { - frame_system::LastRuntimeUpgrade::::put( - frame_system::LastRuntimeUpgradeInfo::from(current.clone()), - ); - debug!("Do some migrations, last: {:?}, current: {:?}", last, current.spec_version); - true - } else { - false - } - }; - - state.execute_with(|| { - if runtime_upgraded() { - Executive::execute_on_runtime_upgrade(); - } - }); - Ok(()) - } -} - -/// Creates valid enclave account with a balance that is above the existential deposit. -/// !! Requires a root to be set. -fn create_enclave_self_account( - enclave_account: AccountId, -) -> Result<(), StfError> -where - Runtime: frame_system::Config + pallet_balances::Config, - <::Lookup as StaticLookup>::Source: From, - Runtime::Balance: From, -{ - pallet_balances::Call::::force_set_balance { - who: enclave_account.into(), - new_free: 1000.into(), - } - .dispatch_bypass_filter(Runtime::RuntimeOrigin::root()) - .map_err(|e| { - StfError::Dispatch(format!("Set Balance for enclave signer account error: {:?}", e.error)) - }) - .map(|_| ()) -} diff --git a/tee-worker/bitacross/app-libs/stf/src/stf_sgx_primitives.rs b/tee-worker/bitacross/app-libs/stf/src/stf_sgx_primitives.rs deleted file mode 100644 index 5c2112d583..0000000000 --- a/tee-worker/bitacross/app-libs/stf/src/stf_sgx_primitives.rs +++ /dev/null @@ -1,29 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use std::marker::PhantomData; - -pub mod types { - pub use itp_types::{AccountData, AccountInfo, BlockNumber, Header as ParentchainHeader}; - pub type State = itp_sgx_externalities::SgxExternalities; - pub type StateType = itp_sgx_externalities::SgxExternalitiesType; - pub type StateDiffType = itp_sgx_externalities::SgxExternalitiesDiffType; -} - -pub struct Stf { - phantom_data: PhantomData<(TCS, G, State, Runtime)>, -} diff --git a/tee-worker/bitacross/app-libs/stf/src/stf_sgx_tests.rs b/tee-worker/bitacross/app-libs/stf/src/stf_sgx_tests.rs deleted file mode 100644 index 479f3b9bb0..0000000000 --- a/tee-worker/bitacross/app-libs/stf/src/stf_sgx_tests.rs +++ /dev/null @@ -1,84 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{Getter, State, Stf, TrustedCall, TrustedCallSigned}; -use ita_sgx_runtime::Runtime; -use itp_node_api::metadata::{metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository}; -use itp_stf_interface::{ - sudo_pallet::SudoPalletInterface, system_pallet::SystemPalletAccountInterface, InitState, - StateCallInterface, -}; -use itp_stf_primitives::types::{AccountId, ShardIdentifier}; -use itp_types::parentchain::ParentchainId; -use litentry_primitives::LitentryMultiSignature; -use sp_core::{ - ed25519::{Pair as Ed25519Pair, Signature as Ed25519Signature}, - Pair, -}; -use std::{sync::Arc, vec::Vec}; - -pub type StfState = Stf; - -pub fn enclave_account_initialization_works() { - let enclave_account = AccountId::new([2u8; 32]); - let mut state = StfState::init_state(enclave_account.clone()); - let _root = StfState::get_root(&mut state); - let account_data = StfState::get_account_data(&mut state, &enclave_account); - - assert_eq!(0, StfState::get_account_nonce(&mut state, &enclave_account)); - assert_eq!(enclave_account, StfState::get_enclave_account(&mut state)); - assert_eq!(1000, account_data.free); -} - -pub fn shield_funds_increments_signer_account_nonce() { - let enclave_call_signer = Ed25519Pair::from_seed(b"14672678901234567890123456789012"); - let enclave_signer_account_id: AccountId = enclave_call_signer.public().into(); - let mut state = StfState::init_state(enclave_signer_account_id.clone()); - - let shield_funds_call = TrustedCallSigned::new( - TrustedCall::balance_shield( - enclave_call_signer.public().into(), - AccountId::new([1u8; 32]), - 500u128, - ParentchainId::Litentry, - ), - 0, - LitentryMultiSignature::Ed25519(Ed25519Signature([0u8; 64])), - ); - - let repo = Arc::new(NodeMetadataRepository::new(NodeMetadataMock::new())); - let shard = ShardIdentifier::default(); - StfState::execute_call( - &mut state, - &shard, - shield_funds_call, - Default::default(), - &mut Vec::new(), - repo, - ) - .unwrap(); - assert_eq!(1, StfState::get_account_nonce(&mut state, &enclave_signer_account_id)); -} - -pub fn test_root_account_exists_after_initialization() { - let enclave_account = AccountId::new([2u8; 32]); - let mut state = StfState::init_state(enclave_account); - let root_account = StfState::get_root(&mut state); - - let account_data = StfState::get_account_data(&mut state, &root_account); - assert!(account_data.free > 0); -} diff --git a/tee-worker/bitacross/app-libs/stf/src/test_genesis.rs b/tee-worker/bitacross/app-libs/stf/src/test_genesis.rs deleted file mode 100644 index fd70e4cc72..0000000000 --- a/tee-worker/bitacross/app-libs/stf/src/test_genesis.rs +++ /dev/null @@ -1,95 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -use frame_support::traits::UnfilteredDispatchable; -use ita_sgx_runtime::{Balance, Runtime, System}; -use itp_sgx_externalities::SgxExternalitiesTrait; -use itp_stf_primitives::error::StfError; -use itp_storage::storage_value_key; -use log::*; -use sgx_tstd as std; -use sp_core::{crypto::AccountId32, ed25519, Pair}; -use sp_runtime::MultiAddress; -use std::{format, vec, vec::Vec}; - -type Seed = [u8; 32]; - -const ALICE_ENCODED: Seed = [ - 212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, - 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125, -]; - -const ENDOWED_SEED: Seed = *b"12345678901234567890123456789012"; -const SECOND_ENDOWED_SEED: Seed = *b"22345678901234567890123456789012"; -const UNENDOWED_SEED: Seed = *b"92345678901234567890123456789012"; - -const ALICE_FUNDS: Balance = 10_000_000_000_000_000; -pub const ENDOWED_ACC_FUNDS: Balance = 2_000_000_000_000; -pub const SECOND_ENDOWED_ACC_FUNDS: Balance = 1_000_000_000_000; - -pub fn endowed_account() -> ed25519::Pair { - ed25519::Pair::from_seed(&ENDOWED_SEED) -} -pub fn second_endowed_account() -> ed25519::Pair { - ed25519::Pair::from_seed(&SECOND_ENDOWED_SEED) -} - -pub fn unendowed_account() -> ed25519::Pair { - ed25519::Pair::from_seed(&UNENDOWED_SEED) -} - -pub fn test_genesis_setup(state: &mut impl SgxExternalitiesTrait) { - // set alice sudo account - set_sudo_account(state, &ALICE_ENCODED); - trace!("Set new sudo account: {:?}", &ALICE_ENCODED); - - let endowees: Vec<(AccountId32, Balance)> = vec![ - (endowed_account().public().into(), ENDOWED_ACC_FUNDS), - (second_endowed_account().public().into(), SECOND_ENDOWED_ACC_FUNDS), - (ALICE_ENCODED.into(), ALICE_FUNDS), - ]; - - endow(state, endowees); -} - -fn set_sudo_account(state: &mut impl SgxExternalitiesTrait, account_encoded: &[u8]) { - state.execute_with(|| { - sp_io::storage::set(&storage_value_key("Sudo", "Key"), account_encoded); - }) -} - -pub fn endow( - state: &mut impl SgxExternalitiesTrait, - endowees: impl IntoIterator, -) { - state.execute_with(|| { - for e in endowees.into_iter() { - let account = e.0; - - ita_sgx_runtime::BalancesCall::::force_set_balance { - who: MultiAddress::Id(account.clone()), - new_free: e.1, - } - .dispatch_bypass_filter(ita_sgx_runtime::RuntimeOrigin::root()) - .map_err(|e| StfError::Dispatch(format!("Balance Set Balance error: {:?}", e.error))) - .unwrap(); - - let print_public: [u8; 32] = account.clone().into(); - let account_info = System::account(&&print_public.into()); - debug!("{:?} balance is {}", print_public, account_info.data.free); - } - }); -} diff --git a/tee-worker/bitacross/app-libs/stf/src/trusted_call.rs b/tee-worker/bitacross/app-libs/stf/src/trusted_call.rs deleted file mode 100644 index 173a5c4126..0000000000 --- a/tee-worker/bitacross/app-libs/stf/src/trusted_call.rs +++ /dev/null @@ -1,433 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - helpers::{enclave_signer_account, ensure_enclave_signer_account}, - trusted_call_result::TrustedCallResult, - Getter, -}; -use codec::{Decode, Encode}; -use frame_support::{ensure, traits::UnfilteredDispatchable}; -pub use ita_sgx_runtime::{Balance, Index, Runtime, System}; -use itp_node_api::metadata::{provider::AccessNodeMetadata, NodeMetadataTrait}; - -use itp_stf_interface::ExecuteCall; -use itp_stf_primitives::{ - error::StfError, - traits::{TrustedCallSigning, TrustedCallVerification}, - types::{AccountId, KeyPair, ShardIdentifier, TrustedOperation}, -}; -use itp_types::{ - parentchain::{ParentchainCall, ParentchainId}, - Moment, H256, -}; -use itp_utils::stringify::account_id_to_string; -pub use litentry_primitives::{ - aes_encrypt_default, AesOutput, Identity, LitentryMultiSignature, ParentchainBlockNumber, - RequestAesKey, ValidationData, -}; -use log::*; -use sp_core::{ - crypto::{AccountId32, UncheckedFrom}, - ed25519, -}; -use sp_io::hashing::blake2_256; -use sp_runtime::MultiAddress; -use std::{format, prelude::v1::*, sync::Arc}; - -#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] -#[allow(non_camel_case_types)] -pub enum TrustedCall { - // original integritee trusted calls, starting from index 50 - #[codec(index = 50)] - noop(Identity), - #[codec(index = 51)] - balance_set_balance(Identity, AccountId, Balance, Balance), - #[codec(index = 52)] - balance_transfer(Identity, AccountId, Balance), - #[codec(index = 53)] - balance_unshield(Identity, AccountId, Balance, ShardIdentifier), // (AccountIncognito, BeneficiaryPublicAccount, Amount, Shard) - #[codec(index = 54)] - balance_shield(Identity, AccountId, Balance, ParentchainId), // (Root, AccountIncognito, Amount, origin parentchain) - #[codec(index = 55)] - timestamp_set(Identity, Moment, ParentchainId), -} - -impl TrustedCall { - pub fn sender_identity(&self) -> &Identity { - match self { - Self::noop(sender_identity) => sender_identity, - Self::balance_set_balance(sender_identity, ..) => sender_identity, - Self::balance_transfer(sender_identity, ..) => sender_identity, - Self::balance_unshield(sender_identity, ..) => sender_identity, - Self::balance_shield(sender_identity, ..) => sender_identity, - Self::timestamp_set(sender_identity, ..) => sender_identity, - } - } -} - -impl TrustedCallSigning for TrustedCall { - fn sign( - &self, - pair: &KeyPair, - nonce: Index, - mrenclave: &[u8; 32], - shard: &ShardIdentifier, - ) -> TrustedCallSigned { - let mut payload = self.encode(); - payload.append(&mut nonce.encode()); - payload.append(&mut mrenclave.encode()); - payload.append(&mut shard.encode()); - - // use blake2_256 hash to shorten the payload - see `verify_signature` below - TrustedCallSigned { call: self.clone(), nonce, signature: pair.sign(&blake2_256(&payload)) } - } -} - -#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] -pub struct TrustedCallSigned { - pub call: TrustedCall, - pub nonce: Index, - pub signature: LitentryMultiSignature, -} - -impl TrustedCallSigned { - pub fn new(call: TrustedCall, nonce: Index, signature: LitentryMultiSignature) -> Self { - TrustedCallSigned { call, nonce, signature } - } - - pub fn into_trusted_operation( - self, - direct: bool, - ) -> TrustedOperation { - match direct { - true => TrustedOperation::direct_call(self), - false => TrustedOperation::indirect_call(self), - } - } -} - -impl Default for TrustedCallSigned { - fn default() -> Self { - Self { - call: TrustedCall::noop(AccountId32::unchecked_from([0u8; 32].into()).into()), - nonce: 0, - signature: LitentryMultiSignature::Ed25519(ed25519::Signature::unchecked_from( - [0u8; 64], - )), - } - } -} -impl TrustedCallVerification for TrustedCallSigned { - fn sender_identity(&self) -> &Identity { - self.call.sender_identity() - } - - fn nonce(&self) -> Index { - self.nonce - } - - fn verify_signature(&self, mrenclave: &[u8; 32], shard: &ShardIdentifier) -> bool { - let mut payload = self.call.encode(); - payload.append(&mut self.nonce.encode()); - payload.append(&mut mrenclave.encode()); - payload.append(&mut shard.encode()); - - self.signature.verify(&blake2_256(&payload), self.call.sender_identity()) - || self.signature.verify(&payload, self.call.sender_identity()) - } - - fn metric_name(&self) -> &'static str { - "unsupported_trusted_call" - } -} - -impl ExecuteCall for TrustedCallSigned -where - NodeMetadataRepository: AccessNodeMetadata, - NodeMetadataRepository::MetadataType: NodeMetadataTrait, -{ - type Error = StfError; - type Result = TrustedCallResult; - - // TODO(Kai@litentry): - // If this function returns Err(), it will feed the executor with Ok(ExecutedOperation::failed()), - // which will remove the failed op from its **own** top pool while preventing it from being included - // in a sidechain block - see `execute_trusted_call_on_stf`. - // - // As a result, when other workers import sidechain blocks, they will treat the op as - // "not yet executed" (before it's not recorded in the sidechain block) and try to execute it again from - // its own top pool (if the op is added to the top pool upon e.g. parentchain block import). - // - // The execution will most likely fail again. However, the state could have been changed already by applying - // the state diff from the imported sidechain block. This could cause an inconsistent/mismatching state, - // for example, the nonce. See the nonce handling below: we increased the nonce no matter the STF is executed - // successfully or not. - // - // This is probably the reason why the nonce-handling test in `demo_shielding_unshielding.sh` sometimes fails. - // - // Update: - // see discussion in https://github.com/integritee-network/worker/issues/1232 - // my current thoughts are: - // - we should return Err() if the STF execution fails, the parentchain effect will get applied regardless - // - the failed top should be removed from the pool - // - however, the failed top hash needs to be included in the sidechain block (still TODO) - // - // Almost every (Litentry) trusted call has a `H256` as parameter, this is used as the request identifier. - // It should be generated by the client (requester), and checked against when getting the response. - // It might seem redundant for direct invocation (DI) as the response is synchronous, however, we do need it - // when the request is handled asynchronously interanlly, which leads to streamed responses. Without it, it's - // impossible to pair the request and response. `top_hash` won't suffice as you can't know all hashes from - // client side beforehand (e.g. those trusted calls signed by enclave signer). - // - // TODO: - // - shall we add `req_ext_hash` in RpcReturnValue and use it to find streamed trustedCalls? - // - show error details for "Invalid" synchronous responses - fn execute( - self, - _shard: &ShardIdentifier, - _top_hash: H256, - _calls: &mut Vec, - _node_metadata_repo: Arc, - ) -> Result { - let sender = self.call.sender_identity().clone(); - let account_id: AccountId = - sender.to_native_account().ok_or(Self::Error::InvalidAccount)?; - let system_nonce = System::account_nonce(&account_id); - ensure!(self.nonce == system_nonce, Self::Error::InvalidNonce(self.nonce, system_nonce)); - - // Increment the nonce no matter if the call succeeds or fails. - // We consider the call "valid" once it reaches here (= it entered the tx pool) - System::inc_account_nonce(&account_id); - - // TODO: maybe we can further simplify this by effacing the duplicate code - match self.call { - TrustedCall::noop(who) => { - debug!("noop called by {}", account_id_to_string(&who),); - Ok(TrustedCallResult::Empty) - }, - TrustedCall::balance_set_balance(root, who, free_balance, reserved_balance) => { - let root_account_id: AccountId = - root.to_native_account().ok_or(Self::Error::InvalidAccount)?; - ensure!( - is_root::(&root_account_id), - Self::Error::MissingPrivileges(root_account_id) - ); - debug!( - "balance_set_balance({}, {}, {})", - account_id_to_string(&who), - free_balance, - reserved_balance - ); - ita_sgx_runtime::BalancesCall::::force_set_balance { - who: MultiAddress::Id(who), - new_free: free_balance, - } - .dispatch_bypass_filter(ita_sgx_runtime::RuntimeOrigin::root()) - .map_err(|e| { - Self::Error::Dispatch(format!("Balance Set Balance error: {:?}", e.error)) - })?; - // This explicit Error type is somehow still needed, otherwise the compiler complains - // multiple `impl`s satisfying `StfError: std::convert::From<_>` - // note: and another `impl` found in the `core` crate: `impl std::convert::From for T;` - // the impl From<..> for StfError conflicts with the standard convert - // - // Alternatively, removing the customised "impl From<..> for StfError" and use map_err directly - // would also work - Ok(TrustedCallResult::Empty) - }, - TrustedCall::balance_transfer(from, to, value) => { - let origin = ita_sgx_runtime::RuntimeOrigin::signed( - from.to_native_account().ok_or(Self::Error::InvalidAccount)?, - ); - std::println!("⣿STF⣿ 🔄 balance_transfer from ⣿⣿⣿ to ⣿⣿⣿ amount ⣿⣿⣿"); - // endow fee to enclave (self) - let fee_recipient: AccountId = enclave_signer_account(); - // fixme: apply fees through standard frame process and tune it - let fee = crate::STF_TX_FEE; - info!( - "from {}, to {}, amount {}, fee {}", - account_id_to_string(&from), - account_id_to_string(&to), - value, - fee - ); - ita_sgx_runtime::BalancesCall::::transfer { - dest: MultiAddress::Id(fee_recipient), - value: fee, - } - .dispatch_bypass_filter(origin.clone()) - .map_err(|e| { - Self::Error::Dispatch(format!("Balance Transfer error: {:?}", e.error)) - })?; - ita_sgx_runtime::BalancesCall::::transfer { - dest: MultiAddress::Id(to), - value, - } - .dispatch_bypass_filter(origin) - .map_err(|e| { - Self::Error::Dispatch(format!("Balance Transfer error: {:?}", e.error)) - })?; - Ok(TrustedCallResult::Empty) - }, - TrustedCall::balance_unshield(account_incognito, beneficiary, value, shard) => { - std::println!( - "⣿STF⣿ 🛡👐 balance_unshield from ⣿⣿⣿ to {}, amount {}", - account_id_to_string(&beneficiary), - value - ); - // endow fee to enclave (self) - let fee_recipient: AccountId = enclave_signer_account(); - // fixme: apply fees through standard frame process and tune it. has to be at least two L1 transfer's fees - let fee = crate::STF_TX_FEE * 3; - - info!( - "balance_unshield(from (L2): {}, to (L1): {}, amount {} (+fee: {}), shard {})", - account_id_to_string(&account_incognito), - account_id_to_string(&beneficiary), - value, - fee, - shard - ); - - let origin = ita_sgx_runtime::RuntimeOrigin::signed( - account_incognito.to_native_account().ok_or(StfError::InvalidAccount)?, - ); - ita_sgx_runtime::BalancesCall::::transfer { - dest: MultiAddress::Id(fee_recipient), - value: fee, - } - .dispatch_bypass_filter(origin) - .map_err(|e| { - Self::Error::Dispatch(format!("Balance Unshielding error: {:?}", e.error)) - })?; - burn_funds( - account_incognito.to_native_account().ok_or(StfError::InvalidAccount)?, - value, - )?; - Ok(TrustedCallResult::Empty) - }, - TrustedCall::balance_shield(enclave_account, who, value, parentchain_id) => { - let account_id: AccountId32 = - enclave_account.to_native_account().ok_or(Self::Error::InvalidAccount)?; - ensure_enclave_signer_account(&account_id)?; - debug!( - "balance_shield({}, {}, {:?})", - account_id_to_string(&who), - value, - parentchain_id - ); - std::println!("⣿STF⣿ 🛡 will shield to {}", account_id_to_string(&who)); - shield_funds(who, value)?; - - Ok(TrustedCallResult::Empty) - }, - TrustedCall::timestamp_set(enclave_account, now, parentchain_id) => { - let account_id: AccountId32 = - enclave_account.to_native_account().ok_or(Self::Error::InvalidAccount)?; - ensure_enclave_signer_account(&account_id)?; - // Litentry: we don't actually set the timestamp, see `BlockMetadata` - warn!("unused timestamp_set({}, {:?})", now, parentchain_id); - Ok(TrustedCallResult::Empty) - }, - } - } - - fn get_storage_hashes_to_update(self) -> Vec> { - debug!("No storage updates needed..."); - Vec::new() - } -} - -fn burn_funds(account: AccountId, amount: u128) -> Result<(), StfError> { - let account_info = System::account(&account); - if account_info.data.free < amount { - return Err(StfError::MissingFunds) - } - - ita_sgx_runtime::BalancesCall::::force_set_balance { - who: MultiAddress::Id(account), - new_free: account_info.data.free - amount, - } - .dispatch_bypass_filter(ita_sgx_runtime::RuntimeOrigin::root()) - .map_err(|e| StfError::Dispatch(format!("Burn funds error: {:?}", e.error)))?; - Ok(()) -} - -fn shield_funds(account: AccountId, amount: u128) -> Result<(), StfError> { - //fixme: make fee configurable and send fee to vault account on L2 - let fee = amount / 571; // approx 0.175% - - // endow fee to enclave (self) - let fee_recipient: AccountId = enclave_signer_account(); - - let account_info = System::account(&fee_recipient); - ita_sgx_runtime::BalancesCall::::force_set_balance { - who: MultiAddress::Id(fee_recipient), - new_free: account_info.data.free + fee, - } - .dispatch_bypass_filter(ita_sgx_runtime::RuntimeOrigin::root()) - .map_err(|e| StfError::Dispatch(format!("Shield funds error: {:?}", e.error)))?; - - // endow shieding amount - fee to beneficiary - let account_info = System::account(&account); - ita_sgx_runtime::BalancesCall::::force_set_balance { - who: MultiAddress::Id(account), - new_free: account_info.data.free + amount - fee, - } - .dispatch_bypass_filter(ita_sgx_runtime::RuntimeOrigin::root()) - .map_err(|e| StfError::Dispatch(format!("Shield funds error: {:?}", e.error)))?; - - Ok(()) -} - -pub(crate) fn is_root(account: &AccountId) -> bool -where - Runtime: frame_system::Config + pallet_sudo::Config, - AccountId: PartialEq, -{ - pallet_sudo::Pallet::::key().map_or(false, |k| account == &k) -} - -#[cfg(test)] -mod tests { - use super::*; - use itp_stf_primitives::types::KeyPair; - use sp_keyring::AccountKeyring; - - #[test] - fn verify_signature_works() { - let nonce = 21; - let mrenclave = [0u8; 32]; - let shard = ShardIdentifier::default(); - - let call = TrustedCall::balance_set_balance( - AccountKeyring::Alice.public().into(), - AccountKeyring::Alice.public().into(), - 42, - 42, - ); - let signed_call = call.sign( - &KeyPair::Sr25519(Box::new(AccountKeyring::Alice.pair())), - nonce, - &mrenclave, - &shard, - ); - - assert!(signed_call.verify_signature(&mrenclave, &shard)); - } -} diff --git a/tee-worker/bitacross/app-libs/stf/src/trusted_call_result.rs b/tee-worker/bitacross/app-libs/stf/src/trusted_call_result.rs deleted file mode 100644 index 60e303ce3b..0000000000 --- a/tee-worker/bitacross/app-libs/stf/src/trusted_call_result.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -// This file contain the RPC response struct which will be encoded and -// passed back to the requester of trustedCall direct invocation (DI). - -use codec::{Decode, Encode}; -use itp_stf_interface::StfExecutionResult; -use std::vec::Vec; - -#[derive(Encode, Decode, Debug)] -pub enum TrustedCallResult { - #[codec(index = 0)] - Empty, - #[codec(index = 1)] - Streamed, -} - -impl StfExecutionResult for TrustedCallResult { - fn get_encoded_result(self) -> Vec { - match self { - Self::Empty => Vec::default(), - Self::Streamed => Vec::default(), - } - } - - fn force_connection_wait(&self) -> bool { - matches!(self, Self::Streamed) - } -} diff --git a/tee-worker/bitacross/assets/teebag_registry.gif b/tee-worker/bitacross/assets/teebag_registry.gif deleted file mode 100644 index b337fb6c3ae6a7694f440837f665ecd5f7a906f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1703551 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

O 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
op&sn4#p@mx5 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

  • @x*KK=FgwLSj-Z^hnX z_W3hpg8#mGuLcAdqqd1aY!SE?PU_k)!!$PlA@kJ-RkM+Kx2CH0Ha9k@`co?xaJu%HFDhIH%{=z8FkV%pVTB*BFPN@SgH|?ZHeR= zM~RzX8B(-`a+Hx_T1A(bLD2blqY1Bx0RZKL z(%1Ch4$&CtR@#}LHNx~=EA?4RU)q+B+0iSGFj_}%<{%yZ@l?nZNFf%i64qm zp&}U6sn#z{$nf7K=abdfh(U5+bW|5>TAOUDW~*iqYXC+OgWh!im7O0Gh()tP2w+mP zKPmx4CGv1Os+L59R7y@UkNQA|!nJ;Q)Wxfe_|yyuaD`cbRD6=;1pR=(nTSAE0 zwa7Fqt>_J9UBnA;WHL4Xlp$L}8z0SjagaHT$sZ1J&?EuEeN)+8WI3WiL`VW5L)sjX z)OVzfaxegplS7B1xiC*u0=Kz+Zt7OF&CT`<1ZfRGIup_ZowCNdmyBINrviu5q?AUT z4HG!18RwDWimgKv-Y~U-7L<)iJI>6xx`&vd5QF5d}l2L|{h- z0HNz!kOLihzzzyxZm&|9LKh6(4SNi%sA0h-KdWB)pf)Q1^bm*{R4EX7uG6|$#Ox?W zQCjnUwR(>#F;q@GmR61dlQZ^;j8_ylhy8a)VtFiI18le8{xFb@gj~o(feHi<#1Kg4 zFqXwO(-mu_zEc5*3rAcg_QIu0>7`{Yy}Yp+PmIiL)aF^lOq(oorV^eAMJUd;VU;MD zIYLrSL}RkhJTVFp6s0qw3-{(l1M9que)NnIy%#3o87){os8rwtrwL1%qkW!=R6AWR zNRt{;jxIH-;U#Lh1fbIO(J+G>nm)u_dDT)ZsIAGB>Rqpk&Ak3Ki*mgbxF{6M#yx}} z0#S)dI6@PwKn#E?>|9~rIxNPc_O5`f?HHYU+unZv8@7w$V7E+r!@$vCg*6!Ng`(x! z>*f@%)%)o^Tw=0ya1KE0^ngXP*jz?up9&-TslVGioi-zk``pL!^#DM; zwNWRXSGTVCTp3t3i!adaMhfzQ8YGm_h%;RO`mRP7a(62*WMMZZzt^QBd@Yx!N+rJG zHhil#E#N4;?79g0f3`B=1_5WJs(zbdKfd>gk`1SQe;$3`R6oV1XA<@q7C7cq^K7Ym zTYlV{O@PCFqF?@&Ihp(wmDKP)?~U-oKepL0N&M(>6ffGkqSkAbM(XFbuW#da*QDNU zoo7wc>ym%1U~wM4_*LMoS=3FLZK$029pH%6h5;hrXUyC6sfL_6UsbSHq{UxtxEX6u zpKZ+s4ZPn~P+(Pb;8)Cpet;m24P9>t9Rsc)KKo_uv1d>s)RGlM0lK{g1AQN&O_%-2f;6@S7#|>E5epDf^>7Z2T()Qs8 zfE0n)sgptUASLP3Q+xy38H5V>(^Z^+H$32bRM8s3ATpSecVO~faA9lvD zOd(dJp?nbH+mRC*)!qz|-aHASS5$&^(P0?TAzHYBAxZ@ak{~wB!zRvyKLh|ah#U%T ziXQ%9Tw<>bUBz`wDJ~;A z?!+RdBcDWL#=sB;8jA(G<8AE!VlMI`HuxeaPNO$&Voc~FH}a#qd>?BR*t&0Hi(wBS3nhFZyCO8U#Obqc~E^D8@&qF(gU;1br-EN%q8BP~vO6 zqC}UZo38GxiY!hyBl2>q|UJArCFoa-&;y_@-)ICLJ3MI@10MOyaLeeHVs^2`W(I&Mlmb=+W^%^FaT)~MRZ7p{$4FjhO6Esw z>8E94Oj&LP?xC3g1wc4q-&CBQ5x$0kDy4bG$bd>0fdZ#}&E-}IBz_fVS6(PKbmlcS z=X(8SYuqP&Os9VyWqu3-FrDZW#mGz==2;xyYII*h$Y@s3Xz5`LcDe?Ras>+sl!fAl znr-GZ%x6{@sdKXb+CU-mOt7Cps8v{4X;on9S!k)Fz-XiFsNH#< zRKCkJ7H3v&1&CG*iBcJSsGyV{q>#mFZK}u_fvH%)nCJQEWm<)p9?oc;36e5SNWLjh z(J5Q%M;-(poq7p)5uQ}&rG9;d4W=hU+*f|>msrG?rS>TV{U_q^pqDtp2G(59L1}p+ z>OrDvj;Lx!Na54<*a%1;S76~*$SSRZg{^w3m^|p=R1tNF=tKG(QO=pGG9;@S>ailr zm(Y}1;HfvQ1+>bDusSGj@+v(=#fEwtViM!BuIhe#tEy~UwMOHg#>cd}3qXd`Mh@zm z6{=Z^YdR+X>wCZ{qGBt)j^4HAt5=xjRt6+S5+*hpsJjyC&?Vt}6r;R0A-%@yxAH5# zo@-q4V@-17XA-72Zlh0Tq&8k;Mh0ZhkmGXR#X*OvaCdULs0r_KsIN<_G4diBt=>z zXr5+IS|dhoWiG0V?m4W`uHt^orhg)B(t@hlMny;Vq{a4S%_fA*uBpxXV`=(j)oyH> z@)tRpWzdFg)e(Yhrfk|yrqU8ixuV6#g5`Y9LpXirFEVaecEdDMY{!~rPS#}QR-LTWfWx(Ps*8a*cVx4R# z=I0(`;EF5h+GXL|CCB3APflab=B(RxEzII$>*DS=POjYA=P=raiT*Ah60HJ?E=dk= zTo!N3KJ9USA#|G@}x~}xTZq0(_H)6x#I^Hlc%4{gC_C9HJ;)cDdD)>63_`W1z z`sB;7vE&w8fO!e(i+30Bcmf6>rU8G zGCgXsLv~{0S|r!5Z~^P=%PKJ^lkhD|@6=M`G`Pb+Aaa-rA6zi%DsOKMrv}+dDJ;9A zEYqW3ZlzA%qw`*E7!xqherOR3vii>Knigj_dt-5$iTLH-GNWT4G@fkq@3Kb!b0zz# zJdY+A7qURwa_hovH_mRy(yYKrF2^b^BG1DxpK|~dG$Nm}ANrmGp+)wtGX^Ki=jIPc z)pJ@*vpbe1UizzN4kj?4=H<%nH&gEeck?t%!_2zFCmM5^FrZlkkwhD09o0r2{O+68 z!h}wRUUkAwQ$@qHa$3yqI9l?e5p{A7G6dtKKwB;ON-YRqBQ{PiACq)Y>a0Suba4)| zKX7$HLmdMKLaAy6lg_l979~#SPbg}^1XS-94#eih#1?1)j#03V!NnHbHKb{D#)yCj zlygE*3F<iCNJG`WF~jA3RuT~CfO0%>!A~Z1zbe_pl0=F;`!9k%lF4W+FMGVFkiqg*D7rGelXRc1i|9 zA*=ywl%Mwc&pdzwZ)=4&Y=J<;HADEdKj`*UFtqIOb;ZzwU!U1~>@pCOW>5m-PJS~O zW9{8`<6iRdK3+FR7ITE7DMEKQ490{^2f$;RHhCj+O|OO=yXw!pcL0b0Y@^l$1OVXD zQhNtLTgOC-YXEPfmIw$0BAl2Rra0h!Lvv1m2GBzli#0G-!&) z1c|%2Kx%*BY74CfL`~kZ{tssXF)BThJrOYk`Kfxu#k@zcK}TJ zi6=ygyMmjgxLbomk7q$M$c7&5L8e6Ur&+fKvwLwpALU^@S7c72uxT8;8GoQr_ z`%ITm!IRf_03gB^O``_rbd3kVOUXe1Y=I7F0BnztiDvYZdp<1x_;hcwpCft~&+G%or!nKC zF{d;Mvb1Geb}`fUoIX0i^P50y+*=s!1)EJYGG+jn_gne3~0s&s`e1sx4Te*uLDDkZ?-_xLbGQ&FqArOC&ZA)M9*is))zu8fP=;(Rc%}Q zK!lJ8@ME)gdE}t)<0?2XPIeiO=D_x1@`m$hA~d2Kv_Yt}p?gWe_x-y8SX(SAhgT=r zgdz(Wfr9FiU|NDAk3DR6!^D&L(1ck+M1x;fFO?Yo!WON$*jv5lM|Fuu{mP5|rH_)~ z3k2eCJJW-r9l?6YhWYIPy+AbRuqOmCRKWy{5;#15d>%eF1Q7)XfSddHAw>PyLxmZd zp*=5gUoN-w?xgF!Y(8=$B3E}pSARkux=IJYqVK(o_&xZC8{nfw%B-@_uuzMKkPguP zmAe89VS~22c&96}u{ZfQSi(So0~yRdjMFwi1PCxsU_paTUh3~-2NPoF;z=@dGY zXi=j_hoTHnOCS}Dz$Rp28sHl^i2=3<^cVpD7)FLVP);CZG3+Xe1%nzq8vs!NDh4M^ zDLHRmw*c7YVZ%Gm-@pLf2IByQ3VKGUdth zDpw9b&l^(HMuDbQy_$7v*RNs6mOYzxZQHkT=hnTOcW>Xnfd>~poOp5L$B`#j9_Wyu z(*^}8mp+|(b?eHXXV<>{;$efps!TW=tx5n(UcYJ4fJlUfTHV0qDcF+hs;CStbql|? zyg|bZ1({$FuevK}KY_?I52TpnLxezq`fEvqyX^9dAuYsHYC?fZBIF^$xC>E4on#x) z#1m1RNuny;vxpvnEV}Qi0KjTUK7r`}gGmI#O5!R3&_M~kj?F{YBnX@>!~({{x3n@y1Z=>;aFv$~WJtWVFoLL-Kiskk zs=H8{6grDq!c?HEYALOvN3D7Y#sGGV$q=y$;>aKk>!Q^#Mm_A0NDgO3)L0WsJXYCd zg(3s1svzugG>qD^)S@Fh1qZ~3#u_#zjI>bdufi~r2peJf!mA;52NNvag8qwlBEiVK ztIW_0v#c+8{ToxD$uhflo*9|{EvnFi5l&d)g&A(x;fEo{E|5Tox-sI5F-ExHjftvI zhlZwVsGBK>WNoK?@x;2tcW=3hJ?nEm|Ip_{%G%zk|yYeqFUQ7=&U94qIaT!xKyiMDec{>%)9uSNNB(O9q6-y@D1Ry zFa^>qG_r-tSoG0JFWvOhQBS?li3JhlM_b!cUG~|F1KoC!X=O;Y?;8Vt|b@7!2jIUl8+6la zpz@6=-eH7&>@Z#mO~ zAylYEeU4B6j|HVIA!d`9gq)Qu3en0nX-uHmXy?GRxvNGdG?58_t4tYFv5u9jWi{(r z(dsaxu9YTWW$RnNMpU@YwT?@&65<3`B}mrmKfy$km%uqDjeZ7{mis2>EGN&jE|#&4 zb?jpy8(G%Sm9mw!ETZIkSc(Wd~F6zn}a4D zB^z7W&X%^dwXF#^n_J!PHgvJ!?Qg%<*~K{SseL)%;}S>O&2)0P9VBdJTFb%15~Qk| z@rGODcw6mmm%H8d?swq?T=9-~x0xmHdA9>rF0Ca??uzJ@Hj_CF0+zWXwNQLL^PeyI zt1jQb?lHj||6l!3{o*a1Wy{l7^HsV$xSAE&Q(wN0hpviEfxS zN8PW|(!eD)@rhBK;+Q&^j{jvbc^Uj-85iX&NImVBIz!#Sa@49|vQUOG%-UwoQpH6! z@{y68kqrQ7^O-!^O@0{W;NeQ z%58S@iDLZbwaM8b6ZElX413i)gXwaH`KD?8g;>ocbIpY|^q~=*=-bel(Tz43lpP)E znaG!NA5AP{LRo3L2zJXkE{Fv~Bj$pZS<$67^{G*pIWQrnwUxodLFZ=Xx%dq!=CJCHZd{kd z#rF2M!5waxE}PtpR(83WJZoOxSEN<_^JMNdQeBV9AMW6)LC8#MaqpYo{r0yEfq_&y z=NsKJ?h$MPUhqZ(hOqVeGNy&r)lQf8ah{QGL<5%0ga4c39ryUhA;y-IDxBow^#&qh z69@;D{LQ67lW4@$5W?!W$DIu2U(57z=89>^Ek-%WfgW_B54}#DFe1y5K3+})MK(s~ zZBUcm)^B{ywA`$6nC^vD&i(mct_?I->q2n!Eqy0MH~ZPqp7!6Ay`?o-LjWN0c8070 z{}My__fFUjo4nI*5ZZy^iiv`Wqo9HzsHlX(@UC{nFP`y@FRMmo5_h=M{q2$;${aM} z_z+GkJAa2M#fX9mOo*QJqc?r&QGa^V8$|Q1cm3;OpX8!go+z1SL-eg|Gw#!t-u0=J=z$)VPIe4}2(=Ie8KUk4g7!#o0Hg5ZIFC&La17Bf4b{-B0D&GxU;ui6 z9)u@#3;|P6@D7WR2FL43|!vAcM?xay0zYP?T!v5TG9_g_jrDGVq(Gds0 z5t@nrFvb63!WL~&4DB)43}O6+g7A8=AQ`eDuaPSRjwZUuiXOxpkl`PQF&_cI4gR4S zE{h%}$04xffGkAJqE4t}D&bCY)kbb6BC@C)GA3nm-;iP={fQYEQ6pI~2|6;?#w!|L zawWq`?}V}_#g46JGAWfZ*p9*`t*9X`G9z`82L7QNg3_oQ$sw$=C}DCE36Lwn^0Jt2 zDao=d*Q_a>vWpDjBD+y19dQJzGA!kiJI-w`?J~Q}GB5Q~_KYGe|JAY}qS7tB5iXzM zE)5ekAnh;}6WI2$F&(qS(9$oTk}U<(8#{6_G4m$sGBY`oSswEkboAELn*Mxhi&ff4jTIbjnyy)!&d!#T;bJjn_={gN5zp)C_& z8lC|T^q>@YVLm-lJUJ6Q?K3~^&OG_EKUE4n)3O!*Ap#un0HC22?w~mJb1w07K^?Rv z{4+u&RGk2HBE10|T7elxVE_i86^MZm1Y+B+i`yR5FdMW)|53DVCUiwv6qPE}DH-7z z=;0rjp*u$`MHll#aWq9+v`2lEgkIDs^?*BVbVo5$M~(DBe{@NiG8Kn=#gAx2{VBb|^ zB{u0k_GDW$Hhq#|siGCgm1IknnLe=Syp#<(d%}B_X&OXBzt#w$18Y|*9MKZamRLfJ2!cu zw*#G5W@mSL`!;&9*8;7VVnzUkrg60)jQGfGHUH z+;kH_6i6BkgUv_ZMpwK9m~}Z=gH5A?|4o?q#FPb|l7Y!fh06uNnm0S#AcJowgjv^x zJ9s=#c!yo@N?CX&MnQM8!zY6nT!-R^j|E?C7)6jcnVL97p16*p*wuKriW~2|>T_KM zBHx(xP+DOW%xP7lSW1C70HSq?XM&7p;)~T)jL)fki>h0v7%6V{i<#kTx>bm^_>2Rf ziyOj^9abRlR}WbBDBRedtT>U?ZbatTB(#GVibW^jDAvG1f;vOr#Yypc-gX6Rf6F_AXvZ*jOv(I6_9nylmp-d z#-N*pnLAXvn{lF}73`w}Af%C^2}+vDDtf1BGLB%HS*Gb(M*yX5f?!-$qC?~cj+v1e zqKIwcs8IyF7y+qeqNTZGsyD`;+8JDPStovZV}zQhjY6qKLVBHrgl-J8ZU@t}Z)unQ|r=VGts_mM0t8ta`K;?9^)F6Fwmo1Y!rhGPJ{YdkLnp z9j&xY8zoEaCNv=cK0yCO&~7mb)EO3{(Vy5h}qNh5G}K z`-ty)fQ?&E!&z_*Z76n|wOQ}P93lo^ySmkPyf)h*x?9e^dRf}rSZupB-}|!S+k?+? zwybw(wmb<}gBD|}bzzXm5Kp_DB8@LHvdlTH; z5PZXV>(DCUx5Hb%|7}9G4Pv>6FTeys6h^PY0{|0PJOBio^nm-kGu-=LY`uZ`l$oUm z3Rxy}9L74_uss}fzPlinTfZH=As~FY8Jxj2K@ti?{q!w+(`|Njw0=`^irn$!%P=3c?cr+{&@s!eem8U#QZ*-1s=Ak+ZuX z+<=Z`HIs{aM)+DqE_#)JxzvFi&Kcse=iJp_S=CJ<$YbILTAe1Qn3BJ`n@>HHOUX!< zn1OHhrwfg@{~aR99o)nz{mGp?05pNMA^Wx{rp04$`(!-JyBx+bJk<3MuH*o$jxt=o z0U2f~SQVSCh#679AqJlHpoVc{|kkt-_U>(TAn)y?uCnD8xXB z(;Tr8TIRDOnNZ1 znV{djD1JSCW<;)mOV)G3=11h#rHrgsF4ui3l2(kuWZVJ3cC=oy0O3*ym5Q@~k# z>1Es#{{mdblitFe{^>1vmQeboSq!K9c)SQ-&nI~0$tGI$pc0Z!B2M5aVt||_ZOvGr z?Hhu%lYHlUUc85X=T(%u3c|l7Ui2zl@L;^fL*MVA{!nb`$@XVy6!`t3VS1Tj4FpsGMS6g!dtc7e4D^;f?y3F6m7KR1 zUbP`y0gdh3F#*6i{oCoD@akUNL7nwmkIvzhVD#ARYyGEuGS*-p=VAFOE_>OZLm~j; z8@K=j3mQBaz#GDa3>!LpxbRy5fe$jwz3T>;eP|D0ZW zfg(v<30{a{h8k|jVTT@mcpQD^2_<5QCNkt%XD1%W)QT>?=pKnMst00?ZRJ-`Ze(Ru z)m6V)^;=mG*;u1o5#n`_U&#d^2$4=c31yU0PDy2UGhT^hmRf4Y-j-f|31*noRY_(? zfE47FSzhfJAXr=h_fSV=+80-Hd`;*{L3=UB;+%f|322~#4r-s6h8~J&iCrcdTM-GV z=NY4trbMWd0mOAyfCT<1)>pZ8TA!SiS|lirGJ zuA(ifYp=fkYEi8n26@$+W$hMZR?21?XP?4Swh*clD%a|>)?SNkw%T$B|7^FoM(XWB zyoO6|xsq;+V2Bc z!!W-{vw1n++>^`21%jzTW?4*?Rmk=@R*&*lo3hVq83J_x|1xr>(^g-Nb=HOQtXV6g zFcSa>VmveoK_;oRvub6J5P*!Er46jjH^c3c*F2T|sKR)k&GlefTgIcZ$nrS2r$Xxn zr-6Sj&UoXFKmOg!rNJG|P;|c(65VcR?$PDF$_?=daBSX_ijGz;|9JqY_q`D0km(z* zy6@)JsVgyM-d(!^>C^W`{ zbESCn-hU5%_*p;i9`c{@ViDftgQooY0gMuZOe#VIziH+##LL+3b1A-ChDRvo+pMpl%2)E z;wA#p3n77Eq7|=*#Vq2-ec)ojPgF2Egme&XJsD$YZU6$<|9tRJ8w}nU0x$v#9t3%< zXo5rD7)R24BZ*2Bj~10Oh}%W+XxHnHx>%8iF(v3sURZ+_2;#_sSm=+GtfVE;r9^PK z4J9Eg4JSv#N7l@*Ty5;z8ada;UtN+TK&wOm2?sCWY|0S{L{MC^P?0(KL6NKMr7wRu zr&Nl_8=(}YiArhAWIhj=8bOn?*rll_2F_j=3cwXS^2h)Xf`!cNrZ>L{9%YI%mB<{Y zIV1K>L5Qq3Ig;iIxv3rfFeH+Z3>7%Upe~;P;ce)aDv-ZhCJfPdMZ?( z3~i`GZ4=Ol^6Z}yt>~@rc`2Y7#1X83L?s9^$!bEx{|fblo0x!kX=%Bt&EZ?Ti+@ovnD7c**r*BNm|z`%5|@N zwc>Q(3fSb~G_ZuNYF{Ha1+ShIuWL{&Sm$}y#)9;)l&!1+gI3tgvT?AQ?QC2vtDS;0 zHnLoOENKmbSFfrzsi1AGYjx+@*n-KevaKy?Uc1kNbXB*KMF?XLQqtY_0k_00uGRi( z+vL88w#sepP>f3)(Pne21gWcCdy5cyiWR!t|LtzUQqoE1iZ?UN9j|#u=v}~-Hlfya zh$HX%Sm_ev59zJ1eT8-2{I;jO`t5H+?mHQHjGJQ{MC8#_N3P>$V`h2i!ehDf>yN)>s(*ioIx#!7Gg6Ef{3~_>l9B+{h4U@^_n2RUdbt0 zQ*4Hq*r%mxh!%!tOkEoVQ?RbHm}6Ys-tHEqywtY2a}6+litHN`$O16TCjt@V10v{S zhzQ&r45{TmEv0U_<8rW{7^G|6LGpU_;@n>G?tgq4J!7;~T)B2Z;t!g{Bik>QtX$ z$ulBfjz3b*>+LFs?F{3y%U#pvMD83v;*WUioFVRz1CtoC^VAq(0{d=mz|JZ)Sr1cXg!D)=zDT= zP23gp`|HFd`O}FIoFJCK_T&$ugLxce0#%^-G!l9HiX>my!>__RbWRL{EWaSP??z5` zVh}21pCEQm{whK^@$Lse1YUr=5fm;F5j5oXNmnJ&Wgh_A-^BI?iOayzzXBJ>%b(Lf)Xa$!RVyZ`jB4!Z(gH~uqE@J|8qQHbyP5g)i#UZP%4KI4Y3DtH<1O}mwN?Kc6z9X{fA3GxJ`&C zcY+WXF9=Blwp-!mSOpjLWeGSNtF~M#lL2){v1rzXnaX5B0^L_#sir^@Y2SJVvsgVbf zj_at3To-llczM8}e*SP1F&Q$^kd8Ppe)Hgs@5YHRS&qeajyZvVP!|mjxK0fCZiqmY zB7=c3RSO_!{}B3jgxG)*!iNwRCy?iOQ*wtXU{;OPcv{w|0S0kv7%_v}NS7ogdxU^G zb;xWBQG_OlBC1%3X2^V^mIciag#@v4MlcUnNC^M<5ONTZ1QC6Tse#a-FOf+QVu=v+ z@RGoAd7m~BLm5*7Q3Vr#5+-2@1F@7B$d17GeJM#0iNuuYMi8W!d(WmNiI^oSmzM@H zg&>u6ei;#f8B--#5ZLycp*R6P6)8e-jIOs3RvAbJ;foFTio}R{E?0Rw$&Mm(2*9vT zhX8rN&~CLr4?HmtA}|kBr*H!Sk(DC?FWG-9hnurCcLAU;YMEV&^>zUudN76s0F#Y% zNuW_y|1Nen5EI}EouG~eaeJ@>ej2a^C?OG=DVEJPY&w7q(}|hGrjVTA5X2{z!lwh% z00<0t5S$QdBkF5+Xn-0p0Wd0#;NXA^7Y#pWo-vVp8XyAya0tOk5PevVQRi=4u$*4^ zQxi~r;*xv(hA6suB?#Jo3)-O6CsR=e5ivTU;|B~-h&m`q1t>wD{})r1fPMx+b@KoY zmQWBFh!A1Qj<@$$BvGc76P9>*d3Rb6I)FOgSr9HapIe7qhJd68h^7mnaM93*D2aKn zQ=ea=dT2F`CZ%A$wFWGR5X(5A1c?$ug1!1a#fvN=Yo)DLk zo5vG601jqZo0%C6Uk(q7dfS1RcFQEm%u$`*8O%s3(4j6}F ziGT?!eHD8UvgxE3g@T4~1xHm^Z8>ZGxdy3tRh`PRJa(5!Ig%~$Q~BB|cqo`1Q8~Zn zfX^8Kw)t`suuEs+kC7>A@}_g51~ys%4G3|HK>I!(SP-qS5W@F^U^zTd858;;|Cd@| zgeEx<(V%^4Ky^8Ba?8h&JmG-5hXbLn?zd|SlXU{C6ET4ZG;0VpTY@-iiGUfH z>BzM-8USIpI)!?-{U@wZM~a&1Ypa7g5`hxlShJXir2#;-6q^&m2M$KMa@Hz+xa6JX z_zkVMdzqQ0!zw`#nX!YnO^@m&H3&PRwOak@XG=B*iZhojtGm#3pqWS=EZ2IKOAwqe zrhdz)G1Y9IAqPJfd`tTg6EF{A*$)Vj2fi8rmMLw&DuxSDj--f&Qg{$XaCtws3ek{w zC<(qukb%5;6NC6`R4{Q0=Wl&WzuE^5S^$w+=eiPeuD|kERZxeA0K5p1{}aT!c^2!g z^h>#0aE!q_c9D_?hKadFNf2J}2M_6|699z{jCoXf5X@@92VH8LFxqt z8>E%9xWlS&>d9;qtfgDe)MQ_x~g+0ItYO`4Y-ti{s497X{94c!a=yi z{U-wDx_sc^Q)s1mxP%1#rvX@Sw*&F81YyG!Y&POL5agP}h61HoV#I`i#AsEXInl%q z3l321pdZY~fjJSa*K;VLrIDhZttSFnu$5Q}$^Tb5cszD=7^CAC|1us-!s=%bsMD}N zIiCa}%NmIiak#iK%(w*Xz)(j@HE0NlJB{MjSk$FhBqf)dI>gVMUJvtf=^B-a*EZQY z5v5EI5ht_6sBj4A4$y!N&9H^tFc003kHIEtV%ZE!TRDa)Y|eWS@GNTe%zTEqo`buw z+-r(22M$}g6W?HpV(Ow9co5NG6EE@4E_xF;fzXC3!dKgVQPscRGR>+}%|J1B*!&IJ z%*{rK5Z$agQQUA_cN1lY1-0N0|17u$5fi|G&_jB8JW60R+879SN3y2%Udb$GB!y`L3e8t-|6komo0{P^z>1g)(GB^w zbMp{*Q~(Y77?7Ij)exbVGi!#?@UcRN6N&r~a;-3n3?tqN8XwD%5`?e?F_dtc8CoC* zdMFVZ3L4n=5eBW#28d4Eh*=3`0enVhwVOM+i`ALkSKY{fe?1zmx^u%OYWippq#Xe6 z5Ti!G3}XG&rwtL9>6gpcl63u6c>N;M>Dlnno3=R=a%vDHZMoy|a4|ZF5JRB61!?hh zjRZjmE34Vh&0Xd)(e6Rk2ngEToD~s9Cjo(Z4RM*XX4UyfM5p2QM{|!q0RNk#hwAR*ckX5a@tRV~{m|5Gh`ZQm45;+7Ry7rqerhT-rX62R&p?%Lk{Ip{!KO& z*2C0dF0SOtg%NK{<5KSBU*118;=&)7f1Wr|Jj_7RF=#_5if1^|M zQ0A@ykdT+=YQE=_u3uVv>7{P!EVJL64jZ2i|LTV>>O)3Ur!MQWzAas9Q{O-jgZ}Ck zQKL&?>$^^6q(O?RqWMn?XbcL@>x>=DeWS` z3C?^J+1~9e<_EEU?dOi}WYXma0~q6hDU}rDOR?_hF7G`2U-XXe`5w*VQHJxb?|UT@ z|1R(YU*U%kyRJd-0Ok=1&+rXzR#)m8dEoF_JtPxv@fYt@UHO6#plI_u&-0X|8a*G~)%JHm1N232 z^f4qF({c3PmGVpP^iLn?prPncPxV!A|MgFg5Tao6TYvI@5di&u^xy@B8`9`<^A~!4Gi1Z~V|r{F*iV$qzlp@BBc{ z{Digq(cd@EZ~f0q{k^|x*$-FO@BO;V{oSwn;m=RsZ~mN0{**rc=|4{A@BVbj{;a?I z@n2EzZ~rt%|4=pk`9I9}4-kh0{|+Qr(BMIY2^B76*wEoah!G`Dq*&47MT{9WZsgd} z<42GoMUEs%GQdfcDOIjy+0x}pm@#F}q*>GEO`JJ(?&R6i=TD$3AqFK{)aX&9NtG^T z+Eiyrs8OX(rCQbMRjgUHZspq5>sPR0#f~Lg)}vFjY1OV}+tw}Hp>O5RrCZnTU8H99 z?&aIp?_a=y1rH`%*zjS*VDTFZQQwa@8;dx_wU)kg%6J{nfP(!$rt}--rV_f=+UK5r*2yL zb?n*I8rR<4`}dvI#g8Xn|K5CPKz>%r85H3Bd-x5(xBUig{(Z;X@$XkzpTF|>0u*pS z0t?g#rYlwo#61HWlW_% z#96nW*xKxX;nMpKq~=JEuY3q(*2i8arb@+c%-n1e?7U7?F!i)N&gj&A5p4GBOC znX*91k38Pw(rB!YCOT`bhgAA&uy?D=*p#xU;w3Kvs6y%hvVgkFuH#n9Wx4B)DQvv+ zCe3D)ujO;4|FqjayCtay9~>no?N%Hpx)*mGalIp#Tr(xd?lYyeSu(qAln&p#aH-Q) z_{hggr)_J~Q+H`{)>{|MX{PzyLT$Eb@BE~~akss5whu>Ab>LxYJova>H~x6DVvW7d zLUcd7cg|sY{&3C@kF`kRvsY<&?XgDwd+@um3umC!c0PLQ2}f!3-D~&z#qPW3K7OIY zxBq^sD5g^-_D{-O@b!7laJpN`{|@m!5z#Mu^Hbo<@b^Fnh9m%2NYGsB#Iydnq<y3~xBN43^DvPNd@M$^yHSXaW)eNChOisKqa~XF+5sV(N%k z#)c(PjcYUry~b3z06wvb{2O5PZupY1V38)N&;%`Bs2DUp?u>!t7aI-9M!I<6kBQ@5 z0i(wf5601syUPp@v7!@AphSdUQA?=^nW{m4vR#KHB@&m&umaW&dQoho4P%%?^c8M! zUV)=dwkQxwT1AwK3*|1sRZ3t+kS-O3AtZC>K34jVZT*|xBncRRpS=nwnargweJQtI zUej5@K4FUGTrU6hABRU!|SP?M9|>{>SY z|9Px#1~hy(E9Uc*X+8p~^O+uuE|k@oi+{bR_$rN5?xF z6o)Ql=L1Absb6C_WZK#Yi=TVKSRRI=Dv|Cr#D zuP52;JU?p^(~4}c!Ie{LjVm)j2o)`xWaJ!8>dMznQK_<}Eo@QZGu!eOCHQO!cP(ny zE%LUr2h2)fH^GRLl(vwgB`&;<`(BfQ2`!66UW?U8!t!vb(j$-(m)ZhK!c3iJk`2oI|ed_AnOk}Bm7WWwBi(RoMyQWCNz zPVscDCKZ_!xVMnlD`#uG!sPx4RO3{`MnTYsDzIbrK$p|A0*>Y4e-Y zYZDamD9AZtR}liB2NqQ6$8Op4N?1UH3}>%R-#CF&+Fa<2DOu74n*)vJ7T~LCxw==T zGN-HTOrutz5K$Hh80{M4GE=p#DH*exL6S9gfx|x>j&)Ad3+cPy8YjB;*Oa=!VV+cY z*LKl$a*9pbW4C6~%Pv=P=kU;=|4s?HxcvR{Xyqka zc1-xEYku(R!4f5Z7~RDua?di0d&X0(I3!F_Zsq9PlPg)eOYGx@rYl9(;PF?Zf8xog zuV&tkk&?*bednG$J?lQ{x*K7B_L%#m;dF}CbT?STD!yI6a&Kk-$e>k{ggCbry(_qj zo(e)By(WP5P7k7y>=TS(<%?Uq@KVkgcgHUpV4um5SCaU~(;MGLL3`(e^`;r+gw!OS zWWf<`bJRmt=b#%gru|eC15;e+C*dw3e9n>VM4l%cZ;9?-!idcuCD6gob?*hWb;p1} z1y{;^<&8r8n6#o9El~j+p#Le)XMa|sQ=X+`0*KKMysgJf|NZH4zTd-Df#N@fY8-nW$7(lte z#9rw`KNJqjGdn>nMN@pW4NRz4dM-q4MH9;dFOwiv%dGgLDWapO7ZbwLNi_O7!BBjQ zG--@e97bZ?2spaH*E&S=TgFL}!iVF*0Prp{ONmF!MO}29Z>WSw3C7a`#b1<^Vhl%d z{JBD`qUo9}EYq|sqpT>L3PVW6I?N}YNCnp;K_TJ9qhJODyhqgV#c#BbaSTX-R0w!< zB>*BnwqnP1OvekvteTptD!j!zj73HiMV7!vB8wNKSQB2Hi*p-=vC|QMG#Yv70XYi* zG1y0C8Az6F$wPpn&-t`*O2gPnsX zI>U*jOpzP7fwX9|0BF2p`v#Mv442HxfF#6LQoYzJ$C|uJ70Uwo(j~6cE}E#glL$Pa z1Tu_l3{KQXOnJ+o7)iBAHmV#Bqf`q{n988QOO*fzZd?wnEKFjo9GhH78VZV>ED4y1 zL^-^x0HDb1dJL=6i5|Fv!y}52Y>ZX#hK^*5%-qbCOv+vQHK*9WATdnVJjGZ+Na{ML z$ifL}!-_x{uxIqUDzv!9tjOaK1EnCj$3O;f(8ez4I1U5O${2+hXp4iH@AG5oiTo zyA@fxl=zg89=HK|*gvSV&Y6(Tzf_IROcDFciT%`$>}*goOe}@VvQU#F3!J-ITF(H8 zKi!lkJk&u9MGd-S36r~(!F&v(z6_T#6fY3Kk6j z7xhb?2o$>*1rR6#J@_*LJq`y=(iLnX_KBpJq@D>ivG6<`cGAL*Fa+3gvV#)~e>$ z(@i~z1^tN{{nL~XuBxv;h1FQrHs8; zED2edMXjB5C@L!1qpDDA|HPPXQPCXz(^yTTmL;T~RhO78TCt*?wMp9E(K(uIEBne> z8*$lPNmZ$JIitPWfO?6VW2+2mD2Yv3ZTVS#qS~sx4y--ftZfN&G}vdosoxQl9%wna z*s+!MR{0=FjY3dc8^2Xrg{rQwblYkj5FL3mH1#v=@HmS_3&ah88z@qzs9T~S z+pa^Np4MUhn&><>b>=mX1 z;@srzUca4G=2e{l|KvLUJ3inI7EUgfo#!c8D`fw1B5my={wKWzz39Dzo2-I91b8VI&40_plj)5Fr_!;n|gm)}`I^jbbEAVl2*L(6VB1tYR)E5iJg5G2VzT z{=$7N<2e~)HD2S0K;uFIVK>ecHlAZT7L_NybV@mI@{DgBNCrvb^MQtmG@9WKeDyPu^mJ)q=C} zg1}LU#wz7V9_2$xWLWlJSN4d4RGYI&8AJ_|@Bansq`6 zEl}l`z%FB6L1L!jK%QnWj%JHMW|mN8s0o~IPMc;{<^bqrY(_w9&Jj^Q=jAD9C7PX? z=;o7v=cgIx!101_hT#QfXOL276-j4)4pe<^2r?Mwd0ypa?qzzGXN4YTRF3C$vgaze zyMTtdf0os1uIN3U=!Ga~R0d~)E@+P?=*9|ZaMtCAR-0AEJB^k!jK1cJZfX2j>4eCG zv*~DY|1Riw{^*7FW}SX$n+|7GhGv;gGMEO)VJ>Q|8|s4iXl_owcs6LLX6C4NXsV`X zwIKt_RO+xoY6uWsv0)9RBT>$#q1kw)ve zE^EC;347-2hF0ai=C`#*Ex6{7w=Qf!Q|gqUYmu(&k+y4+hHS}xY|0KCi5~33N)@L; z1ZLLA%9sFbxRk2tgz`m|!@i7}g$zjBY?zVjw5e%lu4{(&Yue81+MaFO&h4ibY|I8K zT;c3*n1Cz*23->Y5%7mDE$x&L0c?1SHbKp#=mzz5>!ZdF=8os3+g?0{i3wQWw%7vR z|GNp!Mv3OO2_kNZ>rTO;?wpQBXr|t4-41Nro^Sc?YiCB~R?g?%9w<}^gb5%7aF{$J z*h`wg1`QZ)2a8s4z=rZ>3U+}VdGUh@a0TM7iKd$dnQ(`cfL&wVXdzkf=H@U#_-_NW zk1D9}wAkgE@NWP=2@#kFoQT$)3xM$sOa_nTm0s-nhHv`j@g2Wy`F@($=I#9!s8qS- z?T!h0FmBbP3}8?RZ*XVmRtgN)@FWM1(`F3g_J=8H9z%HY>l5xPSBmpai6&}tr3h~` zS?{#w>-hF@I0y2*zHhVE?;=;?Ea`4n(7h(=ZcU=`sT6J$w}i2sAp^i@D}#5sxXZg6})^p==_L3naX*MI=52Y>Dro3w|M54Mb9mQnA!mup zzVmLP5~nc&Ey#j%$DL%KR1z2PXh@qcho*IB8%qZv7q?-54*-9N2I}E-mB0q0)Pgw( z02C*uf6w>E;yokqf|_QNDH-S~5rMVocT%VE;M)Q@sDgO2a`k>`2x%-W|4{K8pY-v5 z;rbZp01$S+oPa49_NU?D1u=Q*F?pz&fKhjE5diaPM+tWh0C#?Jq(Ah@J9EOD2SZ@> zl%Rs7mw4HpfC#G`es}nKg<(sW?f_`;LIC+@FOik5n#C4!_jU?yo^AQI_p}uf&Mt&n z0EbmriCU2OzF&!f7W(x)yjtM)CkF;YSN4+_e3U>0V9)^#00wEFvls7s;%4}iko=RV zad@1aXs88N-%HSTaSa%JU~uPX0Fz;lhQOcrm1u@%nDw1dc3j7h>JE-e3GI`B@lFl> z7X$}H&~#uRcM9jY30UQ`X_Lhl{82}Q!5?q?5OaFi`a$@6&_nZE|Iq@luLZ)Reqi8i zEx3LFpYBWf28o}N=QkhBk9>?j8$^f(o==HY=*uY)eyE8sfB+X@Ai;tL46cylV%s#dQKoIohrz$yh_9t@;a-~@!q~IoL^tr1v}tUyf(}g;r0CM7PahH-U|uv4BIX5%4EXKa0Koh}|BeiKLY7(r3u%r0wRYCT z0Spr)3~chnBhkVa@e6w zg>=`JE1d*DMM3QWLeOLd1wv4J&%rfNEdx<;7cjid7#ng0UAG!ImJpQ3iY40E9*D;^ zl-d@bP2eCnWW;#T1g>lm+(4Usg^Y(=a)}UWU4m)aUY2lm*F2cL)+1~K9Z6dNMm)#X zTPAV^Cz}X0NzgZJ^`@4dZS|(z7QoQMNr?lhIMZ97|B<2HU3YzR$XMD9MBIrQY!MkE zXn|7~IGp4uS$nua*2xxxY_ZC9uMMO_TUEdqrmR+JSgWnK;)-ErJTo2m~Y>z}vQ+cP^!g zidf5IHGXqr#S7VM@RgqjfTieWC(JPE9`-!4p_I?lV?JDo(6d%?wb#~(I`Ia~yWli~ zqzD?+@>w)HAE{myy0`1#Kr15(FPxJ-hnDf|+L&2fFV(h1tkICSwv6;>2X1QK8l+}k zmY#0**z2?3zELpsdbRSt`b717!|nwCKt8$OzyJROFo5k#9{~|^q$)V*5a5W0dJHld zPTYuGwXxQE$OEptY#}}aX<&#rSgE$G|7&wz_`~m})fUWnkYDj4)LohspS~;!WZO}m zZO|70LLfvZ1M`Xw)`XR5T*glYM9qeVh@jp*Br-g-z;XPs9oUeoG2n39yt)F6=VS7V2lW|V1(`LD|fnyMr2l|LF{P22)-&v7;QI@Rg}dQL*iHf_5v2P zyzoycL{g3@Q>Fk+jxyvro)H@+z(+#ze+~3XfG8=Tu4JZu!7`22DrvR-@k9%Nh$JaX zY06Wg5-E*bC1S(_nFiu$GR)J$a#CXiY{<)wP@8}Tpz{q5_N9d)*vdiX!9fvdp%%{( z2rPTUl?LWzUj!MHSvXj`6Bz_c|JSr#6{Hy&go$jG^(mq_OM^s+)TWmc!&YLT8J!Qd zrI#Q=%R2SeDh*fy9O(1rKrC?`f!K*gM#Co56f!yH`G_FESe-6yvxSM3QHBG$i+Xs8 zP(%*IhnOs9v{LEOkK*bpyb@3)3rdrnY!Wp1lbY5J0@9blG^R2QAV-IIE^0-?QbC&$ zTF{cyoW5mUAM;*8qESdaeFGcOxXX1ggM|S2qk18#R5Ws0khkb2FF-noZh!%dTC9yy z0s#g#*5nQ>Iq`GYz(zBmA)jd^vZgmNom{OFRGXZoHpeTgiq^K)8b+q7X%a}hR1V&0d>YrAW)hHopfi<+$!*VkjrxFCLSFEe7 zX6oA4J|#h5DUc=Smr0Zst16wuT3@oZHP`|-xWXOogjkC-@geOqQ#A;b4yD4-kb#Dn zTAWSJn$7D50Aa>;BsobpP|+yWCR%9jLp~GEk#%WIV!hT)zS|J@I`B9jn}AQ0yWK7k zH^2HN$sqN&HTjuTlerbI{#d#{P|73^hS)EI8|>f*hvdEr`^z8%0$;OvC9a~0VqFL~ z6%A{bAl5yN=OpY9x|RT4K%&1*i1cYj>L8+?3(HkGCZ6$@AZ%j?=K+By&E$X&LO=lqb}_T;~!?hB<$ZR%4aE7Gc7bwWy98hUs` z#ac#mprz?#R^vL?4?#7r-4_6_c;(Vk0~V(>ZS0gLq|PhVNl6#h>t{nd+V;^kwX0nf zR|lHbfrjjL9Bu7y=UUq0W?E%}#USXgnLzkkcbnTiH8|tBAfJpkfHye4apODR`X&ju z`|a;)Yg@~;vGTtQuK((NBm7o<>=kcEdf?gm*1+t>G=Vye?)+IA*)B~u$2;C}gM&O; zgL;JhcG+qpzJ%nJKb!C6rS^{Uwx%B@A}t^X7ZHpGVE*5UDo41 z!tx0{=||1?ssBB^=cCVB?u&1H_Sru940$|#;Og?XL)wPQpZ=MTZ~coPi1(x4e(86- zHQILH3)de%`JYAo^PgY#5?(a=+uxz`m%nP<_x

    9(bA?8UFg;e}>%e|1G)0H$)zm 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

    g=a_ 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 ck#N`deW!A7 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�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

    kT?M0~?O_ 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

    8-V*=?1m@EXghhmGkn7H=yM74n~X*-?Um%9-`+ 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%0DMbU==`G(xLePN@zg(@KqGQpJ(ZccZa3_ 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�C4HFa2eclnQ(Ah0Jy)1EQfn; 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κe-^O%k$ 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

    6>3hHcb^TQ*Z)GA3>EVPN*x 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"] -version = "0.1.0" -edition = "2021" - -[dependencies] -codec = { package = "parity-scale-codec", workspace = true } -log = { workspace = true } -sp-std = { workspace = true } - -sgx_tstd = { workspace = true, features = ["net", "thread"], optional = true } - -thiserror = { workspace = true, optional = true } -thiserror_sgx = { workspace = true, optional = true } - -itp-settings = { workspace = true } -itp-sgx-io = { workspace = true } -litentry-primitives = { workspace = true } - -[dev-dependencies] -base64 = { workspace = true, features = ["alloc"] } - -[features] -default = ["std"] -production = [] -sgx = [ - "sgx_tstd", - "thiserror_sgx", - "itp-sgx-io/sgx", - "litentry-primitives/sgx", -] -std = [ - "sp-std/std", - "log/std", - "thiserror", - "itp-sgx-io/std", - "litentry-primitives/std", -] diff --git a/tee-worker/bitacross/bitacross/core/bc-enclave-registry/src/lib.rs b/tee-worker/bitacross/bitacross/core/bc-enclave-registry/src/lib.rs deleted file mode 100644 index b08c7b65c4..0000000000 --- a/tee-worker/bitacross/bitacross/core/bc-enclave-registry/src/lib.rs +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate core; -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -use sp_std::{boxed::Box, fmt::Debug}; - -use log::error; -use std::{collections::BTreeMap, error::Error, path::PathBuf, string::String, vec::Vec}; - -#[cfg(feature = "std")] -use std::sync::RwLock; -#[cfg(feature = "sgx")] -use std::sync::SgxRwLock as RwLock; - -pub type EnclaveRegistryMap = BTreeMap; - -#[derive(Default)] -pub struct EnclaveRegistry { - pub registry: RwLock, - pub seal_path: PathBuf, -} - -impl EnclaveRegistry { - pub fn new(base_dir: PathBuf) -> Self { - EnclaveRegistry { registry: Default::default(), seal_path: base_dir } - } -} - -pub type RegistryResult = Result; - -use litentry_primitives::Address32; -#[cfg(feature = "sgx")] -use thiserror_sgx as thiserror; - -#[derive(Debug, thiserror::Error)] -pub enum RegistryError { - #[error("poison lock")] - PoisonLock, - #[error("empty Enclave registry")] - EmptyRegistry, - #[error(transparent)] - Other(#[from] Box), -} - -impl From for RegistryError { - fn from(e: std::io::Error) -> Self { - Self::Other(e.into()) - } -} - -impl From for RegistryError { - #[cfg(feature = "std")] - fn from(e: codec::Error) -> Self { - Self::Other(e.into()) - } - - #[cfg(feature = "sgx")] - fn from(e: codec::Error) -> Self { - Self::Other(std::format!("{:?}", e).into()) - } -} - -#[cfg(feature = "sgx")] -mod sgx { - use crate::{EnclaveRegistryMap, RegistryError as Error, RegistryResult as Result}; - pub use codec::{Decode, Encode}; - pub use itp_settings::files::ENCLAVE_REGISTRY_FILE; - pub use itp_sgx_io::{seal, unseal, SealedIO}; - pub use log::*; - pub use std::{boxed::Box, fs, path::PathBuf, sgxfs::SgxFile, sync::Arc}; - - #[derive(Clone, Debug)] - pub struct EnclaveRegistrySeal { - base_path: PathBuf, - } - - impl EnclaveRegistrySeal { - pub fn new(base_path: PathBuf) -> Self { - Self { base_path } - } - - pub fn path(&self) -> PathBuf { - self.base_path.join(ENCLAVE_REGISTRY_FILE) - } - } - - impl SealedIO for EnclaveRegistrySeal { - type Error = Error; - type Unsealed = EnclaveRegistryMap; - - fn unseal(&self) -> Result { - Ok(unseal(self.path()).map(|b| Decode::decode(&mut b.as_slice()))??) - } - - fn seal(&self, unsealed: &Self::Unsealed) -> Result<()> { - info!("Seal enclave registry to file: {:?}", unsealed); - Ok(unsealed.using_encoded(|bytes| seal(bytes, self.path()))?) - } - } -} - -#[cfg(feature = "sgx")] -use sgx::*; - -pub trait EnclaveRegistrySealer { - fn seal(&self, state: EnclaveRegistryMap) -> RegistryResult<()>; - fn unseal(&self) -> RegistryResult; -} - -pub trait EnclaveRegistryUpdater { - fn init(&self) -> RegistryResult<()>; - fn update(&self, account: Address32, worker_url: String) -> RegistryResult<()>; - fn remove(&self, account: Address32) -> RegistryResult<()>; -} - -pub trait EnclaveRegistryLookup { - fn contains_key(&self, account: &Address32) -> bool; - fn get_all(&self) -> Vec<(Address32, String)>; - fn get_worker_url(&self, account: &Address32) -> Option; -} - -impl EnclaveRegistrySealer for EnclaveRegistry { - #[cfg(feature = "std")] - fn seal(&self, _state: EnclaveRegistryMap) -> RegistryResult<()> { - Ok(()) - } - - #[cfg(feature = "std")] - fn unseal(&self) -> RegistryResult { - Ok(Default::default()) - } - - #[cfg(feature = "sgx")] - fn seal(&self, mut state: EnclaveRegistryMap) -> RegistryResult<()> { - let mut registry = self.registry.write().map_err(|_| RegistryError::PoisonLock)?; - while let Some((key, val)) = state.pop_first() { - registry.insert(key, val); - } - - let enclave_seal = EnclaveRegistrySeal::new(self.seal_path.clone()); - enclave_seal.seal(®istry) - } - - #[cfg(feature = "sgx")] - fn unseal(&self) -> RegistryResult { - let enclave_seal = EnclaveRegistrySeal::new(self.seal_path.clone()); - enclave_seal.unseal() - } -} - -impl EnclaveRegistryUpdater for EnclaveRegistry { - #[cfg(feature = "std")] - fn init(&self) -> RegistryResult<()> { - Ok(()) - } - - #[cfg(feature = "std")] - fn update(&self, account: Address32, worker_url: String) -> RegistryResult<()> { - let mut registry = self.registry.write().unwrap(); - registry.insert(account, worker_url); - Ok(()) - } - - #[cfg(feature = "std")] - fn remove(&self, _account: Address32) -> RegistryResult<()> { - Ok(()) - } - - // if `ENCLAVE_REGISTRY_FILE` exists, unseal and init from it - // otherwise create a new instance and seal to static file - #[cfg(feature = "sgx")] - fn init(&self) -> RegistryResult<()> { - let enclave_seal = EnclaveRegistrySeal::new(self.seal_path.clone()); - if SgxFile::open(ENCLAVE_REGISTRY_FILE).is_err() { - info!( - "[Enclave] EnclaveRegistry file not found, creating new! {}", - ENCLAVE_REGISTRY_FILE - ); - let registry = self.registry.write().map_err(|_| RegistryError::PoisonLock)?; - enclave_seal.seal(&*registry) - } else { - let m = enclave_seal.unseal()?; - info!("[Enclave] EnclaveRegistry unsealed from file: {:?}", m); - let mut registry = self.registry.write().map_err(|_| RegistryError::PoisonLock)?; - *registry = m; - Ok(()) - } - } - - #[cfg(feature = "sgx")] - fn update(&self, account: Address32, worker_url: String) -> RegistryResult<()> { - let mut registry = self.registry.write().map_err(|_| RegistryError::PoisonLock)?; - registry.insert(account, worker_url); - EnclaveRegistrySeal::new(self.seal_path.clone()).seal(&*registry) - } - - #[cfg(feature = "sgx")] - fn remove(&self, account: Address32) -> RegistryResult<()> { - let mut registry = self.registry.write().map_err(|_| RegistryError::PoisonLock)?; - let old_value = registry.remove(&account); - if old_value.is_some() { - return EnclaveRegistrySeal::new(self.seal_path.clone()).seal(&*registry) - } - Ok(()) - } -} - -impl EnclaveRegistryLookup for EnclaveRegistry { - fn get_all(&self) -> Vec<(Address32, String)> { - let registry = self.registry.read().unwrap(); - registry.iter().map(|(k, v)| (*k, v.clone())).collect() - } - - fn contains_key(&self, account: &Address32) -> bool { - // Using unwrap becaused poisoned locks are unrecoverable errors - let registry = self.registry.read().unwrap(); - registry.contains_key(account) - } - - fn get_worker_url(&self, account: &Address32) -> Option { - // Using unwrap becaused poisoned locks are unrecoverable errors - let registry = self.registry.read().unwrap(); - registry.get(account).cloned() - } -} diff --git a/tee-worker/bitacross/bitacross/core/bc-musig2-ceremony/Cargo.toml b/tee-worker/bitacross/bitacross/core/bc-musig2-ceremony/Cargo.toml deleted file mode 100644 index 352e059c92..0000000000 --- a/tee-worker/bitacross/bitacross/core/bc-musig2-ceremony/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -[package] -name = "bc-musig2-ceremony" -authors = ["Trust Computing GmbH "] -version = "0.1.0" -edition = "2021" - -[dependencies] -musig2 = { workspace = true, optional = true } -rand = { version = "0.8.5", optional = true } - -# sgx dependencies -musig2_sgx = { workspace = true, optional = true } -sgx_rand = { workspace = true, optional = true } -sgx_tstd = { workspace = true, features = ["net", "thread"], optional = true } - -codec = { package = "parity-scale-codec", workspace = true } -k256 = { workspace = true, features = ["ecdsa-core", "schnorr", "alloc"] } -log = { workspace = true } - -itp-sgx-crypto = { workspace = true } -litentry-primitives = { workspace = true } - -[dev-dependencies] -rand = { version = "0.8.5" } -signature = "2.1.0" - -[features] -default = ["std"] -sgx-test = ["sgx"] -std = [ - "musig2", - "log/std", - "litentry-primitives/std", - "itp-sgx-crypto/std", - "rand", -] -sgx = [ - "sgx_tstd", - "musig2_sgx", - "litentry-primitives/sgx", - "itp-sgx-crypto/sgx", - "sgx_rand", -] diff --git a/tee-worker/bitacross/bitacross/core/bc-musig2-ceremony/src/lib.rs b/tee-worker/bitacross/bitacross/core/bc-musig2-ceremony/src/lib.rs deleted file mode 100644 index f30563f526..0000000000 --- a/tee-worker/bitacross/bitacross/core/bc-musig2-ceremony/src/lib.rs +++ /dev/null @@ -1,807 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; -extern crate core; -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use musig2_sgx as musig2; -use std::{format, string::String, sync::Arc}; - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -#[cfg(feature = "std")] -use std::sync::RwLock; - -#[cfg(feature = "sgx")] -use std::sync::SgxRwLock as RwLock; - -use codec::{Decode, Encode}; -use itp_sgx_crypto::{key_repository::AccessKey, schnorr::Pair as SchnorrPair}; -use k256::SecretKey; -pub use k256::{elliptic_curve::sec1::FromEncodedPoint, PublicKey}; -use log::*; -use musig2::{ - secp::{Point, Scalar}, - verify_single, BinaryEncoding, CompactSignature, KeyAggContext, LiftedSignature, - SecNonceSpices, -}; -pub use musig2::{PartialSignature, PubNonce}; -use std::{ - collections::HashMap, - time::{SystemTime, UNIX_EPOCH}, - vec, - vec::Vec, -}; - -pub type CeremonyId = SignBitcoinPayload; -pub type SignaturePayload = Vec; -pub type Signers = Vec; -pub type CeremonyRegistry = HashMap>>, u64)>; -pub type CeremonyCommandTmp = HashMap>>, u64)>; -// enclave public key is used as signer identifier -pub type SignerId = [u8; 32]; -pub type SignersWithKeys = Vec<(SignerId, PublicKey)>; - -#[derive(Debug, Eq, PartialEq, Encode)] -pub enum CeremonyError { - CeremonyInitError(CeremonyErrorReason), - NonceReceivingError(CeremonyErrorReason), - PartialSignatureReceivingError(CeremonyErrorReason), -} - -#[derive(Debug, Eq, PartialEq, Encode)] -pub enum CeremonyErrorReason { - AlreadyExist, - CreateCeremonyError, - SignerNotFound, - ContributionError, - IncorrectRound, - RoundFinalizationError, -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum CeremonyCommand { - InitCeremony(SignersWithKeys, SignBitcoinPayload, bool), - SaveNonce(SignerId, PubNonce), - SavePartialSignature(SignerId, PartialSignature), - KillCeremony, -} - -// events are created by ceremony and executed by runner -#[derive(Debug, Eq, PartialEq)] -pub enum CeremonyEvent { - FirstRoundStarted(Signers, CeremonyId, PubNonce), - SecondRoundStarted(Signers, CeremonyId, PartialSignature), - CeremonyEnded([u8; 64], bool, bool), - CeremonyError(Signers, CeremonyError), -} - -#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, Hash)] -pub enum SignBitcoinPayload { - Derived(SignaturePayload), - TaprootUnspendable(SignaturePayload), - TaprootSpendable(SignaturePayload, [u8; 32]), - WithTweaks(SignaturePayload, Vec<([u8; 32], bool)>), -} - -pub fn generate_aggregated_public_key(mut public_keys: Vec) -> PublicKey { - public_keys.sort(); - KeyAggContext::new(public_keys).unwrap().aggregated_pubkey() -} - -pub struct MuSig2CeremonyData> { - payload: SignBitcoinPayload, - me: SignerId, - signers: SignersWithKeys, - signing_key_access: Arc, - agg_key: PublicKey, - // indicates whether it's check run - signature verification result is returned instead of signature - check_run: bool, -} - -pub struct MuSig2CeremonyState { - first_round: Option, - second_round: Option>, -} - -pub struct MuSig2Ceremony> { - ceremony_data: MuSig2CeremonyData, - ceremony_state: MuSig2CeremonyState, -} - -impl> MuSig2Ceremony { - // Creates new ceremony - pub fn new( - me: SignerId, - mut signers: SignersWithKeys, - payload: SignBitcoinPayload, - signing_key_access: Arc, - check_run: bool, - ) -> Result<(Self, CeremonyEvent), String> { - info!("Creating new ceremony {:?}", payload); - if signers.len() < 3 { - return Err(format!("Not enough signers, minimum: {:?}, actual {:?}", 3, signers.len())) - } - - signers.sort_by_key(|k| k.1); - // we are always the first key in the vector - let my_index = signers.iter().position(|r| r.0 == me).ok_or("Could not determine index")?; - let all_keys = signers.iter().map(|p| p.1).collect::>(); - let key_context = match &payload { - SignBitcoinPayload::TaprootSpendable(_, root_hash) => - KeyAggContext::new(all_keys.iter().map(|p| Point::from(*p))) - .map_err(|e| format!("Key context creation error: {:?}", e))? - .with_taproot_tweak(root_hash) - .map_err(|e| format!("Key context creation error: {:?}", e))?, - SignBitcoinPayload::TaprootUnspendable(_) => - KeyAggContext::new(all_keys.iter().map(|p| Point::from(*p))) - .map_err(|e| format!("Key context creation error: {:?}", e))? - .with_unspendable_taproot_tweak() - .map_err(|e| format!("Key context creation error: {:?}", e))?, - SignBitcoinPayload::Derived(_) => - KeyAggContext::new(all_keys.iter().map(|p| Point::from(*p))) - .map_err(|e| format!("Key context creation error: {:?}", e))?, - SignBitcoinPayload::WithTweaks(_, tweaks) => { - let mut prepared_tweaks = vec![]; - for (tweak_bytes, is_x_only) in tweaks.iter() { - let scalar: Scalar = tweak_bytes.try_into().map_err(|e| { - format!("Key context creation error, could not parse scalar: {:?}", e) - })?; - prepared_tweaks.push((scalar, *is_x_only)); - } - KeyAggContext::new(all_keys.iter().map(|p| Point::from(*p))) - .map_err(|e| format!("Key context creation error: {:?}", e))? - .with_tweaks(prepared_tweaks) - .map_err(|e| format!("Key context creation error: {:?}", e))? - }, - }; - - info!( - "Ceremony aggregated public key: {:?}", - key_context.aggregated_pubkey::().to_sec1_bytes().to_vec() - ); - let agg_key = key_context.aggregated_pubkey::(); - let nonce_seed = random_seed(); - let first_round = - musig2::FirstRound::new(key_context, nonce_seed, my_index, SecNonceSpices::new()) - .map_err(|e| format!("First round creation error: {:?}", e))?; - - let ceremony = Self { - ceremony_data: MuSig2CeremonyData { - payload, - me, - signers, - signing_key_access, - agg_key, - check_run, - }, - ceremony_state: MuSig2CeremonyState { - first_round: Some(first_round), - second_round: None, - }, - }; - let event = ceremony.start_first_round(); - Ok((ceremony, event)) - } - - fn start_first_round(&self) -> CeremonyEvent { - self.ceremony_state - .first_round - .as_ref() - .map(|f| { - CeremonyEvent::FirstRoundStarted( - self.get_signers_except_self(), - self.ceremony_data.payload.clone(), - f.our_public_nonce(), - ) - }) - .unwrap() - } - - // Saves signer's nonce - pub fn receive_nonce( - &mut self, - signer: SignerId, - nonce: PubNonce, - ) -> Result, CeremonyError> { - info!("Saving nonce from signer: {:?}", signer); - let peer_index = self - .ceremony_data - .signers - .iter() - .position(|p| p.0 == signer) - .ok_or(CeremonyError::NonceReceivingError(CeremonyErrorReason::SignerNotFound))?; - - if let Some(ref mut r) = self.ceremony_state.first_round { - r.receive_nonce(peer_index, nonce).map_err(|e| { - error!("Nonce receiving error: {:?}", e); - CeremonyError::NonceReceivingError(CeremonyErrorReason::ContributionError) - })?; - if r.is_complete() { - let secret_key = SecretKey::from_slice( - &self - .ceremony_data - .signing_key_access - .retrieve_key() - .map_err(|e| { - error!("Nonce receiving error: {:?}", e); - CeremonyError::NonceReceivingError( - CeremonyErrorReason::RoundFinalizationError, - ) - })? - .private_bytes(), - ) - .map_err(|e| { - error!("Nonce receiving error: {:?}", e); - CeremonyError::NonceReceivingError(CeremonyErrorReason::RoundFinalizationError) - })?; - self.start_second_round(secret_key).map(Some) - } else { - Ok(None) - } - } else { - Err(CeremonyError::NonceReceivingError(CeremonyErrorReason::IncorrectRound)) - } - } - - // Starts the second round - fn start_second_round( - &mut self, - private_key: SecretKey, - ) -> Result { - let first_round = self - .ceremony_state - .first_round - .take() - .ok_or(CeremonyError::NonceReceivingError(CeremonyErrorReason::IncorrectRound))?; - - let message = match &self.ceremony_data.payload { - SignBitcoinPayload::TaprootSpendable(message, _) => message.clone(), - SignBitcoinPayload::Derived(message) => message.clone(), - SignBitcoinPayload::TaprootUnspendable(message) => message.clone(), - SignBitcoinPayload::WithTweaks(message, _) => message.clone(), - }; - let second_round = first_round.finalize(private_key, message).map_err(|e| { - error!("Could not start second round: {:?}", e); - CeremonyError::NonceReceivingError(CeremonyErrorReason::RoundFinalizationError) - })?; - - let partial_signature: PartialSignature = second_round.our_signature(); - - self.ceremony_state.second_round = Some(second_round); - - Ok(CeremonyEvent::SecondRoundStarted( - self.get_signers_except_self(), - self.get_id_ref().clone(), - partial_signature, - )) - } - - // Saves signer's partial signature - pub fn receive_partial_sign( - &mut self, - signer: SignerId, - partial_signature: impl Into, - ) -> Result, CeremonyError> { - info!("Saving partial signature from signer: {:?}", signer); - let peer_index = self.ceremony_data.signers.iter().position(|p| p.0 == signer).ok_or( - CeremonyError::PartialSignatureReceivingError(CeremonyErrorReason::SignerNotFound), - )?; - - if let Some(ref mut r) = self.ceremony_state.second_round { - r.receive_signature(peer_index, partial_signature).map_err(|e| { - error!("Signature receiving error: {:?}", e); - CeremonyError::PartialSignatureReceivingError( - CeremonyErrorReason::ContributionError, - ) - })?; - if r.is_complete() { - if let Some(r) = self.ceremony_state.second_round.take() { - let signature: CompactSignature = r - .finalize::() - .map_err(|e| { - error!("Could not finish second round: {:?}", e); - CeremonyError::PartialSignatureReceivingError( - CeremonyErrorReason::RoundFinalizationError, - ) - })? - .compact(); - - info!("Ceremony {:?} `has ended`", self.get_id_ref()); - info!("Aggregated public key {:?}", self.ceremony_data.agg_key.to_sec1_bytes()); - info!("Signature {:?}", signature.to_bytes()); - - let message = match &self.ceremony_data.payload { - SignBitcoinPayload::Derived(p) => p, - SignBitcoinPayload::TaprootUnspendable(p) => p, - SignBitcoinPayload::TaprootSpendable(p, _) => p, - SignBitcoinPayload::WithTweaks(p, _) => p, - }; - - let result = - verify_single(self.ceremony_data.agg_key, signature, message).is_ok(); - Ok(Some(CeremonyEvent::CeremonyEnded( - signature.to_bytes(), - self.ceremony_data.check_run, - result, - ))) - } else { - Err(CeremonyError::PartialSignatureReceivingError( - CeremonyErrorReason::IncorrectRound, - )) - } - } else { - Ok(None) - } - } else { - Err(CeremonyError::PartialSignatureReceivingError(CeremonyErrorReason::IncorrectRound)) - } - } - - pub fn get_signers_except_self(&self) -> Signers { - self.ceremony_data - .signers - .iter() - .filter(|e| e.0 != self.ceremony_data.me) - .map(|s| s.0) - .collect() - } - - pub fn get_id_ref(&self) -> &CeremonyId { - &self.ceremony_data.payload - } - - pub fn is_first_round(&self) -> bool { - self.ceremony_state.first_round.is_some() - } -} - -pub fn get_current_timestamp() -> u64 { - SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() -} - -#[cfg(feature = "std")] -fn random_seed() -> [u8; 32] { - use rand::{thread_rng, RngCore}; - - let mut seed = [0u8; 32]; - let mut rand = thread_rng(); - rand.fill_bytes(&mut seed); - seed -} - -#[cfg(feature = "sgx")] -fn random_seed() -> [u8; 32] { - use sgx_rand::{Rng, StdRng}; - let mut seed = [0u8; 32]; - let mut rand = StdRng::new().unwrap(); - rand.fill_bytes(&mut seed); - seed -} - -#[cfg(test)] -pub mod test { - use crate::{ - CeremonyError, CeremonyErrorReason, CeremonyEvent, MuSig2Ceremony, SignBitcoinPayload, - SignerId, SignersWithKeys, - }; - use alloc::sync::Arc; - use itp_sgx_crypto::{key_repository::AccessKey, schnorr::Pair as SchnorrPair}; - use k256::{elliptic_curve::PublicKey, schnorr::SigningKey}; - use musig2::SecNonce; - - pub const MY_SIGNER_ID: SignerId = [0u8; 32]; - - fn my_priv_key() -> SigningKey { - SigningKey::from_bytes(&[ - 252, 240, 35, 85, 243, 83, 129, 54, 7, 155, 24, 114, 254, 0, 134, 251, 207, 83, 177, 9, - 92, 118, 222, 5, 202, 239, 188, 215, 132, 113, 127, 94, - ]) - .unwrap() - } - - fn signer1_priv_key() -> SigningKey { - SigningKey::from_bytes(&[ - 42, 82, 57, 169, 208, 130, 125, 141, 62, 185, 167, 41, 142, 217, 252, 135, 158, 128, - 44, 129, 222, 71, 55, 86, 230, 183, 54, 111, 152, 83, 85, 155, - ]) - .unwrap() - } - - pub const SIGNER_1_ID: SignerId = [1u8; 32]; - pub const SIGNER_1_SEC_NONCE: [u8; 64] = [ - 57, 232, 181, 133, 43, 97, 251, 79, 229, 110, 26, 121, 197, 2, 249, 237, 222, 207, 129, - 232, 8, 227, 120, 202, 127, 61, 209, 41, 92, 54, 8, 91, 80, 31, 9, 126, 14, 137, 126, 143, - 98, 223, 254, 134, 9, 190, 5, 157, 133, 254, 18, 119, 117, 25, 65, 179, 35, 130, 156, 109, - 233, 51, 18, 32, - ]; - - pub const SIGNER_2_ID: SignerId = [2u8; 32]; - - fn signer2_priv_key() -> SigningKey { - SigningKey::from_bytes(&[ - 117, 130, 176, 36, 185, 53, 187, 61, 123, 86, 24, 38, 174, 143, 129, 73, 245, 210, 127, - 148, 115, 136, 32, 98, 62, 47, 26, 196, 57, 211, 171, 185, - ]) - .unwrap() - } - - pub const SIGNER_2_SEC_NONCE: [u8; 64] = [ - 78, 229, 109, 189, 246, 169, 247, 85, 184, 199, 144, 135, 45, 60, 71, 109, 214, 121, 165, - 206, 185, 246, 120, 52, 228, 49, 155, 9, 160, 129, 171, 252, 69, 160, 122, 66, 151, 147, - 141, 118, 226, 189, 100, 94, 74, 163, 158, 245, 111, 99, 108, 202, 224, 110, 71, 106, 178, - 255, 89, 34, 16, 10, 195, 107, - ]; - - fn signers_with_keys() -> SignersWithKeys { - vec![ - (MY_SIGNER_ID, PublicKey::from(my_priv_key().verifying_key())), - (SIGNER_1_ID, PublicKey::from(signer1_priv_key().verifying_key())), - (SIGNER_2_ID, PublicKey::from(signer2_priv_key().verifying_key())), - ] - } - - pub const SAMPLE_SIGNATURE_PAYLOAD: [u8; 32] = [0u8; 32]; - - struct MockedSigningKeyAccess { - signing_key: SigningKey, - } - - impl AccessKey for MockedSigningKeyAccess { - type KeyType = SchnorrPair; - - fn retrieve_key(&self) -> itp_sgx_crypto::Result { - Ok(SchnorrPair::new(self.signing_key.clone())) - } - } - - #[test] - fn it_should_create_ceremony_in_firstround() { - // given - let signing_key_access = MockedSigningKeyAccess { signing_key: my_priv_key() }; - - // when - let result = MuSig2Ceremony::new( - MY_SIGNER_ID, - signers_with_keys(), - SignBitcoinPayload::Derived(SAMPLE_SIGNATURE_PAYLOAD.to_vec()), - Arc::new(signing_key_access), - false, - ); - - // then - assert!(result.is_ok()); - assert!(result.unwrap().0.is_first_round()) - } - - #[test] - fn it_should_prevent_from_creating_ceremony_without_sufficient_signers() { - // given - let signing_key_access = MockedSigningKeyAccess { signing_key: my_priv_key() }; - - // when - let result = MuSig2Ceremony::new( - MY_SIGNER_ID, - signers_with_keys()[0..1].to_vec(), - SignBitcoinPayload::Derived(SAMPLE_SIGNATURE_PAYLOAD.to_vec()), - Arc::new(signing_key_access), - false, - ); - - // then - assert!(result.is_err()); - } - - #[test] - fn it_should_produce_error_due_to_nonce_from_unknown_signer() { - // given - let signing_key_access = MockedSigningKeyAccess { signing_key: my_priv_key() }; - let mut ceremony = MuSig2Ceremony::new( - MY_SIGNER_ID, - signers_with_keys(), - SignBitcoinPayload::Derived(SAMPLE_SIGNATURE_PAYLOAD.to_vec()), - Arc::new(signing_key_access), - false, - ) - .unwrap() - .0; - - assert!(ceremony.ceremony_state.first_round.is_some()); - assert!(ceremony.ceremony_state.second_round.is_none()); - - let event = ceremony.receive_nonce( - [10u8; 32], - SecNonce::from_bytes(&SIGNER_2_SEC_NONCE).unwrap().public_nonce(), - ); - assert!(ceremony.ceremony_state.first_round.is_some()); - assert!(ceremony.ceremony_state.second_round.is_none()); - assert!(event.is_err()); - assert!(matches!( - event.unwrap_err(), - CeremonyError::NonceReceivingError(CeremonyErrorReason::SignerNotFound) - )); - } - - #[test] - fn it_should_complete_successfully() { - // given - let signing_key_access = MockedSigningKeyAccess { signing_key: my_priv_key() }; - let mut ceremony = MuSig2Ceremony::new( - MY_SIGNER_ID, - signers_with_keys(), - SignBitcoinPayload::Derived(SAMPLE_SIGNATURE_PAYLOAD.to_vec()), - Arc::new(signing_key_access), - false, - ) - .unwrap() - .0; - - assert!(ceremony.ceremony_state.first_round.is_some()); - assert!(ceremony.ceremony_state.second_round.is_none()); - - let event = ceremony.receive_nonce( - SIGNER_1_ID, - SecNonce::from_bytes(&SIGNER_1_SEC_NONCE).unwrap().public_nonce(), - ); - assert!(ceremony.ceremony_state.first_round.is_some()); - assert!(ceremony.ceremony_state.second_round.is_none()); - assert!(event.is_ok()); - assert!(event.unwrap().is_none()); - - let event = ceremony.receive_nonce( - SIGNER_2_ID, - SecNonce::from_bytes(&SIGNER_2_SEC_NONCE).unwrap().public_nonce(), - ); - assert!(ceremony.ceremony_state.first_round.is_none()); - assert!(ceremony.ceremony_state.second_round.is_some()); - assert!(event.is_ok()); - let event = event.unwrap(); - assert!(event.is_some()); - assert_eq!( - event.unwrap(), - CeremonyEvent::SecondRoundStarted( - vec![SIGNER_1_ID, SIGNER_2_ID], - SignBitcoinPayload::Derived(SAMPLE_SIGNATURE_PAYLOAD.to_vec()), - ceremony.ceremony_state.second_round.as_ref().unwrap().our_signature(), - ) - ); - } -} - -#[cfg(feature = "sgx-test")] -pub mod sgx_tests { - use super::*; - use crate::{ - generate_aggregated_public_key, CeremonyEvent, MuSig2Ceremony, SignBitcoinPayload, - }; - use alloc::sync::Arc; - use k256::schnorr::SigningKey; - use musig2::verify_single; - - pub const MY_SIGNER_ID: SignerId = [0u8; 32]; - pub const SIGNER_1_ID: SignerId = [1u8; 32]; - pub const SIGNER_2_ID: SignerId = [2u8; 32]; - pub const SAMPLE_SIGNATURE_PAYLOAD: [u8; 32] = [0u8; 32]; - - struct MockedSigningKeyAccess { - pub signing_key: SigningKey, - } - - impl AccessKey for MockedSigningKeyAccess { - type KeyType = SchnorrPair; - - fn retrieve_key(&self) -> itp_sgx_crypto::Result { - Ok(SchnorrPair::new(self.signing_key.clone())) - } - } - - pub fn test_full_flow_with_3_ceremonies() { - // given - let ceremony_id = SignBitcoinPayload::Derived(SAMPLE_SIGNATURE_PAYLOAD.to_vec()); - //my signer - let my_signer_key_access = MockedSigningKeyAccess { signing_key: my_priv_key() }; - let mut my_ceremony = MuSig2Ceremony::new( - MY_SIGNER_ID, - signers_with_keys(), - ceremony_id.clone(), - Arc::new(my_signer_key_access), - false, - ) - .unwrap() - .0; - // signer 1 - let signer1_key_access = MockedSigningKeyAccess { signing_key: signer1_priv_key() }; - let mut signer1_ceremony = MuSig2Ceremony::new( - SIGNER_1_ID, - signers_with_keys(), - ceremony_id.clone(), - Arc::new(signer1_key_access), - false, - ) - .unwrap() - .0; - // signer 2 - let signer2_key_access = MockedSigningKeyAccess { signing_key: signer2_priv_key() }; - let mut signer2_ceremony = MuSig2Ceremony::new( - SIGNER_2_ID, - signers_with_keys(), - ceremony_id.clone(), - Arc::new(signer2_key_access), - false, - ) - .unwrap() - .0; - - let my_ceremony_nonce = - my_ceremony.ceremony_state.first_round.as_ref().unwrap().our_public_nonce(); - let signer1_ceremony_nonce = - signer1_ceremony.ceremony_state.first_round.as_ref().unwrap().our_public_nonce(); - let signer2_ceremony_nonce = - signer2_ceremony.ceremony_state.first_round.as_ref().unwrap().our_public_nonce(); - - // my signer receive nonce - let my_ceremony_receive_first_nonce_ev = - my_ceremony.receive_nonce(SIGNER_1_ID, signer1_ceremony_nonce.clone()).unwrap(); - match my_ceremony_receive_first_nonce_ev { - None => {}, - ev => panic!("except None but get: {:?}", ev), - } - let my_ceremony_second_round_started_ev = - my_ceremony.receive_nonce(SIGNER_2_ID, signer2_ceremony_nonce.clone()).unwrap(); - let my_ceremony_partial_sign = match my_ceremony_second_round_started_ev { - Some(CeremonyEvent::SecondRoundStarted(_, _, partial_sign)) => partial_sign, - ev => panic!("except Some(CeremonyEvent::SecondRoundStarted) but get: {:?}", ev), - }; - - // signer 1 receive nonce - let signer1_ceremony_receive_first_nonce_ev = - signer1_ceremony.receive_nonce(MY_SIGNER_ID, my_ceremony_nonce.clone()).unwrap(); - match signer1_ceremony_receive_first_nonce_ev { - None => {}, - ev => panic!("except None but get: {:?}", ev), - } - let signer1_ceremony_second_round_started_ev = signer1_ceremony - .receive_nonce(SIGNER_2_ID, signer2_ceremony_nonce.clone()) - .unwrap(); - let signer1_ceremony_partial_sign = match signer1_ceremony_second_round_started_ev { - Some(CeremonyEvent::SecondRoundStarted(_, _, partial_sign)) => partial_sign, - ev => panic!("except Some(CeremonyEvent::SecondRoundStarted) but get: {:?}", ev), - }; - - // signer 2 receive nonce - let signer2_ceremony_receive_first_nonce_ev = - signer2_ceremony.receive_nonce(MY_SIGNER_ID, my_ceremony_nonce.clone()).unwrap(); - match signer2_ceremony_receive_first_nonce_ev { - None => {}, - ev => panic!("except None but get: {:?}", ev), - } - let signer2_ceremony_second_round_started_ev = signer2_ceremony - .receive_nonce(SIGNER_1_ID, signer1_ceremony_nonce.clone()) - .unwrap(); - let signer2_ceremony_partial_sign = match signer2_ceremony_second_round_started_ev { - Some(CeremonyEvent::SecondRoundStarted(_, _, partial_sign)) => partial_sign, - ev => panic!("except Some(CeremonyEvent::SecondRoundStarted) but get: {:?}", ev), - }; - - // my signer receive partial_sign - let my_ceremony_receive_first_partial_sign_ev = my_ceremony - .receive_partial_sign(SIGNER_1_ID, signer1_ceremony_partial_sign) - .unwrap(); - match my_ceremony_receive_first_partial_sign_ev { - None => {}, - ev => panic!("except None but get: {:?}", ev), - } - let my_ceremony_ended_ev = my_ceremony - .receive_partial_sign(SIGNER_2_ID, signer2_ceremony_partial_sign) - .unwrap(); - let my_ceremony_final_signature = match my_ceremony_ended_ev { - Some(CeremonyEvent::CeremonyEnded(signature, _, _)) => signature, - ev => panic!("except Some(CeremonyEvent::CeremonyEnded) but get: {:?}", ev), - }; - - // signer 1 receive partial_sign - let signer1_receive_first_partial_sign_ev = signer1_ceremony - .receive_partial_sign(MY_SIGNER_ID, my_ceremony_partial_sign) - .unwrap(); - match signer1_receive_first_partial_sign_ev { - None => {}, - ev => panic!("except None but get: {:?}", ev), - } - let signer1_ceremony_ended_ev = signer1_ceremony - .receive_partial_sign(SIGNER_2_ID, signer2_ceremony_partial_sign) - .unwrap(); - let signer1_ceremony_final_signature = match signer1_ceremony_ended_ev { - Some(CeremonyEvent::CeremonyEnded(signature, _, _)) => signature, - ev => panic!("except Some(CeremonyEvent::CeremonyEnded) but get: {:?}", ev), - }; - - // signer 2 receive partial_sign - let signer2_receive_first_partial_sign_ev = signer2_ceremony - .receive_partial_sign(MY_SIGNER_ID, my_ceremony_partial_sign) - .unwrap(); - match signer2_receive_first_partial_sign_ev { - None => {}, - ev => panic!("except None but get: {:?}", ev), - } - let signer2_ceremony_ended_ev = signer2_ceremony - .receive_partial_sign(SIGNER_1_ID, signer1_ceremony_partial_sign) - .unwrap(); - let signer2_ceremony_final_signature = match signer2_ceremony_ended_ev { - Some(CeremonyEvent::CeremonyEnded(signature, _, _)) => signature, - ev => panic!("except Some(CeremonyEvent::CeremonyEnded) but get: {:?}", ev), - }; - - assert_eq!(my_ceremony_final_signature, signer1_ceremony_final_signature); - assert_eq!(my_ceremony_final_signature, signer2_ceremony_final_signature); - - // let signature = - // k256::schnorr::Signature::try_from(signer1_ceremony_final_signature.as_slice()) - // .unwrap(); - let agg_key = - generate_aggregated_public_key(signers_with_keys().iter().map(|sk| sk.1).collect()); - // let ver_key = k256::schnorr::VerifyingKey::try_from(agg_key).unwrap(); - - // this pass - verify_single(agg_key, signer1_ceremony_final_signature, SAMPLE_SIGNATURE_PAYLOAD).unwrap(); - - // this not pass - // ver_key.verify(&SAMPLE_SIGNATURE_PAYLOAD, &signature).unwrap() - } - - fn signers_with_keys() -> SignersWithKeys { - vec![ - (MY_SIGNER_ID, k256::elliptic_curve::PublicKey::from(my_priv_key().verifying_key())), - ( - SIGNER_1_ID, - k256::elliptic_curve::PublicKey::from(signer1_priv_key().verifying_key()), - ), - ( - SIGNER_2_ID, - k256::elliptic_curve::PublicKey::from(signer2_priv_key().verifying_key()), - ), - ] - } - - fn my_priv_key() -> SigningKey { - SigningKey::from_bytes(&[ - 252, 240, 35, 85, 243, 83, 129, 54, 7, 155, 24, 114, 254, 0, 134, 251, 207, 83, 177, 9, - 92, 118, 222, 5, 202, 239, 188, 215, 132, 113, 127, 94, - ]) - .unwrap() - } - - fn signer1_priv_key() -> SigningKey { - SigningKey::from_bytes(&[ - 42, 82, 57, 169, 208, 130, 125, 141, 62, 185, 167, 41, 142, 217, 252, 135, 158, 128, - 44, 129, 222, 71, 55, 86, 230, 183, 54, 111, 152, 83, 85, 155, - ]) - .unwrap() - } - - fn signer2_priv_key() -> SigningKey { - SigningKey::from_bytes(&[ - 117, 130, 176, 36, 185, 53, 187, 61, 123, 86, 24, 38, 174, 143, 129, 73, 245, 210, 127, - 148, 115, 136, 32, 98, 62, 47, 26, 196, 57, 211, 171, 185, - ]) - .unwrap() - } -} diff --git a/tee-worker/bitacross/bitacross/core/bc-musig2-event/Cargo.toml b/tee-worker/bitacross/bitacross/core/bc-musig2-event/Cargo.toml deleted file mode 100644 index 3d90c0819c..0000000000 --- a/tee-worker/bitacross/bitacross/core/bc-musig2-event/Cargo.toml +++ /dev/null @@ -1,64 +0,0 @@ -[package] -name = "bc-musig2-event" -authors = ["Trust Computing GmbH "] -version = "0.1.0" -edition = "2021" - -[dependencies] -threadpool = { workspace = true, optional = true } - -# sgx dependencies -sgx_tstd = { workspace = true, features = ["net", "thread"], optional = true } -threadpool_sgx = { workspace = true, optional = true } - -bc-enclave-registry = { path = "../bc-enclave-registry", default-features = false } -bc-musig2-ceremony = { path = "../bc-musig2-ceremony", default-features = false } -lc-direct-call = { path = "../../../litentry/core/direct-call", default-features = false } - -itc-direct-rpc-client = { package = "bc-itc-direct-rpc-client", path = "../../../core/direct-rpc-client", default-features = false } -itc-direct-rpc-server = { package = "bc-itc-direct-rpc-server", path = "../../../core/direct-rpc-server", default-features = false } -itp-ocall-api = { workspace = true } -itp-rpc = { workspace = true } -itp-sgx-crypto = { workspace = true } -itp-types = { workspace = true } -itp-utils = { workspace = true } -litentry-primitives = { workspace = true } -rand = { version = "0.8.5", optional = true } -sgx_rand = { workspace = true, optional = true } -sp-core = { workspace = true, features = ["full_crypto"] } - -codec = { package = "parity-scale-codec", workspace = true } -log = { workspace = true } - -[dev-dependencies] -rand = { version = "0.8.5" } - -[features] -default = ["std"] -std = [ - "itc-direct-rpc-client/std", - "itc-direct-rpc-server/std", - "log/std", - "itp-types/std", - "litentry-primitives/std", - "itp-rpc/std", - "bc-musig2-ceremony/std", - "bc-enclave-registry/std", - "lc-direct-call/std", - "itp-sgx-crypto/std", - "rand", - "threadpool", -] -sgx = [ - "sgx_tstd", - "itc-direct-rpc-client/sgx", - "itc-direct-rpc-server/sgx", - "litentry-primitives/sgx", - "itp-rpc/sgx", - "bc-musig2-ceremony/sgx", - "bc-enclave-registry/sgx", - "lc-direct-call/sgx", - "itp-sgx-crypto/sgx", - "sgx_rand", - "threadpool_sgx", -] diff --git a/tee-worker/bitacross/bitacross/core/bc-musig2-event/src/lib.rs b/tee-worker/bitacross/bitacross/core/bc-musig2-event/src/lib.rs deleted file mode 100644 index af9ca183d4..0000000000 --- a/tee-worker/bitacross/bitacross/core/bc-musig2-event/src/lib.rs +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate core; -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -use core::time::Duration; -#[cfg(feature = "std")] -use threadpool::ThreadPool; - -#[cfg(feature = "sgx")] -use threadpool_sgx::ThreadPool; - -#[cfg(feature = "std")] -use std::sync::Mutex; - -#[cfg(feature = "sgx")] -use std::sync::SgxMutex as Mutex; - -#[cfg(feature = "std")] -use std::sync::RwLock; - -#[cfg(feature = "sgx")] -use std::sync::SgxRwLock as RwLock; - -use bc_enclave_registry::EnclaveRegistryLookup; -use bc_musig2_ceremony::{CeremonyEvent, CeremonyId, CeremonyRegistry, SignerId}; -use codec::Encode; -use itc_direct_rpc_client::{DirectRpcClient, DirectRpcClientFactory, RpcClient, RpcClientFactory}; -use itc_direct_rpc_server::SendRpcResponse; -use itp_ocall_api::EnclaveAttestationOCallApi; -use itp_rpc::{Id, RpcRequest}; -use itp_sgx_crypto::{key_repository::AccessKey, schnorr::Pair as SchnorrPair}; -pub use itp_types::{DirectRequestStatus, Hash}; -use itp_utils::hex::ToHexPrefixed; -use lc_direct_call::CeremonyRoundCall; -use litentry_primitives::{Address32, Identity, PlainRequest, ShardIdentifier}; -use log::*; -use sp_core::{blake2_256, ed25519, Pair as SpCorePair, H256}; -use std::{collections::HashMap, string::ToString, sync::Arc, thread::sleep, vec}; - -#[allow(clippy::too_many_arguments)] -pub fn process_event( - signing_key_access: Arc, - ocall_api: Arc, - responder: Arc, - enclave_registry_lookup: Arc, - event: CeremonyEvent, - ceremony_id: CeremonyId, - event_threads_pool: ThreadPool, - peers_map: Arc>>, - ceremony_registry: Arc>>, -) where - OCallApi: EnclaveAttestationOCallApi + 'static, - SIGNINGAK: AccessKey + Send + Sync + 'static, - Responder: SendRpcResponse + 'static, - ECL: EnclaveRegistryLookup + Send + Sync + 'static, - BKR: AccessKey + Send + Sync + 'static, -{ - let my_identity: Address32 = signing_key_access.retrieve_key().unwrap().public().0.into(); - let identity = Identity::Substrate(my_identity); - let mr_enclave = ocall_api.get_mrenclave_of_self().unwrap().m; - - match event { - CeremonyEvent::FirstRoundStarted(signers, message, nonce) => { - let direct_call = CeremonyRoundCall::NonceShare(identity, message, nonce.serialize()); - let request = prepare_request(signing_key_access.as_ref(), mr_enclave, direct_call); - - signers.iter().for_each(|signer_id| { - debug!( - "Sharing nonce with signer: {:?} for ceremony: {:?}", - signer_id, ceremony_id - ); - - let signer_id = *signer_id; - let peers_map_clone = peers_map.clone(); - let request = request.clone(); - let enclave_lookup_cloned = enclave_registry_lookup.clone(); - let ceremony_registry_cloned = ceremony_registry.clone(); - let ceremony_id_cloned = ceremony_id.clone(); - event_threads_pool.execute(move || { - send_request( - signer_id, - &ceremony_id_cloned, - request, - peers_map_clone, - enclave_lookup_cloned, - ceremony_registry_cloned, - ); - }); - }); - }, - CeremonyEvent::SecondRoundStarted(signers, message, signature) => { - let direct_call = - CeremonyRoundCall::PartialSignatureShare(identity, message, signature.serialize()); - let request = prepare_request(signing_key_access.as_ref(), mr_enclave, direct_call); - - signers.iter().for_each(|signer_id| { - debug!( - "Sharing partial signature with signer: {:?} for ceremony: {:?}", - signer_id, ceremony_id - ); - - let signer_id = *signer_id; - let peers_map_clone = peers_map.clone(); - let request = request.clone(); - let enclave_lookup_cloned = enclave_registry_lookup.clone(); - let ceremony_registry_cloned = ceremony_registry.clone(); - let ceremony_id_cloned = ceremony_id.clone(); - event_threads_pool.execute(move || { - send_request( - signer_id, - &ceremony_id_cloned, - request, - peers_map_clone, - enclave_lookup_cloned, - ceremony_registry_cloned, - ); - }); - }); - }, - CeremonyEvent::CeremonyEnded(signature, is_check_run, verification_result) => { - debug!("Ceremony {:?} ended, signature {:?}", ceremony_id, signature); - let hash = blake2_256(&ceremony_id.encode()); - let result = if is_check_run { - verification_result.encode() - } else { - let result = signature; - result.encode() - }; - event_threads_pool.execute(move || { - if let Err(e) = responder.send_state_with_status( - Hash::from_slice(&hash), - result, - DirectRequestStatus::Ok, - ) { - error!("Could not send response to {:?}, reason: {:?}", &hash, e); - } - }); - }, - CeremonyEvent::CeremonyError(signers, error) => { - debug!("Ceremony {:?} error {:?}", ceremony_id, error); - let hash = blake2_256(&ceremony_id.encode()); - let encoded_result = error.encode(); - event_threads_pool.execute(move || { - if let Err(e) = responder.send_state_with_status( - Hash::from_slice(&hash), - encoded_result, - DirectRequestStatus::Error, - ) { - error!("Could not send response to {:?}, reason: {:?}", &hash, e); - } - }); - - let direct_call = CeremonyRoundCall::KillCeremony(identity, ceremony_id.clone()); - let request = prepare_request(signing_key_access.as_ref(), mr_enclave, direct_call); - - //kill ceremonies on other workers - signers.iter().for_each(|signer_id| { - debug!( - "Requesting ceremony kill on signer: {:?} for ceremony: {:?}", - signer_id, ceremony_id - ); - - let signer_id = *signer_id; - let peers_map_clone = peers_map.clone(); - let request = request.clone(); - let enclave_lookup_cloned = enclave_registry_lookup.clone(); - let ceremony_registry_cloned = ceremony_registry.clone(); - let ceremony_id_cloned = ceremony_id.clone(); - event_threads_pool.execute(move || { - send_request( - signer_id, - &ceremony_id_cloned, - request, - peers_map_clone, - enclave_lookup_cloned, - ceremony_registry_cloned, - ); - }); - }); - }, - } -} - -// it will try to send request until it succeeds, the peer is removed from registry or ceremony is removed -fn send_request( - signer_id: SignerId, - ceremony_id: &CeremonyId, - request: RpcRequest, - peers_map: Arc>>, - enclave_registry_lookup: Arc, - ceremony_registry: Arc>>, -) where - ECL: EnclaveRegistryLookup, - BKR: AccessKey, -{ - loop { - let client = peers_map.lock().unwrap().get(&signer_id).cloned(); - if let Some(mut client) = client { - if let Err(e) = client.send(&request) { - error!("Could not send request to signer: {:?}, reason: {:?}", signer_id, e); - sleep(Duration::from_secs(5)); - let mut peers_lock = peers_map.lock().unwrap(); - peers_lock.remove(&signer_id); - } else { - // finish if request was sent - break - } - } else { - // check if ceremony still exists, if not stop - if !ceremony_registry.read().unwrap().contains_key(ceremony_id) { - break - } - - if let Some(url) = enclave_registry_lookup.get_worker_url(&Address32::from(signer_id)) { - match (DirectRpcClientFactory {}).create(&url) { - Ok(new_client) => { - peers_map.lock().unwrap().insert(signer_id, new_client.clone()); - }, - Err(e) => { - error!("Could not connect to peer {}, reason: {:?}", url, e); - sleep(Duration::from_secs(5)); - }, - } - } else { - error!("Could not find {:?} in registry", signer_id.to_hex()); - // stop if peer is not found in registry - break - } - } - } -} - -fn prepare_request( - signing_key_access: &SIGNINGAK, - mr_enclave: [u8; 32], - ceremony_round_call: CeremonyRoundCall, -) -> RpcRequest -where - SIGNINGAK: AccessKey + Send + Sync + 'static, -{ - let shard = ShardIdentifier::from_slice(&mr_enclave); - // same as above - let dc_signed_encoded = ceremony_round_call - .sign(&signing_key_access.retrieve_key().unwrap().into(), &mr_enclave, &shard) - .encode(); - let request = PlainRequest { shard, payload: dc_signed_encoded }; - RpcRequest { - jsonrpc: "2.0".to_string(), - method: "bitacross_btcDataShare".to_string(), - params: vec![request.to_hex()], - id: Id::Number(1), - } -} diff --git a/tee-worker/bitacross/bitacross/core/bc-relayer-registry/Cargo.toml b/tee-worker/bitacross/bitacross/core/bc-relayer-registry/Cargo.toml deleted file mode 100644 index 6ce57e3e91..0000000000 --- a/tee-worker/bitacross/bitacross/core/bc-relayer-registry/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "bc-relayer-registry" -authors = ["Trust Computing GmbH "] -version = "0.1.0" -edition = "2021" - -[dependencies] -codec = { package = "parity-scale-codec", workspace = true } -log = { workspace = true } -sp-std = { workspace = true } -thiserror = { workspace = true, optional = true } - -sgx_tstd = { workspace = true, features = ["net", "thread"], optional = true } -thiserror_sgx = { workspace = true, optional = true } - -itp-settings = { workspace = true } -itp-sgx-io = { workspace = true } -litentry-primitives = { workspace = true } - -[dev-dependencies] -base64 = { workspace = true, features = ["alloc"] } - -[features] -default = ["std"] -development = [] -sgx = [ - "sgx_tstd", - "thiserror_sgx", - "itp-sgx-io/sgx", - "litentry-primitives/sgx", -] -std = [ - "sp-std/std", - "log/std", - "thiserror", - "itp-sgx-io/std", - "litentry-primitives/std", -] diff --git a/tee-worker/bitacross/bitacross/core/bc-relayer-registry/src/lib.rs b/tee-worker/bitacross/bitacross/core/bc-relayer-registry/src/lib.rs deleted file mode 100644 index 337b958a82..0000000000 --- a/tee-worker/bitacross/bitacross/core/bc-relayer-registry/src/lib.rs +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate core; -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -use sp_std::{boxed::Box, fmt::Debug}; - -use litentry_primitives::Identity; -use log::error; -use std::{collections::BTreeMap, path::PathBuf}; - -#[cfg(feature = "std")] -use std::sync::RwLock; -#[cfg(feature = "sgx")] -use std::sync::SgxRwLock as RwLock; - -pub type RelayerRegistryMap = BTreeMap; - -#[derive(Default)] -pub struct RelayerRegistry { - pub registry: RwLock, - pub seal_path: PathBuf, -} - -impl RelayerRegistry { - pub fn new(base_dir: PathBuf) -> Self { - RelayerRegistry { registry: Default::default(), seal_path: base_dir } - } -} - -pub type RegistryResult = core::result::Result; - -#[cfg(feature = "sgx")] -use thiserror_sgx as thiserror; - -#[derive(Debug, thiserror::Error)] -pub enum RegistryError { - #[error("poison lock")] - PoisonLock, - #[error("empty Relayer registry")] - EmptyRegistry, - #[error(transparent)] - Other(#[from] Box), -} - -impl From for RegistryError { - fn from(e: std::io::Error) -> Self { - Self::Other(e.into()) - } -} - -impl From for RegistryError { - #[cfg(feature = "std")] - fn from(e: codec::Error) -> Self { - Self::Other(e.into()) - } - - #[cfg(feature = "sgx")] - fn from(e: codec::Error) -> Self { - Self::Other(std::format!("{:?}", e).into()) - } -} - -#[cfg(feature = "sgx")] -mod sgx { - use crate::{RegistryError as Error, RegistryResult as Result, RelayerRegistryMap}; - pub use codec::{Decode, Encode}; - pub use itp_settings::files::RELAYER_REGISTRY_FILE; - pub use itp_sgx_io::{seal, unseal, SealedIO}; - pub use log::*; - pub use std::{boxed::Box, fs, path::PathBuf, sgxfs::SgxFile, sync::Arc}; - - #[derive(Clone, Debug)] - pub struct RelayerRegistrySeal { - base_path: PathBuf, - } - - impl RelayerRegistrySeal { - pub fn new(base_path: PathBuf) -> Self { - Self { base_path } - } - - pub fn path(&self) -> PathBuf { - self.base_path.join(RELAYER_REGISTRY_FILE) - } - } - - impl SealedIO for RelayerRegistrySeal { - type Error = Error; - type Unsealed = RelayerRegistryMap; - - fn unseal(&self) -> Result { - Ok(unseal(self.path()).map(|b| Decode::decode(&mut b.as_slice()))??) - } - - fn seal(&self, unsealed: &Self::Unsealed) -> Result<()> { - info!("Seal relayer registry to file: {:?}", unsealed); - Ok(unsealed.using_encoded(|bytes| seal(bytes, self.path()))?) - } - } -} - -#[cfg(feature = "sgx")] -use sgx::*; - -pub trait RelayerRegistryUpdater { - fn init(&self) -> RegistryResult<()>; - fn update(&self, account: Identity) -> RegistryResult<()>; - fn remove(&self, account: Identity) -> RegistryResult<()>; -} - -pub trait RelayerRegistryLookup { - fn contains_key(&self, account: &Identity) -> bool; -} - -impl RelayerRegistryUpdater for RelayerRegistry { - #[cfg(feature = "std")] - fn init(&self) -> RegistryResult<()> { - Ok(()) - } - - #[cfg(feature = "std")] - fn update(&self, account: Identity) -> RegistryResult<()> { - let mut registry = self.registry.write().unwrap(); - registry.insert(account, ()); - Ok(()) - } - - #[cfg(feature = "std")] - fn remove(&self, _account: Identity) -> RegistryResult<()> { - Ok(()) - } - - // if `RELAYER_REGISTRY_FILE` exists, unseal and init from it - // otherwise create a new instance and seal to static file - #[cfg(feature = "sgx")] - fn init(&self) -> RegistryResult<()> { - let enclave_seal = RelayerRegistrySeal::new(self.seal_path.clone()); - if SgxFile::open(RELAYER_REGISTRY_FILE).is_err() { - info!( - "[Enclave] RelayerRegistry file not found, creating new! {}", - RELAYER_REGISTRY_FILE - ); - let registry = self.registry.write().map_err(|_| RegistryError::PoisonLock)?; - enclave_seal.seal(&*registry) - } else { - let m = enclave_seal.unseal()?; - info!("[Enclave] RelayerRegistry unsealed from file: {:?}", m); - let mut registry = self.registry.write().map_err(|_| RegistryError::PoisonLock)?; - *registry = m; - Ok(()) - } - } - - #[cfg(feature = "sgx")] - fn update(&self, account: Identity) -> RegistryResult<()> { - let mut registry = self.registry.write().map_err(|_| RegistryError::PoisonLock)?; - registry.insert(account, ()); - RelayerRegistrySeal::new(self.seal_path.clone()).seal(&*registry) - } - - #[cfg(feature = "sgx")] - fn remove(&self, account: Identity) -> RegistryResult<()> { - let mut registry = self.registry.write().map_err(|_| RegistryError::PoisonLock)?; - let old_value = registry.remove(&account); - if old_value.is_some() { - return RelayerRegistrySeal::new(self.seal_path.clone()).seal(&*registry) - } - Ok(()) - } -} - -impl RelayerRegistryLookup for RelayerRegistry { - #[cfg(feature = "std")] - fn contains_key(&self, account: &Identity) -> bool { - let registry = self.registry.read().unwrap(); - registry.contains_key(account) - } - - #[cfg(feature = "sgx")] - fn contains_key(&self, account: &Identity) -> bool { - // Using unwrap becaused poisoned locks are unrecoverable errors - let registry = self.registry.read().unwrap(); - registry.contains_key(account) - } -} diff --git a/tee-worker/bitacross/bitacross/core/bc-signer-registry/Cargo.toml b/tee-worker/bitacross/bitacross/core/bc-signer-registry/Cargo.toml deleted file mode 100644 index 7aaf9887d2..0000000000 --- a/tee-worker/bitacross/bitacross/core/bc-signer-registry/Cargo.toml +++ /dev/null @@ -1,39 +0,0 @@ -[package] -name = "bc-signer-registry" -authors = ["Trust Computing GmbH "] -version = "0.1.0" -edition = "2021" - -[dependencies] -codec = { package = "parity-scale-codec", workspace = true } -log = { workspace = true } -sp-std = { workspace = true } -thiserror = { workspace = true, optional = true } - -sgx_tstd = { workspace = true, features = ["net", "thread"], optional = true } -thiserror_sgx = { workspace = true, optional = true } - -itp-settings = { workspace = true } -itp-sgx-io = { workspace = true } -litentry-primitives = { workspace = true } - -[dev-dependencies] -base64 = { workspace = true, features = ["alloc"] } - -[features] -default = ["std"] -production = [ -] -sgx = [ - "sgx_tstd", - "thiserror_sgx", - "itp-sgx-io/sgx", - "litentry-primitives/sgx", -] -std = [ - "sp-std/std", - "log/std", - "thiserror", - "itp-sgx-io/std", - "litentry-primitives/std", -] diff --git a/tee-worker/bitacross/bitacross/core/bc-signer-registry/src/lib.rs b/tee-worker/bitacross/bitacross/core/bc-signer-registry/src/lib.rs deleted file mode 100644 index 5cd6efb6de..0000000000 --- a/tee-worker/bitacross/bitacross/core/bc-signer-registry/src/lib.rs +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate core; -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -use sp_std::{boxed::Box, fmt::Debug}; - -use log::error; -use std::{collections::BTreeMap, error::Error, path::PathBuf, vec::Vec}; - -#[cfg(feature = "std")] -use std::sync::RwLock; -#[cfg(feature = "sgx")] -use std::sync::SgxRwLock as RwLock; - -pub type PubKey = [u8; 33]; - -pub type SignerRegistryMap = BTreeMap; - -#[derive(Default)] -pub struct SignerRegistry { - pub registry: RwLock, - pub seal_path: PathBuf, -} - -impl SignerRegistry { - pub fn new(base_dir: PathBuf) -> Self { - SignerRegistry { registry: Default::default(), seal_path: base_dir } - } -} - -pub type RegistryResult = Result; - -use litentry_primitives::Address32; -#[cfg(feature = "sgx")] -use thiserror_sgx as thiserror; - -#[derive(Debug, thiserror::Error)] -pub enum RegistryError { - #[error("poison lock")] - PoisonLock, - #[error("empty Signer registry")] - EmptyRegistry, - #[error(transparent)] - Other(#[from] Box), -} - -impl From for RegistryError { - fn from(e: std::io::Error) -> Self { - Self::Other(e.into()) - } -} - -impl From for RegistryError { - #[cfg(feature = "std")] - fn from(e: codec::Error) -> Self { - Self::Other(e.into()) - } - - #[cfg(feature = "sgx")] - fn from(e: codec::Error) -> Self { - Self::Other(std::format!("{:?}", e).into()) - } -} - -#[cfg(feature = "sgx")] -mod sgx { - use crate::{RegistryError as Error, RegistryResult as Result, SignerRegistryMap}; - pub use codec::{Decode, Encode}; - pub use itp_settings::files::SIGNER_REGISTRY_FILE; - pub use itp_sgx_io::{seal, unseal, SealedIO}; - pub use log::*; - pub use std::{boxed::Box, fs, path::PathBuf, sgxfs::SgxFile, sync::Arc}; - - #[derive(Clone, Debug)] - pub struct SignerRegistrySeal { - base_path: PathBuf, - } - - impl SignerRegistrySeal { - pub fn new(base_path: PathBuf) -> Self { - Self { base_path } - } - - pub fn path(&self) -> PathBuf { - self.base_path.join(SIGNER_REGISTRY_FILE) - } - } - - impl SealedIO for SignerRegistrySeal { - type Error = Error; - type Unsealed = SignerRegistryMap; - - fn unseal(&self) -> Result { - Ok(unseal(self.path()).map(|b| Decode::decode(&mut b.as_slice()))??) - } - - fn seal(&self, unsealed: &Self::Unsealed) -> Result<()> { - info!("Seal signer registry to file: {:?}", unsealed); - Ok(unsealed.using_encoded(|bytes| seal(bytes, self.path()))?) - } - } -} - -#[cfg(feature = "sgx")] -use sgx::*; - -pub trait SignerRegistrySealer { - fn seal(&self, state: SignerRegistryMap) -> RegistryResult<()>; - fn unseal(&self) -> RegistryResult; -} - -pub trait SignerRegistryUpdater { - fn init(&self) -> RegistryResult<()>; - fn update(&self, account: Address32, key: PubKey) -> RegistryResult<()>; - fn remove(&self, account: Address32) -> RegistryResult<()>; -} - -pub trait SignerRegistryLookup { - fn contains_key(&self, account: &Address32) -> bool; - fn get_all(&self) -> Vec<(Address32, PubKey)>; -} - -impl SignerRegistrySealer for SignerRegistry { - #[cfg(feature = "std")] - fn seal(&self, _state: SignerRegistryMap) -> RegistryResult<()> { - Ok(()) - } - - #[cfg(feature = "std")] - fn unseal(&self) -> RegistryResult { - Ok(Default::default()) - } - - #[cfg(feature = "sgx")] - fn seal(&self, mut state: SignerRegistryMap) -> RegistryResult<()> { - let mut registry = self.registry.write().map_err(|_| RegistryError::PoisonLock)?; - while let Some((key, val)) = state.pop_first() { - registry.insert(key, val); - } - - let signer_seal = SignerRegistrySeal::new(self.seal_path.clone()); - signer_seal.seal(®istry) - } - - #[cfg(feature = "sgx")] - fn unseal(&self) -> RegistryResult { - let signer_seal = SignerRegistrySeal::new(self.seal_path.clone()); - signer_seal.unseal() - } -} - -impl SignerRegistryUpdater for SignerRegistry { - #[cfg(feature = "std")] - fn init(&self) -> RegistryResult<()> { - Ok(()) - } - - #[cfg(feature = "std")] - fn update(&self, account: Address32, key: PubKey) -> RegistryResult<()> { - let mut registry = self.registry.write().unwrap(); - registry.insert(account, key); - Ok(()) - } - - #[cfg(feature = "std")] - fn remove(&self, _account: Address32) -> RegistryResult<()> { - Ok(()) - } - - // if `SIGNER_REGISTRY_FILE` exists, unseal and init from it - // otherwise create a new instance and seal to static file - #[cfg(feature = "sgx")] - fn init(&self) -> RegistryResult<()> { - let enclave_seal = SignerRegistrySeal::new(self.seal_path.clone()); - if SgxFile::open(SIGNER_REGISTRY_FILE).is_err() { - info!("[Signer] SignerRegistry file not found, creating new! {}", SIGNER_REGISTRY_FILE); - let registry = self.registry.write().map_err(|_| RegistryError::PoisonLock)?; - enclave_seal.seal(&*registry) - } else { - let m = enclave_seal.unseal()?; - info!("[Signer] SignerRegistry unsealed from file: {:?}", m); - let mut registry = self.registry.write().map_err(|_| RegistryError::PoisonLock)?; - *registry = m; - Ok(()) - } - } - - #[cfg(feature = "sgx")] - fn update(&self, account: Address32, key: PubKey) -> RegistryResult<()> { - let mut registry = self.registry.write().map_err(|_| RegistryError::PoisonLock)?; - registry.insert(account, key); - SignerRegistrySeal::new(self.seal_path.clone()).seal(&*registry) - } - - #[cfg(feature = "sgx")] - fn remove(&self, account: Address32) -> RegistryResult<()> { - let mut registry = self.registry.write().map_err(|_| RegistryError::PoisonLock)?; - let old_value = registry.remove(&account); - if old_value.is_some() { - return SignerRegistrySeal::new(self.seal_path.clone()).seal(&*registry) - } - Ok(()) - } -} - -impl SignerRegistryLookup for SignerRegistry { - #[cfg(feature = "std")] - fn contains_key(&self, account: &Address32) -> bool { - let registry = self.registry.read().unwrap(); - registry.contains_key(account) - } - - #[cfg(feature = "std")] - fn get_all(&self) -> Vec<(Address32, PubKey)> { - let registry = self.registry.read().unwrap(); - registry.iter().map(|(k, v)| (*k, *v)).collect() - } - - #[cfg(feature = "sgx")] - fn contains_key(&self, account: &Address32) -> bool { - // Using unwrap because poisoned locks are unrecoverable errors - let registry = self.registry.read().unwrap(); - registry.contains_key(account) - } - - #[cfg(feature = "sgx")] - fn get_all(&self) -> Vec<(Address32, PubKey)> { - // Using unwrap because poisoned locks are unrecoverable errors - let registry = self.registry.read().unwrap(); - registry.iter().map(|(k, v)| (k.clone(), v.clone())).collect() - } -} diff --git a/tee-worker/bitacross/bitacross/core/bc-task-processor/Cargo.toml b/tee-worker/bitacross/bitacross/core/bc-task-processor/Cargo.toml deleted file mode 100644 index f315dc434a..0000000000 --- a/tee-worker/bitacross/bitacross/core/bc-task-processor/Cargo.toml +++ /dev/null @@ -1,90 +0,0 @@ -[package] -name = "bc-task-processor" -authors = ["Trust Computing GmbH "] -version = "0.1.0" -edition = "2021" - -[dependencies] -futures_sgx = { workspace = true, optional = true } -thiserror = { workspace = true, optional = true } -thiserror_sgx = { workspace = true, optional = true } -threadpool = { workspace = true, optional = true } - -sgx_tstd = { workspace = true, features = ["net", "thread"], optional = true } -threadpool_sgx = { workspace = true, optional = true } - -codec = { package = "parity-scale-codec", workspace = true } -frame-support = { workspace = true } -log = { workspace = true } - -ita-stf = { package = "bc-ita-stf", path = "../../../app-libs/stf", default-features = false } -itp-enclave-metrics = { workspace = true } -itp-ocall-api = { workspace = true } -itp-sgx-crypto = { workspace = true } -itp-sgx-externalities = { workspace = true } -itp-stf-executor = { package = "bc-itp-stf-executor", path = "../../../core-primitives/stf-executor", default-features = false } -itp-stf-state-handler = { workspace = true } - -bc-enclave-registry = { path = "../bc-enclave-registry", default-features = false } -bc-musig2-ceremony = { path = "../bc-musig2-ceremony", default-features = false } -bc-musig2-event = { path = "../bc-musig2-event", default-features = false } -bc-relayer-registry = { path = "../bc-relayer-registry", default-features = false } -bc-signer-registry = { path = "../bc-signer-registry", default-features = false } -bc-task-sender = { path = "../bc-task-sender", default-features = false } -itc-direct-rpc-client = { package = "bc-itc-direct-rpc-client", path = "../../../core/direct-rpc-client", default-features = false } -itc-direct-rpc-server = { package = "bc-itc-direct-rpc-server", path = "../../../core/direct-rpc-server", default-features = false } -lc-direct-call = { path = "../../../litentry/core/direct-call", default-features = false } -litentry-primitives = { workspace = true } - -sgx_crypto_helper = { workspace = true } -sp-core = { workspace = true, features = ["full_crypto"] } - -[features] -default = ["std"] -sgx = [ - "threadpool_sgx", - "sgx_tstd", - "bc-musig2-ceremony/sgx", - "bc-musig2-event/sgx", - "bc-task-sender/sgx", - "bc-enclave-registry/sgx", - "bc-relayer-registry/sgx", - "bc-signer-registry/sgx", - "lc-direct-call/sgx", - "litentry-primitives/sgx", - "ita-stf/sgx", - "itp-enclave-metrics/sgx", - "itp-sgx-crypto/sgx", - "itp-sgx-externalities/sgx", - "itp-stf-executor/sgx", - "itp-stf-state-handler/sgx", - "thiserror_sgx", - "futures_sgx", - "itc-direct-rpc-server/sgx", - "itc-direct-rpc-client/sgx", - "sgx_crypto_helper/mesalock_sgx", -] -std = [ - "threadpool", - "log/std", - "bc-musig2-ceremony/std", - "bc-musig2-event/std", - "bc-task-sender/std", - "bc-enclave-registry/std", - "bc-relayer-registry/std", - "bc-signer-registry/std", - "lc-direct-call/std", - "litentry-primitives/std", - "ita-stf/std", - "itp-enclave-metrics/std", - "itp-ocall-api/std", - "itp-sgx-crypto/std", - "itp-sgx-externalities/std", - "itp-stf-executor/std", - "itp-stf-state-handler/std", - "thiserror", - "itc-direct-rpc-server/std", - "itc-direct-rpc-client/std", - "sgx_crypto_helper/default", -] -development = [] diff --git a/tee-worker/bitacross/bitacross/core/bc-task-processor/src/lib.rs b/tee-worker/bitacross/bitacross/core/bc-task-processor/src/lib.rs deleted file mode 100644 index 64171055bf..0000000000 --- a/tee-worker/bitacross/bitacross/core/bc-task-processor/src/lib.rs +++ /dev/null @@ -1,769 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate core; -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -// re-export module to properly feature gate sgx and regular std environment -#[cfg(all(not(feature = "std"), feature = "sgx"))] -pub mod sgx_reexport_prelude { - pub use futures_sgx as futures; - pub use thiserror_sgx as thiserror; - pub use threadpool_sgx as threadpool; -} - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -pub use crate::sgx_reexport_prelude::*; - -#[cfg(feature = "std")] -use std::sync::RwLock; - -#[cfg(feature = "sgx")] -use std::sync::SgxRwLock as RwLock; - -#[cfg(feature = "std")] -use std::sync::Mutex; - -#[cfg(feature = "sgx")] -use std::sync::SgxMutex as Mutex; - -use bc_enclave_registry::EnclaveRegistryLookup; -use bc_musig2_ceremony::{ - get_current_timestamp, CeremonyCommand, CeremonyCommandTmp, CeremonyError, CeremonyErrorReason, - CeremonyEvent, CeremonyId, CeremonyRegistry, MuSig2Ceremony, SignBitcoinPayload, -}; -use bc_musig2_event::{process_event, DirectRequestStatus, Hash}; -use bc_relayer_registry::RelayerRegistryLookup; -use bc_signer_registry::SignerRegistryLookup; -use bc_task_sender::{ - init_bit_across_task_sender_storage, BitAcrossProcessingResult, BitAcrossRequest, -}; -use codec::{Decode, Encode}; -use core::{ops::Deref, time::Duration}; -use frame_support::{ensure, sp_runtime::app_crypto::sp_core::blake2_256}; -use ita_stf::TrustedCallSigned; -use itc_direct_rpc_client::{DirectRpcClient, DirectRpcClientFactory, RpcClientFactory}; -use itc_direct_rpc_server::SendRpcResponse; -use itp_enclave_metrics::EnclaveMetric; -use itp_ocall_api::{EnclaveAttestationOCallApi, EnclaveMetricsOCallApi, EnclaveOnChainOCallApi}; -use itp_sgx_crypto::{ - ecdsa::Pair as EcdsaPair, - key_repository::{AccessKey, AccessPubkey}, - schnorr::Pair as SchnorrPair, - ShieldingCryptoDecrypt, ShieldingCryptoEncrypt, -}; -use itp_sgx_externalities::SgxExternalitiesTrait; -use itp_stf_executor::traits::StfEnclaveSigning; -use itp_stf_state_handler::handle_state::HandleState; -use lc_direct_call::{ - handler::{ - kill_ceremony, nonce_share, partial_signature_share, - sign_bitcoin::{self, SignBitcoinError}, - sign_ethereum, sign_ton, - }, - CeremonyRoundCall, CeremonyRoundCallSigned, DirectCall, DirectCallSigned, -}; -use litentry_primitives::{Address32, PlainRequest}; -use log::*; -use sgx_crypto_helper::rsa3072::Rsa3072PubKey; -use sp_core::{ed25519, Pair, H256}; -use std::{ - collections::HashMap, - string::{String, ToString}, - sync::Arc, - vec, - vec::Vec, -}; -use threadpool::ThreadPool; - -#[derive(Debug, thiserror::Error, Clone)] -pub enum Error { - #[error("Request error: {0}")] - RequestError(String), - - #[error("Other error: {0}")] - OtherError(String), -} - -pub struct BitAcrossTaskContext< - SKR, - SIGNINGAK, - EKR, - BKR, - TKR, - S: StfEnclaveSigning, - H: HandleState, - O: EnclaveOnChainOCallApi, - RRL: RelayerRegistryLookup, - ERL: EnclaveRegistryLookup, - SRL: SignerRegistryLookup, - Responder, -> where - SKR: AccessKey + AccessPubkey, - SIGNINGAK: AccessKey, - EKR: AccessKey, - BKR: AccessKey, - TKR: AccessKey, - ::KeyType: ShieldingCryptoEncrypt + 'static, - Responder: SendRpcResponse, -{ - pub shielding_key: Arc, - pub signing_key_access: Arc, - pub ethereum_key_repository: Arc, - pub bitcoin_key_repository: Arc, - pub ton_key_repository: Arc, - pub enclave_signer: Arc, - pub state_handler: Arc, - pub ocall_api: Arc, - pub relayer_registry_lookup: Arc, - pub enclave_registry_lookup: Arc, - pub signer_registry_lookup: Arc, - pub signing_key_pub: [u8; 32], - pub responder: Arc, - pub ceremony_registry: Arc>>, - pub ceremony_command_tmp: Arc>, -} - -impl< - SKR, - SIGNINGAK, - EKR, - BKR, - TKR, - S: StfEnclaveSigning, - H: HandleState, - O: EnclaveOnChainOCallApi, - RRL: RelayerRegistryLookup, - ERL: EnclaveRegistryLookup, - SRL: SignerRegistryLookup, - Responder, - > BitAcrossTaskContext -where - SKR: AccessKey + AccessPubkey, - SIGNINGAK: AccessKey, - EKR: AccessKey, - BKR: AccessKey, - TKR: AccessKey, - ::KeyType: ShieldingCryptoEncrypt + 'static, - H::StateT: SgxExternalitiesTrait, - Responder: SendRpcResponse, -{ - #[allow(clippy::too_many_arguments)] - pub fn new( - shielding_key: Arc, - signing_key_access: Arc, - ethereum_key_repository: Arc, - bitcoin_key_repository: Arc, - ton_key_repository: Arc, - enclave_signer: Arc, - state_handler: Arc, - ocall_api: Arc, - relayer_registry_lookup: Arc, - enclave_registry_lookup: Arc, - signer_registry_lookup: Arc, - signing_key_pub: [u8; 32], - ceremony_registry: Arc>>, - ceremony_command_tmp: Arc>, - responder: Arc, - ) -> Self { - Self { - shielding_key, - signing_key_access, - ethereum_key_repository, - bitcoin_key_repository, - ton_key_repository, - enclave_signer, - state_handler, - ocall_api, - relayer_registry_lookup, - enclave_registry_lookup, - signer_registry_lookup, - signing_key_pub, - ceremony_registry, - ceremony_command_tmp, - responder, - } - } -} - -#[allow(clippy::type_complexity)] -pub fn run_bit_across_handler_runner< - SKR, - SIGNINGAK, - EKR, - BKR, - TKR, - S, - H, - O, - RRL, - ERL, - SRL, - Responder, ->( - context: Arc< - BitAcrossTaskContext, - >, - ceremony_commands_thread_count: u8, - ceremony_events_thread_count: u8, -) where - SKR: AccessKey + AccessPubkey + Send + Sync + 'static, - SIGNINGAK: AccessKey + Send + Sync + 'static, - EKR: AccessKey + Send + Sync + 'static, - BKR: AccessKey + Send + Sync + 'static, - TKR: AccessKey + Send + Sync + 'static, - ::KeyType: ShieldingCryptoEncrypt + ShieldingCryptoDecrypt + 'static, - S: StfEnclaveSigning + Send + Sync + 'static, - H: HandleState + Send + Sync + 'static, - H::StateT: SgxExternalitiesTrait, - O: EnclaveOnChainOCallApi + EnclaveMetricsOCallApi + EnclaveAttestationOCallApi + 'static, - RRL: RelayerRegistryLookup + Send + Sync + 'static, - ERL: EnclaveRegistryLookup + Send + Sync + 'static, - SRL: SignerRegistryLookup + Send + Sync + 'static, - Responder: SendRpcResponse + Send + Sync + 'static, -{ - // timeout tick - let ceremony_registry = context.ceremony_registry.clone(); - let ceremony_command_tmp = context.ceremony_command_tmp.clone(); - let responder = context.responder.clone(); - let time_to_live = 30u64; - let cloned_ocall_api = context.ocall_api.clone(); - std::thread::spawn(move || loop { - std::thread::sleep(Duration::from_secs(3)); - let now = get_current_timestamp(); - let mut timed_out_count: u8 = 0; - { - let mut ceremony_registry_write = ceremony_registry.write().unwrap(); - ceremony_registry_write.retain(|_, (ceremony, create_time)| { - let if_retain = now - *create_time < time_to_live; - if !if_retain { - let ceremony_rwlock = ceremony.clone(); - let ceremony = ceremony_rwlock.read().unwrap(); - let hash = blake2_256(&ceremony.get_id_ref().encode()); - let encrypted_result = SignBitcoinError::CeremonyError.encode(); - if let Err(e) = responder.send_state_with_status( - Hash::from_slice(&hash), - encrypted_result, - DirectRequestStatus::Error, - ) { - error!("Could not send response to {:?}, reason: {:?}", &hash, e); - } - timed_out_count += 1; - } - if_retain - }); - } - { - let mut command_tmp_write = ceremony_command_tmp.write().unwrap(); - command_tmp_write.retain(|_, &mut (_, create_time)| now - create_time < time_to_live); - } - if timed_out_count > 0 { - let _ = cloned_ocall_api - .update_metric(EnclaveMetric::Musig2CeremonyTimedout(timed_out_count)); - } - }); - - let bit_across_task_receiver = init_bit_across_task_sender_storage(); - let peers_map = Arc::new(Mutex::new(HashMap::<[u8; 32], DirectRpcClient>::new())); - let command_threads_pool = ThreadPool::new(ceremony_commands_thread_count.into()); - let event_threads_pool = ThreadPool::new(ceremony_events_thread_count.into()); - - while let Ok(req) = bit_across_task_receiver.recv() { - let context = context.clone(); - let event_threads_pool = event_threads_pool.clone(); - let peers_map = peers_map.clone(); - command_threads_pool.execute(move || { - if let Some((ceremony_id, command)) = handle_request(req, context.clone()) { - handle_ceremony_command( - context, - ceremony_id, - command, - event_threads_pool, - peers_map, - ); - } - }); - } - - command_threads_pool.join(); - event_threads_pool.join(); - warn!("bit_across_handler_runner loop terminated"); -} - -#[allow(clippy::type_complexity)] -fn handle_ceremony_command( - context: Arc< - BitAcrossTaskContext, - >, - ceremony_id: CeremonyId, - command: CeremonyCommand, - event_threads_pool: ThreadPool, - peers_map: Arc>>, -) where - SKR: AccessKey + AccessPubkey + Send + Sync + 'static, - SIGNINGAK: AccessKey + Send + Sync + 'static, - EKR: AccessKey + Send + Sync + 'static, - BKR: AccessKey + Send + Sync + 'static, - TKR: AccessKey + Send + Sync + 'static, - ::KeyType: ShieldingCryptoEncrypt + ShieldingCryptoDecrypt + 'static, - S: StfEnclaveSigning + Send + Sync + 'static, - H: HandleState + Send + Sync + 'static, - H::StateT: SgxExternalitiesTrait, - O: EnclaveOnChainOCallApi + EnclaveMetricsOCallApi + EnclaveAttestationOCallApi + 'static, - RRL: RelayerRegistryLookup + Send + Sync + 'static, - ERL: EnclaveRegistryLookup + Send + Sync + 'static, - SRL: SignerRegistryLookup + Send + Sync + 'static, - Responder: SendRpcResponse + Send + Sync + 'static, -{ - // check whether to store command to tmp - let is_first_round = { - context - .ceremony_registry - .read() - .unwrap() - .get(&ceremony_id) - .map(|(c, _)| c.read().unwrap().is_first_round()) - }; - match (is_first_round, &command) { - (None, CeremonyCommand::InitCeremony(_, _, _)) - | (Some(true), CeremonyCommand::SaveNonce(_, _)) - | (Some(false), CeremonyCommand::SavePartialSignature(_, _)) - | (_, CeremonyCommand::KillCeremony) => {}, - (None, CeremonyCommand::SaveNonce(_, _)) - | (Some(true), CeremonyCommand::SavePartialSignature(_, _)) => { - context - .ceremony_command_tmp - .write() - .unwrap() - .entry(ceremony_id) - .and_modify(|(command_tmp, _)| command_tmp.write().unwrap().push(command.clone())) - .or_insert((Arc::new(RwLock::new(vec![command])), get_current_timestamp())); - return - }, - (is_first_round, command) => { - error!( - "receive wrong command: is_first_round: {:?}, command: {:?}, drop it", - is_first_round, command - ); - return - }, - } - - // try to udpate peers_map - let my_identity: Address32 = - context.signing_key_access.retrieve_key().unwrap().public().0.into(); - context - .enclave_registry_lookup - .get_all() - .iter() - .for_each(|(identity, address)| { - if my_identity != *identity - && !peers_map.lock().unwrap().contains_key(identity.as_ref()) - { - info!("creating new connection to peer: {:?}", address); - match (DirectRpcClientFactory {}).create(address) { - Ok(client) => { - peers_map.lock().unwrap().insert(*identity.as_ref(), client); - }, - Err(e) => error!("Could not connect to peer {}, reason: {:?}", address, e), - } - } - }); - - // process commands and events - let mut commands_to_process = vec![command]; - while !commands_to_process.is_empty() { - let command = commands_to_process.pop().unwrap(); - - let event = process_command(context.clone(), ceremony_id.clone(), command); - - if let Some(event) = event { - // update metrics - match event { - CeremonyEvent::FirstRoundStarted(_, _, _) => { - let _ = context.ocall_api.update_metric(EnclaveMetric::Musig2CeremonyStarted); - }, - CeremonyEvent::CeremonyError(_, _) => { - let _ = context.ocall_api.update_metric(EnclaveMetric::Musig2CeremonyFailed); - }, - CeremonyEvent::CeremonyEnded(_, _, _) => { - let ceremony_start_time = - context.ceremony_registry.read().unwrap().get(&ceremony_id).unwrap().1; - let _ = context.ocall_api.update_metric(EnclaveMetric::Musig2CeremonyDuration( - Duration::from_millis(get_current_timestamp() - ceremony_start_time), - )); - }, - _ => {}, - } - - match event { - CeremonyEvent::FirstRoundStarted(_, _, _) - | CeremonyEvent::SecondRoundStarted(_, _, _) => { - // get all ceremony_command_tmp - let mut ceremony_command_tmp_write = - context.ceremony_command_tmp.write().unwrap(); - if let Some((ceremony_command_tmp, _)) = - ceremony_command_tmp_write.remove(&ceremony_id) - { - commands_to_process = ceremony_command_tmp.read().unwrap().clone(); - } - }, - CeremonyEvent::CeremonyEnded(_, _, _) | CeremonyEvent::CeremonyError(_, _) => { - // remove ceremony - { - let mut registry_write = context.ceremony_registry.write().unwrap(); - registry_write.remove(&ceremony_id); - } - { - context.ceremony_command_tmp.write().unwrap().remove(&ceremony_id); - } - }, - } - - process_event( - context.signing_key_access.clone(), - context.ocall_api.clone(), - context.responder.clone(), - context.enclave_registry_lookup.clone(), - event, - ceremony_id.clone(), - event_threads_pool.clone(), - peers_map.clone(), - context.ceremony_registry.clone(), - ); - } - } -} - -#[allow(clippy::type_complexity)] -fn process_command( - context: Arc< - BitAcrossTaskContext, - >, - ceremony_id: CeremonyId, - command: CeremonyCommand, -) -> Option -where - SKR: AccessKey + AccessPubkey + Send + Sync + 'static, - SIGNINGAK: AccessKey + Send + Sync + 'static, - EKR: AccessKey + Send + Sync + 'static, - BKR: AccessKey + Send + Sync + 'static, - TKR: AccessKey + Send + Sync + 'static, - ::KeyType: ShieldingCryptoEncrypt + ShieldingCryptoDecrypt + 'static, - S: StfEnclaveSigning + Send + Sync + 'static, - H: HandleState + Send + Sync + 'static, - H::StateT: SgxExternalitiesTrait, - O: EnclaveOnChainOCallApi + EnclaveMetricsOCallApi + EnclaveAttestationOCallApi + 'static, - RRL: RelayerRegistryLookup + Send + Sync + 'static, - ERL: EnclaveRegistryLookup + Send + Sync + 'static, - SRL: SignerRegistryLookup + Send + Sync + 'static, - Responder: SendRpcResponse + Send + Sync + 'static, -{ - match command { - CeremonyCommand::InitCeremony(signers, payload, check_run) => { - // InitCeremony should create ceremony first - let result = MuSig2Ceremony::new( - context.signing_key_pub, - signers, - payload, - context.bitcoin_key_repository.clone(), - check_run, - ); - - match result { - Ok((ceremony, event)) => { - { - let mut registry_write = context.ceremony_registry.write().unwrap(); - if registry_write.contains_key(&ceremony_id) { - let error = - CeremonyError::CeremonyInitError(CeremonyErrorReason::AlreadyExist); - return Some(CeremonyEvent::CeremonyError(vec![], error)) - } - registry_write.insert( - ceremony_id, - (Arc::new(RwLock::new(ceremony)), get_current_timestamp()), - ); - } - Some(event) - }, - Err(e) => { - error!("Could not start ceremony, error: {:?}", e); - let error = - CeremonyError::CeremonyInitError(CeremonyErrorReason::CreateCeremonyError); - Some(CeremonyEvent::CeremonyError(vec![], error)) - }, - } - }, - CeremonyCommand::SaveNonce(signer, nonce) => { - let ceremony_rwlock = - context.ceremony_registry.read().unwrap().get(&ceremony_id).cloned(); - if let Some(ceremony_rwlock) = ceremony_rwlock { - let mut ceremony_write_lock = ceremony_rwlock.0.write().unwrap(); - let event_ret = ceremony_write_lock.receive_nonce(signer, nonce); - match event_ret { - Ok(event) => event, - Err(e) => Some(CeremonyEvent::CeremonyError( - ceremony_write_lock.get_signers_except_self(), - e, - )), - } - } else { - None - } - }, - CeremonyCommand::SavePartialSignature(signer, partial_signature) => { - let ceremony_rwlock = - context.ceremony_registry.read().unwrap().get(&ceremony_id).cloned(); - if let Some(ceremony_rwlock) = ceremony_rwlock { - let mut ceremony_write_lock = ceremony_rwlock.0.write().unwrap(); - let event_ret = ceremony_write_lock.receive_partial_sign(signer, partial_signature); - match event_ret { - Ok(event) => event, - Err(e) => Some(CeremonyEvent::CeremonyError( - ceremony_write_lock.get_signers_except_self(), - e, - )), - } - } else { - None - } - }, - CeremonyCommand::KillCeremony => { - { - context.ceremony_registry.write().unwrap().remove(&ceremony_id); - } - { - context.ceremony_command_tmp.write().unwrap().remove(&ceremony_id); - } - None - }, - } -} - -#[allow(clippy::type_complexity)] -fn handle_request( - request: BitAcrossRequest, - context: Arc< - BitAcrossTaskContext, - >, -) -> Option<(CeremonyId, CeremonyCommand)> -where - SKR: AccessKey + AccessPubkey, - SIGNINGAK: AccessKey, - EKR: AccessKey, - BKR: AccessKey, - TKR: AccessKey + Send + Sync + 'static, - ::KeyType: ShieldingCryptoEncrypt + ShieldingCryptoDecrypt + 'static, - S: StfEnclaveSigning + Send + Sync + 'static, - H: HandleState + Send + Sync + 'static, - O: EnclaveOnChainOCallApi + EnclaveMetricsOCallApi + EnclaveAttestationOCallApi + 'static, - RRL: RelayerRegistryLookup + 'static, - ERL: EnclaveRegistryLookup + 'static, - SRL: SignerRegistryLookup + 'static, - Responder: SendRpcResponse + Send + Sync + 'static, -{ - match request { - BitAcrossRequest::Request(request, sender) => match handle_direct_call(request, context) { - Ok((processing_ret, to_process)) => { - if let Some(processing_ret) = processing_ret { - if let Err(e) = sender.send(Ok(processing_ret)) { - warn!("Unable to submit response back to the handler: {:?}", e); - } - } - to_process - }, - Err(e) => { - if let Err(e) = sender.send(Err(e)) { - warn!("Unable to submit response back to the handler: {:?}", e); - } - None - }, - }, - BitAcrossRequest::ShareCeremonyData(request) => - handle_ceremony_round_call(request, context).unwrap_or_default(), - } -} - -#[allow(clippy::type_complexity)] -fn handle_direct_call( - request: PlainRequest, - context: Arc< - BitAcrossTaskContext, - >, -) -> Result<(Option, Option<(CeremonyId, CeremonyCommand)>), Vec> -where - SKR: AccessKey + AccessPubkey, - SIGNINGAK: AccessKey, - EKR: AccessKey, - BKR: AccessKey, - TKR: AccessKey + Send + Sync + 'static, - ::KeyType: ShieldingCryptoEncrypt + ShieldingCryptoDecrypt + 'static, - S: StfEnclaveSigning + Send + Sync + 'static, - H: HandleState + Send + Sync + 'static, - O: EnclaveOnChainOCallApi + EnclaveMetricsOCallApi + EnclaveAttestationOCallApi + 'static, - RRL: RelayerRegistryLookup + 'static, - ERL: EnclaveRegistryLookup + 'static, - SRL: SignerRegistryLookup + 'static, - Responder: SendRpcResponse + Send + Sync + 'static, -{ - let dc = DirectCallSigned::decode(&mut request.payload.as_slice()).map_err(|e| { - error!("{}", e); - "Failed to decode payload".to_string() - })?; - - let mrenclave = match context.ocall_api.get_mrenclave_of_self() { - Ok(m) => m.m, - Err(_) => { - let err = "Failed to get mrenclave"; - error!("{}", err); - return Err(err.encode()) - }, - }; - debug!("Direct call is: {:?}", dc); - ensure!(dc.verify_signature(&mrenclave, &request.shard), "Failed to verify sig".to_string()); - match dc.call { - DirectCall::SignBitcoin(signer, payload) => { - let hash = blake2_256(&payload.encode()); - let command = sign_bitcoin::handle( - signer, - payload.clone(), - context.relayer_registry_lookup.deref(), - context.signer_registry_lookup.clone(), - context.enclave_registry_lookup.as_ref(), - false, - ) - .map_err(|e| { - error!("SignBitcoin error: {:?}", e); - e.encode() - })?; - let ret = BitAcrossProcessingResult::Submitted(hash); - Ok((Some(ret), Some((payload, command)))) - }, - DirectCall::CheckSignBitcoin(signer) => { - let payload = SignBitcoinPayload::Derived([0u8; 32].to_vec()); - let hash = blake2_256(&payload.encode()); - let command = sign_bitcoin::handle( - signer, - payload.clone(), - context.relayer_registry_lookup.deref(), - context.signer_registry_lookup.clone(), - context.enclave_registry_lookup.as_ref(), - true, - ) - .map_err(|e| { - error!("SignBitcoinCheck error: {:?}", e); - e.encode() - })?; - let ret = BitAcrossProcessingResult::Submitted(hash); - Ok((Some(ret), Some((payload, command)))) - }, - DirectCall::SignEthereum(signer, msg) => sign_ethereum::handle( - signer, - msg, - context.relayer_registry_lookup.deref(), - context.ethereum_key_repository.deref(), - ) - .map_err(|e| { - error!("SignEthereum error: {:?}", e); - e.encode() - }) - .map(|r| (Some(BitAcrossProcessingResult::Ok(r.encode())), None)), - DirectCall::SignTon(signer, payload) => sign_ton::handle( - signer, - payload, - context.relayer_registry_lookup.deref(), - context.ton_key_repository.deref(), - ) - .map_err(|e| { - error!("SignTon error: {:?}", e); - e.encode() - }) - .map(|r| (Some(BitAcrossProcessingResult::Ok(r.encode())), None)), - } -} - -#[allow(clippy::type_complexity)] -fn handle_ceremony_round_call( - request: PlainRequest, - context: Arc< - BitAcrossTaskContext, - >, -) -> Result, Vec> -where - SKR: AccessKey + AccessPubkey, - SIGNINGAK: AccessKey, - EKR: AccessKey, - BKR: AccessKey, - TKR: AccessKey + Send + Sync + 'static, - ::KeyType: ShieldingCryptoEncrypt + ShieldingCryptoDecrypt + 'static, - S: StfEnclaveSigning + Send + Sync + 'static, - H: HandleState + Send + Sync + 'static, - O: EnclaveOnChainOCallApi + EnclaveMetricsOCallApi + EnclaveAttestationOCallApi + 'static, - RRL: RelayerRegistryLookup + 'static, - ERL: EnclaveRegistryLookup + 'static, - SRL: SignerRegistryLookup + 'static, - Responder: SendRpcResponse + Send + Sync + 'static, -{ - let crc = CeremonyRoundCallSigned::decode(&mut request.payload.as_slice()).map_err(|e| { - error!("{}", e); - "Failed to decode payload".to_string() - })?; - - let mrenclave = match context.ocall_api.get_mrenclave_of_self() { - Ok(m) => m.m, - Err(_) => { - let err = "Failed to get mrenclave"; - error!("{}", err); - return Err(err.encode()) - }, - }; - debug!("Ceremony round call is: {:?}", crc); - ensure!(crc.verify_signature(&mrenclave, &request.shard), "Failed to verify sig".to_string()); - match crc.call { - CeremonyRoundCall::NonceShare(signer, message, nonce) => - nonce_share::handle(signer, &message, nonce, context.enclave_registry_lookup.clone()) - .map_err(|e| { - error!("NonceShare error: {:?}", e); - e.encode() - }) - .map(|command| Some((message, command))), - CeremonyRoundCall::PartialSignatureShare(signer, message, signature) => - partial_signature_share::handle( - signer, - &message, - signature, - context.enclave_registry_lookup.clone(), - ) - .map_err(|e| { - error!("PartialSignatureShare error: {:?}", e); - e.encode() - }) - .map(|command| Some((message, command))), - CeremonyRoundCall::KillCeremony(signer, message) => - kill_ceremony::handle(signer, context.enclave_registry_lookup.as_ref()) - .map_err(|e| { - error!("KillCeremony error: {:?}", e); - e.encode() - }) - .map(|command| Some((message, command))), - } -} diff --git a/tee-worker/bitacross/bitacross/core/bc-task-sender/Cargo.toml b/tee-worker/bitacross/bitacross/core/bc-task-sender/Cargo.toml deleted file mode 100644 index 6d0233f459..0000000000 --- a/tee-worker/bitacross/bitacross/core/bc-task-sender/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "bc-task-sender" -authors = ["Trust Computing GmbH "] -version = "0.1.0" -edition = "2021" - -[dependencies] -futures = { workspace = true, optional = true } - -futures_sgx = { workspace = true, optional = true } -sgx_tstd = { workspace = true, features = ["net", "thread"], optional = true } - -codec = { package = "parity-scale-codec", workspace = true } -lazy_static = { workspace = true } - -litentry-primitives = { workspace = true } - -[features] -default = ["std"] -sgx = [ - "futures_sgx", - "sgx_tstd", - "futures_sgx", - "litentry-primitives/sgx", -] -std = [ - "futures", - "futures", - "litentry-primitives/std", -] diff --git a/tee-worker/bitacross/bitacross/core/bc-task-sender/src/lib.rs b/tee-worker/bitacross/bitacross/core/bc-task-sender/src/lib.rs deleted file mode 100644 index 6cbc693766..0000000000 --- a/tee-worker/bitacross/bitacross/core/bc-task-sender/src/lib.rs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -#![feature(trait_alias)] -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -// re-export module to properly feature gate sgx and regular std environment -#[cfg(all(not(feature = "std"), feature = "sgx"))] -pub mod sgx_reexport_prelude { - pub use futures_sgx as futures; -} - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -pub use crate::sgx_reexport_prelude::*; - -use codec::{Decode, Encode}; -use futures::channel::oneshot; -use lazy_static::lazy_static; -use litentry_primitives::PlainRequest; -#[cfg(feature = "std")] -use std::sync::Mutex; -#[cfg(feature = "sgx")] -use std::sync::SgxMutex as Mutex; -use std::{ - format, - string::String, - sync::{ - mpsc::{channel, Receiver, Sender as MpscSender}, - Arc, - }, - vec::Vec, -}; - -#[derive(Debug)] -pub enum BitAcrossRequest { - Request(PlainRequest, oneshot::Sender>>), - ShareCeremonyData(PlainRequest), -} - -#[derive(Encode, Decode, Clone, Debug)] -pub enum BitAcrossProcessingResult { - // we got immediate response - Ok(Vec), - // the response will be produced in the future - Submitted([u8; 32]), -} - -#[derive(Encode, Decode, Clone)] -pub struct BitAcrossResponse { - pub payload: Vec, -} - -pub type BitAcrossSender = MpscSender; - -// Global storage of the sender. Should not be accessed directly. -lazy_static! { - static ref GLOBAL_BIT_ACROSS_TASK_SENDER: Arc>> = - Arc::new(Mutex::new(Default::default())); -} - -pub struct BitAcrossRequestSender {} -impl BitAcrossRequestSender { - pub fn new() -> Self { - Self {} - } -} - -impl Default for BitAcrossRequestSender { - fn default() -> Self { - Self::new() - } -} - -impl BitAcrossRequestSender { - pub fn send(&self, request: BitAcrossRequest) -> Result<(), String> { - // Acquire lock on extrinsic sender - let mutex_guard = GLOBAL_BIT_ACROSS_TASK_SENDER.lock().unwrap(); - let bit_across_task_sender = mutex_guard.clone().unwrap(); - // Release mutex lock, so we don't block the lock longer than necessary. - drop(mutex_guard); - - // Send the request to the receiver loop. - bit_across_task_sender.send(request)?; - - Ok(()) - } -} - -/// Initialization of the task sender. Needs to be called before any sender access. -pub fn init_bit_across_task_sender_storage() -> Receiver { - let (sender, receiver) = channel(); - // It makes no sense to handle the unwrap, as this statement fails only if the lock has been poisoned - // I believe at that point it is an unrecoverable error - let mut bit_across_task_storage = GLOBAL_BIT_ACROSS_TASK_SENDER.lock().unwrap(); - *bit_across_task_storage = Some(BitAcrossTaskSender::new(sender)); - receiver -} - -/// Wrapping struct around the actual sender. Should not be accessed directly. (unnecessary) -#[derive(Clone, Debug)] -pub struct BitAcrossTaskSender { - sender: BitAcrossSender, -} - -impl BitAcrossTaskSender { - pub fn new(sender: BitAcrossSender) -> Self { - Self { sender } - } - - fn send(&self, request: BitAcrossRequest) -> Result<(), String> { - self.sender - .send(request) - .map_err(|e| format!("Failed to send message to BitAcross Handler: {:?}", e)) - } -} diff --git a/tee-worker/bitacross/build.Dockerfile b/tee-worker/bitacross/build.Dockerfile deleted file mode 100644 index d3f5fda9d3..0000000000 --- a/tee-worker/bitacross/build.Dockerfile +++ /dev/null @@ -1,179 +0,0 @@ -# syntax=docker/dockerfile:1 -# Copyright 2021 Integritee AG -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# This is a multi-stage docker file, where the first stage is used -# for building and the second deploys the built application. - -### Builder Stage -################################################## -# todo: we might need to change this image in future -FROM litentry/litentry-tee-dev:latest AS builder -LABEL maintainer="Trust Computing GmbH " - -# set environment variables -ENV SGX_SDK=/opt/sgxsdk -ENV PATH="$PATH:${SGX_SDK}/bin:${SGX_SDK}/bin/x64:/opt/rust/bin" -ENV PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:${SGX_SDK}/pkgconfig" -ENV LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${SGX_SDK}/sdk_libs" -ENV CARGO_NET_GIT_FETCH_WITH_CLI=true - -ENV SCCACHE_CACHE_SIZE="20G" -ENV SCCACHE_DIR="/opt/rust/sccache" -ENV RUSTC_WRAPPER="/opt/rust/bin/sccache" - -# Default SGX MODE is software mode -ARG SGX_MODE=SW -ENV SGX_MODE=$SGX_MODE - -ARG SGX_PRODUCTION=0 -ENV SGX_PRODUCTION=$SGX_PRODUCTION - -ENV HOME=/home/ubuntu - -ARG WORKER_MODE_ARG -ENV WORKER_MODE=$WORKER_MODE_ARG - -ARG ADDITIONAL_FEATURES_ARG -ENV ADDITIONAL_FEATURES=$ADDITIONAL_FEATURES_ARG - -ARG IMAGE_FOR_RELEASE=false -ENV IMAGE_FOR_RELEASE=$IMAGE_FOR_RELEASE - -ARG FINGERPRINT=none - -ARG SGX_COMMERCIAL_KEY -ENV SGX_COMMERCIAL_KEY=$SGX_COMMERCIAL_KEY - -WORKDIR $HOME/tee-worker/bitacross -COPY . $HOME - -RUN unset RUSTC_WRAPPER; -RUN make -RUN make mrenclave 2>&1 | grep MRENCLAVE | awk '{print $2}' > mrenclave.txt -RUN cargo test --release - - -### Base Runner Stage -################################################## -FROM node:18-bookworm-slim AS runner - -RUN apt update && apt install -y libssl-dev iproute2 jq curl protobuf-compiler -RUN corepack enable && corepack prepare pnpm@8.7.6 --activate && corepack enable pnpm - - -### Deployed CLI client -################################################## -FROM runner AS deployed-client -LABEL maintainer="Trust Computing GmbH " - -ARG SCRIPT_DIR=/usr/local/worker-cli -ARG LOG_DIR=/usr/local/log - -ENV SCRIPT_DIR=${SCRIPT_DIR} -ENV LOG_DIR=${LOG_DIR} - -COPY --from=local-builder:latest /home/ubuntu/tee-worker/bitacross/bin/bitacross-cli /usr/local/bin -COPY --from=local-builder:latest /home/ubuntu/tee-worker/bitacross/cli/*.sh /usr/local/worker-cli/ - -RUN chmod +x /usr/local/bin/bitacross-cli ${SCRIPT_DIR}/*.sh -RUN mkdir ${LOG_DIR} - -RUN ldd /usr/local/bin/bitacross-cli && /usr/local/bin/bitacross-cli --version - -ENTRYPOINT ["/usr/local/bin/bitacross-cli"] - - -### Deployed worker service -################################################## -FROM runner AS deployed-worker -LABEL maintainer="Trust Computing GmbH " - -WORKDIR /usr/local/bin - -COPY --from=local-builder:latest /opt/sgxsdk /opt/sgxsdk -COPY --from=local-builder:latest /home/ubuntu/tee-worker/bitacross/bin/* /usr/local/bin -COPY --from=local-builder:latest /home/ubuntu/tee-worker/bitacross/cli/*.sh /usr/local/worker-cli/ -COPY --from=local-builder:latest /lib/x86_64-linux-gnu/libsgx* /lib/x86_64-linux-gnu/ -COPY --from=local-builder:latest /lib/x86_64-linux-gnu/libdcap* /lib/x86_64-linux-gnu/ -COPY --from=local-builder:latest /lib/x86_64-linux-gnu/libprotobuf* /lib/x86_64-linux-gnu/ - -RUN touch spid.txt key.txt -RUN chmod +x /usr/local/bin/bitacross-worker -RUN ls -al /usr/local/bin - -# checks -ENV SGX_SDK=/opt/sgxsdk -ENV SGX_ENCLAVE_SIGNER=$SGX_SDK/bin/x64/sgx_sign -ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/intel/sgx-aesm-service/aesm:$SGX_SDK/sdk_libs -ENV AESM_PATH=/opt/intel/sgx-aesm-service/aesm - -RUN ldd /usr/local/bin/bitacross-worker && /usr/local/bin/bitacross-worker --version - -# TODO: use entrypoint and aesm service launch, see P-295 too -ENTRYPOINT ["/usr/local/bin/bitacross-worker"] - - -### Release worker image -################################################## -FROM ubuntu:22.04 AS worker-release -LABEL maintainer="Trust Computing GmbH " - -RUN apt update && apt install -y libssl-dev iproute2 curl protobuf-compiler - -# Adding default user litentry with uid 1000 -ARG UID=1000 -RUN adduser -u ${UID} --disabled-password --gecos '' litentry -RUN adduser -u ${UID} litentry sudo -RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers -# to fix Multi-node distributed worker encounters SGX permission errors. -RUN groupadd -g 121 sgx_prv && \ - groupadd -g 108 sgx && \ - usermod -aG sgx litentry && \ - usermod -aG sgx_prv litentry - -COPY --from=local-builder:latest /opt/sgxsdk /opt/sgxsdk -COPY --from=local-builder:latest /lib/x86_64-linux-gnu/libsgx* /lib/x86_64-linux-gnu/ -COPY --from=local-builder:latest /lib/x86_64-linux-gnu/libdcap* /lib/x86_64-linux-gnu/ -COPY --from=local-builder:latest /lib/x86_64-linux-gnu/libprotobuf* /lib/x86_64-linux-gnu/ - -ENV DEBIAN_FRONTEND=noninteractive -ENV TERM=xterm -ENV SGX_SDK=/opt/sgxsdk -ENV PATH="$PATH:${SGX_SDK}/bin:${SGX_SDK}/bin/x64:/opt/rust/bin" -ENV PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:${SGX_SDK}/pkgconfig" -ENV LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${SGX_SDK}/sdk_libs" - -RUN mkdir -p /origin /data - -COPY --from=local-builder:latest /home/ubuntu/tee-worker/bitacross/bin/* /origin -COPY --from=local-builder:latest /home/ubuntu/tee-worker/bitacross/mrenclave.txt /origin -COPY --from=local-builder:latest /home/ubuntu/tee-worker/bitacross/entrypoint.sh /usr/local/bin/entrypoint.sh - -WORKDIR /origin - -RUN touch spid.txt key.txt && \ - cp ./bitacross-* /usr/local/bin/ && \ - chmod +x /usr/local/bin/bitacross-* && \ - chmod +x /usr/local/bin/entrypoint.sh && \ - ls -al /usr/local/bin - -RUN ldd /usr/local/bin/bitacross-worker && /usr/local/bin/bitacross-worker --version - -ENV DATA_DIR=/data - -USER litentry -WORKDIR /data - -ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] \ No newline at end of file diff --git a/tee-worker/bitacross/cli/Cargo.toml b/tee-worker/bitacross/cli/Cargo.toml deleted file mode 100644 index 86bcdb8911..0000000000 --- a/tee-worker/bitacross/cli/Cargo.toml +++ /dev/null @@ -1,54 +0,0 @@ -[package] -name = "bitacross-cli" -version = "0.0.1" -authors = ['Trust Computing GmbH ', 'Integritee AG '] -edition = "2021" - -[dependencies] -base58 = { workspace = true } -chrono = "*" -clap = { version = "=4.1.0", features = ["derive"] } -codec = { package = "parity-scale-codec", workspace = true, features = ["std"] } -env_logger = { workspace = true } -hdrhistogram = "7.5.0" -hex = { workspace = true, features = ["std"] } -log = { workspace = true, features = ["std"] } -rand = "0.8.5" -rayon = "1.5.1" -regex = "1.9.5" -reqwest = { version = "0.11", features = ["blocking", "json"] } -serde = { workspace = true, features = ["std"] } -serde_json = { workspace = true, features = ["std"] } -thiserror = { workspace = true } -urlencoding = "2.1.3" - -sgx_crypto_helper = { workspace = true, features = ["ucrypto_help"] } -substrate-api-client = { workspace = true } -substrate-client-keystore = { workspace = true } - -sp-application-crypto = { workspace = true, features = ["std"] } -sp-core = { workspace = true, features = ["std"] } -sp-keyring = { workspace = true } -sp-keystore = { workspace = true, features = ["std"] } -sp-runtime = { workspace = true, features = ["std"] } - -bc-musig2-ceremony = { path = "../bitacross/core/bc-musig2-ceremony" } -ita-parentchain-interface = { package = "bc-ita-parentchain-interface", path = "../app-libs/parentchain-interface" } -ita-stf = { package = "bc-ita-stf", path = "../app-libs/stf" } -lc-direct-call = { path = "../litentry/core/direct-call" } - -itc-rpc-client = { workspace = true } -itp-node-api = { workspace = true, features = ["std"] } -itp-rpc = { workspace = true, features = ["std"] } -itp-sgx-crypto = { workspace = true, features = ["std"] } -itp-stf-primitives = { workspace = true, features = ["std"] } -itp-types = { workspace = true, features = ["std"] } -itp-utils = { workspace = true, features = ["std"] } -litentry-primitives = { workspace = true, features = ["std"] } - -[features] -default = [] -offchain-worker = [] -development = [] -# dcap feature flag is not used in this crate, but for easier build purposes only it present here as well -dcap = [] diff --git a/tee-worker/bitacross/cli/README.md b/tee-worker/bitacross/cli/README.md deleted file mode 100644 index a1eb6463f5..0000000000 --- a/tee-worker/bitacross/cli/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Integritee CLI client -Interact with the Integritee chain and workers from the command line - -Includes -* keystore (incompatible with polkadot js app json) -* basic balance transfer -* Integritee-specific calls - -## examples -``` -> ./bitacross-cli transfer //Bob //Alice 12345 -> ./bitacross-cli -u ws://127.0.0.1 list-workers -number of workers registered: 1 -Enclave 1 - AccountId: 5HN8RGEiJuc9iNA3vfiYj7Lk6ULWzBZXvSDheohBu3usSUqn - MRENCLAVE: 4GMb72Acyg8hnnnGEJ89jZK5zxNC4LvSe2ME96wLRV6J - RA timestamp: 2022-03-16 10:43:12.001 UTC - URL: wss://127.0.0.1:2345 -> ./bitacross-cli -P 2345 trusted --direct --mrenclave 4GMb72Acyg8hnnn -GE4LvSe2ME96wLRV6J unshield-funds //Bob //Alice 12345 -from ss58 is 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty -to ss58 is 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY -send trusted call unshield_funds from 5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty to 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY: 12345 -Trusted call 0x69ddfd1698bd2d629180c2dca34ce7add087526c51f43cf68245241b3f13154e is Submitted -Trusted call 0x69ddfd1698bd2d629180c2dca34ce7add087526c51f43cf68245241b3f13154e is Invalid - -``` - -## housekeeping tasks - -populate all TCBinfo's Intel has published -``` -../target/release/bitacross-cli register-tcb-info //Alice --fmspc 00606a000000 -../target/release/bitacross-cli register-tcb-info //Alice --all -``` diff --git a/tee-worker/bitacross/cli/benchmark.sh b/tee-worker/bitacross/cli/benchmark.sh deleted file mode 100755 index 080651fdc6..0000000000 --- a/tee-worker/bitacross/cli/benchmark.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash - -while getopts ":m:p:A:u:V:C:" opt; do - case $opt in - m) - READMRENCLAVE=$OPTARG - ;; - p) - NPORT=$OPTARG - ;; - A) - WORKER1PORT=$OPTARG - ;; - u) - NODEURL=$OPTARG - ;; - V) - WORKER1URL=$OPTARG - ;; - C) - CLIENT_BIN=$OPTARG - ;; - *) - ;; - esac -done - -# using default port if none given as arguments -NPORT=${NPORT:-9944} -NODEURL=${NODEURL:-"ws://127.0.0.1"} - -WORKER1PORT=${WORKER1PORT:-2000} -WORKER1URL=${WORKER1URL:-"wss://127.0.0.1"} - -CLIENT_BIN=${CLIENT_BIN:-"./../bin/bitacross-cli"} - -echo "Using client binary ${CLIENT_BIN}" -echo "Using node uri ${NODEURL}:${NPORT}" -echo "Using trusted-worker uri ${WORKER1URL}:${WORKER1PORT}" - -CLIENTWORKER1="${CLIENT_BIN} -p ${NPORT} -P ${WORKER1PORT} -u ${NODEURL} -U ${WORKER1URL}" - -if [ "$READMRENCLAVE" = "file" ] -then - read -r MRENCLAVE <<< "$(cat ~/mrenclave.b58)" - echo "Reading MRENCLAVE from file: ${MRENCLAVE}" -else - # this will always take the first MRENCLAVE found in the registry !! - read -r MRENCLAVE <<< "$($CLIENTWORKER1 list-workers | awk '/ MRENCLAVE: / { print $2; exit }')" - echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" -fi -[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; } - -# needed when many clients are started -ulimit -S -n 4096 - -echo "Starting benchmark" -${CLIENTWORKER1} trusted --direct --mrenclave "${MRENCLAVE}" benchmark 20 100 -w -echo "" - -exit 0 diff --git a/tee-worker/bitacross/cli/lit_parentchain_nonce.sh b/tee-worker/bitacross/cli/lit_parentchain_nonce.sh deleted file mode 100755 index 95eda7d275..0000000000 --- a/tee-worker/bitacross/cli/lit_parentchain_nonce.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -# Copyright 2020-2023 Trust Computing GmbH. - -while getopts ":p:A:B:u:W:V:C:" opt; do - case $opt in - p) - NPORT=$OPTARG - ;; - A) - WORKER1PORT=$OPTARG - ;; - u) - NODEURL=$OPTARG - ;; - V) - WORKER1URL=$OPTARG - ;; - C) - CLIENT_BIN=$OPTARG - ;; - esac -done - -# Using default port if none given as arguments. -NPORT=${NPORT:-9944} -NODEURL=${NODEURL:-"ws://litentry-node"} - -WORKER1PORT=${WORKER1PORT:-2000} -WORKER1URL=${WORKER1URL:-"wss://litentry-node"} - -CLIENT_BIN=${CLIENT_BIN:-"./../bin/bitacross-cli"} - -echo "Using client binary $CLIENT_BIN" -echo "Using node uri $NODEURL:$NPORT" -echo "Using trusted-worker uri $WORKER1URL:$WORKER1PORT" -echo "" - -CLIENT="$CLIENT_BIN -p $NPORT -P $WORKER1PORT -u $NODEURL -U $WORKER1URL" -echo "CLIENT is: $CLIENT" - -echo "* Query on-chain enclave registry:" -WORKERS=$($CLIENT list-workers) -echo "WORKERS: " -echo "${WORKERS}" -echo "" - -if [ "$READMRENCLAVE" = "file" ] -then - read MRENCLAVE <<< $(cat ~/mrenclave.b58) - echo "Reading MRENCLAVE from file: ${MRENCLAVE}" -else - # This will always take the first MRENCLAVE found in the registry !! - read MRENCLAVE <<< $(echo "$WORKERS" | awk '/ MRENCLAVE: / { print $2; exit }') - echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" -fi -[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; } - -sleep 10 -echo "* Send wrong parentchain extrinsic" -${CLIENT} trusted --mrenclave $MRENCLAVE --direct send-erroneous-parentchain-call -echo "" - -sleep 20 -# wait for 10 `ParentchainBlockProcessed` events, which should take around 2 min (1 worker) -# if the incoming parentchain extrinsic is blocked (due to the wrong nonce), there won't be -# such many events. -set -e -timeout -v --foreground 150s $CLIENT listen -e 10 diff --git a/tee-worker/bitacross/cli/lit_ts_integration_test.sh b/tee-worker/bitacross/cli/lit_ts_integration_test.sh deleted file mode 100755 index 04c297a7c3..0000000000 --- a/tee-worker/bitacross/cli/lit_ts_integration_test.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/bin/bash - -# Copyright 2020-2024 Trust Computing GmbH. - -set -euo pipefail - -while getopts ":p:A:u:W:V:C:" opt; do - case $opt in - p) - NPORT=$OPTARG - ;; - A) - WORKER1PORT=$OPTARG - ;; - u) - NODEURL=$OPTARG - ;; - W) - NODEHTTPURL=$OPTARG - ;; - V) - WORKER1URL=$OPTARG - ;; - C) - CLIENT_BIN=$OPTARG - ;; - esac -done - -# Using default port if none given as arguments. -NPORT=${NPORT:-9944} -NODEURL=${NODEURL:-"ws://litentry-node"} -NODEHTTPURL=${NODEHTTPURL:-"http://litentry-node"} -WORKER1PORT=${WORKER1PORT:-2011} -WORKER1URL=${WORKER1URL:-"ws://bitacross-worker-1"} - -CLIENT_BIN=${CLIENT_BIN:-"/usr/local/bin/bitacross-cli"} - -CLIENT="${CLIENT_BIN} -p ${NPORT} -P ${WORKER1PORT} -u ${NODEURL} -U ${WORKER1URL}" - -function usage() { - echo "" - echo "This is a script for bitacross-worker integration ts tests. Pass test name as first argument" - echo "" -} - -[ $# -ne 1 ] && (usage; exit 1) -TEST=$1 - -echo "Using client binary $CLIENT_BIN" -echo "Using node uri $NODEURL:$NPORT" -echo "Using trusted-worker uri $WORKER1URL:$WORKER1PORT" -echo "Using node http uri $NODEHTTPURL:$NPORT" -echo "" - -cd /ts-tests -pnpm install - -NODE_ENV=staging pnpm --filter integration-tests run test $TEST diff --git a/tee-worker/bitacross/cli/src/attesteer/commands/mod.rs b/tee-worker/bitacross/cli/src/attesteer/commands/mod.rs deleted file mode 100644 index 70119bf399..0000000000 --- a/tee-worker/bitacross/cli/src/attesteer/commands/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -mod send_dcap_quote; -mod send_ias_attestation; - -pub use self::{ - send_dcap_quote::SendDcapQuoteCmd, send_ias_attestation::SendIasAttestationReportCmd, -}; diff --git a/tee-worker/bitacross/cli/src/attesteer/commands/send_dcap_quote.rs b/tee-worker/bitacross/cli/src/attesteer/commands/send_dcap_quote.rs deleted file mode 100644 index 6ee0baf02f..0000000000 --- a/tee-worker/bitacross/cli/src/attesteer/commands/send_dcap_quote.rs +++ /dev/null @@ -1,65 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{command_utils::get_worker_api_direct, Cli}; -use itc_rpc_client::direct_client::DirectApi; -use itp_rpc::{Id, RpcRequest, RpcResponse, RpcReturnValue}; -use itp_types::DirectRequestStatus; -use itp_utils::FromHexPrefixed; -use log::*; -use std::fs::read_to_string; - -/// Forward DCAP quote for verification. -#[derive(Debug, Clone, Parser)] -pub struct SendDcapQuoteCmd { - /// Hex encoded DCAP quote filename. - quote: String, -} - -impl SendDcapQuoteCmd { - pub fn run(&self, cli: &Cli) { - let direct_api = get_worker_api_direct(cli); - let hex_encoded_quote = match read_to_string(&self.quote) { - Ok(hex_encoded_quote) => hex_encoded_quote, - Err(e) => panic!("Opening hex encoded DCAP quote file failed: {:?}", e), - }; - - let rpc_method = "attesteer_forwardDcapQuote".to_owned(); - let jsonrpc_call: String = RpcRequest::compose_jsonrpc_call( - Id::Text("1".to_string()), - rpc_method, - vec![hex_encoded_quote], - ) - .unwrap(); - - let rpc_response_str = direct_api.get(&jsonrpc_call).unwrap(); - - // Decode RPC response. - let Ok(rpc_response) = serde_json::from_str::(&rpc_response_str) else { - panic!("Can't parse RPC response: '{rpc_response_str}'"); - }; - let rpc_return_value = match RpcReturnValue::from_hex(&rpc_response.result) { - Ok(rpc_return_value) => rpc_return_value, - Err(e) => panic!("Failed to decode RpcReturnValue: {:?}", e), - }; - - match rpc_return_value.status { - DirectRequestStatus::Ok => println!("DCAP quote verification succeded."), - _ => error!("DCAP quote verification failed"), - } - } -} diff --git a/tee-worker/bitacross/cli/src/attesteer/commands/send_ias_attestation.rs b/tee-worker/bitacross/cli/src/attesteer/commands/send_ias_attestation.rs deleted file mode 100644 index af4128b138..0000000000 --- a/tee-worker/bitacross/cli/src/attesteer/commands/send_ias_attestation.rs +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use itc_rpc_client::direct_client::DirectApi; -use itp_rpc::{Id, RpcRequest, RpcResponse, RpcReturnValue}; -use itp_types::DirectRequestStatus; -use itp_utils::FromHexPrefixed; -use log::*; -use std::fs::read_to_string; - -use crate::{command_utils::get_worker_api_direct, Cli}; - -/// Forward IAS attestation report for verification. -#[derive(Debug, Clone, Parser)] -pub struct SendIasAttestationReportCmd { - /// Hex encoded IAS attestation report filename. - report: String, -} - -impl SendIasAttestationReportCmd { - pub fn run(&self, cli: &Cli) { - let direct_api = get_worker_api_direct(cli); - let hex_encoded_report = match read_to_string(&self.report) { - Ok(hex_encoded_report) => hex_encoded_report, - Err(e) => panic!("Opening hex encoded IAS attestation report file failed: {:?}", e), - }; - - let rpc_method = "attesteer_forwardIasAttestationReport".to_owned(); - let jsonrpc_call: String = RpcRequest::compose_jsonrpc_call( - Id::Text("1".to_string()), - rpc_method, - vec![hex_encoded_report], - ) - .unwrap(); - - let rpc_response_str = direct_api.get(&jsonrpc_call).unwrap(); - - // Decode RPC response. - let Ok(rpc_response) = serde_json::from_str::(&rpc_response_str) else { - panic!("Can't parse RPC response: '{rpc_response_str}'"); - }; - let rpc_return_value = match RpcReturnValue::from_hex(&rpc_response.result) { - Ok(rpc_return_value) => rpc_return_value, - Err(e) => panic!("Failed to decode RpcReturnValue: {:?}", e), - }; - - match rpc_return_value.status { - DirectRequestStatus::Ok => println!("IAS attestation report verification succeded."), - _ => error!("IAS attestation report verification failed"), - } - } -} diff --git a/tee-worker/bitacross/cli/src/attesteer/mod.rs b/tee-worker/bitacross/cli/src/attesteer/mod.rs deleted file mode 100644 index 9f03c59065..0000000000 --- a/tee-worker/bitacross/cli/src/attesteer/mod.rs +++ /dev/null @@ -1,41 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::Cli; - -use self::commands::{SendDcapQuoteCmd, SendIasAttestationReportCmd}; - -mod commands; - -/// Attesteer subcommands for the CLI. -#[derive(Debug, clap::Subcommand)] -pub enum AttesteerCommand { - /// Forward DCAP quote for verification. - SendDCAPQuote(SendDcapQuoteCmd), - - /// Forward IAS attestation report for verification. - SendIASAttestationReport(SendIasAttestationReportCmd), -} - -impl AttesteerCommand { - pub fn run(&self, cli: &Cli) { - match self { - AttesteerCommand::SendDCAPQuote(cmd) => cmd.run(cli), - AttesteerCommand::SendIASAttestationReport(cmd) => cmd.run(cli), - } - } -} diff --git a/tee-worker/bitacross/cli/src/base_cli/commands/balance.rs b/tee-worker/bitacross/cli/src/base_cli/commands/balance.rs deleted file mode 100644 index cea86ae48b..0000000000 --- a/tee-worker/bitacross/cli/src/base_cli/commands/balance.rs +++ /dev/null @@ -1,39 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - command_utils::{get_accountid_from_str, get_chain_api}, - Cli, CliResult, CliResultOk, -}; -use substrate_api_client::GetAccountInformation; - -#[derive(Parser)] -pub struct BalanceCommand { - /// AccountId in ss58check format - account: String, -} - -impl BalanceCommand { - pub(crate) fn run(&self, cli: &Cli) -> CliResult { - let api = get_chain_api(cli); - let accountid = get_accountid_from_str(&self.account); - let balance = - if let Some(data) = api.get_account_data(&accountid).unwrap() { data.free } else { 0 }; - println!("{}", balance); - Ok(CliResultOk::Balance { balance }) - } -} diff --git a/tee-worker/bitacross/cli/src/base_cli/commands/faucet.rs b/tee-worker/bitacross/cli/src/base_cli/commands/faucet.rs deleted file mode 100644 index 256b59a72e..0000000000 --- a/tee-worker/bitacross/cli/src/base_cli/commands/faucet.rs +++ /dev/null @@ -1,65 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - command_utils::{get_accountid_from_str, get_chain_api}, - Cli, CliResult, CliResultOk, -}; -use codec::Compact; -use itp_types::{parentchain::AccountId, OpaqueCall}; -use sp_keyring::AccountKeyring; -use sp_runtime::MultiAddress; -use std::vec::Vec; -use substrate_api_client::{ - ac_compose_macros::{compose_call, compose_extrinsic_offline}, - SubmitExtrinsic, -}; - -const PREFUNDING_AMOUNT: u128 = 1_000_000_000; - -#[derive(Parser)] -pub struct FaucetCommand { - /// Account(s) to be funded, ss58check encoded - #[clap(num_args = 1.., required = true)] - accounts: Vec, -} - -impl FaucetCommand { - pub(crate) fn run(&self, cli: &Cli) -> CliResult { - let mut api = get_chain_api(cli); - api.set_signer(AccountKeyring::Alice.pair().into()); - let mut nonce = api.get_nonce().unwrap(); - for account in &self.accounts { - let to = get_accountid_from_str(account); - let call = OpaqueCall::from_tuple(&compose_call!( - api.metadata(), - "Balances", - "transfer_keep_alive", - MultiAddress::::Id(to.clone()), - Compact(PREFUNDING_AMOUNT) - )); - #[allow(clippy::redundant_clone)] - let xt = compose_extrinsic_offline!(api.signer().unwrap(), call, api.extrinsic_params(nonce)); - // send and watch extrinsic until finalized - println!("Faucet drips to {} (Alice's nonce={})", to, nonce); - let _blockh = api.submit_extrinsic(xt).unwrap(); - nonce += 1; - } - - Ok(CliResultOk::None) - } -} diff --git a/tee-worker/bitacross/cli/src/base_cli/commands/listen.rs b/tee-worker/bitacross/cli/src/base_cli/commands/listen.rs deleted file mode 100644 index 9213b45f76..0000000000 --- a/tee-worker/bitacross/cli/src/base_cli/commands/listen.rs +++ /dev/null @@ -1,88 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{command_utils::get_chain_api, Cli, CliResult, CliResultOk}; - -use itp_types::parentchain::events::{BalanceTransfer, ParentchainBlockProcessed}; -use log::*; -use substrate_api_client::{GetChainInfo, SubscribeEvents}; - -#[derive(Parser)] -pub struct ListenCommand { - /// exit after given number of parentchain events - #[clap(short, long = "exit-after")] - events: Option, - - /// exit after given number of blocks - #[clap(short, long = "await-blocks")] - blocks: Option, -} - -impl ListenCommand { - pub(crate) fn run(&self, cli: &Cli) -> CliResult { - println!("{:?} {:?}", self.events, self.blocks); - let api = get_chain_api(cli); - info!("Subscribing to events (solo or para)"); - let mut count = 0u32; - let mut blocks = 0u32; - let mut subscription = api.subscribe_events().unwrap(); - loop { - if let Some(e) = self.events { - if count >= e { - return Ok(CliResultOk::None) - } - }; - if let Some(b) = self.blocks { - if blocks >= b { - return Ok(CliResultOk::None) - } - }; - - let events = subscription.next_events_from_metadata().unwrap().unwrap(); - blocks += 1; - let header = api.get_header(None).unwrap().unwrap(); - println!("block number (HEAD): {}", header.number); - for event in events.iter() { - let event = event.unwrap(); - count += 1; - match event.pallet_name() { - "System" => continue, - "TransactionPayment" => continue, - "Treasury" => continue, - "Balances" => match event.variant_name() { - "Deposit" => continue, - "Withdraw" => continue, - "Transfer" => - if let Ok(Some(ev)) = event.as_event::() { - println!("{:?}", ev); - }, - _ => println!("{}::{}", event.pallet_name(), event.variant_name()), - }, - "Teebag" => match event.variant_name() { - "ParentchainBlockProcessed" => { - if let Ok(Some(ev)) = event.as_event::() { - println!("{:?}", ev); - } - }, - _ => println!("{}::{}", event.pallet_name(), event.variant_name()), - }, - _ => println!("{}::{}", event.pallet_name(), event.variant_name()), - } - } - } - } -} diff --git a/tee-worker/bitacross/cli/src/base_cli/commands/litentry/mod.rs b/tee-worker/bitacross/cli/src/base_cli/commands/litentry/mod.rs deleted file mode 100644 index 20182fa371..0000000000 --- a/tee-worker/bitacross/cli/src/base_cli/commands/litentry/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . diff --git a/tee-worker/bitacross/cli/src/base_cli/commands/mod.rs b/tee-worker/bitacross/cli/src/base_cli/commands/mod.rs deleted file mode 100644 index 033b15b253..0000000000 --- a/tee-worker/bitacross/cli/src/base_cli/commands/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -pub mod balance; -pub mod faucet; -pub mod listen; -pub mod litentry; -pub mod register_tcb_info; -pub mod transfer; diff --git a/tee-worker/bitacross/cli/src/base_cli/commands/register_tcb_info.rs b/tee-worker/bitacross/cli/src/base_cli/commands/register_tcb_info.rs deleted file mode 100644 index 4ab9b55a45..0000000000 --- a/tee-worker/bitacross/cli/src/base_cli/commands/register_tcb_info.rs +++ /dev/null @@ -1,146 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - command_utils::{get_chain_api, *}, - Cli, CliResult, CliResultOk, -}; -use itp_node_api::api_client::TEEBAG; -use itp_types::{parentchain::Hash, OpaqueCall}; -use itp_utils::ToHexPrefixed; -use log::*; -use regex::Regex; -use serde::Deserialize; -use substrate_api_client::{ - ac_compose_macros::{compose_call, compose_extrinsic_offline}, - SubmitAndWatch, XtStatus, -}; -use urlencoding; - -#[derive(Debug, Deserialize)] -struct Platform { - fmspc: String, - #[serde(rename = "platform")] - _platform: String, -} - -#[derive(Parser)] -pub struct RegisterTcbInfoCommand { - /// Sender's parentchain AccountId in ss58check format. - sender: String, - /// Intel's Family-Model-Stepping-Platform-Custom SKU. 6-Byte non-prefixed hex value - #[clap(short, long, action, conflicts_with = "all")] - fmspc: Option, - /// registers all fmspc currently published by Intel - #[clap(short, long, action)] - all: bool, -} - -impl RegisterTcbInfoCommand { - pub(crate) fn run(&self, cli: &Cli) -> CliResult { - let mut chain_api = get_chain_api(cli); - - // Get the sender. - let from = get_pair_from_str(&self.sender); - chain_api.set_signer(from.into()); - - let fmspcs = if self.all { - trace!("fetching all fmspc's from api.trustedservices.intel.com"); - let fmspcs = reqwest::blocking::get( - "https://api.trustedservices.intel.com/sgx/certification/v4/fmspcs", - ) - .unwrap(); - let fmspcs: Vec = fmspcs.json().expect("Error parsing JSON"); - println!("{:?}", fmspcs); - fmspcs.into_iter().map(|f| f.fmspc).collect() - } else if let Some(fmspc) = self.fmspc.clone() { - vec![fmspc] - } else { - panic!("must specify either '--all' or '--fmspc'"); - }; - let mut nonce = chain_api.get_nonce().unwrap(); - let xt_hashes: Vec<(String, Option)> = fmspcs - .into_iter() - .map(|fmspc| { - println!( - "fetching tcb info for fmspc {} from api.trustedservices.intel.com", - fmspc - ); - let response = reqwest::blocking::get(format!( - "https://api.trustedservices.intel.com/sgx/certification/v4/tcb?fmspc={}", - fmspc - )) - .unwrap(); - //extract certificate chain from header - let certificate_chain = urlencoding::decode( - response.headers().get("TCB-Info-Issuer-Chain").unwrap().to_str().unwrap(), - ) - .unwrap() - .to_string(); - trace!("certificate chain: \n{}", certificate_chain); - - let body = response.text().unwrap(); - trace!("raw json: \n{}", body); - let re = Regex::new(r#"tcbInfo\"\s?:(\{.*\}),\s?\"signature"#).unwrap(); - let tcb_info = &re.captures(&body).unwrap()[1]; - let re = Regex::new(r#"\"signature\"\s?:\s?\"(.*)\"\}"#).unwrap(); - let intel_signature_hex = &re.captures(&body).unwrap()[1]; - trace!("TCB info: {}", tcb_info); - trace!("signature: {}", intel_signature_hex); - - let intel_signature = hex::decode(intel_signature_hex).unwrap(); - - let call = OpaqueCall::from_tuple(&compose_call!( - chain_api.metadata(), - TEEBAG, - "register_tcb_info", - tcb_info, - intel_signature, - certificate_chain - )); - - trace!( - "encoded call to be sent as extrinsic with nonce {}: {}", - nonce, - call.to_hex() - ); - - let xt = compose_extrinsic_offline!( - chain_api.clone().signer().unwrap(), - call, - chain_api.extrinsic_params(nonce) - ); - nonce += 1; - match chain_api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock) { - Ok(xt_report) => { - println!( - "[+] register_tcb_info. extrinsic hash: {:?} / status: {:?}", - xt_report.extrinsic_hash, xt_report.status, - ); - (fmspc, Some(xt_report.extrinsic_hash)) - }, - Err(e) => { - error!("register_tcb_info extrinsic failed {:?}", e); - (fmspc, None) - }, - } - }) - .collect(); - println!("{:?}", xt_hashes); - Ok(CliResultOk::None) - } -} diff --git a/tee-worker/bitacross/cli/src/base_cli/commands/transfer.rs b/tee-worker/bitacross/cli/src/base_cli/commands/transfer.rs deleted file mode 100644 index d8b26f0b90..0000000000 --- a/tee-worker/bitacross/cli/src/base_cli/commands/transfer.rs +++ /dev/null @@ -1,61 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - command_utils::{get_accountid_from_str, get_chain_api, *}, - Cli, CliResult, CliResultOk, -}; -use ita_parentchain_interface::integritee::Balance; -use log::*; -use sp_core::{crypto::Ss58Codec, Pair}; -use substrate_api_client::{ - extrinsic::BalancesExtrinsics, GetAccountInformation, SubmitAndWatch, XtStatus, -}; - -#[derive(Parser)] -pub struct TransferCommand { - /// sender's AccountId in ss58check format - from: String, - - /// recipient's AccountId in ss58check format - to: String, - - /// amount to be transferred - amount: Balance, -} - -impl TransferCommand { - pub(crate) fn run(&self, cli: &Cli) -> CliResult { - let from_account = get_pair_from_str(&self.from); - let to_account = get_accountid_from_str(&self.to); - info!("from ss58 is {}", from_account.public().to_ss58check()); - info!("to ss58 is {}", to_account.to_ss58check()); - let mut api = get_chain_api(cli); - api.set_signer(from_account.into()); - let xt = api.balance_transfer_allow_death(to_account.clone().into(), self.amount); - let tx_report = api.submit_and_watch_extrinsic_until(xt, XtStatus::InBlock).unwrap(); - println!( - "[+] L1 extrinsic success. extrinsic hash: {:?} / status: {:?}", - tx_report.extrinsic_hash, tx_report.status - ); - let result = api.get_account_data(&to_account).unwrap().unwrap(); - let balance = result.free; - println!("balance for {} is now {}", to_account, balance); - - Ok(CliResultOk::Balance { balance }) - } -} diff --git a/tee-worker/bitacross/cli/src/base_cli/mod.rs b/tee-worker/bitacross/cli/src/base_cli/mod.rs deleted file mode 100644 index d74d538f2f..0000000000 --- a/tee-worker/bitacross/cli/src/base_cli/mod.rs +++ /dev/null @@ -1,176 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - base_cli::commands::{ - balance::BalanceCommand, faucet::FaucetCommand, listen::ListenCommand, - register_tcb_info::RegisterTcbInfoCommand, transfer::TransferCommand, - }, - command_utils::*, - Cli, CliResult, CliResultOk, ED25519_KEY_TYPE, SR25519_KEY_TYPE, -}; -use base58::ToBase58; -use chrono::{DateTime, Utc}; -use clap::Subcommand; -use itc_rpc_client::direct_client::DirectApi; -use itp_node_api::api_client::PalletTeebagApi; -use itp_types::WorkerType; -use sp_core::crypto::Ss58Codec; -use sp_keystore::Keystore; -use std::{ - path::PathBuf, - time::{Duration, UNIX_EPOCH}, -}; -use substrate_client_keystore::LocalKeystore; - -mod commands; - -#[derive(Subcommand)] -pub enum BaseCommand { - /// query parentchain balance for AccountId - Balance(BalanceCommand), - - /// generates a new account for the integritee chain in your local keystore - NewAccount, - - /// lists all accounts in your local keystore for the integritee chain - ListAccounts, - - /// query node metadata and print it as json to stdout - PrintMetadata, - - /// query sgx-runtime metadata and print it as json to stdout - PrintSgxMetadata, - - /// send some bootstrapping funds to supplied account(s) - Faucet(FaucetCommand), - - /// transfer funds from one parentchain account to another - Transfer(TransferCommand), - - /// query enclave registry and list all workers - ListWorkers, - - /// listen to parentchain events - Listen(ListenCommand), - - /// Register TCB info for FMSPC - RegisterTcbInfo(RegisterTcbInfoCommand), - - // Litentry's commands below - /// query sgx-runtime metadata and print the raw (hex-encoded) metadata to stdout - /// we could have added a parameter like `--raw` to `PrintSgxMetadata`, but - /// we want to keep our changes isolated - PrintSgxMetadataRaw, -} - -impl BaseCommand { - pub fn run(&self, cli: &Cli) -> CliResult { - match self { - BaseCommand::Balance(cmd) => cmd.run(cli), - BaseCommand::NewAccount => new_account(), - BaseCommand::ListAccounts => list_accounts(), - BaseCommand::PrintMetadata => print_metadata(cli), - BaseCommand::PrintSgxMetadata => print_sgx_metadata(cli), - BaseCommand::Faucet(cmd) => cmd.run(cli), - BaseCommand::Transfer(cmd) => cmd.run(cli), - BaseCommand::ListWorkers => list_workers(cli), - BaseCommand::Listen(cmd) => cmd.run(cli), - BaseCommand::RegisterTcbInfo(cmd) => cmd.run(cli), - // Litentry's commands below - BaseCommand::PrintSgxMetadataRaw => print_sgx_metadata_raw(cli), - } - } -} - -fn new_account() -> CliResult { - let store = LocalKeystore::open(PathBuf::from(&KEYSTORE_PATH), None).unwrap(); - let key = LocalKeystore::sr25519_generate_new(&store, SR25519_KEY_TYPE, None).unwrap(); - let key_base58 = key.to_ss58check(); - drop(store); - println!("{}", key_base58); - Ok(CliResultOk::PubKeysBase58 { - pubkeys_sr25519: Some(vec![key_base58]), - pubkeys_ed25519: None, - }) -} - -fn list_accounts() -> CliResult { - let store = LocalKeystore::open(PathBuf::from(&KEYSTORE_PATH), None).unwrap(); - println!("sr25519 keys:"); - let mut keys_sr25519 = vec![]; - for pubkey in store.sr25519_public_keys(SR25519_KEY_TYPE).into_iter() { - let key_ss58 = pubkey.to_ss58check(); - println!("{}", key_ss58); - keys_sr25519.push(key_ss58); - } - println!("ed25519 keys:"); - let mut keys_ed25519 = vec![]; - for pubkey in store.ed25519_public_keys(ED25519_KEY_TYPE).into_iter() { - let key_ss58 = pubkey.to_ss58check(); - println!("{}", key_ss58); - keys_ed25519.push(key_ss58); - } - drop(store); - - Ok(CliResultOk::PubKeysBase58 { - pubkeys_sr25519: Some(keys_sr25519), - pubkeys_ed25519: Some(keys_ed25519), - }) -} - -fn print_metadata(cli: &Cli) -> CliResult { - let api = get_chain_api(cli); - let meta = api.metadata(); - println!("Metadata:\n {}", &meta.pretty_format().unwrap()); - Ok(CliResultOk::Metadata { metadata: meta.clone() }) -} -fn print_sgx_metadata(cli: &Cli) -> CliResult { - let worker_api_direct = get_worker_api_direct(cli); - let metadata = worker_api_direct.get_state_metadata().unwrap(); - println!("Metadata:\n {}", metadata.pretty_format().unwrap()); - Ok(CliResultOk::Metadata { metadata }) -} - -fn print_sgx_metadata_raw(cli: &Cli) -> CliResult { - let worker_api_direct = get_worker_api_direct(cli); - let metadata = worker_api_direct.get_state_metadata_raw().unwrap(); - println!("{metadata}"); - Ok(CliResultOk::None) -} - -fn list_workers(cli: &Cli) -> CliResult { - let api = get_chain_api(cli); - let enclaves = api.all_enclaves(WorkerType::BitAcross, None).unwrap(); - println!("number of enclaves registered: {}", enclaves.len()); - - let mr_enclaves = enclaves - .iter() - .map(|enclave| { - println!("Enclave"); - println!(" MRENCLAVE: {}", enclave.mrenclave.to_base58()); - let timestamp = DateTime::::from( - UNIX_EPOCH + Duration::from_millis(enclave.last_seen_timestamp), - ); - println!(" Last seen: {}", timestamp); - println!(" URL: {}", String::from_utf8_lossy(enclave.url.as_slice())); - enclave.mrenclave.to_base58() - }) - .collect(); - - Ok(CliResultOk::MrEnclaveBase58 { mr_enclaves }) -} diff --git a/tee-worker/bitacross/cli/src/benchmark/mod.rs b/tee-worker/bitacross/cli/src/benchmark/mod.rs deleted file mode 100644 index e46887084e..0000000000 --- a/tee-worker/bitacross/cli/src/benchmark/mod.rs +++ /dev/null @@ -1,378 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - command_utils::get_worker_api_direct, - get_layer_two_nonce, - trusted_cli::TrustedCli, - trusted_command_utils::{get_identifiers, get_keystore_path, get_pair_from_str}, - trusted_operation::{get_json_request, get_state, wait_until}, - Cli, CliResult, CliResultOk, SR25519_KEY_TYPE, -}; -use codec::Decode; -use hdrhistogram::Histogram; -use ita_stf::{ - Getter, Index, PublicGetter, TrustedCall, TrustedCallSigned, TrustedGetter, STF_TX_FEE, -}; -use itc_rpc_client::direct_client::{DirectApi, DirectClient}; -use itp_stf_primitives::{ - traits::TrustedCallSigning, - types::{KeyPair, TrustedOperation}, -}; -use itp_types::{ - Balance, ShardIdentifier, TrustedOperationStatus, - TrustedOperationStatus::{InSidechainBlock, Submitted}, -}; -use log::*; -use rand::Rng; -use rayon::prelude::*; -use sgx_crypto_helper::rsa3072::Rsa3072PubKey; -use sp_application_crypto::sr25519; -use sp_core::{sr25519 as sr25519_core, Pair}; -use sp_keystore::Keystore; -use std::{ - boxed::Box, - string::ToString, - sync::mpsc::{channel, Receiver}, - thread, time, - time::Instant, - vec::Vec, -}; -use substrate_client_keystore::LocalKeystore; - -// Needs to be above the existential deposit minimum, otherwise an account will not -// be created and the state is not increased. -const EXISTENTIAL_DEPOSIT: Balance = 1000; - -#[derive(Parser)] -pub struct BenchmarkCommand { - /// The number of clients (=threads) to be used in the benchmark - #[clap(default_value_t = 10)] - number_clients: u32, - - /// The number of iterations to execute for each client - #[clap(default_value_t = 30)] - number_iterations: u128, - - /// Adds a random wait before each transaction. This is the lower bound for the interval in ms. - #[clap(default_value_t = 0)] - random_wait_before_transaction_min_ms: u32, - - /// Adds a random wait before each transaction. This is the upper bound for the interval in ms. - #[clap(default_value_t = 0)] - random_wait_before_transaction_max_ms: u32, - - /// Whether to wait for "InSidechainBlock" confirmation for each transaction - #[clap(short, long)] - wait_for_confirmation: bool, - - /// Account to be used for initial funding of generated accounts used in benchmark - #[clap(default_value_t = String::from("//Alice"))] - funding_account: String, -} - -struct BenchmarkClient { - account: sr25519_core::Pair, - current_balance: u128, - client_api: DirectClient, - receiver: Receiver, -} - -impl BenchmarkClient { - fn new( - account: sr25519_core::Pair, - initial_balance: u128, - initial_request: String, - cli: &Cli, - ) -> Self { - debug!("get direct api"); - let client_api = get_worker_api_direct(cli); - - debug!("setup sender and receiver"); - let (sender, receiver) = channel(); - client_api.watch(initial_request, sender); - BenchmarkClient { account, current_balance: initial_balance, client_api, receiver } - } -} - -/// Stores timing information about a specific transaction -struct BenchmarkTransaction { - started: Instant, - submitted: Instant, - confirmed: Option, -} - -impl BenchmarkCommand { - pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { - let random_wait_before_transaction_ms: (u32, u32) = ( - self.random_wait_before_transaction_min_ms, - self.random_wait_before_transaction_max_ms, - ); - let store = LocalKeystore::open(get_keystore_path(trusted_args, cli), None).unwrap(); - let funding_account_keys = get_pair_from_str(trusted_args, &self.funding_account, cli); - - let (mrenclave, shard) = get_identifiers(trusted_args, cli); - - // Get shielding pubkey. - let worker_api_direct = get_worker_api_direct(cli); - let shielding_pubkey: Rsa3072PubKey = match worker_api_direct.get_rsa_pubkey() { - Ok(key) => key, - Err(err_msg) => panic!("{}", err_msg.to_string()), - }; - - let nonce_start = get_layer_two_nonce!(funding_account_keys, cli, trusted_args); - println!("Nonce for account {}: {}", self.funding_account, nonce_start); - - let mut accounts = Vec::new(); - let initial_balance = (self.number_iterations + 1) * (STF_TX_FEE + EXISTENTIAL_DEPOSIT); - // Setup new accounts and initialize them with money from Alice. - for i in 0..self.number_clients { - let nonce = i + nonce_start; - println!("Initializing account {} with initial amount {:?}", i, initial_balance); - - // Create new account to use. - let a = LocalKeystore::sr25519_generate_new(&store, SR25519_KEY_TYPE, None).unwrap(); - let account = get_pair_from_str(trusted_args, a.to_string().as_str(), cli); - - // Transfer amount from Alice to new account. - let top: TrustedOperation = TrustedCall::balance_transfer( - funding_account_keys.public().into(), - account.public().into(), - initial_balance, - ) - .sign( - &KeyPair::Sr25519(Box::new(funding_account_keys.clone())), - nonce, - &mrenclave, - &shard, - ) - .into_trusted_operation(trusted_args.direct); - - // For the last account we wait for confirmation in order to ensure all accounts were setup correctly - let wait_for_confirmation = i == self.number_clients - 1; - let account_funding_request = get_json_request(shard, &top, shielding_pubkey); - - let client = - BenchmarkClient::new(account, initial_balance, account_funding_request, cli); - let _result = wait_for_top_confirmation(wait_for_confirmation, &client); - accounts.push(client); - } - - rayon::ThreadPoolBuilder::new() - .num_threads(self.number_clients as usize) - .build_global() - .unwrap(); - - let overall_start = Instant::now(); - - // Run actual benchmark logic, in parallel, for each account initialized above. - let outputs: Vec> = accounts - .into_par_iter() - .map(move |mut client| { - let mut output: Vec = Vec::new(); - - for i in 0..self.number_iterations { - println!("Iteration: {}", i); - - if random_wait_before_transaction_ms.1 > 0 { - random_wait(random_wait_before_transaction_ms); - } - - // Create new account. - let account_keys = LocalKeystore::sr25519_generate_new(&store, SR25519_KEY_TYPE, None).unwrap(); - - let new_account = - get_pair_from_str(trusted_args, account_keys.to_string().as_str(), cli); - - println!(" Transfer amount: {}", EXISTENTIAL_DEPOSIT); - println!(" From: {:?}", client.account.public()); - println!(" To: {:?}", new_account.public()); - - // Get nonce of account. - let nonce = get_nonce(client.account.clone(), shard, &client.client_api); - - // Transfer money from client account to new account. - let top: TrustedOperation = TrustedCall::balance_transfer( - client.account.public().into(), - new_account.public().into(), - EXISTENTIAL_DEPOSIT, - ) - .sign(&KeyPair::Sr25519(Box::new(client.account.clone())), nonce, &mrenclave, &shard) - .into_trusted_operation(trusted_args.direct); - - let last_iteration = i == self.number_iterations - 1; - let jsonrpc_call = get_json_request(shard, &top, shielding_pubkey); - client.client_api.send(&jsonrpc_call).unwrap(); - let result = wait_for_top_confirmation( - self.wait_for_confirmation || last_iteration, - &client, - ); - - client.current_balance -= EXISTENTIAL_DEPOSIT; - - let balance = get_balance(client.account.clone(), shard, &client.client_api); - println!("Balance: {}", balance.unwrap_or_default()); - assert_eq!(client.current_balance, balance.unwrap_or_default()); - - output.push(result); - - // FIXME: We probably should re-fund the account in this case. - if client.current_balance <= EXISTENTIAL_DEPOSIT + STF_TX_FEE { - error!("Account {:?} does not have enough balance anymore. Finishing benchmark early", client.account.public()); - break; - } - } - - client.client_api.close().unwrap(); - - output - }) - .collect(); - - println!( - "Finished benchmark with {} clients and {} transactions in {} ms", - self.number_clients, - self.number_iterations, - overall_start.elapsed().as_millis() - ); - - print_benchmark_statistic(outputs, self.wait_for_confirmation); - - Ok(CliResultOk::None) - } -} - -fn get_balance( - account: sr25519::Pair, - shard: ShardIdentifier, - direct_client: &DirectClient, -) -> Option { - let getter = Getter::trusted( - TrustedGetter::free_balance(account.public().into()) - .sign(&KeyPair::Sr25519(Box::new(account.clone()))), - ); - - let getter_start_timer = Instant::now(); - let getter_result = get_state(direct_client, shard, &getter).ok().unwrap_or_default(); - let getter_execution_time = getter_start_timer.elapsed().as_millis(); - - let balance = decode_balance(getter_result); - info!("Balance getter execution took {} ms", getter_execution_time,); - debug!("Retrieved {:?} Balance for {:?}", balance.unwrap_or_default(), account.public()); - balance -} - -fn get_nonce( - account: sr25519::Pair, - shard: ShardIdentifier, - direct_client: &DirectClient, -) -> Index { - let getter = Getter::public(PublicGetter::nonce(account.public().into())); - - let getter_start_timer = Instant::now(); - let nonce = get_state::(direct_client, shard, &getter).ok().unwrap_or_default(); - let getter_execution_time = getter_start_timer.elapsed().as_millis(); - info!("Nonce getter execution took {} ms", getter_execution_time,); - debug!("Retrieved {:?} nonce for {:?}", nonce, account.public()); - nonce -} - -fn print_benchmark_statistic(outputs: Vec>, wait_for_confirmation: bool) { - let mut hist = Histogram::::new(1).unwrap(); - for output in outputs { - for t in output { - let benchmarked_timestamp = - if wait_for_confirmation { t.confirmed } else { Some(t.submitted) }; - if let Some(confirmed) = benchmarked_timestamp { - hist += confirmed.duration_since(t.started).as_millis() as u64; - } else { - println!("Missing measurement data"); - } - } - } - - for i in (5..=100).step_by(5) { - let text = format!( - "{} percent are done within {} ms", - i, - hist.value_at_quantile(i as f64 / 100.0) - ); - println!("{}", text); - } -} - -fn random_wait(random_wait_before_transaction_ms: (u32, u32)) { - let mut rng = rand::thread_rng(); - let sleep_time = time::Duration::from_millis( - rng.gen_range(random_wait_before_transaction_ms.0..=random_wait_before_transaction_ms.1) - .into(), - ); - println!("Sleep for: {}ms", sleep_time.as_millis()); - thread::sleep(sleep_time); -} - -fn wait_for_top_confirmation( - wait_for_sidechain_block: bool, - client: &BenchmarkClient, -) -> BenchmarkTransaction { - let started = Instant::now(); - - let submitted = wait_until(&client.receiver, is_submitted); - - let confirmed = if wait_for_sidechain_block { - // We wait for the transaction hash that actually matches the submitted hash - loop { - let transaction_information = wait_until(&client.receiver, is_sidechain_block); - if let Some((hash, _)) = transaction_information { - if hash == submitted.unwrap().0 { - break transaction_information - } - } - } - } else { - None - }; - if let (Some(s), Some(c)) = (submitted, confirmed) { - // Assert the two hashes are identical - assert_eq!(s.0, c.0); - } - - BenchmarkTransaction { - started, - submitted: submitted.unwrap().1, - confirmed: confirmed.map(|v| v.1), - } -} - -fn is_submitted(s: TrustedOperationStatus) -> bool { - matches!(s, Submitted) -} - -fn is_sidechain_block(s: TrustedOperationStatus) -> bool { - matches!(s, InSidechainBlock(_)) -} - -fn decode_balance(maybe_encoded_balance: Option>) -> Option { - maybe_encoded_balance.and_then(|encoded_balance| { - if let Ok(vd) = Balance::decode(&mut encoded_balance.as_slice()) { - Some(vd) - } else { - warn!("Could not decode balance. maybe hasn't been set? {:x?}", encoded_balance); - None - } - }) -} diff --git a/tee-worker/bitacross/cli/src/command_utils.rs b/tee-worker/bitacross/cli/src/command_utils.rs deleted file mode 100644 index 09a046af35..0000000000 --- a/tee-worker/bitacross/cli/src/command_utils.rs +++ /dev/null @@ -1,87 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::Cli; -use base58::FromBase58; -use ita_parentchain_interface::integritee::{AccountId, Signature}; -use itc_rpc_client::direct_client::{DirectApi, DirectClient as DirectWorkerApi}; -use itp_node_api::api_client::{ParentchainApi, TungsteniteRpcClient}; -use log::*; -use sgx_crypto_helper::rsa3072::Rsa3072PubKey; -use sp_application_crypto::sr25519; -use sp_core::{crypto::Ss58Codec, Pair}; -use sp_runtime::traits::{IdentifyAccount, Verify}; -use std::path::PathBuf; -use substrate_client_keystore::LocalKeystore; - -type AccountPublic = ::Signer; -pub(crate) const KEYSTORE_PATH: &str = "my_keystore"; - -/// Retrieves the public shielding key via the enclave websocket server. -pub(crate) fn get_shielding_key(cli: &Cli) -> Result { - let worker_api_direct = get_worker_api_direct(cli); - worker_api_direct.get_rsa_pubkey().map_err(|e| e.to_string()) -} - -pub(crate) fn get_chain_api(cli: &Cli) -> ParentchainApi { - let url = format!("{}:{}", cli.node_url, cli.node_port); - info!("connecting to {}", url); - ParentchainApi::new(TungsteniteRpcClient::new(&url, 5).unwrap()).unwrap() -} - -pub(crate) fn get_accountid_from_str(account: &str) -> AccountId { - match &account[..2] { - "//" => AccountPublic::from(sr25519::Pair::from_string(account, None).unwrap().public()) - .into_account(), - _ => AccountPublic::from(sr25519::Public::from_ss58check(account).unwrap()).into_account(), - } -} - -pub(crate) fn get_worker_api_direct(cli: &Cli) -> DirectWorkerApi { - let url = format!("{}:{}", cli.worker_url, cli.trusted_worker_port); - info!("Connecting to bitacross-worker-direct-port on '{}'", url); - DirectWorkerApi::new(url) -} - -/// get a pair either form keyring (well known keys) or from the store -pub(crate) fn get_pair_from_str(account: &str) -> sr25519::AppPair { - info!("getting pair for {}", account); - match &account[..2] { - "//" => sr25519::AppPair::from_string(account, None).unwrap(), - _ => { - info!("fetching from keystore at {}", &KEYSTORE_PATH); - // open store without password protection - let store = LocalKeystore::open(PathBuf::from(&KEYSTORE_PATH), None) - .expect("store should exist"); - info!("store opened"); - let _pair = store - .key_pair::( - &sr25519::Public::from_ss58check(account).unwrap().into(), - ) - .unwrap() - .unwrap(); - drop(store); - _pair - }, - } -} - -pub(crate) fn mrenclave_from_base58(src: &str) -> [u8; 32] { - let mut mrenclave = [0u8; 32]; - mrenclave.copy_from_slice(&src.from_base58().expect("mrenclave has to be base58 encoded")); - mrenclave -} diff --git a/tee-worker/bitacross/cli/src/commands.rs b/tee-worker/bitacross/cli/src/commands.rs deleted file mode 100644 index 17b5ea42c4..0000000000 --- a/tee-worker/bitacross/cli/src/commands.rs +++ /dev/null @@ -1,47 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -extern crate chrono; -use crate::{base_cli::BaseCommand, trusted_cli::TrustedCli, Cli, CliResult, CliResultOk}; -use clap::Subcommand; - -use crate::attesteer::AttesteerCommand; - -#[derive(Subcommand)] -pub enum Commands { - #[clap(flatten)] - Base(BaseCommand), - - /// trusted calls to worker enclave - #[clap(after_help = "stf subcommands depend on the stf crate this has been built against")] - Trusted(TrustedCli), - - /// Subcommand for the attesteer. - #[clap(subcommand)] - Attesteer(AttesteerCommand), -} - -pub fn match_command(cli: &Cli) -> CliResult { - match &cli.command { - Commands::Base(cmd) => cmd.run(cli), - Commands::Trusted(trusted_cli) => trusted_cli.run(cli), - Commands::Attesteer(cmd) => { - cmd.run(cli); - Ok(CliResultOk::None) - }, - } -} diff --git a/tee-worker/bitacross/cli/src/lib.rs b/tee-worker/bitacross/cli/src/lib.rs deleted file mode 100644 index 9ee0f5f7fd..0000000000 --- a/tee-worker/bitacross/cli/src/lib.rs +++ /dev/null @@ -1,112 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! an RPC client to Integritee using websockets -//! -//! examples -//! litentry_cli 127.0.0.1:9944 transfer //Alice 5G9RtsTbiYJYQYMHbWfyPoeuuxNaCbC16tZ2JGrZ4gRKwz14 1000 -//! -#![feature(rustc_private)] -#[macro_use] -extern crate clap; -extern crate chrono; - -extern crate env_logger; -extern crate log; - -mod attesteer; -mod base_cli; -mod benchmark; -mod command_utils; -mod trusted_base_cli; -mod trusted_cli; -mod trusted_command_utils; -mod trusted_operation; - -pub mod commands; - -use crate::commands::Commands; -use clap::Parser; -use itp_node_api::api_client::Metadata; -use sp_application_crypto::KeyTypeId; -use sp_core::H256; -use thiserror::Error; - -const VERSION: &str = env!("CARGO_PKG_VERSION"); - -pub(crate) const SR25519_KEY_TYPE: KeyTypeId = KeyTypeId(*b"sr25"); -pub(crate) const ED25519_KEY_TYPE: KeyTypeId = KeyTypeId(*b"ed25"); - -#[derive(Parser)] -#[clap(name = "bitacross-cli")] -#[clap(version = VERSION)] -#[clap(author = "Trust Computing GmbH ")] -#[clap(about = "cli tool to interact with litentry-parachain and workers", long_about = None)] -#[clap(after_help = "stf subcommands depend on the stf crate this has been built against")] -pub struct Cli { - /// node url - #[clap(short = 'u', long, default_value_t = String::from("ws://127.0.0.1"))] - node_url: String, - - /// node port - #[clap(short = 'p', long, default_value_t = String::from("9944"))] - node_port: String, - - /// worker url - #[clap(short = 'U', long, default_value_t = String::from("wss://127.0.0.1"))] - worker_url: String, - - /// worker direct invocation port - #[clap(short = 'P', long, default_value_t = String::from("2000"))] - trusted_worker_port: String, - - #[clap(subcommand)] - command: Commands, -} - -pub enum CliResultOk { - PubKeysBase58 { pubkeys_sr25519: Option>, pubkeys_ed25519: Option> }, - Balance { balance: u128 }, - MrEnclaveBase58 { mr_enclaves: Vec }, - Metadata { metadata: Metadata }, - H256 { hash: H256 }, - // TODO should ideally be removed; or at least drastically less used - // We WANT all commands exposed by the cli to return something useful for the caller(ie instead of printing) - None, -} - -#[derive(Debug, Error)] -pub enum CliError { - #[error("extrinsic error: {:?}", msg)] - Extrinsic { msg: String }, - #[error("trusted operation error: {:?}", msg)] - TrustedOp { msg: String }, - #[error("worker rpc api error: {:?}", msg)] - WorkerRpcApi { msg: String }, -} - -pub type CliResult = Result; - -/// This is used for the commands that directly call `perform_trusted_operation` -/// which typically return `CliResultOk::None` -/// -/// eg: `SetBalanceCommand`,`TransferCommand`,`UnshieldFundsCommand` -impl From for CliError { - fn from(value: trusted_operation::TrustedOperationError) -> Self { - CliError::TrustedOp { msg: value.to_string() } - } -} diff --git a/tee-worker/bitacross/cli/src/main.rs b/tee-worker/bitacross/cli/src/main.rs deleted file mode 100644 index 625b4f0aa0..0000000000 --- a/tee-worker/bitacross/cli/src/main.rs +++ /dev/null @@ -1,29 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use bitacross_cli::{commands, Cli}; -use clap::Parser; - -fn main() { - env_logger::builder() - .format_timestamp(Some(env_logger::TimestampPrecision::Millis)) - .init(); - - let cli = Cli::parse(); - - commands::match_command(&cli).unwrap(); -} diff --git a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/balance.rs b/tee-worker/bitacross/cli/src/trusted_base_cli/commands/balance.rs deleted file mode 100644 index 3b5b9f4f33..0000000000 --- a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/balance.rs +++ /dev/null @@ -1,34 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - trusted_cli::TrustedCli, trusted_command_utils::get_balance, Cli, CliResult, CliResultOk, -}; - -#[derive(Parser)] -pub struct BalanceCommand { - /// AccountId in ss58check format - account: String, -} - -impl BalanceCommand { - pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { - let balance = get_balance(cli, trusted_args, &self.account).unwrap_or_default(); - println!("{}", balance); - Ok(CliResultOk::Balance { balance }) - } -} diff --git a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/bitacross/direct_call_sign_bitcoin.rs b/tee-worker/bitacross/cli/src/trusted_base_cli/commands/bitacross/direct_call_sign_bitcoin.rs deleted file mode 100644 index 748b1d2a79..0000000000 --- a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/bitacross/direct_call_sign_bitcoin.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -use crate::{ - trusted_base_cli::commands::bitacross::utils::send_direct_request_and_watch, - trusted_cli::TrustedCli, - trusted_command_utils::{get_identifiers, get_pair_from_str}, - Cli, CliResult, CliResultOk, -}; -use bc_musig2_ceremony::SignBitcoinPayload; -use itp_stf_primitives::types::KeyPair; -use lc_direct_call::DirectCall; -use sp_core::Pair; - -#[derive(Parser)] -pub struct RequestDirectCallSignBitcoinCommand { - payload: Vec, - merkle_root: String, -} - -impl RequestDirectCallSignBitcoinCommand { - pub(crate) fn run(&self, cli: &Cli, trusted_cli: &TrustedCli) -> CliResult { - let alice = get_pair_from_str(trusted_cli, "//Alice", cli); - let (mrenclave, shard) = get_identifiers(trusted_cli, cli); - - let merkle_root_bytes = hex::decode(self.merkle_root.clone()).unwrap(); - - let dc = DirectCall::SignBitcoin( - alice.public().into(), - SignBitcoinPayload::TaprootSpendable( - self.payload.clone(), - merkle_root_bytes.try_into().unwrap(), - ), - ) - .sign(&KeyPair::Sr25519(Box::new(alice)), &mrenclave, &shard); - - let signature: Vec = send_direct_request_and_watch(cli, trusted_cli, dc).unwrap(); - println!("Got signature: {:?}", signature); - - Ok(CliResultOk::None) - } -} diff --git a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/bitacross/direct_call_sign_ethereum.rs b/tee-worker/bitacross/cli/src/trusted_base_cli/commands/bitacross/direct_call_sign_ethereum.rs deleted file mode 100644 index ff6cac9a76..0000000000 --- a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/bitacross/direct_call_sign_ethereum.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -use crate::{ - trusted_base_cli::commands::bitacross::utils::send_direct_request, - trusted_cli::TrustedCli, - trusted_command_utils::{get_identifiers, get_pair_from_str}, - Cli, CliResult, CliResultOk, -}; -use itp_rpc::{RpcResponse, RpcReturnValue}; -use itp_stf_primitives::types::KeyPair; -use itp_utils::FromHexPrefixed; -use lc_direct_call::{DirectCall, PrehashedEthereumMessage}; -use sp_core::Pair; - -#[derive(Parser)] -pub struct RequestDirectCallSignEthereumCommand { - payload: Vec, -} - -impl RequestDirectCallSignEthereumCommand { - pub(crate) fn run(&self, cli: &Cli, trusted_cli: &TrustedCli) -> CliResult { - let alice = get_pair_from_str(trusted_cli, "//Alice", cli); - let (mrenclave, shard) = get_identifiers(trusted_cli, cli); - let msg: PrehashedEthereumMessage = - self.payload.clone().try_into().expect("Unable to convert payload to [u8; 32]"); - - let dc = DirectCall::SignEthereum(alice.public().into(), msg).sign( - &KeyPair::Sr25519(Box::new(alice)), - &mrenclave, - &shard, - ); - - let result: String = send_direct_request(cli, trusted_cli, dc).unwrap(); - let response: RpcResponse = serde_json::from_str(&result).unwrap(); - if let Ok(return_value) = RpcReturnValue::from_hex(&response.result) { - println!("Got return value: {:?}", return_value); - } else { - println!("Could not decode return value: {:?}", response.result); - } - println!("Got result: {:?}", result); - - Ok(CliResultOk::None) - } -} diff --git a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/bitacross/mod.rs b/tee-worker/bitacross/cli/src/trusted_base_cli/commands/bitacross/mod.rs deleted file mode 100644 index 149e066e59..0000000000 --- a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/bitacross/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -pub mod direct_call_sign_bitcoin; -pub mod direct_call_sign_ethereum; - -pub mod utils; diff --git a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/bitacross/utils.rs b/tee-worker/bitacross/cli/src/trusted_base_cli/commands/bitacross/utils.rs deleted file mode 100644 index 4ed145405b..0000000000 --- a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/bitacross/utils.rs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -use crate::{ - command_utils::get_worker_api_direct, trusted_cli::TrustedCli, trusted_operation::read_shard, - Cli, -}; -use codec::{Decode, Encode, Input}; -use itc_rpc_client::direct_client::DirectApi; -use itp_rpc::{Id, RpcRequest, RpcResponse, RpcReturnValue}; -use itp_stf_primitives::error::StfError; -use itp_types::{parentchain::Hash, DirectRequestStatus, TrustedOperationStatus}; -use itp_utils::{FromHexPrefixed, ToHexPrefixed}; -use lc_direct_call::DirectCallSigned; -use litentry_primitives::{PlainRequest, ShardIdentifier}; -use log::{debug, error}; -use std::sync::mpsc::channel; - -pub fn send_direct_request( - cli: &Cli, - trusted_args: &TrustedCli, - call: DirectCallSigned, -) -> Result { - let shard = read_shard(trusted_args, cli).unwrap(); - let jsonrpc_call: String = get_bitacross_json_request(shard, call); - let direct_api = get_worker_api_direct(cli); - direct_api.get(&jsonrpc_call).map_err(|e| e.to_string()) -} - -pub fn send_direct_request_and_watch( - cli: &Cli, - trusted_args: &TrustedCli, - call: DirectCallSigned, -) -> Result { - let shard = read_shard(trusted_args, cli).unwrap(); - let jsonrpc_call: String = get_bitacross_json_request(shard, call); - let direct_api = get_worker_api_direct(cli); - - let (sender, receiver) = channel(); - direct_api.watch(jsonrpc_call, sender); - - debug!("waiting for rpc response"); - loop { - match receiver.recv() { - Ok(response) => { - debug!("received response"); - let response: RpcResponse = serde_json::from_str(&response).unwrap(); - if let Ok(return_value) = RpcReturnValue::from_hex(&response.result) { - match return_value.status { - DirectRequestStatus::Error => { - debug!("request status is error"); - if let Ok(value) = String::decode(&mut return_value.value.as_slice()) { - error!("{}", value); - } - direct_api.close().unwrap(); - return Err("[Error] DirectRequestStatus::Error".to_string()) - }, - DirectRequestStatus::TrustedOperationStatus(status, top_hash) => { - debug!("request status is: {:?}, top_hash: {:?}", status, top_hash); - - if matches!(status, TrustedOperationStatus::Invalid) { - let error = StfError::decode(&mut return_value.value.as_slice()) - .map_err(|e| { - format!("Could not decode error value: {:?}", e) - })?; - return Err(format!( - "[Error] Error occurred while executing trusted call: {:?}", - error - )) - } - if let Ok(value) = Hash::decode(&mut return_value.value.as_slice()) { - println!("Trusted call {:?} is {:?}", value, status); - } - if !return_value.do_watch { - direct_api.close().unwrap(); - let value = - decode_response_value(&mut return_value.value.as_slice())?; - return Ok(value) - } - }, - DirectRequestStatus::Processing(_hash) => { - println!("Request is processing..."); - }, - DirectRequestStatus::Ok => { - debug!("request status is ignored"); - direct_api.close().unwrap(); - return Err("Unexpected status: DirectRequestStatus::Ok".to_string()) - }, - } - }; - }, - Err(e) => { - error!("failed to receive rpc response: {:?}", e); - direct_api.close().unwrap(); - return Err("failed to receive rpc response".to_string()) - }, - }; - } -} - -pub fn get_bitacross_json_request(shard: ShardIdentifier, call: DirectCallSigned) -> String { - // compose jsonrpc call - let request = PlainRequest { shard, payload: call.encode() }; - RpcRequest::compose_jsonrpc_call( - Id::Number(1), - "bitacross_submitRequest".to_string(), - vec![request.to_hex()], - ) - .unwrap() -} - -fn decode_response_value(value: &mut I) -> Result { - T::decode(value).map_err(|e| format!("Could not decode result value: {:?}", e)) -} diff --git a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/get_shard.rs b/tee-worker/bitacross/cli/src/trusted_base_cli/commands/get_shard.rs deleted file mode 100644 index 5a1df5032b..0000000000 --- a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/get_shard.rs +++ /dev/null @@ -1,69 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - command_utils::get_worker_api_direct, trusted_cli::TrustedCli, Cli, CliError, CliResult, - CliResultOk, -}; -use base58::ToBase58; -use codec::{Decode, Encode}; - -use itc_rpc_client::direct_client::DirectApi; -use itp_rpc::{Id, RpcRequest, RpcResponse, RpcReturnValue}; - -use itp_types::DirectRequestStatus; -use itp_utils::FromHexPrefixed; -use log::*; - -use sp_core::H256; - -#[derive(Parser)] -pub struct GetShardCommand {} - -impl GetShardCommand { - pub(crate) fn run(&self, cli: &Cli, _trusted_args: &TrustedCli) -> CliResult { - let direct_api = get_worker_api_direct(cli); - let rpc_method = "author_getShard".to_owned(); - let jsonrpc_call: String = - RpcRequest::compose_jsonrpc_call(Id::Text("1".to_string()), rpc_method, vec![]) - .unwrap(); - let rpc_response_str = direct_api.get(&jsonrpc_call).unwrap(); - // Decode RPC response. - let rpc_response: RpcResponse = serde_json::from_str(&rpc_response_str) - .map_err(|err| CliError::WorkerRpcApi { msg: err.to_string() })?; - let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result) - // Replace with `inspect_err` once it's stable. - .map_err(|err| { - error!("Failed to decode RpcReturnValue: {:?}", err); - CliError::WorkerRpcApi { msg: "failed to decode RpcReturnValue".to_string() } - })?; - - if rpc_return_value.status == DirectRequestStatus::Error { - error!("{}", String::decode(&mut rpc_return_value.value.as_slice()).unwrap()); - return Err(CliError::WorkerRpcApi { msg: "rpc error".to_string() }) - } - - let shard = H256::decode(&mut rpc_return_value.value.as_slice()) - // Replace with `inspect_err` once it's stable. - .map_err(|err| { - error!("Failed to decode shard: {:?}", err); - CliError::WorkerRpcApi { msg: err.to_string() } - })?; - println!("{}", shard.encode().to_base58()); - Ok(CliResultOk::H256 { hash: shard }) - } -} diff --git a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/mod.rs b/tee-worker/bitacross/cli/src/trusted_base_cli/commands/mod.rs deleted file mode 100644 index 3ed5fd5b08..0000000000 --- a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod balance; -pub mod bitacross; -pub mod get_shard; -pub mod nonce; -pub mod set_balance; -pub mod transfer; -pub mod unshield_funds; diff --git a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/nonce.rs b/tee-worker/bitacross/cli/src/trusted_base_cli/commands/nonce.rs deleted file mode 100644 index f8abee5519..0000000000 --- a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/nonce.rs +++ /dev/null @@ -1,44 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - command_utils::get_worker_api_direct, - trusted_cli::TrustedCli, - trusted_command_utils::{get_identifiers, get_pair_from_str}, - Cli, CliResult, CliResultOk, -}; -use itc_rpc_client::direct_client::DirectApi; -use sp_core::Pair; - -#[derive(Parser)] -pub struct NonceCommand { - /// AccountId in ss58check format - account: String, -} - -impl NonceCommand { - pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { - let (_mrenclave, shard) = get_identifiers(trusted_args, cli); - let who = get_pair_from_str(trusted_args, &self.account, cli); - let worker_api_direct = get_worker_api_direct(cli); - let nonce_ret = worker_api_direct.get_next_nonce(&shard, &(who.public().into())); - let nonce = nonce_ret.expect("get nonce error!"); - println!("{}", nonce); - worker_api_direct.close().unwrap(); - Ok(CliResultOk::None) - } -} diff --git a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/set_balance.rs b/tee-worker/bitacross/cli/src/trusted_base_cli/commands/set_balance.rs deleted file mode 100644 index d549a67fd1..0000000000 --- a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/set_balance.rs +++ /dev/null @@ -1,64 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - get_layer_two_nonce, - trusted_cli::TrustedCli, - trusted_command_utils::{get_identifiers, get_pair_from_str}, - trusted_operation::perform_trusted_operation, - Cli, CliResult, CliResultOk, -}; -use ita_parentchain_interface::integritee::Balance; -use ita_stf::{Getter, Index, TrustedCall, TrustedCallSigned}; -use itp_stf_primitives::{ - traits::TrustedCallSigning, - types::{KeyPair, TrustedOperation}, -}; -use log::*; -use sp_core::{crypto::Ss58Codec, Pair}; -use std::boxed::Box; - -#[derive(Parser)] -pub struct SetBalanceCommand { - /// sender's AccountId in ss58check format - account: String, - - /// amount to be transferred - amount: Balance, -} - -impl SetBalanceCommand { - pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { - let who = get_pair_from_str(trusted_args, &self.account, cli); - let signer = get_pair_from_str(trusted_args, "//Alice", cli); - info!("account ss58 is {}", who.public().to_ss58check()); - - println!("send trusted call set-balance({}, {})", who.public(), self.amount); - - let (mrenclave, shard) = get_identifiers(trusted_args, cli); - let nonce = get_layer_two_nonce!(signer, cli, trusted_args); - let top: TrustedOperation = TrustedCall::balance_set_balance( - signer.public().into(), - who.public().into(), - self.amount, - self.amount, - ) - .sign(&KeyPair::Sr25519(Box::new(signer)), nonce, &mrenclave, &shard) - .into_trusted_operation(trusted_args.direct); - Ok(perform_trusted_operation::<()>(cli, trusted_args, &top).map(|_| CliResultOk::None)?) - } -} diff --git a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/transfer.rs b/tee-worker/bitacross/cli/src/trusted_base_cli/commands/transfer.rs deleted file mode 100644 index 0b6e093395..0000000000 --- a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/transfer.rs +++ /dev/null @@ -1,72 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - get_layer_two_nonce, - trusted_cli::TrustedCli, - trusted_command_utils::{get_accountid_from_str, get_identifiers, get_pair_from_str}, - trusted_operation::perform_trusted_operation, - Cli, CliResult, CliResultOk, -}; -use ita_parentchain_interface::integritee::Balance; -use ita_stf::{Getter, Index, TrustedCall, TrustedCallSigned}; -use itp_stf_primitives::{ - traits::TrustedCallSigning, - types::{KeyPair, TrustedOperation}, -}; -use log::*; -use sp_core::{crypto::Ss58Codec, Pair}; -use std::boxed::Box; - -#[derive(Parser)] -pub struct TransferCommand { - /// sender's AccountId in ss58check format - from: String, - - /// recipient's AccountId in ss58check format - to: String, - - /// amount to be transferred - amount: Balance, -} - -impl TransferCommand { - pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { - let from = get_pair_from_str(trusted_args, &self.from, cli); - let to = get_accountid_from_str(&self.to); - info!("from ss58 is {}", from.public().to_ss58check()); - info!("to ss58 is {}", to.to_ss58check()); - - let (mrenclave, shard) = get_identifiers(trusted_args, cli); - let nonce = get_layer_two_nonce!(from, cli, trusted_args); - println!( - "send trusted call transfer from {} to {}: {}, nonce: {}", - from.public(), - to, - self.amount, - nonce - ); - let top: TrustedOperation = - TrustedCall::balance_transfer(from.public().into(), to, self.amount) - .sign(&KeyPair::Sr25519(Box::new(from)), nonce, &mrenclave, &shard) - .into_trusted_operation(trusted_args.direct); - let res = - perform_trusted_operation::<()>(cli, trusted_args, &top).map(|_| CliResultOk::None)?; - info!("trusted call transfer executed"); - Ok(res) - } -} diff --git a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/unshield_funds.rs b/tee-worker/bitacross/cli/src/trusted_base_cli/commands/unshield_funds.rs deleted file mode 100644 index 9133315136..0000000000 --- a/tee-worker/bitacross/cli/src/trusted_base_cli/commands/unshield_funds.rs +++ /dev/null @@ -1,67 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - get_layer_two_nonce, - trusted_cli::TrustedCli, - trusted_command_utils::{get_accountid_from_str, get_identifiers, get_pair_from_str}, - trusted_operation::perform_trusted_operation, - Cli, CliResult, CliResultOk, -}; -use ita_parentchain_interface::integritee::Balance; -use ita_stf::{Getter, Index, TrustedCall, TrustedCallSigned}; -use itp_stf_primitives::{ - traits::TrustedCallSigning, - types::{KeyPair, TrustedOperation}, -}; -use sp_core::{crypto::Ss58Codec, Pair}; -use std::boxed::Box; -#[derive(Parser)] -pub struct UnshieldFundsCommand { - /// Sender's incognito AccountId in ss58check format - from: String, - - /// Recipient's parentchain AccountId in ss58check format - to: String, - - /// amount to be transferred - amount: Balance, -} - -impl UnshieldFundsCommand { - pub(crate) fn run(&self, cli: &Cli, trusted_args: &TrustedCli) -> CliResult { - let from = get_pair_from_str(trusted_args, &self.from, cli); - let to = get_accountid_from_str(&self.to); - println!("from ss58 is {}", from.public().to_ss58check()); - println!("to ss58 is {}", to.to_ss58check()); - - println!( - "send trusted call unshield_funds from {} to {}: {}", - from.public(), - to, - self.amount - ); - - let (mrenclave, shard) = get_identifiers(trusted_args, cli); - let nonce = get_layer_two_nonce!(from, cli, trusted_args); - let top: TrustedOperation = - TrustedCall::balance_unshield(from.public().into(), to, self.amount, shard) - .sign(&KeyPair::Sr25519(Box::new(from)), nonce, &mrenclave, &shard) - .into_trusted_operation(trusted_args.direct); - Ok(perform_trusted_operation::<()>(cli, trusted_args, &top).map(|_| CliResultOk::None)?) - } -} diff --git a/tee-worker/bitacross/cli/src/trusted_base_cli/mod.rs b/tee-worker/bitacross/cli/src/trusted_base_cli/mod.rs deleted file mode 100644 index 77ea62abca..0000000000 --- a/tee-worker/bitacross/cli/src/trusted_base_cli/mod.rs +++ /dev/null @@ -1,122 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - trusted_base_cli::commands::{ - balance::BalanceCommand, - bitacross::{ - direct_call_sign_bitcoin::RequestDirectCallSignBitcoinCommand, - direct_call_sign_ethereum::RequestDirectCallSignEthereumCommand, - }, - get_shard::GetShardCommand, - nonce::NonceCommand, - set_balance::SetBalanceCommand, - transfer::TransferCommand, - unshield_funds::UnshieldFundsCommand, - }, - trusted_cli::TrustedCli, - trusted_command_utils::get_keystore_path, - Cli, CliResult, CliResultOk, ED25519_KEY_TYPE, SR25519_KEY_TYPE, -}; -use log::*; -use sp_core::crypto::Ss58Codec; -use sp_keystore::Keystore; -use substrate_client_keystore::LocalKeystore; - -mod commands; - -#[derive(Subcommand)] -pub enum TrustedBaseCommand { - /// generates a new incognito account for the given shard - NewAccount, - - /// lists all incognito accounts in a given shard - ListAccounts, - - /// send funds from one incognito account to another - Transfer(TransferCommand), - - /// ROOT call to set some account balance to an arbitrary number - SetBalance(SetBalanceCommand), - - /// query balance for incognito account in keystore - Balance(BalanceCommand), - - /// Transfer funds from an incognito account to an parentchain account - UnshieldFunds(UnshieldFundsCommand), - - /// gets the nonce of a given account, taking the pending trusted calls - /// in top pool in consideration - Nonce(NonceCommand), - - /// get shard for this worker - GetShard(GetShardCommand), - - /// sign bitcoin transaction using custodian wallet - RequestDirectCallSignBitcoin(RequestDirectCallSignBitcoinCommand), - - /// sign ethereum transaction using custodian wallet - RequestDirectCallSignEthereum(RequestDirectCallSignEthereumCommand), -} - -impl TrustedBaseCommand { - pub fn run(&self, cli: &Cli, trusted_cli: &TrustedCli) -> CliResult { - match self { - TrustedBaseCommand::NewAccount => new_account(trusted_cli, cli), - TrustedBaseCommand::ListAccounts => list_accounts(trusted_cli, cli), - TrustedBaseCommand::Transfer(cmd) => cmd.run(cli, trusted_cli), - TrustedBaseCommand::SetBalance(cmd) => cmd.run(cli, trusted_cli), - TrustedBaseCommand::Balance(cmd) => cmd.run(cli, trusted_cli), - TrustedBaseCommand::UnshieldFunds(cmd) => cmd.run(cli, trusted_cli), - TrustedBaseCommand::Nonce(cmd) => cmd.run(cli, trusted_cli), - TrustedBaseCommand::GetShard(cmd) => cmd.run(cli, trusted_cli), - TrustedBaseCommand::RequestDirectCallSignBitcoin(cmd) => cmd.run(cli, trusted_cli), - TrustedBaseCommand::RequestDirectCallSignEthereum(cmd) => cmd.run(cli, trusted_cli), - } - } -} - -fn new_account(trusted_args: &TrustedCli, cli: &Cli) -> CliResult { - let store = LocalKeystore::open(get_keystore_path(trusted_args, cli), None).unwrap(); - let key = LocalKeystore::sr25519_generate_new(&store, SR25519_KEY_TYPE, None).unwrap(); - drop(store); - info!("new account {}", key.to_ss58check()); - let key_str = key.to_ss58check(); - println!("{}", key_str); - - Ok(CliResultOk::PubKeysBase58 { pubkeys_sr25519: Some(vec![key_str]), pubkeys_ed25519: None }) -} - -fn list_accounts(trusted_args: &TrustedCli, cli: &Cli) -> CliResult { - let store = LocalKeystore::open(get_keystore_path(trusted_args, cli), None).unwrap(); - info!("sr25519 keys:"); - for pubkey in store.sr25519_public_keys(SR25519_KEY_TYPE).into_iter() { - println!("{}", pubkey.to_ss58check()); - } - info!("ed25519 keys:"); - let pubkeys: Vec = store - .ed25519_public_keys(ED25519_KEY_TYPE) - .into_iter() - .map(|pubkey| pubkey.to_ss58check()) - .collect(); - for pubkey in &pubkeys { - println!("{}", pubkey); - } - drop(store); - - Ok(CliResultOk::PubKeysBase58 { pubkeys_sr25519: None, pubkeys_ed25519: Some(pubkeys) }) -} diff --git a/tee-worker/bitacross/cli/src/trusted_cli.rs b/tee-worker/bitacross/cli/src/trusted_cli.rs deleted file mode 100644 index f0fffe8b96..0000000000 --- a/tee-worker/bitacross/cli/src/trusted_cli.rs +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{benchmark::BenchmarkCommand, Cli, CliResult}; - -use crate::trusted_base_cli::TrustedBaseCommand; - -#[derive(Args)] -pub struct TrustedCli { - /// targeted worker MRENCLAVE - #[clap(short, long)] - pub(crate) mrenclave: Option, - - /// shard identifier - #[clap(short, long)] - pub(crate) shard: Option, - - /// signer for publicly observable extrinsic - #[clap(short='a', long, default_value_t = String::from("//Alice"))] - pub(crate) xt_signer: String, - - /// insert if direct invocation call is desired - #[clap(short, long)] - pub(crate) direct: bool, - - #[clap(subcommand)] - pub(crate) command: TrustedCommand, -} - -#[derive(Subcommand)] -pub enum TrustedCommand { - #[clap(flatten)] - BaseTrusted(TrustedBaseCommand), - - /// Run Benchmark - Benchmark(BenchmarkCommand), -} - -impl TrustedCli { - pub(crate) fn run(&self, cli: &Cli) -> CliResult { - match &self.command { - TrustedCommand::BaseTrusted(cmd) => cmd.run(cli, self), - TrustedCommand::Benchmark(cmd) => cmd.run(cli, self), - } - } -} diff --git a/tee-worker/bitacross/cli/src/trusted_command_utils.rs b/tee-worker/bitacross/cli/src/trusted_command_utils.rs deleted file mode 100644 index 5df8008302..0000000000 --- a/tee-worker/bitacross/cli/src/trusted_command_utils.rs +++ /dev/null @@ -1,169 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - command_utils::{get_worker_api_direct, mrenclave_from_base58}, - trusted_cli::TrustedCli, - trusted_operation::{perform_trusted_operation, read_shard}, - Cli, -}; -use base58::{FromBase58, ToBase58}; -use codec::{Decode, Encode}; -use ita_parentchain_interface::integritee::Balance; -use ita_stf::{Getter, TrustedCallSigned, TrustedGetter}; -use itc_rpc_client::direct_client::DirectApi; -use itp_rpc::{Id, RpcRequest, RpcResponse, RpcReturnValue}; -use itp_stf_primitives::types::{AccountId, KeyPair, ShardIdentifier, TrustedOperation}; -use itp_types::DirectRequestStatus; -use itp_utils::{FromHexPrefixed, ToHexPrefixed}; -use log::*; -use sp_application_crypto::sr25519; -use sp_core::{crypto::Ss58Codec, sr25519 as sr25519_core, Pair}; -use sp_runtime::traits::IdentifyAccount; -use std::{boxed::Box, path::PathBuf}; -use substrate_client_keystore::LocalKeystore; - -#[macro_export] -macro_rules! get_layer_two_nonce { - ($signer_pair:ident, $cli: ident, $trusted_args:ident ) => {{ - use ita_stf::{Getter, PublicGetter, TrustedCallSigned}; - use itp_stf_primitives::types::TrustedOperation; - use litentry_primitives::Identity; - use $crate::{ - trusted_command_utils::get_pending_trusted_calls_for, - trusted_operation::perform_trusted_operation, - }; - let top = TrustedOperation::::get(Getter::public( - PublicGetter::nonce(Identity::Substrate($signer_pair.public().into())), - )); - // final nonce = current system nonce + pending tx count, panic early - let nonce = perform_trusted_operation::($cli, $trusted_args, &top) - .ok() - .unwrap_or_default(); - log::debug!("got system nonce: {:?}", nonce); - let pending_tx_count = - get_pending_trusted_calls_for($cli, $trusted_args, &$signer_pair.public().into()).len(); - let pending_tx_count = Index::try_from(pending_tx_count).unwrap(); - nonce + pending_tx_count - }}; -} - -const TRUSTED_KEYSTORE_PATH: &str = "my_trusted_keystore"; - -pub(crate) fn get_balance(cli: &Cli, trusted_args: &TrustedCli, arg_who: &str) -> Option { - debug!("arg_who = {:?}", arg_who); - let who = get_pair_from_str(trusted_args, arg_who, cli); - let top = TrustedOperation::::get(Getter::trusted( - TrustedGetter::free_balance(who.public().into()).sign(&KeyPair::Sr25519(Box::new(who))), - )); - perform_trusted_operation::(cli, trusted_args, &top).ok() -} - -pub(crate) fn get_keystore_path(trusted_args: &TrustedCli, cli: &Cli) -> PathBuf { - let (_mrenclave, shard) = get_identifiers(trusted_args, cli); - PathBuf::from(&format!("{}/{}", TRUSTED_KEYSTORE_PATH, shard.encode().to_base58())) -} - -pub(crate) fn get_identifiers(trusted_args: &TrustedCli, cli: &Cli) -> ([u8; 32], ShardIdentifier) { - let mrenclave = if let Some(mrenclave) = &trusted_args.mrenclave { - mrenclave_from_base58(mrenclave) - } else { - let direct_api = get_worker_api_direct(cli); - match direct_api.get_state_mrenclave() { - Ok(mrenclave) => mrenclave, - Err(e) => panic!("Unable to retrieve MRENCLAVE from endpoint: {:?}", e), - } - }; - let shard = match &trusted_args.shard { - Some(val) => - ShardIdentifier::from_slice(&val.from_base58().expect("shard has to be base58 encoded")), - None => ShardIdentifier::from_slice(&mrenclave), - }; - (mrenclave, shard) -} - -// TODO this function is redundant with client::main -pub(crate) fn get_accountid_from_str(account: &str) -> AccountId { - match &account[..2] { - "//" => sr25519::Pair::from_string(account, None) - .unwrap() - .public() - .into_account() - .into(), - _ => sr25519::Public::from_ss58check(account).unwrap().into_account().into(), - } -} - -// TODO this function is ALMOST redundant with client::main -// get a pair either form keyring (well known keys) or from the store -pub(crate) fn get_pair_from_str( - trusted_args: &TrustedCli, - account: &str, - cli: &Cli, -) -> sr25519_core::Pair { - info!("getting pair for {}", account); - match &account[..2] { - "//" => { - let pair = sr25519_core::Pair::from_string(account, None).unwrap(); - info!("public_key: {:?}", &pair.public().to_hex()); - pair - }, - _ => { - info!("fetching from keystore at {}", &TRUSTED_KEYSTORE_PATH); - // open store without password protection - let store = LocalKeystore::open(get_keystore_path(trusted_args, cli), None) - .expect("store should exist"); - info!("store opened"); - let public_key = &sr25519::AppPublic::from_ss58check(account).unwrap(); - info!("public_key: {:?}", &public_key); - let _pair = store.key_pair::(public_key).unwrap().unwrap(); - info!("key pair fetched"); - drop(store); - _pair.into() - }, - } -} - -// helper method to get the pending trusted calls for a given account via direct RPC -pub(crate) fn get_pending_trusted_calls_for( - cli: &Cli, - trusted_args: &TrustedCli, - who: &AccountId, -) -> Vec> { - let shard = read_shard(trusted_args, cli).unwrap(); - let direct_api = get_worker_api_direct(cli); - let rpc_method = "author_pendingTrustedCallsFor".to_owned(); - let jsonrpc_call: String = RpcRequest::compose_jsonrpc_call( - Id::Text("1".to_string()), - rpc_method, - vec![shard.encode().to_base58(), who.to_hex()], - ) - .unwrap(); - - let rpc_response_str = direct_api.get(&jsonrpc_call).unwrap(); - let rpc_response: RpcResponse = serde_json::from_str(&rpc_response_str).unwrap(); - let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result).unwrap(); - - if rpc_return_value.status == DirectRequestStatus::Error { - error!("{}", String::decode(&mut rpc_return_value.value.as_slice()).unwrap()); - direct_api.close().unwrap(); - return vec![] - } - - direct_api.close().unwrap(); - Decode::decode(&mut rpc_return_value.value.as_slice()).unwrap_or_default() -} diff --git a/tee-worker/bitacross/cli/src/trusted_operation.rs b/tee-worker/bitacross/cli/src/trusted_operation.rs deleted file mode 100644 index e3cdcacb11..0000000000 --- a/tee-worker/bitacross/cli/src/trusted_operation.rs +++ /dev/null @@ -1,443 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - command_utils::{get_chain_api, get_pair_from_str, get_shielding_key, get_worker_api_direct}, - trusted_cli::TrustedCli, - Cli, -}; -use base58::{FromBase58, ToBase58}; -use codec::{Decode, Encode, Input}; -use ita_stf::{Getter, TrustedCallSigned}; -use itc_rpc_client::direct_client::{DirectApi, DirectClient}; -use itp_node_api::api_client::{ApiClientError, TEEBAG}; -use itp_rpc::{Id, RpcRequest, RpcResponse, RpcReturnValue}; -use itp_sgx_crypto::ShieldingCryptoEncrypt; -use itp_stf_primitives::{ - error::StfError, - types::{ShardIdentifier, TrustedOperation}, -}; -use itp_types::{ - parentchain::{events::ParentchainBlockProcessed, BlockHash, BlockNumber, Hash}, - DirectRequestStatus, RsaRequest, TrustedOperationStatus, -}; -use itp_utils::{FromHexPrefixed, ToHexPrefixed}; -use log::*; -use sp_core::H256; -use std::{ - fmt::Debug, - result::Result as StdResult, - sync::mpsc::{channel, Receiver}, - time::Instant, -}; -use substrate_api_client::{ - ac_compose_macros::compose_extrinsic, GetChainInfo, SubmitAndWatch, SubscribeEvents, XtStatus, -}; -use thiserror::Error; - -const TIMEOUT_BLOCKS: BlockNumber = 10; - -#[derive(Debug, Error)] -pub(crate) enum TrustedOperationError { - #[error("{0:?}")] - ApiClient(ApiClientError), - #[error("Could not retrieve Header from node")] - MissingBlock, - #[error("confirmation timed out after ({0:?}) blocks")] - ConfirmationTimedOut(BlockNumber), - #[error("Confirmed Block Number ({0:?}) exceeds expected one ({0:?})")] - ConfirmedBlockNumberTooHigh( - itp_types::parentchain::BlockNumber, - itp_types::parentchain::BlockNumber, - ), - #[error("Confirmed Block Hash ({0:?}) does not match expected one ({0:?})")] - ConfirmedBlockHashDoesNotMatchExpected(BlockHash, BlockHash), - #[error("invocation extrinsic L1 error: {msg:?}")] - IndirectInvocationFailed { msg: String }, - #[error("default error: {msg:?}")] - Default { msg: String }, -} - -impl From for TrustedOperationError { - fn from(error: ApiClientError) -> Self { - Self::ApiClient(error) - } -} - -pub(crate) type TrustedOpResult = StdResult; - -pub(crate) fn perform_trusted_operation( - cli: &Cli, - trusted_args: &TrustedCli, - top: &TrustedOperation, -) -> TrustedOpResult { - match top { - TrustedOperation::indirect_call(_) => send_indirect_request::(cli, trusted_args, top), - TrustedOperation::direct_call(_) => send_direct_request::(cli, trusted_args, top), - TrustedOperation::get(getter) => - execute_getter_from_cli_args::(cli, trusted_args, getter), - } -} - -fn execute_getter_from_cli_args( - cli: &Cli, - trusted_args: &TrustedCli, - getter: &Getter, -) -> TrustedOpResult { - let shard = read_shard(trusted_args, cli).unwrap(); - let direct_api = get_worker_api_direct(cli); - get_state(&direct_api, shard, getter) -} - -pub(crate) fn get_state( - direct_api: &DirectClient, - shard: ShardIdentifier, - getter: &Getter, -) -> TrustedOpResult { - // Compose jsonrpc call. - let data = RsaRequest::new(shard, getter.encode()); - let rpc_method = "state_executeGetter".to_owned(); - let jsonrpc_call: String = RpcRequest::compose_jsonrpc_call( - Id::Text("1".to_string()), - rpc_method, - vec![data.to_hex()], - ) - .unwrap(); - - let rpc_response_str = direct_api.get(&jsonrpc_call).unwrap(); - - // Decode RPC response. - let rpc_response: RpcResponse = serde_json::from_str(&rpc_response_str) - .map_err(|err| TrustedOperationError::Default { msg: err.to_string() })?; - let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result) - // Replace with `inspect_err` once it's stable. - .map_err(|err| { - error!("Failed to decode RpcReturnValue: {:?}", err); - TrustedOperationError::Default { msg: "RpcReturnValue::from_hex".to_string() } - })?; - - if rpc_return_value.status == DirectRequestStatus::Error { - error!("{}", String::decode(&mut rpc_return_value.value.as_slice()).unwrap()); - return Err(TrustedOperationError::Default { - msg: "[Error] DirectRequestStatus::Error".to_string(), - }) - } - - let maybe_state: Option> = Option::decode(&mut rpc_return_value.value.as_slice()) - // Replace with `inspect_err` once it's stable. - .map_err(|err| { - error!("Failed to decode return value: {:?}", err); - TrustedOperationError::Default { msg: "Option::decode".to_string() } - })?; - - match maybe_state { - Some(state) => { - let decoded = decode_response_value(&mut state.as_slice())?; - Ok(decoded) - }, - None => Err(TrustedOperationError::Default { msg: "Value not present".to_string() }), - } -} - -fn send_indirect_request( - cli: &Cli, - trusted_args: &TrustedCli, - trusted_operation: &TrustedOperation, -) -> TrustedOpResult { - let mut chain_api = get_chain_api(cli); - let encryption_key = get_shielding_key(cli).unwrap(); - let call_encrypted = encryption_key.encrypt(&trusted_operation.encode()).unwrap(); - - let shard = read_shard(trusted_args, cli).unwrap(); - debug!( - "invoke indirect send_request: trusted operation: {:?}, shard: {}", - trusted_operation, - shard.encode().to_base58() - ); - let arg_signer = &trusted_args.xt_signer; - let signer = get_pair_from_str(arg_signer); - chain_api.set_signer(signer.into()); - - let request = RsaRequest::new(shard, call_encrypted); - let xt = compose_extrinsic!(&chain_api, TEEBAG, "post_opaque_task", request); - - let invocation_block_hash = match chain_api - .submit_and_watch_extrinsic_until(xt, XtStatus::InBlock) - { - Ok(xt_report) => { - println!( - "[+] invoke TrustedOperation extrinsic success. extrinsic hash: {:?} / status: {:?} / block hash: {:?}", - xt_report.extrinsic_hash, xt_report.status, xt_report.block_hash.unwrap() - ); - xt_report.block_hash.unwrap() - }, - Err(e) => { - error!("invoke TrustedOperation extrinsic failed {:?}", e); - return Err(TrustedOperationError::IndirectInvocationFailed { msg: format!("{:?}", e) }) - }, - }; - let invocation_block_number = chain_api - .get_header(Some(invocation_block_hash))? - .ok_or(TrustedOperationError::MissingBlock)? - .number; - info!( - "Trusted call extrinsic sent for shard {} and successfully included in parentchain block {} with hash {:?}.", - shard.encode().to_base58(), invocation_block_number, invocation_block_hash - ); - info!("Waiting for execution confirmation from enclave..."); - let mut blocks = 0u32; - let mut subscription = chain_api.subscribe_events().unwrap(); - loop { - let events = subscription.next_events_from_metadata().unwrap().unwrap(); - blocks += 1; - if blocks > TIMEOUT_BLOCKS { - return Err(TrustedOperationError::ConfirmationTimedOut(blocks)) - } - for event in events.iter() { - let event = event.unwrap(); - match event.pallet_name() { - "Teebag" => match event.variant_name() { - "ParentchainBlockProcessed" => { - if let Ok(Some(ev)) = event.as_event::() { - println!("Teebag::{:?}", ev); - debug!( - "Invocation block Number we're waiting for: {:?}", - invocation_block_number - ); - debug!("Confirmed block Number: {:?}", ev.block_number); - // The returned block number belongs to a subsequent event. We missed our event and can break the loop. - if ev.block_number > invocation_block_number { - return Err(TrustedOperationError::ConfirmedBlockNumberTooHigh( - ev.block_number, - invocation_block_number, - )) - } - // The block number is correct, but the block hash does not fit. - if invocation_block_number == ev.block_number - && invocation_block_hash != ev.block_hash - { - return Err( - TrustedOperationError::ConfirmedBlockHashDoesNotMatchExpected( - ev.block_hash, - invocation_block_hash, - ), - ) - } - if ev.block_hash == invocation_block_hash { - let value = decode_response_value( - &mut invocation_block_hash.encode().as_slice(), - )?; - return Ok(value) - } - } - }, - _ => continue, - }, - _ => continue, - } - } - } -} - -pub fn read_shard( - trusted_args: &TrustedCli, - cli: &Cli, -) -> StdResult { - match &trusted_args.shard { - Some(s) => match s.from_base58() { - Ok(s) => ShardIdentifier::decode(&mut &s[..]), - _ => panic!("shard argument must be base58 encoded"), - }, - None => match trusted_args.mrenclave.clone() { - Some(mrenclave) => - if let Ok(s) = mrenclave.from_base58() { - ShardIdentifier::decode(&mut &s[..]) - } else { - panic!("Mrenclave argument must be base58 encoded") - }, - None => { - // Fetch mrenclave from worker - let direct_api = get_worker_api_direct(cli); - if let Ok(s) = direct_api.get_state_mrenclave() { - ShardIdentifier::decode(&mut &s[..]) - } else { - panic!("Unable to fetch MRENCLAVE from worker endpoint"); - } - }, - }, - } -} - -/// sends a rpc watch request to the worker api server -fn send_direct_request( - cli: &Cli, - trusted_args: &TrustedCli, - top: &TrustedOperation, -) -> TrustedOpResult { - let encryption_key = get_shielding_key(cli).unwrap(); - let shard = read_shard(trusted_args, cli).unwrap(); - let jsonrpc_call: String = get_json_request(shard, top, encryption_key); - - debug!("get direct api"); - let direct_api = get_worker_api_direct(cli); - - debug!("setup sender and receiver"); - let (sender, receiver) = channel(); - direct_api.watch(jsonrpc_call, sender); - - debug!("waiting for rpc response"); - loop { - match receiver.recv() { - Ok(response) => { - debug!("received response"); - let response: RpcResponse = serde_json::from_str(&response).unwrap(); - if let Ok(return_value) = RpcReturnValue::from_hex(&response.result) { - match return_value.status { - DirectRequestStatus::Error => { - debug!("request status is error"); - if let Ok(value) = String::decode(&mut return_value.value.as_slice()) { - error!("{}", value); - } - direct_api.close().unwrap(); - return Err(TrustedOperationError::Default { - msg: "[Error] DirectRequestStatus::Error".to_string(), - }) - }, - DirectRequestStatus::TrustedOperationStatus(status, top_hash) => { - debug!("request status is: {:?}, top_hash: {:?}", status, top_hash); - - if matches!(status, TrustedOperationStatus::Invalid) { - let error = StfError::decode(&mut return_value.value.as_slice()) - .map_err(|e| TrustedOperationError::Default { - msg: format!("Could not decode error value: {:?}", e), - })?; - return Err(TrustedOperationError::Default { - msg: format!( - "[Error] Error occurred while executing trusted call: {:?}", - error - ), - }) - } - if let Ok(value) = Hash::decode(&mut return_value.value.as_slice()) { - println!("Trusted call {:?} is {:?}", value, status); - } - if !return_value.do_watch { - direct_api.close().unwrap(); - let value = - decode_response_value(&mut return_value.value.as_slice())?; - return Ok(value) - } - }, - DirectRequestStatus::Ok | DirectRequestStatus::Processing(_) => { - debug!("request status is ignored"); - direct_api.close().unwrap(); - return Err(TrustedOperationError::Default { - msg: "Unexpected status: DirectRequestStatus::Ok".to_string(), - }) - }, - } - }; - }, - Err(e) => { - error!("failed to receive rpc response: {:?}", e); - direct_api.close().unwrap(); - return Err(TrustedOperationError::Default { - msg: "failed to receive rpc response".to_string(), - }) - }, - }; - } -} - -fn decode_response_value( - value: &mut I, -) -> StdResult { - T::decode(value).map_err(|e| TrustedOperationError::Default { - msg: format!("Could not decode result value: {:?}", e), - }) -} - -pub(crate) fn get_json_request( - shard: ShardIdentifier, - top: &TrustedOperation, - shielding_pubkey: sgx_crypto_helper::rsa3072::Rsa3072PubKey, -) -> String { - let encrypted_top = shielding_pubkey.encrypt(&top.encode()).unwrap(); - - // compose jsonrpc call - let request = RsaRequest::new(shard, encrypted_top); - RpcRequest::compose_jsonrpc_call( - Id::Text("1".to_string()), - "author_submitAndWatchRsaRequest".to_string(), - vec![request.to_hex()], - ) - .unwrap() -} - -pub(crate) fn wait_until( - receiver: &Receiver, - until: impl Fn(TrustedOperationStatus) -> bool, -) -> Option<(H256, Instant)> { - debug!("waiting for rpc response"); - loop { - match receiver.recv() { - Ok(response) => { - debug!("received response: {}", response); - let parse_result: Result = serde_json::from_str(&response); - if let Ok(response) = parse_result { - if let Ok(return_value) = RpcReturnValue::from_hex(&response.result) { - debug!("successfully decoded rpc response: {:?}", return_value); - match return_value.status { - DirectRequestStatus::Error => { - debug!("request status is error"); - if let Ok(value) = - String::decode(&mut return_value.value.as_slice()) - { - error!("{}", value); - } - return None - }, - DirectRequestStatus::TrustedOperationStatus(status, top_hash) => { - debug!("request status is: {:?}, top_hash: {:?}", status, top_hash); - if let Ok(value) = Hash::decode(&mut return_value.value.as_slice()) - { - println!("Trusted call {:?} is {:?}", value, status); - if until(status.clone()) { - return Some((top_hash, Instant::now())) - } else if status == TrustedOperationStatus::Invalid { - error!("Invalid request"); - return None - } - } - }, - DirectRequestStatus::Ok | DirectRequestStatus::Processing(_) => { - debug!("request status is ignored"); - return None - }, - } - }; - } else { - error!("Could not parse response"); - }; - }, - Err(e) => { - error!("failed to receive rpc response: {:?}", e); - return None - }, - }; - } -} diff --git a/tee-worker/bitacross/cli/test_auto_shielding_with_transfer_bob.sh b/tee-worker/bitacross/cli/test_auto_shielding_with_transfer_bob.sh deleted file mode 100644 index 255d3f5bbc..0000000000 --- a/tee-worker/bitacross/cli/test_auto_shielding_with_transfer_bob.sh +++ /dev/null @@ -1,141 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# Verifies that auto shielding transfers sent to vault account: //Alice are verified from sender //Bob -# - -while getopts ":m:p:A:u:V:w:x:y:z:C:" opt; do - case $opt in - p) - INTEGRITEE_RPC_PORT=$OPTARG - ;; - A) - WORKER_1_PORT=$OPTARG - ;; - u) - INTEGRITEE_RPC_URL=$OPTARG - ;; - V) - WORKER_1_URL=$OPTARG - ;; - w) - TARGET_A_PARENTCHAIN_RPC_URL=$OPTARG - ;; - x) - TARGET_A_PARENTCHAIN_RPC_PORT=$OPTARG - ;; - C) - CLIENT_BIN=$OPTARG - ;; - *) - echo "invalid arg ${OPTARG}" - exit 1 - esac -done - -# Using default port if none given as arguments. -INTEGRITEE_RPC_PORT=${INTEGRITEE_RPC_PORT:-9944} -INTEGRITEE_RPC_URL=${INTEGRITEE_RPC_URL:-"ws://127.0.0.1"} -TARGET_A_PARENTCHAIN_RPC_PORT=${TARGET_A_PARENTCHAIN_RPC_PORT:-9966} -TARGET_A_PARENTCHAIN_RPC_URL=${TARGET_A_PARENTCHAIN_RPC_URL:-"ws://127.0.0.1"} - -WORKER_1_PORT=${WORKER_1_PORT:-2000} -WORKER_1_URL=${WORKER_1_URL:-"wss://127.0.0.1"} - -CLIENT_BIN=${CLIENT_BIN:-"./../bin/integritee-cli"} - -echo "Using client binary ${CLIENT_BIN}" -${CLIENT_BIN} --version -echo "Using Integritee RPC uri ${INTEGRITEE_RPC_URL}:${INTEGRITEE_RPC_PORT}" -echo "Using Target A RPC uri ${TARGET_A_PARENTCHAIN_RPC_URL}:${TARGET_A_PARENTCHAIN_RPC_PORT}" -echo "Using trusted-worker 1 uri ${WORKER_1_URL}:${WORKER_1_PORT}" -echo "" - -# the parentchain token is 12 decimal -UNIT=$(( 10 ** 12 )) -FEE_TOLERANCE=$((10 ** 11)) - -# make these amounts greater than ED -AMOUNT_SHIELD=$(( 6 * UNIT )) - -CLIENT="${CLIENT_BIN} -p ${INTEGRITEE_RPC_PORT} -P ${WORKER_1_PORT} -u ${INTEGRITEE_RPC_URL} -U ${WORKER_1_URL}" -CLIENT2="${CLIENT_BIN} -p ${TARGET_A_PARENTCHAIN_RPC_PORT} -P ${WORKER_1_PORT} -u ${TARGET_A_PARENTCHAIN_RPC_URL} -U ${WORKER_1_URL}" - -# interval and max rounds to wait to check the given account balance in sidechain -WAIT_INTERVAL_SECONDS=10 -WAIT_ROUNDS=20 - -# Poll and assert the given account's state is equal to expected, -# with timeout WAIT_INTERVAL_SECONDS * WAIT_ROUNDS -# usage: -# wait_assert_state -# the `state-name` has to be the supported subcommand, e.g. `balance`, `nonce` -function wait_assert_state() -{ - for i in $(seq 1 $WAIT_ROUNDS); do - sleep $WAIT_INTERVAL_SECONDS - state=$(${CLIENT} trusted --mrenclave "$1" "$3" "$2") - if (( $4 >= state ? $4 - state < FEE_TOLERANCE : state - $4 < FEE_TOLERANCE)); then - return - else - : - fi - done - echo - echo "Assert $2 $3 failed, expected = $4, actual = $state, tolerance = $FEE_TOLERANCE" - exit 1 -} - -# Do a live query and assert the given account's state is equal to expected -# usage: -# assert_state -function assert_state() -{ - state=$(${CLIENT} trusted --mrenclave "$1" "$3" "$2") - if [ -z "$state" ]; then - echo "Query $2 $3 failed" - exit 1 - fi - - if [ $state -eq "$4" ]; then - return - fi - echo - echo "Assert $2 $3 failed, expected = $4, actual = $state" - exit 1 -} - -echo "* Query on-chain enclave registry:" -${CLIENT} list-workers -echo "" - -# this will always take the first MRENCLAVE found in the registry !! -read MRENCLAVE <<< $($CLIENT list-workers | awk '/ MRENCLAVE: / { print $2; exit }') -echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" - -[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; } - -VAULTACCOUNT=//Alice -## Sender account to shield for -BOBTRUSTEDACCOUNT=//Bob -echo " Bob's trusted account (same as public account) = ${BOBTRUSTEDACCOUNT}" -echo "" - -# Assert the initial trusted balance of Alice incognito -TRUSTED_BALANCE_BOB=1000000000000000 -wait_assert_state ${MRENCLAVE} ${BOBTRUSTEDACCOUNT} balance ${TRUSTED_BALANCE_BOB} - - -echo "* Send ${AMOUNT_SHIELD} from //Bob to //Alice on the Target A parentchain, which should trigger the shield process" -${CLIENT2} transfer //Bob ${VAULTACCOUNT} ${AMOUNT_SHIELD} -echo "" - -echo "* Wait and assert Bob's incognito account balance, should be $(( TRUSTED_BALANCE_BOB + AMOUNT_SHIELD ))" -wait_assert_state ${MRENCLAVE} ${BOBTRUSTEDACCOUNT} balance $(( TRUSTED_BALANCE_BOB + AMOUNT_SHIELD )) -echo "✔ ok" - -echo "" -echo "-----------------------" -echo "✔ The test passed!" -echo "-----------------------" -echo "" diff --git a/tee-worker/bitacross/cli/test_shield_on_target_nodes_with_transfer_to_alice.sh b/tee-worker/bitacross/cli/test_shield_on_target_nodes_with_transfer_to_alice.sh deleted file mode 100755 index b1670e5bb8..0000000000 --- a/tee-worker/bitacross/cli/test_shield_on_target_nodes_with_transfer_to_alice.sh +++ /dev/null @@ -1,159 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# Verifies that shielding from the Target A and B parentchains works by sending a transfer to //Alice. -# -# Note: This test does not do anything meaningful. It only verifies the basic functionality of the Target parentchain -# connections. - -while getopts ":m:p:A:u:V:w:x:y:z:C:" opt; do - case $opt in - p) - LITENTRY_RPC_PORT=$OPTARG - ;; - A) - WORKER_1_PORT=$OPTARG - ;; - u) - LITENTRY_RPC_URL=$OPTARG - ;; - V) - WORKER_1_URL=$OPTARG - ;; - w) - TARGET_A_PARENTCHAIN_RPC_URL=$OPTARG - ;; - x) - TARGET_A_PARENTCHAIN_RPC_PORT=$OPTARG - ;; - y) - TARGET_B_PARENTCHAIN_RPC_URL=$OPTARG - ;; - z) - TARGET_B_PARENTCHAIN_RPC_PORT=$OPTARG - ;; - C) - CLIENT_BIN=$OPTARG - ;; - *) - echo "invalid arg ${OPTARG}" - exit 1 - esac -done - -# Using default port if none given as arguments. -LITENTRY_RPC_PORT=${LITENTRY_RPC_PORT:-9944} -LITENTRY_RPC_URL=${LITENTRY_RPC_URL:-"ws://127.0.0.1"} -TARGET_A_PARENTCHAIN_RPC_PORT=${TARGET_A_PARENTCHAIN_RPC_PORT:-9966} -TARGET_A_PARENTCHAIN_RPC_URL=${TARGET_A_PARENTCHAIN_RPC_URL:-"ws://127.0.0.1"} -TARGET_B_PARENTCHAIN_RPC_PORT=${TARGET_B_PARENTCHAIN_RPC_PORT:-9988} -TARGET_B_PARENTCHAIN_RPC_URL=${TARGET_B_PARENTCHAIN_RPC_URL:-"ws://127.0.0.1"} - -WORKER_1_PORT=${WORKER_1_PORT:-2000} -WORKER_1_URL=${WORKER_1_URL:-"wss://127.0.0.1"} - -CLIENT_BIN=${CLIENT_BIN:-"./../bin/bitacross-cli"} - -echo "Using client binary ${CLIENT_BIN}" -${CLIENT_BIN} --version -echo "Using Integritee RPC uri ${LITENTRY_RPC_URL}:${LITENTRY_RPC_PORT}" -echo "Using Target A RPC uri ${TARGET_A_PARENTCHAIN_RPC_URL}:${TARGET_A_PARENTCHAIN_RPC_PORT}" -echo "Using Target B RPC uri ${TARGET_B_PARENTCHAIN_RPC_URL}:${TARGET_B_PARENTCHAIN_RPC_PORT}" -echo "Using trusted-worker 1 uri ${WORKER_1_URL}:${WORKER_1_PORT}" -echo "" - -# the parentchain token is 12 decimal -UNIT=$(( 10 ** 12 )) -FEE_TOLERANCE=$((10 ** 11)) - -# make these amounts greater than ED -AMOUNT_SHIELD=$(( 6 * UNIT )) - -CLIENT="${CLIENT_BIN} -p ${LITENTRY_RPC_PORT} -P ${WORKER_1_PORT} -u ${LITENTRY_RPC_URL} -U ${WORKER_1_URL}" -CLIENT2="${CLIENT_BIN} -p ${TARGET_A_PARENTCHAIN_RPC_PORT} -P ${WORKER_1_PORT} -u ${TARGET_A_PARENTCHAIN_RPC_URL} -U ${WORKER_1_URL}" -CLIENT3="${CLIENT_BIN} -p ${TARGET_B_PARENTCHAIN_RPC_PORT} -P ${WORKER_1_PORT} -u ${TARGET_B_PARENTCHAIN_RPC_URL} -U ${WORKER_1_URL}" - -# interval and max rounds to wait to check the given account balance in sidechain -WAIT_INTERVAL_SECONDS=10 -WAIT_ROUNDS=20 - -# Poll and assert the given account's state is equal to expected, -# with timeout WAIT_INTERVAL_SECONDS * WAIT_ROUNDS -# usage: -# wait_assert_state -# the `state-name` has to be the supported subcommand, e.g. `balance`, `nonce` -function wait_assert_state() -{ - for i in $(seq 1 $WAIT_ROUNDS); do - sleep $WAIT_INTERVAL_SECONDS - state=$(${CLIENT} trusted --mrenclave "$1" "$3" "$2") - if (( $4 >= state ? $4 - state < FEE_TOLERANCE : state - $4 < FEE_TOLERANCE)); then - return - else - : - fi - done - echo - echo "Assert $2 $3 failed, expected = $4, actual = $state, tolerance = $FEE_TOLERANCE" - exit 1 -} - -# Do a live query and assert the given account's state is equal to expected -# usage: -# assert_state -function assert_state() -{ - state=$(${CLIENT} trusted --mrenclave "$1" "$3" "$2") - if [ -z "$state" ]; then - echo "Query $2 $3 failed" - exit 1 - fi - - if [ $state -eq "$4" ]; then - return - fi - echo - echo "Assert $2 $3 failed, expected = $4, actual = $state" - exit 1 -} - -echo "* Query on-chain enclave registry:" -${CLIENT} list-workers -echo "" - -# this will always take the first MRENCLAVE found in the registry !! -read MRENCLAVE <<< $($CLIENT list-workers | awk '/ MRENCLAVE: / { print $2; exit }') -echo "Reading MRENCLAVE from worker list: ${MRENCLAVE}" - -[[ -z $MRENCLAVE ]] && { echo "MRENCLAVE is empty. cannot continue" ; exit 1; } - -ALICETRUSTEDACCOUNT=//Alice -echo " Alice's trusted account (same as public account) = ${ALICETRUSTEDACCOUNT}" -echo "" - -# Assert the initial trusted balance of Alice incognito -TRUSTED_BALANCE_ALICE=1000000000000000 -wait_assert_state ${MRENCLAVE} ${ALICETRUSTEDACCOUNT} balance ${TRUSTED_BALANCE_ALICE} - - -echo "* Send ${AMOUNT_SHIELD} from //Alice to //Alice on the Target A parentchain, which should trigger the shield process" -${CLIENT2} transfer //Alice ${ALICETRUSTEDACCOUNT} ${AMOUNT_SHIELD} -echo "" - -echo "* Wait and assert Alice's incognito account balance, should be $(( TRUSTED_BALANCE_ALICE + AMOUNT_SHIELD ))" -wait_assert_state ${MRENCLAVE} ${ALICETRUSTEDACCOUNT} balance $(( TRUSTED_BALANCE_ALICE + AMOUNT_SHIELD )) -echo "✔ ok" - -echo "* Send ${AMOUNT_SHIELD} from //Alice to //Alice on the Target B Parentchain, which should trigger the shield process again" -${CLIENT3} transfer //Alice ${ALICETRUSTEDACCOUNT} ${AMOUNT_SHIELD} -echo "" - -echo "* Wait and assert Alice's incognito account balance, should be $(( TRUSTED_BALANCE_ALICE + 2*AMOUNT_SHIELD ))" -wait_assert_state ${MRENCLAVE} ${ALICETRUSTEDACCOUNT} balance $(( TRUSTED_BALANCE_ALICE + 2*AMOUNT_SHIELD )) -echo "✔ ok" - -echo "" -echo "-----------------------" -echo "✔ The test passed!" -echo "-----------------------" -echo "" diff --git a/tee-worker/bitacross/cli/tests/basic_tests.rs b/tee-worker/bitacross/cli/tests/basic_tests.rs deleted file mode 100644 index e01d318681..0000000000 --- a/tee-worker/bitacross/cli/tests/basic_tests.rs +++ /dev/null @@ -1,24 +0,0 @@ -use bitacross_cli::Cli; -use clap::Parser; - -fn init() { - let _ = env_logger::try_init(); -} - -#[test] -fn test_version() { - init(); - - let res = Cli::try_parse_from(vec!["placeholder_cli_path", "--version"]); - let _err = clap::Error::new(clap::error::ErrorKind::DisplayVersion); - assert!(matches!(res, Err(_err))); -} - -#[test] -fn test_help() { - init(); - - let res = Cli::try_parse_from(vec!["placeholder_cli_path", "--help"]); - let _err = clap::Error::new(clap::error::ErrorKind::DisplayHelp); - assert!(matches!(res, Err(_err))); -} diff --git a/tee-worker/bitacross/core-primitives/enclave-api/Cargo.toml b/tee-worker/bitacross/core-primitives/enclave-api/Cargo.toml deleted file mode 100644 index ca364fa05d..0000000000 --- a/tee-worker/bitacross/core-primitives/enclave-api/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -name = "bc-itp-enclave-api" -version = "0.1.0" -authors = ['Trust Computing GmbH ', 'Integritee AG '] -edition = "2021" - -[dependencies] -codec = { package = "parity-scale-codec", workspace = true } -hex = { workspace = true } -log = { workspace = true } -serde_json = { workspace = true } -thiserror = { workspace = true } - -sgx_crypto_helper = { workspace = true } -sgx_types = { workspace = true } -sgx_urts = { workspace = true, optional = true } - -frame-support = { workspace = true } -sp-core = { workspace = true } -sp-runtime = { workspace = true } - -itp-enclave-api-ffi = { package = "bc-itp-enclave-api-ffi", path = "ffi" } -itp-settings = { workspace = true } -itp-sgx-crypto = { workspace = true } -itp-stf-interface = { workspace = true } -itp-storage = { workspace = true } -itp-types = { workspace = true } - -[features] -default = [] -implement-ffi = [ - "sgx_urts", - "itp-enclave-api-ffi/link-sgx-libs", -] diff --git a/tee-worker/bitacross/core-primitives/enclave-api/build.rs b/tee-worker/bitacross/core-primitives/enclave-api/build.rs deleted file mode 100644 index 1c20ea4c84..0000000000 --- a/tee-worker/bitacross/core-primitives/enclave-api/build.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -fn main() { - // If the linker failed to find libsgx_dcap_ql.so, please make sure that - // (1) libsgx-dcap-ql is installed - // (2) libsgx_dcap_ql.so exists. typicall at /usr/lib/x86_64-linux-gnu - // if libsgx_dcap_ql.so.1 is there, but no libsgx-dcap_ql, - // just create a symlink by - // ln -s libsgx_dcap_ql.so.1 libsgx_dcap_ql.so - println!("cargo:rustc-link-lib=dylib=sgx_dcap_ql"); - println!("cargo:rustc-link-lib=dylib=sgx_dcap_quoteverify"); - println!("cargo:rustc-link-lib=dylib=dcap_quoteprov"); -} diff --git a/tee-worker/bitacross/core-primitives/enclave-api/ffi/Cargo.toml b/tee-worker/bitacross/core-primitives/enclave-api/ffi/Cargo.toml deleted file mode 100644 index 1daa3ab730..0000000000 --- a/tee-worker/bitacross/core-primitives/enclave-api/ffi/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "bc-itp-enclave-api-ffi" -version = "0.1.0" -edition = "2021" - -[dependencies] -sgx_types = { workspace = true } - -[features] -# necessary to run cargo tests without any preliminaries -# See: https://github.com/rust-lang/cargo/issues/2549 -link-sgx-libs = [] diff --git a/tee-worker/bitacross/core-primitives/enclave-api/ffi/build.rs b/tee-worker/bitacross/core-primitives/enclave-api/ffi/build.rs deleted file mode 100644 index 09ccb3b707..0000000000 --- a/tee-worker/bitacross/core-primitives/enclave-api/ffi/build.rs +++ /dev/null @@ -1,44 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -fn main() { - if cfg!(feature = "link-sgx-libs") { - use std::env; - - let sdk_dir = env::var("SGX_SDK").unwrap_or_else(|_| "/opt/intel/sgxsdk".to_string()); - let is_sim = env::var("SGX_MODE").unwrap_or_else(|_| "HW".to_string()); - - // NOTE: if the crate is a workspace member rustc-paths are relative from the root directory - println!("cargo:rustc-link-search=native=./bitacross/lib"); - println!("cargo:rustc-link-lib=static=Enclave_u"); - - println!("cargo:rustc-link-search=native={}/lib64", sdk_dir); - println!("cargo:rustc-link-lib=static=sgx_uprotected_fs"); - match is_sim.as_ref() { - "SW" => { - println!("cargo:rustc-link-lib=dylib=sgx_urts_sim"); - println!("cargo:rustc-link-lib=dylib=sgx_uae_service_sim"); - }, - _ => { - // HW by default - println!("cargo:rustc-link-lib=dylib=sgx_urts"); - println!("cargo:rustc-link-lib=dylib=sgx_uae_service"); - }, - } - } -} diff --git a/tee-worker/bitacross/core-primitives/enclave-api/ffi/src/lib.rs b/tee-worker/bitacross/core-primitives/enclave-api/ffi/src/lib.rs deleted file mode 100644 index 339aa7bb97..0000000000 --- a/tee-worker/bitacross/core-primitives/enclave-api/ffi/src/lib.rs +++ /dev/null @@ -1,270 +0,0 @@ -///! FFI's that call into the enclave. These functions need to be added to the -/// enclave edl file and be implemented within the enclave. -use sgx_types::{ - c_int, sgx_enclave_id_t, sgx_ql_qve_collateral_t, sgx_quote_sign_type_t, sgx_status_t, - sgx_target_info_t, -}; - -extern "C" { - - pub fn generate_dcap_ra_extrinsic_from_quote( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - w_url: *const u8, - w_url_size: u32, - quote: *const u8, - quote_size: u32, - unchecked_extrinsic: *mut u8, - unchecked_extrinsic_max_size: u32, - unchecked_extrinsic_size: *mut u32, - ) -> sgx_status_t; - - pub fn init( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - mu_ra_addr: *const u8, - mu_ra_addr_size: u32, - untrusted_worker_addr: *const u8, - untrusted_worker_addr_size: u32, - encoded_base_dir_str: *const u8, - encoded_base_dir_size: u32, - ceremony_commands_thread_count: u8, - ceremony_events_thread_count: u8, - ) -> sgx_status_t; - - pub fn init_direct_invocation_server( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - server_addr: *const u8, - server_addr_size: u32, - ) -> sgx_status_t; - - pub fn init_parentchain_components( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - params: *const u8, - params_size: usize, - latest_header: *mut u8, - latest_header_size: usize, - ) -> sgx_status_t; - - pub fn init_shard( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - shard: *const u8, - shard_size: u32, - ) -> sgx_status_t; - - pub fn init_shard_creation_parentchain_header( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - shard: *const u8, - shard_size: u32, - parentchain_id: *const u8, - parentchain_id_size: u32, - header: *const u8, - header_size: u32, - ) -> sgx_status_t; - - pub fn get_shard_creation_info( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - shard: *const u8, - shard_size: u32, - creation: *mut u8, - creation_size: u32, - ) -> sgx_status_t; - - pub fn sync_parentchain( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - blocks: *const u8, - blocks_size: usize, - events: *const u8, - events_size: usize, - events_proofs: *const u8, - events_proofs_size: usize, - parentchain_id: *const u8, - parentchain_id_size: u32, - immediate_import: c_int, - ) -> sgx_status_t; - - pub fn set_nonce( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - nonce: *const u32, - parentchain_id: *const u8, - parentchain_id_size: u32, - ) -> sgx_status_t; - - pub fn set_node_metadata( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - node_metadata: *const u8, - node_metadata_size: u32, - parentchain_id: *const u8, - parentchain_id_size: u32, - ) -> sgx_status_t; - - pub fn get_rsa_encryption_pubkey( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - pubkey: *mut u8, - pubkey_size: u32, - ) -> sgx_status_t; - - pub fn get_ecc_signing_pubkey( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - pubkey: *mut u8, - pubkey_size: u32, - ) -> sgx_status_t; - - pub fn get_bitcoin_wallet_pair( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - pair: *mut u8, - pair_size: u32, - ) -> sgx_status_t; - - pub fn get_ethereum_wallet_pair( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - pair: *mut u8, - pair_size: u32, - ) -> sgx_status_t; - - pub fn get_ton_wallet_pair( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - pair: *mut u8, - pair_size: u32, - ) -> sgx_status_t; - - pub fn get_mrenclave( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - mrenclave: *mut u8, - mrenclave_size: u32, - ) -> sgx_status_t; - - pub fn generate_ias_ra_extrinsic( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - w_url: *const u8, - w_url_size: u32, - unchecked_extrinsic: *mut u8, - unchecked_extrinsic_max_size: u32, - unchecked_extrinsic_size: *mut u32, - skip_ra: c_int, - ) -> sgx_status_t; - - pub fn generate_dcap_ra_extrinsic( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - w_url: *const u8, - w_url_size: u32, - unchecked_extrinsic: *mut u8, - unchecked_extrinsic_max_size: u32, - unchecked_extrinsic_size: *mut u32, - skip_ra: c_int, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, - ) -> sgx_status_t; - - pub fn generate_dcap_ra_quote( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - skip_ra: c_int, - quoting_enclave_target_info: &sgx_target_info_t, - quote_size: u32, - dcap_quote_p: *mut u8, - dcap_quote_size: u32, - ) -> sgx_status_t; - - pub fn generate_register_quoting_enclave_extrinsic( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - collateral: *const sgx_ql_qve_collateral_t, - unchecked_extrinsic: *mut u8, - unchecked_extrinsic_max_size: u32, - unchecked_extrinsic_size: *mut u32, - ) -> sgx_status_t; - - pub fn generate_register_tcb_info_extrinsic( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - collateral: *const sgx_ql_qve_collateral_t, - unchecked_extrinsic: *mut u8, - unchecked_extrinsic_max_size: u32, - unchecked_extrinsic_size: *mut u32, - ) -> sgx_status_t; - - pub fn dump_ias_ra_cert_to_disk( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - ) -> sgx_status_t; - - pub fn dump_dcap_ra_cert_to_disk( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - quoting_enclave_target_info: &sgx_target_info_t, - quote_size: u32, - ) -> sgx_status_t; - - pub fn dump_dcap_collateral_to_disk( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - collateral: *const sgx_ql_qve_collateral_t, - ) -> sgx_status_t; - - pub fn test_main_entrance(eid: sgx_enclave_id_t, retval: *mut sgx_status_t) -> sgx_status_t; - - pub fn run_state_provisioning_server( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - socket_fd: c_int, - sign_type: sgx_quote_sign_type_t, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, - skip_ra: c_int, - ) -> sgx_status_t; - - pub fn request_state_provisioning( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - socket_fd: c_int, - sign_type: sgx_quote_sign_type_t, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, - shard: *const u8, - shard_size: u32, - skip_ra: c_int, - ) -> sgx_status_t; - - // litentry - pub fn migrate_shard( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - new_shard: *const u8, - shard_size: u32, - ) -> sgx_status_t; - - pub fn ignore_parentchain_block_import_validation_until( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - until: *const u32, - ) -> sgx_status_t; - - pub fn publish_wallets(eid: sgx_enclave_id_t, retval: *mut sgx_status_t) -> sgx_status_t; - - pub fn finish_enclave_init(eid: sgx_enclave_id_t, retval: *mut sgx_status_t) -> sgx_status_t; - - pub fn init_wallets( - eid: sgx_enclave_id_t, - retval: *mut sgx_status_t, - encoded_base_dir_str: *const u8, - encoded_base_dir_size: u32, - ) -> sgx_status_t; - -} diff --git a/tee-worker/bitacross/core-primitives/enclave-api/src/enclave_base.rs b/tee-worker/bitacross/core-primitives/enclave-api/src/enclave_base.rs deleted file mode 100644 index 61ce8f2ba9..0000000000 --- a/tee-worker/bitacross/core-primitives/enclave-api/src/enclave_base.rs +++ /dev/null @@ -1,510 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::EnclaveResult; -use codec::Decode; -use core::fmt::Debug; -use itp_sgx_crypto::{ecdsa, schnorr}; -use itp_stf_interface::ShardCreationInfo; -use itp_types::{ - parentchain::{Header, ParentchainId, ParentchainInitParams}, - EnclaveFingerprint, ShardIdentifier, -}; -use sgx_crypto_helper::rsa3072::Rsa3072PubKey; -use sp_core::ed25519; - -/// Trait for base/common Enclave API functions -pub trait EnclaveBase: Send + Sync + 'static { - /// Initialize the enclave (needs to be called once at application startup). - fn init( - &self, - mu_ra_addr: &str, - untrusted_worker_addr: &str, - base_dir: &str, - ceremony_commands_thread_count: u8, - ceremony_events_thread_count: u8, - ) -> EnclaveResult<()>; - /// Initialize the direct invocation RPC server. - fn init_direct_invocation_server(&self, rpc_server_addr: String) -> EnclaveResult<()>; - - /// Initialize the light client (needs to be called once at application startup). - fn init_parentchain_components( - &self, - params: ParentchainInitParams, - ) -> EnclaveResult

    ; - - /// Initialize a new shard. - fn init_shard(&self, shard: Vec) -> EnclaveResult<()>; - - /// Initialize parentchain checkpoint after which invocations will be processed - fn init_shard_creation_parentchain_header( - &self, - shard: &ShardIdentifier, - parentchain_id: &ParentchainId, - header: &Header, - ) -> EnclaveResult<()>; - - fn get_shard_creation_info(&self, shard: &ShardIdentifier) -> EnclaveResult; - - fn set_nonce(&self, nonce: u32, parentchain_id: ParentchainId) -> EnclaveResult<()>; - - fn set_node_metadata( - &self, - metadata: Vec, - parentchain_id: ParentchainId, - ) -> EnclaveResult<()>; - - fn get_rsa_shielding_pubkey(&self) -> EnclaveResult; - - fn get_ecc_signing_pubkey(&self) -> EnclaveResult; - - /// retrieve the btc wallet key pair, only works in non-prod - fn get_bitcoin_wallet_pair(&self) -> EnclaveResult; - - /// retrieve the eth wallet key pair, only works in non-prod - fn get_ethereum_wallet_pair(&self) -> EnclaveResult; - - /// retrieve the ton wallet key pair, only works in non-prod - fn get_ton_wallet_pair(&self) -> EnclaveResult; - - fn get_fingerprint(&self) -> EnclaveResult; - - // litentry - fn migrate_shard(&self, new_shard: Vec) -> EnclaveResult<()>; - - /// Publish generated wallets on parachain - fn publish_wallets(&self) -> EnclaveResult<()>; - - /// finish enclave initialization - fn finish_enclave_init(&self) -> EnclaveResult<()>; - - /// init custodian wallets - fn init_wallets(&self, base_dir: &str) -> EnclaveResult<()>; -} - -/// EnclaveApi implementation for Enclave struct -#[cfg(feature = "implement-ffi")] -mod impl_ffi { - use super::{ecdsa, schnorr, EnclaveBase}; - use crate::{error::Error, Enclave, EnclaveResult}; - use codec::{Decode, Encode}; - use core::fmt::Debug; - use frame_support::ensure; - use itp_enclave_api_ffi as ffi; - use itp_settings::worker::{ - HEADER_MAX_SIZE, MR_ENCLAVE_SIZE, SHIELDING_KEY_SIZE, SIGNING_KEY_SIZE, - }; - use itp_stf_interface::ShardCreationInfo; - use itp_types::{ - parentchain::{Header, ParentchainId, ParentchainInitParams}, - EnclaveFingerprint, ShardIdentifier, - }; - use log::*; - use sgx_crypto_helper::rsa3072::Rsa3072PubKey; - use sgx_types::*; - use sp_core::{ed25519, Pair}; - - impl EnclaveBase for Enclave { - fn init( - &self, - mu_ra_addr: &str, - untrusted_worker_addr: &str, - base_dir: &str, - ceremony_commands_thread_count: u8, - ceremony_events_thread_count: u8, - ) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let encoded_mu_ra_addr = mu_ra_addr.encode(); - let encoded_untrusted_worker_addr = untrusted_worker_addr.encode(); - let encoded_base_dir = base_dir.encode(); - - let result = unsafe { - ffi::init( - self.eid, - &mut retval, - encoded_mu_ra_addr.as_ptr(), - encoded_mu_ra_addr.len() as u32, - encoded_untrusted_worker_addr.as_ptr(), - encoded_untrusted_worker_addr.len() as u32, - encoded_base_dir.as_ptr(), - encoded_base_dir.len() as u32, - ceremony_commands_thread_count, - ceremony_events_thread_count, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn init_direct_invocation_server(&self, rpc_server_addr: String) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let encoded_rpc_server_addr = rpc_server_addr.encode(); - - let result = unsafe { - ffi::init_direct_invocation_server( - self.eid, - &mut retval, - encoded_rpc_server_addr.as_ptr(), - encoded_rpc_server_addr.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn init_parentchain_components( - &self, - params: ParentchainInitParams, - ) -> EnclaveResult
    { - let latest_header_encoded = init_parentchain_components_ffi(self.eid, params.encode())?; - - let latest = Header::decode(&mut latest_header_encoded.as_slice())?; - info!("Latest Header {:?}", latest); - - Ok(latest) - } - - fn init_shard(&self, shard: Vec) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let result = unsafe { - ffi::init_shard(self.eid, &mut retval, shard.as_ptr(), shard.len() as u32) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn init_shard_creation_parentchain_header( - &self, - shard: &ShardIdentifier, - parentchain_id: &ParentchainId, - header: &Header, - ) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - let parentchain_id_enc = parentchain_id.encode(); - let header_bytes = header.encode(); - let shard_bytes = shard.encode(); - let result = unsafe { - ffi::init_shard_creation_parentchain_header( - self.eid, - &mut retval, - shard_bytes.as_ptr(), - shard_bytes.len() as u32, - parentchain_id_enc.as_ptr(), - parentchain_id_enc.len() as u32, - header_bytes.as_ptr(), - header_bytes.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn get_shard_creation_info( - &self, - shard: &ShardIdentifier, - ) -> EnclaveResult { - let mut retval = sgx_status_t::SGX_SUCCESS; - let mut creation_info = [0u8; std::mem::size_of::()]; - let shard_bytes = shard.encode(); - - let result = unsafe { - ffi::get_shard_creation_info( - self.eid, - &mut retval, - shard_bytes.as_ptr(), - shard_bytes.len() as u32, - creation_info.as_mut_ptr(), - creation_info.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - Decode::decode(&mut creation_info.as_slice()).map_err(|e| Error::Codec(e.into())) - } - - fn set_nonce(&self, nonce: u32, parentchain_id: ParentchainId) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let parentchain_id_enc = parentchain_id.encode(); - - let result = unsafe { - ffi::set_nonce( - self.eid, - &mut retval, - &nonce, - parentchain_id_enc.as_ptr(), - parentchain_id_enc.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn set_node_metadata( - &self, - metadata: Vec, - parentchain_id: ParentchainId, - ) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let parentchain_id_enc = parentchain_id.encode(); - - let result = unsafe { - ffi::set_node_metadata( - self.eid, - &mut retval, - metadata.as_ptr(), - metadata.len() as u32, - parentchain_id_enc.as_ptr(), - parentchain_id_enc.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn get_rsa_shielding_pubkey(&self) -> EnclaveResult { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let pubkey_size = SHIELDING_KEY_SIZE; - let mut pubkey = vec![0u8; pubkey_size]; - - let result = unsafe { - ffi::get_rsa_encryption_pubkey( - self.eid, - &mut retval, - pubkey.as_mut_ptr(), - pubkey.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - let rsa_pubkey: Rsa3072PubKey = - serde_json::from_slice(pubkey.as_slice()).expect("Invalid public key"); - debug!("got RSA pubkey {:?}", rsa_pubkey); - Ok(rsa_pubkey) - } - - fn get_ecc_signing_pubkey(&self) -> EnclaveResult { - let mut retval = sgx_status_t::SGX_SUCCESS; - let mut pubkey = [0u8; SIGNING_KEY_SIZE]; - - let result = unsafe { - ffi::get_ecc_signing_pubkey( - self.eid, - &mut retval, - pubkey.as_mut_ptr(), - pubkey.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(ed25519::Public::from_raw(pubkey)) - } - - fn get_bitcoin_wallet_pair(&self) -> EnclaveResult { - let mut retval = sgx_status_t::SGX_SUCCESS; - let mut private_key = [0u8; 32]; - - let result = unsafe { - ffi::get_bitcoin_wallet_pair( - self.eid, - &mut retval, - private_key.as_mut_ptr(), - private_key.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - schnorr::Pair::from_bytes(&private_key) - .map_err(|e| Error::Other(format!("{:?}", e).into())) - } - - fn get_ethereum_wallet_pair(&self) -> EnclaveResult { - let mut retval = sgx_status_t::SGX_SUCCESS; - let mut private_key = [0u8; 32]; - - let result = unsafe { - ffi::get_ethereum_wallet_pair( - self.eid, - &mut retval, - private_key.as_mut_ptr(), - private_key.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - ecdsa::Pair::from_bytes(&private_key) - .map_err(|e| Error::Other(format!("{:?}", e).into())) - } - - fn get_ton_wallet_pair(&self) -> EnclaveResult { - let mut retval = sgx_status_t::SGX_SUCCESS; - let mut private_key = [0u8; 32]; - - let result = unsafe { - ffi::get_ton_wallet_pair( - self.eid, - &mut retval, - private_key.as_mut_ptr(), - private_key.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(ed25519::Pair::from_seed(&private_key)) - } - - fn get_fingerprint(&self) -> EnclaveResult { - let mut retval = sgx_status_t::SGX_SUCCESS; - let mut mr_enclave = [0u8; MR_ENCLAVE_SIZE]; - - let result = unsafe { - ffi::get_mrenclave( - self.eid, - &mut retval, - mr_enclave.as_mut_ptr(), - mr_enclave.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(mr_enclave.into()) - } - - fn migrate_shard(&self, new_shard: Vec) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let result = unsafe { - ffi::migrate_shard( - self.eid, - &mut retval, - new_shard.as_ptr(), - new_shard.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn publish_wallets(&self) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let result = unsafe { ffi::publish_wallets(self.eid, &mut retval) }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn finish_enclave_init(&self) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let result = unsafe { ffi::finish_enclave_init(self.eid, &mut retval) }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn init_wallets(&self, base_dir: &str) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let encoded_base_dir = base_dir.encode(); - - let result = unsafe { - ffi::init_wallets( - self.eid, - &mut retval, - encoded_base_dir.as_ptr(), - encoded_base_dir.len() as u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - } - - fn init_parentchain_components_ffi( - enclave_id: sgx_enclave_id_t, - params: Vec, - ) -> EnclaveResult> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let latest_header_size = HEADER_MAX_SIZE; - let mut latest_header = vec![0u8; latest_header_size]; - - let result = unsafe { - ffi::init_parentchain_components( - enclave_id, - &mut retval, - params.as_ptr(), - params.len(), - latest_header.as_mut_ptr(), - latest_header.len(), - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(latest_header) - } -} diff --git a/tee-worker/bitacross/core-primitives/enclave-api/src/enclave_test.rs b/tee-worker/bitacross/core-primitives/enclave-api/src/enclave_test.rs deleted file mode 100644 index aaf3a8e97d..0000000000 --- a/tee-worker/bitacross/core-primitives/enclave-api/src/enclave_test.rs +++ /dev/null @@ -1,48 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::EnclaveResult; - -pub trait EnclaveTest: Send + Sync + 'static { - fn test_main_entrance(&self) -> EnclaveResult<()>; -} - -#[cfg(feature = "implement-ffi")] -mod impl_ffi { - use super::EnclaveTest; - use crate::{error::Error, Enclave, EnclaveResult}; - use frame_support::ensure; - use itp_enclave_api_ffi as ffi; - use log::*; - use sgx_types::sgx_status_t; - - impl EnclaveTest for Enclave { - fn test_main_entrance(&self) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let result = unsafe { ffi::test_main_entrance(self.eid, &mut retval) }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - debug!("[+] successfully executed enclave test main"); - - Ok(()) - } - } -} diff --git a/tee-worker/bitacross/core-primitives/enclave-api/src/error.rs b/tee-worker/bitacross/core-primitives/enclave-api/src/error.rs deleted file mode 100644 index d510c56db4..0000000000 --- a/tee-worker/bitacross/core-primitives/enclave-api/src/error.rs +++ /dev/null @@ -1,14 +0,0 @@ -use codec::Error as CodecError; -use sgx_types::{sgx_quote3_error_t, sgx_status_t}; - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("{0}")] - Codec(#[from] CodecError), - #[error("Enclave Error: {0}")] - Sgx(sgx_status_t), - #[error("Enclave Quote Error: {0}")] - SgxQuote(sgx_quote3_error_t), - #[error("Error, other: {0}")] - Other(Box), -} diff --git a/tee-worker/bitacross/core-primitives/enclave-api/src/lib.rs b/tee-worker/bitacross/core-primitives/enclave-api/src/lib.rs deleted file mode 100644 index 131f4e9b7a..0000000000 --- a/tee-worker/bitacross/core-primitives/enclave-api/src/lib.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! Some definitions and traits that facilitate interaction with the enclave. -//! -//! This serves as a proof of concept on how we could design the interface between the worker and -//! the enclave. -//! -//! Design principle here should be to keep the traits as slim as possible - because then the -//! worker can also define slim interfaces with less demanding trait bounds. -//! -//! This can further be simplified once https://github.com/integritee-network/worker/issues/254 -//! is implemented. Then we can replace the several ffi:: and the boilerplate code -//! around it with a simple `fn ecall(call: CallEnum) -> Result`, which wraps one single -//! ffi function. - -use crate::error::Error; - -pub mod enclave_base; -pub mod enclave_test; -pub mod error; -pub mod remote_attestation; -pub mod sidechain; -pub mod utils; - -#[cfg(feature = "implement-ffi")] -pub use sgx_urts::SgxEnclave; - -#[cfg(feature = "implement-ffi")] -use sgx_types::sgx_enclave_id_t; - -pub type EnclaveResult = Result; - -#[cfg(feature = "implement-ffi")] -#[derive(Clone, Debug, Default)] -pub struct Enclave { - eid: sgx_enclave_id_t, - sgx_enclave: SgxEnclave, -} - -#[cfg(feature = "implement-ffi")] -impl Enclave { - pub fn new(sgx_enclave: SgxEnclave) -> Self { - Enclave { eid: sgx_enclave.geteid(), sgx_enclave } - } - - pub fn destroy(self) { - self.sgx_enclave.destroy() - } -} diff --git a/tee-worker/bitacross/core-primitives/enclave-api/src/remote_attestation.rs b/tee-worker/bitacross/core-primitives/enclave-api/src/remote_attestation.rs deleted file mode 100644 index 15691f7172..0000000000 --- a/tee-worker/bitacross/core-primitives/enclave-api/src/remote_attestation.rs +++ /dev/null @@ -1,870 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::EnclaveResult; -use itp_types::{Fmspc, ShardIdentifier}; -use sgx_types::*; - -/// Struct that unites all relevant data reported by the QVE -pub struct QveReport { - pub supplemental_data: Vec, - pub qve_report_info_return_value: sgx_ql_qe_report_info_t, - pub quote_verification_result: sgx_ql_qv_result_t, - pub collateral_expiration_status: u32, -} - -/// general remote attestation methods -pub trait RemoteAttestation { - fn generate_ias_ra_extrinsic(&self, w_url: &str, skip_ra: bool) -> EnclaveResult>; - - fn generate_dcap_ra_extrinsic(&self, w_url: &str, skip_ra: bool) -> EnclaveResult>; - fn generate_dcap_ra_extrinsic_from_quote( - &self, - url: String, - quote: &[u8], - ) -> EnclaveResult>; - fn generate_dcap_ra_quote(&self, skip_ra: bool) -> EnclaveResult>; - - fn generate_register_quoting_enclave_extrinsic(&self, fmspc: Fmspc) -> EnclaveResult>; - - fn generate_register_tcb_info_extrinsic(&self, fmspc: Fmspc) -> EnclaveResult>; - - fn dump_ias_ra_cert_to_disk(&self) -> EnclaveResult<()>; - - fn dump_dcap_ra_cert_to_disk(&self) -> EnclaveResult<()>; - - fn dump_dcap_collateral_to_disk(&self, fmspc: Fmspc) -> EnclaveResult<()>; - - fn set_ql_qe_enclave_paths(&self) -> EnclaveResult<()>; - - fn set_sgx_qpl_logging(&self) -> EnclaveResult<()>; - - fn qe_get_target_info(&self) -> EnclaveResult; - - fn qe_get_quote_size(&self) -> EnclaveResult; - - fn get_dcap_collateral(&self, fmspc: Fmspc) -> EnclaveResult<*const sgx_ql_qve_collateral_t>; -} - -/// call-backs that are made from inside the enclave (using o-call), to e-calls again inside the enclave -pub trait RemoteAttestationCallBacks { - fn init_quote(&self) -> EnclaveResult<(sgx_target_info_t, sgx_epid_group_id_t)>; - - fn calc_quote_size(&self, revocation_list: Vec) -> EnclaveResult; - - fn get_quote( - &self, - revocation_list: Vec, - report: sgx_report_t, - quote_type: sgx_quote_sign_type_t, - spid: sgx_spid_t, - quote_nonce: sgx_quote_nonce_t, - quote_length: u32, - ) -> EnclaveResult<(sgx_report_t, Vec)>; - - fn get_dcap_quote(&self, report: sgx_report_t, quote_size: u32) -> EnclaveResult>; - - fn get_qve_report_on_quote( - &self, - quote: Vec, - current_time: i64, - quote_collateral: &sgx_ql_qve_collateral_t, - qve_report_info: sgx_ql_qe_report_info_t, - supplemental_data_size: u32, - ) -> EnclaveResult; - - fn get_update_info( - &self, - platform_blob: sgx_platform_info_t, - enclave_trusted: i32, - ) -> EnclaveResult; -} - -/// TLS remote attestations methods -pub trait TlsRemoteAttestation { - fn run_state_provisioning_server( - &self, - socket_fd: c_int, - sign_type: sgx_quote_sign_type_t, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, - skip_ra: bool, - ) -> EnclaveResult<()>; - - fn request_state_provisioning( - &self, - socket_fd: c_int, - sign_type: sgx_quote_sign_type_t, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, - shard: &ShardIdentifier, - skip_ra: bool, - ) -> EnclaveResult<()>; -} - -#[cfg(feature = "implement-ffi")] -mod impl_ffi { - use super::{QveReport, RemoteAttestation, RemoteAttestationCallBacks, TlsRemoteAttestation}; - use crate::{error::Error, utils, Enclave, EnclaveResult}; - use codec::Encode; - use frame_support::ensure; - use itp_enclave_api_ffi as ffi; - use itp_settings::worker::EXTRINSIC_MAX_SIZE; - use itp_types::{Fmspc, ShardIdentifier}; - use log::*; - use sgx_types::*; - - const OS_SYSTEM_PATH: &str = "/usr/lib/x86_64-linux-gnu/"; - const C_STRING_ENDING: &str = "\0"; - const PCE_ENCLAVE: &str = "libsgx_pce.signed.so.1"; - const QE3_ENCLAVE: &str = "libsgx_qe3.signed.so.1"; - const ID_ENCLAVE: &str = "libsgx_id_enclave.signed.so.1"; - const LIBDCAP_QUOTEPROV: &str = "libdcap_quoteprov.so.1"; - const QVE_ENCLAVE: &str = "libsgx_qve.signed.so.1"; - - impl RemoteAttestation for Enclave { - fn generate_ias_ra_extrinsic(&self, w_url: &str, skip_ra: bool) -> EnclaveResult> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; - let mut unchecked_extrinsic_size: u32 = 0; - - trace!("Generating ias_ra_extrinsic with URL: {}", w_url); - - let url = w_url.encode(); - - let result = unsafe { - ffi::generate_ias_ra_extrinsic( - self.eid, - &mut retval, - url.as_ptr(), - url.len() as u32, - unchecked_extrinsic.as_mut_ptr(), - unchecked_extrinsic.len() as u32, - &mut unchecked_extrinsic_size as *mut u32, - skip_ra.into(), - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - ensure!( - (unchecked_extrinsic_size as usize) < unchecked_extrinsic.len(), - Error::Sgx(sgx_status_t::SGX_ERROR_INVALID_PARAMETER) - ); - Ok(Vec::from(&unchecked_extrinsic[..unchecked_extrinsic_size as usize])) - } - fn generate_dcap_ra_extrinsic_from_quote( - &self, - url: String, - quote: &[u8], - ) -> EnclaveResult> { - let mut retval = sgx_status_t::SGX_SUCCESS; - let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; - let mut unchecked_extrinsic_size: u32 = 0; - let url = url.encode(); - - let result = unsafe { - ffi::generate_dcap_ra_extrinsic_from_quote( - self.eid, - &mut retval, - url.as_ptr(), - url.len() as u32, - quote.as_ptr(), - quote.len() as u32, - unchecked_extrinsic.as_mut_ptr(), - unchecked_extrinsic.len() as u32, - &mut unchecked_extrinsic_size as *mut u32, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - ensure!( - (unchecked_extrinsic_size as usize) < unchecked_extrinsic.len(), - Error::Sgx(sgx_status_t::SGX_ERROR_INVALID_PARAMETER) - ); - Ok(Vec::from(&unchecked_extrinsic[..unchecked_extrinsic_size as usize])) - } - - fn generate_dcap_ra_quote(&self, skip_ra: bool) -> EnclaveResult> { - let mut retval = sgx_status_t::SGX_SUCCESS; - let quoting_enclave_target_info = self.qe_get_target_info()?; - let quote_size = self.qe_get_quote_size()?; - - let mut dcap_quote_vec: Vec = vec![0; quote_size as usize]; - let (dcap_quote_p, dcap_quote_size) = - (dcap_quote_vec.as_mut_ptr(), dcap_quote_vec.len() as u32); - - let result = unsafe { - ffi::generate_dcap_ra_quote( - self.eid, - &mut retval, - skip_ra.into(), - "ing_enclave_target_info, - quote_size, - dcap_quote_p, - dcap_quote_size, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - unsafe { - trace!("Generating DCAP RA Quote: {}", *dcap_quote_p); - } - - Ok(dcap_quote_vec) - } - - fn generate_dcap_ra_extrinsic(&self, w_url: &str, skip_ra: bool) -> EnclaveResult> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - self.set_ql_qe_enclave_paths()?; - let quoting_enclave_target_info = if !skip_ra { - match self.qe_get_target_info() { - Ok(target_info) => Some(target_info), - Err(e) => return Err(e), - } - } else { - None - }; - let quote_size = if !skip_ra { - match self.qe_get_quote_size() { - Ok(quote_size) => Some(quote_size), - Err(e) => return Err(e), - } - } else { - None - }; - info!("Retrieved quote size of {:?}", quote_size); - - trace!("Generating dcap_ra_extrinsic with URL: {}", w_url); - - let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; - let mut unchecked_extrinsic_size: u32 = 0; - let url = w_url.encode(); - - let result = unsafe { - ffi::generate_dcap_ra_extrinsic( - self.eid, - &mut retval, - url.as_ptr(), - url.len() as u32, - unchecked_extrinsic.as_mut_ptr(), - unchecked_extrinsic.len() as u32, - &mut unchecked_extrinsic_size as *mut u32, - skip_ra.into(), - quoting_enclave_target_info.as_ref(), - quote_size.as_ref(), - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - ensure!( - (unchecked_extrinsic_size as usize) < unchecked_extrinsic.len(), - Error::Sgx(sgx_status_t::SGX_ERROR_INVALID_PARAMETER) - ); - Ok(Vec::from(&unchecked_extrinsic[..unchecked_extrinsic_size as usize])) - } - - fn generate_register_quoting_enclave_extrinsic( - &self, - fmspc: Fmspc, - ) -> EnclaveResult> { - let mut retval = sgx_status_t::SGX_SUCCESS; - let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; - let mut unchecked_extrinsic_size: u32 = 0; - - trace!("Generating register quoting enclave"); - - let collateral_ptr = self.get_dcap_collateral(fmspc)?; - - let result = unsafe { - ffi::generate_register_quoting_enclave_extrinsic( - self.eid, - &mut retval, - collateral_ptr, - unchecked_extrinsic.as_mut_ptr(), - unchecked_extrinsic.len() as u32, - &mut unchecked_extrinsic_size as *mut u32, - ) - }; - let free_status = unsafe { sgx_ql_free_quote_verification_collateral(collateral_ptr) }; - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - ensure!( - free_status == sgx_quote3_error_t::SGX_QL_SUCCESS, - Error::SgxQuote(free_status) - ); - ensure!( - (unchecked_extrinsic_size as usize) < unchecked_extrinsic.len(), - Error::Sgx(sgx_status_t::SGX_ERROR_INVALID_PARAMETER) - ); - Ok(Vec::from(&unchecked_extrinsic[..unchecked_extrinsic_size as usize])) - } - - fn generate_register_tcb_info_extrinsic(&self, fmspc: Fmspc) -> EnclaveResult> { - let mut retval = sgx_status_t::SGX_SUCCESS; - let mut unchecked_extrinsic: Vec = vec![0u8; EXTRINSIC_MAX_SIZE]; - let mut unchecked_extrinsic_size: u32 = 0; - - trace!("Generating tcb_info registration"); - - let collateral_ptr = self.get_dcap_collateral(fmspc)?; - - let result = unsafe { - ffi::generate_register_tcb_info_extrinsic( - self.eid, - &mut retval, - collateral_ptr, - unchecked_extrinsic.as_mut_ptr(), - unchecked_extrinsic.len() as u32, - &mut unchecked_extrinsic_size as *mut u32, - ) - }; - let free_status = unsafe { sgx_ql_free_quote_verification_collateral(collateral_ptr) }; - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - ensure!( - free_status == sgx_quote3_error_t::SGX_QL_SUCCESS, - Error::SgxQuote(free_status) - ); - ensure!( - (unchecked_extrinsic_size as usize) < unchecked_extrinsic.len(), - Error::Sgx(sgx_status_t::SGX_ERROR_INVALID_PARAMETER) - ); - Ok(Vec::from(&unchecked_extrinsic[..unchecked_extrinsic_size as usize])) - } - - fn dump_ias_ra_cert_to_disk(&self) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let result = unsafe { ffi::dump_ias_ra_cert_to_disk(self.eid, &mut retval) }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn dump_dcap_ra_cert_to_disk(&self) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - self.set_ql_qe_enclave_paths()?; - let quoting_enclave_target_info = self.qe_get_target_info()?; - let quote_size = self.qe_get_quote_size()?; - - let result = unsafe { - ffi::dump_dcap_ra_cert_to_disk( - self.eid, - &mut retval, - "ing_enclave_target_info, - quote_size, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn set_ql_qe_enclave_paths(&self) -> EnclaveResult<()> { - set_ql_path(sgx_ql_path_type_t::SGX_QL_PCE_PATH, PCE_ENCLAVE)?; - set_ql_path(sgx_ql_path_type_t::SGX_QL_QE3_PATH, QE3_ENCLAVE)?; - set_ql_path(sgx_ql_path_type_t::SGX_QL_IDE_PATH, ID_ENCLAVE)?; - if set_ql_path(sgx_ql_path_type_t::SGX_QL_QPL_PATH, LIBDCAP_QUOTEPROV).is_err() { - // Ignore the error, because user may want to get cert type=3 quote. - warn!("Cannot set QPL directory, you may get ECDSA quote with `Encrypted PPID` cert type.\n"); - }; - set_qv_path(sgx_qv_path_type_t::SGX_QV_QVE_PATH, QVE_ENCLAVE)?; - - Ok(()) - } - - fn set_sgx_qpl_logging(&self) -> EnclaveResult<()> { - let log_level = sgx_ql_log_level_t::SGX_QL_LOG_INFO; - let res = unsafe { sgx_ql_set_logging_callback(forward_qpl_log, log_level) }; - if res == sgx_quote3_error_t::SGX_QL_SUCCESS { - Ok(()) - } else { - error!("Setting logging function failed with: {:?}", res); - Err(Error::SgxQuote(res)) - } - } - - fn qe_get_target_info(&self) -> EnclaveResult { - let mut quoting_enclave_target_info: sgx_target_info_t = sgx_target_info_t::default(); - let qe3_ret = - unsafe { sgx_qe_get_target_info(&mut quoting_enclave_target_info as *mut _) }; - ensure!(qe3_ret == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(qe3_ret)); - - Ok(quoting_enclave_target_info) - } - - fn qe_get_quote_size(&self) -> EnclaveResult { - let mut quote_size: u32 = 0; - let qe3_ret = unsafe { sgx_qe_get_quote_size(&mut quote_size as *mut _) }; - ensure!(qe3_ret == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(qe3_ret)); - - Ok(quote_size) - } - - fn dump_dcap_collateral_to_disk(&self, fmspc: Fmspc) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - let collateral_ptr = self.get_dcap_collateral(fmspc)?; - let result = - unsafe { ffi::dump_dcap_collateral_to_disk(self.eid, &mut retval, collateral_ptr) }; - let free_status = unsafe { sgx_ql_free_quote_verification_collateral(collateral_ptr) }; - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!( - free_status == sgx_quote3_error_t::SGX_QL_SUCCESS, - Error::SgxQuote(free_status) - ); - Ok(()) - } - - fn get_dcap_collateral( - &self, - fmspc: Fmspc, - ) -> EnclaveResult<*const sgx_ql_qve_collateral_t> { - let pck_ra = b"processor\x00"; - - // SAFETY: Just get a nullptr for the FFI to overwrite later - let mut collateral_ptr: *mut sgx_ql_qve_collateral_t = unsafe { std::mem::zeroed() }; - - let collateral_ptr_ptr: *mut *mut sgx_ql_qve_collateral_t = &mut collateral_ptr; - // SAFETY: All parameters are properly initialized so the FFI call should be fine - let sgx_status = unsafe { - sgx_ql_get_quote_verification_collateral( - fmspc.as_ptr(), - fmspc.len() as uint16_t, //fmspc len is fixed in the function signature - pck_ra.as_ptr() as _, - collateral_ptr_ptr, - ) - }; - - trace!("FMSPC: {:?}", hex::encode(fmspc)); - - if collateral_ptr.is_null() { - error!("PCK quote collateral data is null, sgx_status is: {}", sgx_status); - return Err(Error::SgxQuote(sgx_status)) - } - - trace!("collateral:"); - // SAFETY: the previous block checks for `collateral_ptr` being null. - // SAFETY: the fields should be nul terminated C strings. - unsafe { - let collateral = &*collateral_ptr; - trace!( - "version: {}\n, \ - tee_type: {}\n, \ - pck_crl_issuer_chain: {:?}\n, \ - pck_crl_issuer_chain_size: {}\n, \ - root_ca_crl: {:?}\n, \ - root_ca_crl_size: {}\n, \ - pck_crl: {:?}\n, \ - pck_crl_size: {}\n, \ - tcb_info_issuer_chain: {:?}\n, \ - tcb_info_issuer_chain_size: {}\n, \ - tcb_info: {}\n, \ - tcb_info_size: {}\n, \ - qe_identity_issuer_chain: {:?}\n, \ - qe_identity_issuer_chain_size: {}\n, \ - qe_identity: {}\n, \ - qe_identity_size: {}\n", - collateral.version, - collateral.tee_type, - std::ffi::CStr::from_ptr(collateral.pck_crl_issuer_chain).to_string_lossy(), - collateral.pck_crl_issuer_chain_size, - std::ffi::CStr::from_ptr(collateral.root_ca_crl).to_string_lossy(), - collateral.root_ca_crl_size, - std::ffi::CStr::from_ptr(collateral.pck_crl).to_string_lossy(), - collateral.pck_crl_size, - std::ffi::CStr::from_ptr(collateral.tcb_info_issuer_chain).to_string_lossy(), - collateral.tcb_info_issuer_chain_size, - std::ffi::CStr::from_ptr(collateral.tcb_info).to_string_lossy(), - collateral.tcb_info_size, - std::ffi::CStr::from_ptr(collateral.qe_identity_issuer_chain).to_string_lossy(), - collateral.qe_identity_issuer_chain_size, - std::ffi::CStr::from_ptr(collateral.qe_identity).to_string_lossy(), - collateral.qe_identity_size, - ); - }; - - ensure!(sgx_status == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(sgx_status)); - Ok(collateral_ptr) - } - } - - #[cfg(feature = "implement-ffi")] - impl RemoteAttestationCallBacks for Enclave { - fn init_quote(&self) -> EnclaveResult<(sgx_target_info_t, sgx_epid_group_id_t)> { - let mut ti: sgx_target_info_t = sgx_target_info_t::default(); - let mut eg: sgx_epid_group_id_t = sgx_epid_group_id_t::default(); - - let result = unsafe { - sgx_init_quote( - &mut ti as *mut sgx_target_info_t, - &mut eg as *mut sgx_epid_group_id_t, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - - Ok((ti, eg)) - } - - fn calc_quote_size(&self, revocation_list: Vec) -> EnclaveResult { - let mut real_quote_len: u32 = 0; - - let (p_sig_rl, sig_rl_size) = utils::vec_to_c_pointer_with_len(revocation_list); - - let result = unsafe { - sgx_calc_quote_size(p_sig_rl, sig_rl_size, &mut real_quote_len as *mut u32) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - - Ok(real_quote_len) - } - - fn get_quote( - &self, - revocation_list: Vec, - report: sgx_report_t, - quote_type: sgx_quote_sign_type_t, - spid: sgx_spid_t, - quote_nonce: sgx_quote_nonce_t, - quote_length: u32, - ) -> EnclaveResult<(sgx_report_t, Vec)> { - let (p_sig_rl, sig_rl_size) = utils::vec_to_c_pointer_with_len(revocation_list); - let p_report = &report as *const sgx_report_t; - let p_spid = &spid as *const sgx_spid_t; - let p_nonce = "e_nonce as *const sgx_quote_nonce_t; - - let mut qe_report = sgx_report_t::default(); - let p_qe_report = &mut qe_report as *mut sgx_report_t; - - let mut return_quote_buf = vec![0u8; quote_length as usize]; - let p_quote = return_quote_buf.as_mut_ptr(); - - let ret = unsafe { - sgx_get_quote( - p_report, - quote_type, - p_spid, - p_nonce, - p_sig_rl, - sig_rl_size, - p_qe_report, - p_quote as *mut sgx_quote_t, - quote_length, - ) - }; - - ensure!(ret == sgx_status_t::SGX_SUCCESS, Error::Sgx(ret)); - - Ok((qe_report, return_quote_buf)) - } - - fn get_dcap_quote(&self, report: sgx_report_t, quote_size: u32) -> EnclaveResult> { - let mut quote_vec: Vec = vec![0; quote_size as usize]; - let qe3_ret = - unsafe { sgx_qe_get_quote(&report, quote_size, quote_vec.as_mut_ptr() as _) }; - - ensure!(qe3_ret == sgx_quote3_error_t::SGX_QL_SUCCESS, Error::SgxQuote(qe3_ret)); - - Ok(quote_vec) - } - - fn get_qve_report_on_quote( - &self, - quote: Vec, - current_time: i64, - quote_collateral: &sgx_ql_qve_collateral_t, - qve_report_info: sgx_ql_qe_report_info_t, - supplemental_data_size: u32, - ) -> EnclaveResult { - let mut collateral_expiration_status = 1u32; - let mut quote_verification_result = sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK; - let mut supplemental_data: Vec = vec![0; supplemental_data_size as usize]; - let mut qve_report_info_return_value: sgx_ql_qe_report_info_t = qve_report_info; - - // Set QvE (Quote verification Enclave) loading policy. - let dcap_ret = unsafe { - sgx_qv_set_enclave_load_policy(sgx_ql_request_policy_t::SGX_QL_EPHEMERAL) - }; - - if dcap_ret != sgx_quote3_error_t::SGX_QL_SUCCESS { - error!("sgx_qv_set_enclave_load_policy failed: {:#04x}", dcap_ret as u32); - return Err(Error::SgxQuote(dcap_ret)) - } - - // Retrieve supplemental data size from QvE. - let mut qve_supplemental_data_size = 0u32; - let dcap_ret = - unsafe { sgx_qv_get_quote_supplemental_data_size(&mut qve_supplemental_data_size) }; - - if dcap_ret != sgx_quote3_error_t::SGX_QL_SUCCESS { - error!("sgx_qv_get_quote_supplemental_data_size failed: {:?}", dcap_ret); - return Err(Error::SgxQuote(dcap_ret)) - } - if qve_supplemental_data_size != supplemental_data_size { - warn!("Quote supplemental data size is different between DCAP QVL and QvE, please make sure you installed DCAP QVL and QvE from same release."); - return Err(Error::Sgx(sgx_status_t::SGX_ERROR_INVALID_PARAMETER)) - } - - // Check if a collateral has been given, or if it's a simple zero assignment. - // If it's zero, let the pointer point to null. The collateral will then be retrieved - // directly by the QvE in `sgx_qv_verify_quote`. - let p_quote_collateral: *const sgx_ql_qve_collateral_t = - if quote_collateral.version == 0 { - std::ptr::null() - } else { - quote_collateral as *const sgx_ql_qve_collateral_t - }; - - // Call the QvE for quote verification - // here you can choose 'trusted' or 'untrusted' quote verification by specifying parameter '&qve_report_info' - // if '&qve_report_info' is NOT NULL, this API will call Intel QvE to verify quote - // if '&qve_report_info' is NULL, this API will call 'untrusted quote verify lib' to verify quote, - // this mode doesn't rely on SGX capable system, but the results can not be cryptographically authenticated - let dcap_ret = unsafe { - sgx_qv_verify_quote( - quote.as_ptr(), - quote.len() as u32, - p_quote_collateral, - current_time, - &mut collateral_expiration_status as *mut u32, - &mut quote_verification_result as *mut sgx_ql_qv_result_t, - &mut qve_report_info_return_value as *mut sgx_ql_qe_report_info_t, - supplemental_data_size, - supplemental_data.as_mut_ptr(), - ) - }; - - if sgx_quote3_error_t::SGX_QL_SUCCESS != dcap_ret { - error!("sgx_qv_verify_quote failed: {:?}", dcap_ret); - error!("quote_verification_result: {:?}", quote_verification_result); - return Err(Error::SgxQuote(dcap_ret)) - } - - // Check and print verification result. - match quote_verification_result { - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OK => { - // Check verification collateral expiration status. - // This value should be considered in your own attestation/verification policy. - if 0u32 == collateral_expiration_status { - info!("QvE verification completed successfully."); - } else { - warn!("QvE verification completed, but collateral is out of date based on 'expiration_check_date' you provided."); - } - }, - sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_NEEDED - | sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE - | sgx_ql_qv_result_t::SGX_QL_QV_RESULT_OUT_OF_DATE_CONFIG_NEEDED - | sgx_ql_qv_result_t::SGX_QL_QV_RESULT_SW_HARDENING_NEEDED - | sgx_ql_qv_result_t::SGX_QL_QV_RESULT_CONFIG_AND_SW_HARDENING_NEEDED => { - warn!( - "QvE verification completed with Non-terminal result: {:?}", - quote_verification_result - ); - }, - _ => { - error!( - "QvE verification completed with Terminal result: {:?}", - quote_verification_result - ); - }, - } - - // Check supplemental data. - if supplemental_data_size > 0 { - // For now we simply print it, no checks done. - let p_supplemental_data: *const sgx_ql_qv_supplemental_t = - supplemental_data.as_ptr() as *const sgx_ql_qv_supplemental_t; - let qv_supplemental_data: sgx_ql_qv_supplemental_t = - unsafe { *p_supplemental_data }; - info!( - "QvE verification: Supplemental data version: {}", - qv_supplemental_data.version - ); - } - - Ok(QveReport { - collateral_expiration_status, - quote_verification_result, - qve_report_info_return_value, - supplemental_data, - }) - } - - fn get_update_info( - &self, - platform_blob: sgx_platform_info_t, - enclave_trusted: i32, - ) -> EnclaveResult { - let mut update_info: sgx_update_info_bit_t = sgx_update_info_bit_t::default(); - - let result = unsafe { - sgx_report_attestation_status( - &platform_blob as *const sgx_platform_info_t, - enclave_trusted, - &mut update_info as *mut sgx_update_info_bit_t, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - - Ok(update_info) - } - } - - #[cfg(feature = "implement-ffi")] - impl TlsRemoteAttestation for Enclave { - fn run_state_provisioning_server( - &self, - socket_fd: c_int, - sign_type: sgx_quote_sign_type_t, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, - skip_ra: bool, - ) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let result = unsafe { - ffi::run_state_provisioning_server( - self.eid, - &mut retval, - socket_fd, - sign_type, - quoting_enclave_target_info, - quote_size, - skip_ra.into(), - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn request_state_provisioning( - &self, - socket_fd: c_int, - sign_type: sgx_quote_sign_type_t, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, - shard: &ShardIdentifier, - skip_ra: bool, - ) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let encoded_shard = shard.encode(); - - let result = unsafe { - ffi::request_state_provisioning( - self.eid, - &mut retval, - socket_fd, - sign_type, - quoting_enclave_target_info, - quote_size, - encoded_shard.as_ptr(), - encoded_shard.len() as u32, - skip_ra.into(), - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - } - - fn create_system_path(file_name: &str) -> String { - trace!("create_system_path:: file_name={}", &file_name); - let default_path = format!("{}{}", OS_SYSTEM_PATH, file_name); - - let full_path = find_library_by_name(file_name).unwrap_or(default_path); - - let c_terminated_path = format!("{}{}", full_path, C_STRING_ENDING); - trace!("create_system_path:: created path={}", &c_terminated_path); - c_terminated_path - } - - fn find_library_by_name(lib_name: &str) -> Option { - use std::process::Command; - // ldconfig -p | grep libsgx_pce_logic.so.1 - - let ldconfig_output = Command::new("ldconfig").args(["-p"]).output().ok()?; - let possible_path = String::from_utf8(ldconfig_output.stdout) - .ok()? - .lines() - .filter(|line| line.contains(lib_name)) - .map(|lib_name_and_path| { - lib_name_and_path - .rsplit_once("=>") - .map(|(_, lib_path)| lib_path.trim().to_owned()) - }) - .next()?; - - possible_path - } - - fn set_ql_path(path_type: sgx_ql_path_type_t, path: &str) -> EnclaveResult<()> { - let ret_val = unsafe { sgx_ql_set_path(path_type, create_system_path(path).as_ptr() as _) }; - if ret_val != sgx_quote3_error_t::SGX_QL_SUCCESS { - error!("Could not set {:?}", path_type); - return Err(Error::SgxQuote(ret_val)) - } - Ok(()) - } - - fn set_qv_path(path_type: sgx_qv_path_type_t, path: &str) -> EnclaveResult<()> { - let ret_val = unsafe { sgx_qv_set_path(path_type, create_system_path(path).as_ptr() as _) }; - if ret_val != sgx_quote3_error_t::SGX_QL_SUCCESS { - error!("Could not set {:?}", path_type); - return Err(Error::SgxQuote(ret_val)) - } - Ok(()) - } - - #[allow(clippy::not_unsafe_ptr_arg_deref)] - /// Make sure that the `log_slice_ptr` points to a null terminated string. - // This function must not be marked as `unsafe`, because `sgx_ql_set_logging_callback` expects a safe (i.e. not `unsafe`) function. - pub extern "C" fn forward_qpl_log(log_level: sgx_ql_log_level_t, log_slice_ptr: *const c_char) { - if log_slice_ptr.is_null() { - error!("[QPL - ERROR], slice to print was NULL"); - return - } - // This is safe, as the previous block checks for `NULL` pointer. - let slice = unsafe { core::ffi::CStr::from_ptr(log_slice_ptr) }; - match log_level { - sgx_ql_log_level_t::SGX_QL_LOG_INFO => info!("[QPL - INFO], {:?}", slice), - sgx_ql_log_level_t::SGX_QL_LOG_ERROR => error!("[QPL - ERROR], {:?}", slice), - } - } -} diff --git a/tee-worker/bitacross/core-primitives/enclave-api/src/sidechain.rs b/tee-worker/bitacross/core-primitives/enclave-api/src/sidechain.rs deleted file mode 100644 index 9dee8e3bb4..0000000000 --- a/tee-worker/bitacross/core-primitives/enclave-api/src/sidechain.rs +++ /dev/null @@ -1,109 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::EnclaveResult; -use codec::Encode; -use itp_storage::StorageProof; -use itp_types::parentchain::ParentchainId; -use sp_runtime::generic::SignedBlock; - -/// trait for handling blocks on the side chain -pub trait Sidechain: Send + Sync + 'static { - /// Sync parentchain blocks and events. Execute pending tops - /// and events proof in the enclave. - fn sync_parentchain( - &self, - blocks: &[SignedBlock], - events: &[Vec], - events_proofs: &[StorageProof], - parentchain_id: &ParentchainId, - is_syncing: bool, - ) -> EnclaveResult<()>; - - // litentry - /// Ignore the parentchain block import validation until the given block number - /// TODO: use the generic Header::Number trait - fn ignore_parentchain_block_import_validation_until(&self, until: u32) -> EnclaveResult<()>; -} - -#[cfg(feature = "implement-ffi")] -mod impl_ffi { - use super::Sidechain; - use crate::{error::Error, Enclave, EnclaveResult}; - use codec::Encode; - use frame_support::ensure; - use itp_enclave_api_ffi as ffi; - use itp_storage::StorageProof; - use itp_types::parentchain::ParentchainId; - use sgx_types::sgx_status_t; - use sp_runtime::generic::SignedBlock; - - impl Sidechain for Enclave { - fn sync_parentchain( - &self, - blocks: &[SignedBlock], - events: &[Vec], - events_proofs: &[StorageProof], - parentchain_id: &ParentchainId, - is_syncing: bool, - ) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - let blocks_enc = blocks.encode(); - let events_enc = events.encode(); - let events_proofs_enc = events_proofs.encode(); - let parentchain_id_enc = parentchain_id.encode(); - - let result = unsafe { - ffi::sync_parentchain( - self.eid, - &mut retval, - blocks_enc.as_ptr(), - blocks_enc.len(), - events_enc.as_ptr(), - events_enc.len(), - events_proofs_enc.as_ptr(), - events_proofs_enc.len(), - parentchain_id_enc.as_ptr(), - parentchain_id_enc.len() as u32, - is_syncing.into(), - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - - fn ignore_parentchain_block_import_validation_until( - &self, - until: u32, - ) -> EnclaveResult<()> { - let mut retval = sgx_status_t::SGX_SUCCESS; - - let result = unsafe { - ffi::ignore_parentchain_block_import_validation_until(self.eid, &mut retval, &until) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, Error::Sgx(result)); - ensure!(retval == sgx_status_t::SGX_SUCCESS, Error::Sgx(retval)); - - Ok(()) - } - } -} diff --git a/tee-worker/bitacross/core-primitives/enclave-api/src/utils.rs b/tee-worker/bitacross/core-primitives/enclave-api/src/utils.rs deleted file mode 100644 index e36764f7ac..0000000000 --- a/tee-worker/bitacross/core-primitives/enclave-api/src/utils.rs +++ /dev/null @@ -1,27 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use std::ptr; - -pub fn vec_to_c_pointer_with_len(input: Vec) -> (*const A, u32) { - if input.is_empty() { - (ptr::null(), 0) - } else { - (input.as_ptr(), input.len() as u32) - } -} diff --git a/tee-worker/bitacross/core-primitives/stf-executor/Cargo.toml b/tee-worker/bitacross/core-primitives/stf-executor/Cargo.toml deleted file mode 100644 index 06d777cebf..0000000000 --- a/tee-worker/bitacross/core-primitives/stf-executor/Cargo.toml +++ /dev/null @@ -1,81 +0,0 @@ -[package] -name = "bc-itp-stf-executor" -version = "0.1.0" -authors = ['Trust Computing GmbH ', 'Integritee AG '] -edition = "2021" - -[dependencies] -hex = { workspace = true } - -sgx_tstd = { workspace = true, features = ["untrusted_time"], optional = true } -sgx_types = { workspace = true } - -itp-enclave-metrics = { workspace = true } -itp-node-api = { workspace = true } -itp-ocall-api = { workspace = true } -itp-sgx-crypto = { workspace = true } -itp-sgx-externalities = { workspace = true } -itp-stf-interface = { workspace = true } -itp-stf-primitives = { workspace = true } -itp-stf-state-handler = { workspace = true } -itp-stf-state-observer = { workspace = true } -itp-time-utils = { workspace = true } -itp-top-pool-author = { package = "bc-itp-top-pool-author", path = "../top-pool-author", default-features = false } -itp-types = { workspace = true } - -thiserror = { workspace = true, optional = true } -thiserror_sgx = { workspace = true, optional = true } - -codec = { package = "parity-scale-codec", workspace = true } -log = { workspace = true } - -# substrate dependencies -sp-core = { workspace = true, features = ["full_crypto"] } -sp-runtime = { workspace = true } - -# dev dependencies -itc-parentchain-test = { workspace = true, optional = true } -itp-test = { workspace = true, optional = true } - -[dev-dependencies] -itp-stf-state-observer = { workspace = true, features = ["std", "mocks"] } -itp-stf-interface = { workspace = true, features = ["std", "mocks"] } -itp-top-pool = { package = "bc-itp-top-pool", path = "../top-pool", features = ["std", "mocks"] } -itp-test = { workspace = true, features = ["std"] } - -[features] -default = ["std"] -std = [ - "itp-node-api/std", - "itp-ocall-api/std", - "itp-sgx-crypto/std", - "itp-sgx-externalities/std", - "itp-stf-interface/std", - "itp-stf-state-handler/std", - "itp-stf-state-observer/std", - "itp-top-pool-author/std", - "itp-types/std", - "itp-time-utils/std", - "log/std", - "codec/std", - "sp-core/std", - "sp-runtime/std", - "thiserror", -] -sgx = [ - "sgx_tstd", - "itp-node-api/sgx", - "itp-sgx-crypto/sgx", - "itp-sgx-externalities/sgx", - "itp-stf-state-handler/sgx", - "itp-stf-state-observer/sgx", - "itp-top-pool-author/sgx", - "itp-time-utils/sgx", - "thiserror_sgx", -] -test = [ - "itc-parentchain-test", - "itp-node-api/mocks", - "itp-test", -] -mocks = [] diff --git a/tee-worker/bitacross/core-primitives/stf-executor/src/enclave_signer.rs b/tee-worker/bitacross/core-primitives/stf-executor/src/enclave_signer.rs deleted file mode 100644 index a4d4fd8c01..0000000000 --- a/tee-worker/bitacross/core-primitives/stf-executor/src/enclave_signer.rs +++ /dev/null @@ -1,151 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - error::{Error, Result}, - traits::StfEnclaveSigning, - H256, -}; -use codec::{Decode, Encode}; -use core::{fmt::Debug, marker::PhantomData}; -use itp_ocall_api::EnclaveAttestationOCallApi; -use itp_sgx_crypto::{ed25519_derivation::DeriveEd25519, key_repository::AccessKey}; -use itp_sgx_externalities::SgxExternalitiesTrait; -use itp_stf_interface::system_pallet::SystemPalletAccountInterface; -use itp_stf_primitives::{ - traits::TrustedCallSigning, - types::{AccountId, KeyPair}, -}; -use itp_stf_state_observer::traits::ObserveState; -use itp_top_pool_author::traits::AuthorApi; -use itp_types::{Index, MrEnclave, ShardIdentifier}; -use sp_core::{ed25519::Pair as Ed25519Pair, Pair}; -use std::{boxed::Box, sync::Arc, vec::Vec}; - -pub struct StfEnclaveSigner< - OCallApi, - StateObserver, - ShieldingKeyRepository, - Stf, - TopPoolAuthor, - TCS, - G, -> { - state_observer: Arc, - pub ocall_api: Arc, - shielding_key_repo: Arc, - top_pool_author: Arc, - _phantom: PhantomData<(Stf, TCS, G)>, -} - -impl - StfEnclaveSigner -where - OCallApi: EnclaveAttestationOCallApi, - StateObserver: ObserveState, - StateObserver::StateType: SgxExternalitiesTrait, - ShieldingKeyRepository: AccessKey, - ::KeyType: DeriveEd25519, - Stf: SystemPalletAccountInterface, - Stf::Index: Into, - TopPoolAuthor: AuthorApi + Send + Sync + 'static, - TCS: PartialEq + Encode + Decode + Debug + Send + Sync, - G: PartialEq + Encode + Decode + Debug + Send + Sync, -{ - pub fn new( - state_observer: Arc, - ocall_api: Arc, - shielding_key_repo: Arc, - top_pool_author: Arc, - ) -> Self { - Self { - state_observer, - ocall_api, - shielding_key_repo, - top_pool_author, - _phantom: Default::default(), - } - } - - fn get_enclave_account_nonce(&self, shard: &ShardIdentifier) -> Result { - let enclave_account = self.get_enclave_account()?; - let nonce = self - .state_observer - .observe_state(shard, move |state| Stf::get_account_nonce(state, &enclave_account))?; - - Ok(nonce) - } - - fn get_enclave_call_signing_key(&self) -> Result { - let shielding_key = self.shielding_key_repo.retrieve_key()?; - shielding_key.derive_ed25519().map_err(|e| e.into()) - } -} - -impl - StfEnclaveSigning - for StfEnclaveSigner -where - OCallApi: EnclaveAttestationOCallApi, - StateObserver: ObserveState, - StateObserver::StateType: SgxExternalitiesTrait, - ShieldingKeyRepository: AccessKey, - ::KeyType: DeriveEd25519, - Stf: SystemPalletAccountInterface, - Stf::Index: Into, - TopPoolAuthor: AuthorApi + Send + Sync + 'static, - TCS: PartialEq + Encode + Decode + Debug + Send + Sync, - G: PartialEq + Encode + Decode + Debug + Send + Sync, -{ - fn get_enclave_account(&self) -> Result { - self.get_enclave_call_signing_key().map(|key| key.public().into()) - } - - fn get_mrenclave(&self) -> Result { - Ok(self.ocall_api.get_mrenclave_of_self().map(|m| m.m)?) - } - - fn sign_call_with_self>( - &self, - trusted_call: &TC, - shard: &ShardIdentifier, - ) -> Result { - let mrenclave = self.get_mrenclave()?; - let enclave_account = self.get_enclave_account()?; - let enclave_call_signing_key = self.get_enclave_call_signing_key()?; - - let current_nonce = self.get_enclave_account_nonce(shard)?; - let pending_tx_count = self - .top_pool_author - .get_pending_trusted_calls_for(*shard, &enclave_account) - .len(); - let pending_tx_count = - Index::try_from(pending_tx_count).map_err(|e| Error::Other(e.into()))?; - let adjusted_nonce: Index = current_nonce.into() + pending_tx_count; - - Ok(trusted_call.sign( - &KeyPair::Ed25519(Box::new(enclave_call_signing_key)), - adjusted_nonce, - &mrenclave, - shard, - )) - } - - fn sign(&self, payload: &[u8]) -> Result> { - self.get_enclave_call_signing_key().map(|key| key.sign(payload).0.to_vec()) - } -} diff --git a/tee-worker/bitacross/core-primitives/stf-executor/src/error.rs b/tee-worker/bitacross/core-primitives/stf-executor/src/error.rs deleted file mode 100644 index ec46defdcd..0000000000 --- a/tee-worker/bitacross/core-primitives/stf-executor/src/error.rs +++ /dev/null @@ -1,88 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; - -use itp_stf_primitives::error::StfError; -use sgx_types::sgx_status_t; -use std::{boxed::Box, format}; - -pub type Result = core::result::Result; - -/// STF-Executor error -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("Trusted operation has invalid signature")] - GetterIsNotAuthorized, - #[error("Invalid or unsupported trusted call type")] - InvalidTrustedCallType, - #[error("SGX error, status: {0}")] - Sgx(sgx_status_t), - #[error("State handling error: {0}")] - StateHandler(#[from] itp_stf_state_handler::error::Error), - #[error("State observer error: {0}")] - StateObserver(#[from] itp_stf_state_observer::error::Error), - #[error("Node metadata error: {0:?}")] - NodeMetadata(itp_node_api::metadata::Error), - #[error("Node metadata provider error: {0:?}")] - NodeMetadataProvider(#[from] itp_node_api::metadata::provider::Error), - #[error("STF error: {0}")] - Stf(StfError), - #[error("Ocall Api error: {0}")] - OcallApi(itp_ocall_api::Error), - #[error("Crypto error: {0}")] - Crypto(itp_sgx_crypto::error::Error), - #[error(transparent)] - Other(#[from] Box), -} - -impl From for Error { - fn from(sgx_status: sgx_status_t) -> Self { - Self::Sgx(sgx_status) - } -} - -impl From for Error { - fn from(e: codec::Error) -> Self { - Self::Other(format!("{:?}", e).into()) - } -} - -impl From for Error { - fn from(error: StfError) -> Self { - Self::Stf(error) - } -} - -impl From for Error { - fn from(error: itp_ocall_api::Error) -> Self { - Self::OcallApi(error) - } -} - -impl From for Error { - fn from(error: itp_sgx_crypto::error::Error) -> Self { - Self::Crypto(error) - } -} - -impl From for Error { - fn from(e: itp_node_api::metadata::Error) -> Self { - Self::NodeMetadata(e) - } -} diff --git a/tee-worker/bitacross/core-primitives/stf-executor/src/executor.rs b/tee-worker/bitacross/core-primitives/stf-executor/src/executor.rs deleted file mode 100644 index ffc6d92fce..0000000000 --- a/tee-worker/bitacross/core-primitives/stf-executor/src/executor.rs +++ /dev/null @@ -1,393 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - error::{Error, Result}, - traits::{StatePostProcessing, StateUpdateProposer, StfUpdateState}, - BatchExecutionResult, ExecutedOperation, -}; -use codec::{Decode, Encode}; -use itp_enclave_metrics::EnclaveMetric; -use itp_node_api::metadata::{provider::AccessNodeMetadata, NodeMetadataTrait}; -use itp_ocall_api::{EnclaveAttestationOCallApi, EnclaveMetricsOCallApi, EnclaveOnChainOCallApi}; -use itp_sgx_externalities::{SgxExternalitiesTrait, StateHash}; -use itp_stf_interface::{ - parentchain_pallet::ParentchainPalletInstancesInterface, - runtime_upgrade::RuntimeUpgradeInterface, StateCallInterface, StfExecutionResult, UpdateState, -}; -use itp_stf_primitives::{ - traits::TrustedCallVerification, - types::{ShardIdentifier, TrustedOperation, TrustedOperationOrHash}, -}; -use itp_stf_state_handler::{handle_state::HandleState, query_shard_state::QueryShardState}; -use itp_time_utils::duration_now; -use itp_types::{ - parentchain::{Header as ParentchainHeader, ParentchainCall, ParentchainId}, - storage::StorageEntryVerified, - H256, -}; -use log::*; -use sp_runtime::traits::Header as HeaderTrait; -use std::{ - collections::BTreeMap, fmt::Debug, marker::PhantomData, string::ToString, sync::Arc, - time::Duration, vec, vec::Vec, -}; - -pub struct StfExecutor -where - TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, - G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, -{ - ocall_api: Arc, - state_handler: Arc, - node_metadata_repo: Arc, - _phantom: PhantomData<(Stf, TCS, G)>, -} - -impl - StfExecutor -where - OCallApi: EnclaveAttestationOCallApi + EnclaveOnChainOCallApi + EnclaveMetricsOCallApi, - StateHandler: HandleState, - StateHandler::StateT: SgxExternalitiesTrait + Encode, - NodeMetadataRepository: AccessNodeMetadata, - NodeMetadataRepository::MetadataType: NodeMetadataTrait, - Stf: UpdateState< - StateHandler::StateT, - ::SgxExternalitiesDiffType, - > + StateCallInterface, - ::SgxExternalitiesDiffType: - IntoIterator, Option>)> + From, Option>>>, - >::Error: Debug, - TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, - G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, -{ - pub fn new( - ocall_api: Arc, - state_handler: Arc, - node_metadata_repo: Arc, - ) -> Self { - StfExecutor { ocall_api, state_handler, node_metadata_repo, _phantom: PhantomData } - } - - /// Execute a trusted call on the STF - /// - /// We distinguish between an error in the execution, which maps to `Err` and - /// an invalid trusted call, which results in `Ok(ExecutionStatus::Failure)`. The latter - /// can be used to remove the trusted call from a queue. In the former case we might keep the - /// trusted call and just re-try the operation. - fn execute_trusted_call_on_stf( - &self, - state: &mut StateHandler::StateT, - trusted_operation: &TrustedOperation, - _header: &PH, - shard: &ShardIdentifier, - post_processing: StatePostProcessing, - ) -> Result> - where - PH: HeaderTrait, - { - debug!("query mrenclave of self"); - let mrenclave = self.ocall_api.get_mrenclave_of_self()?; - - let top_or_hash = TrustedOperationOrHash::from_top(trusted_operation.clone()); - let operation_hash = trusted_operation.hash(); - debug!("Operation hash {:?}", operation_hash); - - // TODO(Litentry): do we need to send any error notification to parachain? - let trusted_call = match trusted_operation.to_call().ok_or(Error::InvalidTrustedCallType) { - Ok(c) => c, - Err(e) => { - error!("Error: {:?}", e); - return Ok(ExecutedOperation::failed(operation_hash, top_or_hash, vec![], vec![])) - }, - }; - - if !trusted_call.verify_signature(&mrenclave.m, &shard) { - error!("TrustedCallSigned: bad signature"); - return Ok(ExecutedOperation::failed(operation_hash, top_or_hash, vec![], vec![])) - } - - debug!("execute on STF, call with nonce {}", trusted_call.nonce()); - - let mut extrinsic_call_backs: Vec = Vec::new(); - return match Stf::execute_call( - state, - shard, - trusted_call.clone(), - trusted_operation.hash(), - &mut extrinsic_call_backs, - self.node_metadata_repo.clone(), - ) { - Err(e) => { - if let Err(e) = - self.ocall_api.update_metric(EnclaveMetric::FailedTrustedOperationIncrement( - trusted_call.metric_name().to_string(), - )) { - warn!("Failed to update metric for failed trusted operations: {:?}", e); - } - error!("Stf execute failed: {:?}", e); - let rpc_response_value: Vec = e.encode(); - Ok(ExecutedOperation::failed( - operation_hash, - top_or_hash, - extrinsic_call_backs, - rpc_response_value, - )) - }, - Ok(result) => { - if let Err(e) = self.ocall_api.update_metric( - EnclaveMetric::SuccessfulTrustedOperationIncrement( - trusted_call.metric_name().to_string(), - ), - ) { - warn!("Failed to update metric for succesfull trusted operations: {:?}", e); - } - let force_connection_wait = result.force_connection_wait(); - let rpc_response_value = result.get_encoded_result(); - if let StatePostProcessing::Prune = post_processing { - state.prune_state_diff(); - } - for call in extrinsic_call_backs.clone() { - trace!( - "trusted_call wants to send encoded call: 0x{}", - hex::encode(call.encode()) - ); - } - Ok(ExecutedOperation::success( - operation_hash, - top_or_hash, - extrinsic_call_backs, - rpc_response_value, - force_connection_wait, - )) - }, - } - } -} - -impl - StfUpdateState - for StfExecutor -where - OCallApi: EnclaveAttestationOCallApi + EnclaveOnChainOCallApi, - StateHandler: HandleState + QueryShardState, - StateHandler::StateT: SgxExternalitiesTrait + Encode, - NodeMetadataRepository: AccessNodeMetadata, - Stf: UpdateState< - StateHandler::StateT, - ::SgxExternalitiesDiffType, - > + ParentchainPalletInstancesInterface, - ::SgxExternalitiesDiffType: - IntoIterator, Option>)>, - >::Error: - Debug, - ::SgxExternalitiesDiffType: - From, Option>>>, - TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, - G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, -{ - fn update_states( - &self, - header: &ParentchainHeader, - parentchain_id: &ParentchainId, - ) -> Result<()> { - debug!("Update STF storage upon block import!"); - let storage_hashes = Stf::storage_hashes_to_update_on_block(parentchain_id); - - // global requests they are the same for every shard - let state_diff_update = self - .ocall_api - .get_multiple_storages_verified(storage_hashes, header, parentchain_id) - .map(into_map)?; - - // Update parentchain block on all states. - // TODO: Investigate if this is still necessary. We load and clone the entire state here, - // which scales badly for increasing state size. - let shards = self.state_handler.list_shards()?; - for shard_id in shards { - let (state_lock, mut state) = self.state_handler.load_for_mutation(&shard_id)?; - match Stf::update_parentchain_litentry_block(&mut state, header.clone()) { - Ok(_) => { - self.state_handler.write_after_mutation(state, state_lock, &shard_id)?; - }, - Err(e) => error!("Could not update parentchain block. {:?}: {:?}", shard_id, e), - } - } - - if parentchain_id != &ParentchainId::Litentry { - // nothing else to do - return Ok(()) - } - - // look for new shards and initialize them - if let Some(maybe_shards) = state_diff_update.get(&shards_key_hash()) { - match maybe_shards { - Some(shards) => self.initialize_new_shards(header, &state_diff_update, &shards)?, - None => debug!("No shards are on the chain yet"), - }; - }; - Ok(()) - } -} - -impl - StfExecutor -where - ::SgxExternalitiesDiffType: - From, Option>>> + IntoIterator, Option>)>, - >::Error: - Debug, - NodeMetadataRepository: AccessNodeMetadata, - OCallApi: EnclaveAttestationOCallApi + EnclaveOnChainOCallApi, - StateHandler: HandleState + QueryShardState, - StateHandler::StateT: Encode + SgxExternalitiesTrait, - Stf: ParentchainPalletInstancesInterface - + UpdateState< - StateHandler::StateT, - ::SgxExternalitiesDiffType, - >, - TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, - G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, -{ - fn initialize_new_shards( - &self, - header: &ParentchainHeader, - state_diff_update: &BTreeMap, Option>>, - shards: &Vec, - ) -> Result<()> { - let shards: Vec = Decode::decode(&mut shards.as_slice())?; - - for shard_id in shards { - let (state_lock, mut state) = self.state_handler.load_for_mutation(&shard_id)?; - trace!("Successfully loaded state, updating states ..."); - - // per shard (cid) requests - let per_shard_hashes = storage_hashes_to_update_per_shard(&shard_id); - let per_shard_update = self - .ocall_api - .get_multiple_storages_verified(per_shard_hashes, header, &ParentchainId::Litentry) - .map(into_map)?; - - Stf::apply_state_diff(&mut state, per_shard_update.into()); - Stf::apply_state_diff(&mut state, state_diff_update.clone().into()); - if let Err(e) = Stf::update_parentchain_litentry_block(&mut state, header.clone()) { - error!("Could not update parentchain block. {:?}: {:?}", shard_id, e) - } - - self.state_handler.write_after_mutation(state, state_lock, &shard_id)?; - } - Ok(()) - } -} - -impl StateUpdateProposer - for StfExecutor -where - OCallApi: EnclaveAttestationOCallApi + EnclaveOnChainOCallApi + EnclaveMetricsOCallApi, - StateHandler: HandleState, - StateHandler::StateT: SgxExternalitiesTrait + Encode + StateHash, - ::SgxExternalitiesType: Encode, - NodeMetadataRepository: AccessNodeMetadata, - NodeMetadataRepository::MetadataType: NodeMetadataTrait, - Stf: UpdateState< - StateHandler::StateT, - ::SgxExternalitiesDiffType, - > + StateCallInterface - + RuntimeUpgradeInterface, - ::SgxExternalitiesDiffType: - IntoIterator, Option>)>, - ::SgxExternalitiesDiffType: - From, Option>>>, - >::Error: Debug, - >::Error: Debug, - TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, - G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, -{ - type Externalities = StateHandler::StateT; - - fn propose_state_update( - &self, - trusted_calls: &[TrustedOperation], - header: &PH, - shard: &ShardIdentifier, - max_exec_duration: Duration, - prepare_state_function: F, - ) -> Result> - where - PH: HeaderTrait, - F: FnOnce(Self::Externalities) -> Self::Externalities, - { - let ends_at = duration_now() + max_exec_duration; - - let (state, state_hash_before_execution) = self.state_handler.load_cloned(shard)?; - - // Execute any pre-processing steps. - let mut state = prepare_state_function(state); - let mut executed_and_failed_calls = Vec::>::new(); - - // TODO: maybe we can move it to `prepare_state_function`. It seems more reasonable. - let _ = Stf::on_runtime_upgrade(&mut state); - - // Iterate through all calls until time is over. - for trusted_call_signed in trusted_calls.into_iter() { - // Break if allowed time window is over. - if ends_at < duration_now() { - info!("Aborting execution of trusted calls because slot time is up"); - break - } - - match self.execute_trusted_call_on_stf( - &mut state, - &trusted_call_signed, - header, - shard, - StatePostProcessing::None, - ) { - Ok(executed_or_failed_call) => { - executed_and_failed_calls.push(executed_or_failed_call); - }, - Err(e) => { - error!("Fatal Error. Failed to attempt call execution: {:?}", e); - }, - }; - } - - Ok(BatchExecutionResult { - executed_operations: executed_and_failed_calls, - state_hash_before_execution, - state_after_execution: state, - }) - } -} - -fn into_map( - storage_entries: Vec>>, -) -> BTreeMap, Option>> { - storage_entries.into_iter().map(|e| e.into_tuple()).collect() -} - -// todo: we need to clarify where these functions belong and if we need them at all. moved them from ita-stf but we can no longer depend on that -pub fn storage_hashes_to_update_per_shard(_shard: &ShardIdentifier) -> Vec> { - Vec::new() -} - -pub fn shards_key_hash() -> Vec { - // here you have to point to a storage value containing a Vec of - // ShardIdentifiers the enclave uses this to autosubscribe to no shards - vec![] -} diff --git a/tee-worker/bitacross/core-primitives/stf-executor/src/executor_tests.rs b/tee-worker/bitacross/core-primitives/stf-executor/src/executor_tests.rs deleted file mode 100644 index 2eb0185bcd..0000000000 --- a/tee-worker/bitacross/core-primitives/stf-executor/src/executor_tests.rs +++ /dev/null @@ -1,279 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{executor::StfExecutor, traits::StateUpdateProposer}; -use codec::Encode; -use itc_parentchain_test::ParentchainHeaderBuilder; -use itp_node_api::metadata::{metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository}; -use itp_ocall_api::EnclaveAttestationOCallApi; -use itp_sgx_externalities::{SgxExternalities as State, SgxExternalitiesTrait}; -use itp_stf_primitives::{traits::TrustedCallSigning, types::ShardIdentifier}; -use itp_stf_state_handler::handle_state::HandleState; -use itp_test::mock::{ - handle_state_mock::HandleStateMock, - onchain_mock::OnchainMock, - stf_mock::{GetterMock, StfMock, TrustedCallMock, TrustedCallSignedMock}, -}; -use itp_types::H256; -use sp_core::{ed25519, Pair}; -use sp_runtime::app_crypto::sp_core::blake2_256; -use std::{sync::Arc, time::Duration, vec}; -// FIXME: Create unit tests for update_states, execute_trusted_call, execute_trusted_call_on_stf #554 - -pub fn propose_state_update_executes_all_calls_given_enough_time() { - // given - let (stf_executor, ocall_api, state_handler) = stf_executor(); - let mrenclave = ocall_api.get_mrenclave_of_self().unwrap().m; - let (_, shard) = init_state_and_shard_with_state_handler(state_handler.as_ref()); - let sender = endowed_account(); - let signed_call_1 = TrustedCallMock::balance_transfer( - sender.public().into(), - sender.public().into(), - 42, - ) - .sign(&sender.clone().into(), 0, &mrenclave, &shard); - let trusted_operation_1 = signed_call_1.into_trusted_operation(true); - let call_operation_hash_1: H256 = blake2_256(&trusted_operation_1.encode()).into(); - let signed_call_2 = - TrustedCallMock::balance_transfer(sender.public().into(), sender.public().into(), 100) - .sign(&sender.clone().into(), 1, &mrenclave, &shard); - let trusted_operation_2 = signed_call_2.into_trusted_operation(true); - let call_operation_hash_2: H256 = blake2_256(&trusted_operation_2.encode()).into(); - - let (_, old_state_hash) = state_handler.load_cloned(&shard).unwrap(); - - // when - let batch_execution_result = stf_executor - .propose_state_update( - &vec![trusted_operation_1, trusted_operation_2], - &ParentchainHeaderBuilder::default().build(), - &shard, - Duration::from_secs(1000), - |state| state, - ) - .unwrap(); - - // then - assert_eq!(old_state_hash, batch_execution_result.state_hash_before_execution); - assert_eq!(batch_execution_result.executed_operations.len(), 2); - assert_eq!( - batch_execution_result.get_executed_operation_hashes(), - vec![call_operation_hash_1, call_operation_hash_2] - ); - // Ensure that state has been updated and not actually written. - assert_ne!( - state_handler.load_cloned(&shard).unwrap().0, - batch_execution_result.state_after_execution - ); -} - -pub fn propose_state_update_executes_only_one_trusted_call_given_not_enough_time() { - // given - let (stf_executor, ocall_api, state_handler) = stf_executor(); - let mrenclave = ocall_api.get_mrenclave_of_self().unwrap().m; - let (_, shard) = init_state_and_shard_with_state_handler(state_handler.as_ref()); - let sender = endowed_account(); - let signed_call_1 = TrustedCallMock::waste_time_ms(sender.public().into(), 10).sign( - &sender.clone().into(), - 0, - &mrenclave, - &shard, - ); - let trusted_operation_1 = signed_call_1.into_trusted_operation(true); - let call_operation_hash_1: H256 = blake2_256(&trusted_operation_1.encode()).into(); - - let signed_call_2 = TrustedCallMock::waste_time_ms(sender.public().into(), 10).sign( - &sender.clone().into(), - 0, - &mrenclave, - &shard, - ); - let trusted_operation_2 = signed_call_2.into_trusted_operation(true); - - let (_, old_state_hash) = state_handler.load_cloned(&shard).unwrap(); - // when - let batch_execution_result = stf_executor - .propose_state_update( - &vec![trusted_operation_1.clone(), trusted_operation_2.clone()], - &ParentchainHeaderBuilder::default().build(), - &shard, - Duration::from_millis(5), - |state| state, - ) - .unwrap(); - - // then - assert_eq!(old_state_hash, batch_execution_result.state_hash_before_execution); - assert_eq!(batch_execution_result.executed_operations.len(), 1); - assert_eq!(batch_execution_result.get_executed_operation_hashes(), vec![call_operation_hash_1]); - // Ensure that state has been updated and not actually written. - assert_ne!( - state_handler.load_cloned(&shard).unwrap().0, - batch_execution_result.state_after_execution - ); -} - -pub fn propose_state_update_executes_noop_leaving_state_untouched() { - // given - let (stf_executor, ocall_api, state_handler) = stf_executor(); - let mrenclave = ocall_api.get_mrenclave_of_self().unwrap().m; - let (_, shard) = init_state_and_shard_with_state_handler(state_handler.as_ref()); - let sender = endowed_account(); - let signed_call_1 = TrustedCallMock::noop(sender.public().into()).sign( - &sender.clone().into(), - 0, - &mrenclave, - &shard, - ); - let trusted_operation_1 = signed_call_1.into_trusted_operation(true); - let call_operation_hash_1: H256 = blake2_256(&trusted_operation_1.encode()).into(); - - let (_, old_state_hash) = state_handler.load_cloned(&shard).unwrap(); - // when - let batch_execution_result = stf_executor - .propose_state_update( - &vec![trusted_operation_1.clone()], - &ParentchainHeaderBuilder::default().build(), - &shard, - Duration::from_millis(5), // 1000 yields 0, 2000 yields 1, 4000 yields 1, 25_000 yields 2 - |state| state, - ) - .unwrap(); - - // then - assert_eq!(old_state_hash, batch_execution_result.state_hash_before_execution); - assert_eq!(batch_execution_result.executed_operations.len(), 1); - assert_eq!(batch_execution_result.get_executed_operation_hashes(), vec![call_operation_hash_1]); - assert_eq!( - state_handler.load_cloned(&shard).unwrap().0, - batch_execution_result.state_after_execution - ); -} - -pub fn propose_state_update_executes_no_trusted_calls_given_no_time() { - // given - let (stf_executor, ocall_api, state_handler) = stf_executor(); - let mrenclave = ocall_api.get_mrenclave_of_self().unwrap().m; - let (_, shard) = init_state_and_shard_with_state_handler(state_handler.as_ref()); - let sender = endowed_account(); - let signed_call_1 = TrustedCallMock::balance_transfer( - sender.public().into(), - sender.public().into(), - 42, - ) - .sign(&sender.clone().into(), 0, &mrenclave, &shard); - let trusted_operation_1 = signed_call_1.into_trusted_operation(true); - - let signed_call_2 = - TrustedCallMock::balance_transfer(sender.public().into(), sender.public().into(), 100) - .sign(&sender.clone().into(), 0, &mrenclave, &shard); - let trusted_operation_2 = signed_call_2.into_trusted_operation(true); - - let (_, old_state_hash) = state_handler.load_cloned(&shard).unwrap(); - - // when - let batch_execution_result = stf_executor - .propose_state_update( - &vec![trusted_operation_1.clone(), trusted_operation_2.clone()], - &ParentchainHeaderBuilder::default().build(), - &shard, - Duration::ZERO, - |state| state, - ) - .unwrap(); - - // then - assert_eq!(old_state_hash, batch_execution_result.state_hash_before_execution); - assert_eq!(batch_execution_result.executed_operations.len(), 0); - assert_eq!(batch_execution_result.get_executed_operation_hashes(), vec![]); -} - -pub fn propose_state_update_always_executes_preprocessing_step() { - // given - let shard = ShardIdentifier::default(); - let (stf_executor, _, state_handler) = stf_executor(); - let _init_hash = state_handler.initialize_shard(shard).unwrap(); - let key = "my_key".encode(); - let value = "my_value".encode(); - let (old_state, old_state_hash) = state_handler.load_cloned(&shard).unwrap(); - - // when - let batch_execution_result = stf_executor - .propose_state_update( - &vec![], - &ParentchainHeaderBuilder::default().build(), - &shard, - Duration::ZERO, - |mut state| { - state.insert(key.clone(), value.clone()); - state - }, - ) - .unwrap(); - - // then - assert_eq!(old_state_hash, batch_execution_result.state_hash_before_execution); - - // Ensure that state has been updated. - let retrieved_value = batch_execution_result.state_after_execution.get(key.as_slice()).unwrap(); - assert_eq!(*retrieved_value, value); - // Ensure that state has not been actually written. - assert_ne!(old_state, batch_execution_result.state_after_execution); -} - -// Helper Functions -fn stf_executor() -> ( - StfExecutor< - OnchainMock, - HandleStateMock, - NodeMetadataRepository, - StfMock, - TrustedCallSignedMock, - GetterMock, - >, - Arc, - Arc, -) { - let ocall_api = Arc::new(OnchainMock::default()); - let state_handler = Arc::new(HandleStateMock::default()); - let node_metadata_repo = Arc::new(NodeMetadataRepository::new(NodeMetadataMock::new())); - let executor = StfExecutor::new(ocall_api.clone(), state_handler.clone(), node_metadata_repo); - (executor, ocall_api, state_handler) -} - -/// Returns a test setup initialized `State` with the corresponding `ShardIdentifier`. -pub(crate) fn init_state_and_shard_with_state_handler>( - state_handler: &S, -) -> (State, ShardIdentifier) { - let shard = ShardIdentifier::default(); - let _hash = state_handler.initialize_shard(shard).unwrap(); - - let (lock, mut state) = state_handler.load_for_mutation(&shard).unwrap(); - test_genesis_setup(&mut state); - - state_handler.write_after_mutation(state.clone(), lock, &shard).unwrap(); - - (state, shard) -} - -pub fn endowed_account() -> ed25519::Pair { - ed25519::Pair::from_seed(&[42u8; 32].into()) -} - -pub fn test_genesis_setup(_state: &mut impl SgxExternalitiesTrait) { - // set alice sudo account -} diff --git a/tee-worker/bitacross/core-primitives/stf-executor/src/getter_executor.rs b/tee-worker/bitacross/core-primitives/stf-executor/src/getter_executor.rs deleted file mode 100644 index b968efc18b..0000000000 --- a/tee-worker/bitacross/core-primitives/stf-executor/src/getter_executor.rs +++ /dev/null @@ -1,137 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Getter executor uses the state observer to get the most recent state and runs the getter on it. -//! The getter is verified (signature verfification) inside the `GetState` implementation. - -use crate::{error::Result, state_getter::GetState}; -use codec::Decode; -use itp_stf_primitives::traits::GetterAuthorization; -use itp_stf_state_observer::traits::ObserveState; -use itp_types::ShardIdentifier; -use log::*; -use std::{marker::PhantomData, sync::Arc, time::Instant, vec::Vec}; - -/// Trait to execute a getter for a specific shard. -pub trait ExecuteGetter { - fn execute_getter( - &self, - shard: &ShardIdentifier, - encoded_signed_getter: Vec, - ) -> Result>>; -} - -pub struct GetterExecutor -where - G: PartialEq, -{ - state_observer: Arc, - _phantom: PhantomData, - _phantom_getter: PhantomData, -} - -impl GetterExecutor -where - G: PartialEq, -{ - pub fn new(state_observer: Arc) -> Self { - Self { state_observer, _phantom: Default::default(), _phantom_getter: Default::default() } - } -} - -impl ExecuteGetter for GetterExecutor -where - StateObserver: ObserveState, - StateGetter: GetState, - G: PartialEq + Decode + GetterAuthorization, -{ - fn execute_getter( - &self, - shard: &ShardIdentifier, - encoded_signed_getter: Vec, - ) -> Result>> { - let getter = G::decode(&mut encoded_signed_getter.as_slice())?; - trace!("Successfully decoded trusted getter"); - - let getter_timer_start = Instant::now(); - let state_result = self - .state_observer - .observe_state(shard, |state| StateGetter::get_state(getter, state))??; - - debug!("Getter executed in {} ms", getter_timer_start.elapsed().as_millis()); - - Ok(state_result) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use codec::{Decode, Encode}; - - use itp_stf_state_observer::mock::ObserveStateMock; - use itp_test::mock::stf_mock::{ - GetterMock, PublicGetterMock, TrustedGetterMock, TrustedGetterSignedMock, - }; - - type TestState = u64; - type TestStateObserver = ObserveStateMock; - - struct TestStateGetter; - impl GetState for TestStateGetter { - fn get_state(_getter: GetterMock, state: &mut TestState) -> Result>> { - Ok(Some(state.encode())) - } - } - - type TestGetterExecutor = GetterExecutor; - - #[test] - fn executing_getters_works() { - let test_state = 23489u64; - let state_observer = Arc::new(TestStateObserver::new(test_state)); - let getter_executor = TestGetterExecutor::new(state_observer); - let getter = GetterMock::trusted(dummy_trusted_getter()); - - let state_result = getter_executor - .execute_getter(&ShardIdentifier::default(), getter.encode()) - .unwrap() - .unwrap(); - let decoded_state: TestState = Decode::decode(&mut state_result.as_slice()).unwrap(); - assert_eq!(decoded_state, test_state); - } - - #[test] - fn executing_public_getter_works() { - let test_state = 23489u64; - let state_observer = Arc::new(TestStateObserver::new(test_state)); - let getter_executor = TestGetterExecutor::new(state_observer); - let getter = GetterMock::public(PublicGetterMock::some_value); - - let state_result = getter_executor - .execute_getter(&ShardIdentifier::default(), getter.encode()) - .unwrap() - .unwrap(); - let decoded_state: TestState = Decode::decode(&mut state_result.as_slice()).unwrap(); - assert_eq!(decoded_state, test_state); - } - fn dummy_trusted_getter() -> TrustedGetterSignedMock { - TrustedGetterSignedMock { getter: TrustedGetterMock::some_value, signature: true } - // TrustedGetter::nonce(AccountId::new([0u8; 32])), - // MultiSignature::Ed25519(Signature::from_raw([0u8; 64])), - } -} diff --git a/tee-worker/bitacross/core-primitives/stf-executor/src/lib.rs b/tee-worker/bitacross/core-primitives/stf-executor/src/lib.rs deleted file mode 100644 index 6e1c8e21e8..0000000000 --- a/tee-worker/bitacross/core-primitives/stf-executor/src/lib.rs +++ /dev/null @@ -1,305 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(test, feature(assert_matches))] - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -use codec::{Decode, Encode}; -use core::fmt::Debug; -use itp_sgx_externalities::SgxExternalitiesTrait; -use itp_stf_primitives::types::TrustedOperationOrHash; -use itp_types::{parentchain::ParentchainCall, H256}; -use std::vec::Vec; - -// re-export module to properly feature gate sgx and regular std environment -#[cfg(all(not(feature = "std"), feature = "sgx"))] -pub mod sgx_reexport_prelude { - pub use thiserror_sgx as thiserror; -} - -pub mod error; -pub mod getter_executor; -pub mod state_getter; -pub mod traits; - -#[cfg(feature = "sgx")] -pub mod executor; - -#[cfg(feature = "sgx")] -pub mod enclave_signer; - -#[cfg(all(feature = "sgx", feature = "test"))] -pub mod executor_tests; - -#[cfg(feature = "mocks")] -pub mod mocks; - -pub type RpcResponseValue = Vec; - -/// Execution status of a trusted operation -/// -/// In case of success, it includes the operation hash, as well as -/// any extrinsic callbacks (e.g. unshield extrinsics) that need to be executed on-chain -/// -/// Litentry: -/// we have made a few changes: -/// - we add the encoded rpc response that will be passed back to the requester -/// - for failed top, we apply the parachain effects too -#[derive(Clone, Debug, PartialEq)] -pub enum ExecutionStatus { - Success(H256, Vec, RpcResponseValue, bool), - Failure(H256, Vec, RpcResponseValue), -} - -impl ExecutionStatus { - pub fn get_extrinsic_callbacks(&self) -> Vec { - match self { - ExecutionStatus::Success(_, opaque_calls, _, _) => opaque_calls.clone(), - ExecutionStatus::Failure(_, opaque_calls, _) => opaque_calls.clone(), - } - } - - pub fn get_executed_operation_hash(&self) -> Option { - match self { - ExecutionStatus::Success(operation_hash, ..) => Some(*operation_hash), - _ => None, - } - } - - pub fn get_operation_hash(&self) -> H256 { - match self { - ExecutionStatus::Success(operation_hash, ..) => *operation_hash, - ExecutionStatus::Failure(operation_hash, ..) => *operation_hash, - } - } - - pub fn get_rpc_response_value(&self) -> RpcResponseValue { - match self { - ExecutionStatus::Success(_, _, res, _) => res.clone(), - ExecutionStatus::Failure(_, _, res) => res.clone(), - } - } - - pub fn get_force_wait(&self) -> bool { - match self { - ExecutionStatus::Success(_, _, _, wait) => *wait, - _ => false, - } - } -} - -/// Information about an executed trusted operation -/// -/// -#[derive(Clone, Debug, PartialEq)] -pub struct ExecutedOperation -where - TCS: PartialEq + Encode + Decode + Debug + Send + Sync, - G: PartialEq + Encode + Decode + Debug + Send + Sync, -{ - pub status: ExecutionStatus, - pub trusted_operation_or_hash: TrustedOperationOrHash, -} - -impl ExecutedOperation -where - TCS: PartialEq + Encode + Decode + Debug + Send + Sync, - G: PartialEq + Encode + Decode + Debug + Send + Sync, -{ - /// Constructor for a successfully executed trusted operation. - pub fn success( - operation_hash: H256, - trusted_operation_or_hash: TrustedOperationOrHash, - extrinsic_call_backs: Vec, - rpc_response_value: RpcResponseValue, - force_connection_wait: bool, - ) -> Self { - ExecutedOperation { - status: ExecutionStatus::Success( - operation_hash, - extrinsic_call_backs, - rpc_response_value, - force_connection_wait, - ), - trusted_operation_or_hash, - } - } - - /// Constructor for a failed trusted operation execution. - pub fn failed( - operation_hash: H256, - trusted_operation_or_hash: TrustedOperationOrHash, - extrinsic_call_backs: Vec, - rpc_response_value: RpcResponseValue, - ) -> Self { - ExecutedOperation { - status: ExecutionStatus::Failure( - operation_hash, - extrinsic_call_backs, - rpc_response_value, - ), - trusted_operation_or_hash, - } - } - - /// Returns true if the executed operation was a success. - pub fn is_success(&self) -> bool { - matches!(self.status, ExecutionStatus::Success(..)) - } -} - -/// Result of an execution on the STF -/// -/// Contains multiple executed operations -#[derive(Clone, Debug)] -pub struct BatchExecutionResult -where - TCS: PartialEq + Encode + Decode + Debug + Send + Sync, - G: PartialEq + Encode + Decode + Debug + Send + Sync, -{ - pub state_hash_before_execution: H256, - pub executed_operations: Vec>, - pub state_after_execution: Externalities, -} - -impl BatchExecutionResult -where - Externalities: SgxExternalitiesTrait + Encode, - TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, - G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, -{ - pub fn get_extrinsic_callbacks(&self) -> Vec { - self.executed_operations - .iter() - .flat_map(|e| e.status.get_extrinsic_callbacks()) - .collect() - } - - /// Returns all successfully exectued operation hashes. - pub fn get_executed_operation_hashes(&self) -> Vec { - self.executed_operations - .iter() - .flat_map(|ec| ec.status.get_executed_operation_hash()) - .collect() - } - - /// Returns all operations that were not executed. - pub fn get_failed_operations(&self) -> Vec> { - self.executed_operations.iter().filter(|ec| !ec.is_success()).cloned().collect() - } - - // Litentry: returns all (top_hash, (rpc_response_value, force_wait) tuples - pub fn get_connection_updates(&self) -> Vec<(H256, (RpcResponseValue, bool))> { - self.executed_operations - .iter() - .map(|ec| { - ( - ec.status.get_operation_hash(), - (ec.status.get_rpc_response_value(), ec.status.get_force_wait()), - ) - }) - .collect() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use itp_sgx_externalities::SgxExternalities; - use itp_test::mock::stf_mock::{GetterMock, TrustedCallSignedMock}; - use itp_types::OpaqueCall; - - #[test] - fn is_success_works() { - let (success, _) = create_success_operation_from_u8(1); - let failed = create_failed_operation_from_u8(7); - - assert!(success.is_success()); - assert!(!failed.is_success()); - } - - #[test] - fn get_executed_operation_hashes_works() { - let (success_one, hash_success_one) = create_success_operation_from_u8(1); - let (success_two, hash_success_two) = create_success_operation_from_u8(3); - let failed = create_failed_operation_from_u8(7); - let result = batch_execution_result(vec![success_one, failed, success_two]); - - let success_operations = result.get_executed_operation_hashes(); - - assert_eq!(success_operations.len(), 2); - assert!(success_operations.contains(&hash_success_one)); - assert!(success_operations.contains(&hash_success_two)); - } - - #[test] - fn get_failed_operations_works() { - let failed_one = create_failed_operation_from_u8(1); - let failed_two = create_failed_operation_from_u8(3); - let (success, _) = create_success_operation_from_u8(10); - let result = batch_execution_result(vec![failed_one.clone(), failed_two.clone(), success]); - - let failed_operations = result.get_failed_operations(); - - assert_eq!(failed_operations.len(), 2); - assert!(failed_operations.contains(&failed_one)); - assert!(failed_operations.contains(&failed_two)); - } - - fn batch_execution_result( - executed_calls: Vec>, - ) -> BatchExecutionResult { - BatchExecutionResult { - executed_operations: executed_calls, - state_hash_before_execution: H256::default(), - state_after_execution: SgxExternalities::default(), - } - } - - fn create_failed_operation_from_u8( - int: u8, - ) -> ExecutedOperation { - ExecutedOperation::failed( - H256::from([int; 32]), - TrustedOperationOrHash::Hash(H256::from([int; 32])), - vec![], - vec![], - ) - } - - fn create_success_operation_from_u8( - int: u8, - ) -> (ExecutedOperation, H256) { - let hash = H256::from([int; 32]); - let opaque_call: Vec = - vec![ParentchainCall::Litentry(OpaqueCall(vec![int; 10]))]; - let operation = ExecutedOperation::success( - hash, - TrustedOperationOrHash::Hash(hash), - opaque_call, - vec![], - false, - ); - (operation, hash) - } -} diff --git a/tee-worker/bitacross/core-primitives/stf-executor/src/mocks.rs b/tee-worker/bitacross/core-primitives/stf-executor/src/mocks.rs deleted file mode 100644 index d328a2e24e..0000000000 --- a/tee-worker/bitacross/core-primitives/stf-executor/src/mocks.rs +++ /dev/null @@ -1,168 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - error::Result, - state_getter::GetState, - traits::{StateUpdateProposer, StfEnclaveSigning}, - BatchExecutionResult, ExecutedOperation, -}; -use codec::{Decode, Encode}; -use core::fmt::Debug; -use itp_sgx_externalities::SgxExternalitiesTrait; -use itp_stf_primitives::{ - traits::TrustedCallSigning, - types::{AccountId, KeyPair, ShardIdentifier, TrustedOperationOrHash}, -}; -use itp_types::{MrEnclave, H256}; -use sp_core::Pair; -use sp_runtime::traits::Header as HeaderTrait; -#[cfg(feature = "std")] -use std::sync::RwLock; -use std::{boxed::Box, marker::PhantomData, ops::Deref, time::Duration, vec::Vec}; - -use itp_stf_primitives::{ - traits::{GetterAuthorization, TrustedCallVerification}, - types::TrustedOperation, -}; - -#[cfg(feature = "sgx")] -use std::sync::SgxRwLock as RwLock; - -/// Mock for the StfExecutor. -#[derive(Default)] -pub struct StfExecutorMock { - pub state: RwLock, -} - -impl StfExecutorMock { - pub fn new(state: State) -> Self { - Self { state: RwLock::new(state) } - } - - pub fn get_state(&self) -> State { - (*self.state.read().unwrap().deref()).clone() - } -} - -impl StateUpdateProposer for StfExecutorMock -where - State: SgxExternalitiesTrait + Encode + Clone, - TCS: PartialEq + Encode + Decode + Clone + Debug + Send + Sync + TrustedCallVerification, - G: PartialEq + Encode + Decode + Clone + Debug + Send + Sync, -{ - type Externalities = State; - - fn propose_state_update( - &self, - trusted_calls: &[TrustedOperation], - _header: &PH, - _shard: &ShardIdentifier, - _max_exec_duration: Duration, - prepare_state_function: F, - ) -> Result> - where - PH: HeaderTrait, - F: FnOnce(Self::Externalities) -> Self::Externalities, - { - let mut lock = self.state.write().unwrap(); - - let updated_state = prepare_state_function((*lock.deref()).clone()); - - *lock = updated_state.clone(); - - let executed_operations: Vec> = trusted_calls - .iter() - .map(|c| { - let operation_hash = c.hash(); - let top_or_hash = TrustedOperationOrHash::::from_top(c.clone()); - ExecutedOperation::success( - operation_hash, - top_or_hash, - Vec::new(), - Vec::new(), - false, - ) - }) - .collect(); - - Ok(BatchExecutionResult { - executed_operations, - state_hash_before_execution: H256::default(), - state_after_execution: updated_state, - }) - } -} - -/// Enclave signer mock. -pub struct StfEnclaveSignerMock { - mr_enclave: [u8; 32], - signer: sp_core::ed25519::Pair, -} - -impl StfEnclaveSignerMock { - pub fn new(mr_enclave: [u8; 32]) -> Self { - type Seed = [u8; 32]; - const TEST_SEED: Seed = *b"42345678901234567890123456789012"; - - Self { mr_enclave, signer: sp_core::ed25519::Pair::from_seed(&TEST_SEED) } - } -} - -impl Default for StfEnclaveSignerMock { - fn default() -> Self { - Self::new([0u8; 32]) - } -} - -impl StfEnclaveSigning for StfEnclaveSignerMock { - fn get_enclave_account(&self) -> Result { - Ok(self.signer.public().into()) - } - - fn get_mrenclave(&self) -> Result { - Ok(self.mr_enclave) - } - - fn sign_call_with_self>( - &self, - trusted_call: &TC, - shard: &ShardIdentifier, - ) -> Result { - Ok(trusted_call.sign(&KeyPair::Ed25519(Box::new(self.signer)), 1, &self.mr_enclave, shard)) - } - - fn sign(&self, _payload: &[u8]) -> Result> { - Ok([0u8; 32].to_vec()) - } -} - -/// GetState mock -#[derive(Default)] -pub struct GetStateMock { - _phantom: PhantomData, -} - -impl GetState for GetStateMock -where - StateType: Encode, - G: PartialEq + Decode + GetterAuthorization, -{ - fn get_state(_getter: G, state: &mut StateType) -> Result>> { - Ok(Some(state.encode())) - } -} diff --git a/tee-worker/bitacross/core-primitives/stf-executor/src/state_getter.rs b/tee-worker/bitacross/core-primitives/stf-executor/src/state_getter.rs deleted file mode 100644 index ca047a36eb..0000000000 --- a/tee-worker/bitacross/core-primitives/stf-executor/src/state_getter.rs +++ /dev/null @@ -1,85 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::error::{Error, Result}; -use codec::Decode; -use core::marker::PhantomData; -use itp_sgx_externalities::SgxExternalities; -use itp_stf_interface::StateGetterInterface; -use itp_stf_primitives::traits::GetterAuthorization; -use log::*; -use std::vec::Vec; - -/// Abstraction for accessing state with a getter. -pub trait GetState { - /// Executes a trusted getter on a state and return its value, if available. - /// - /// Also verifies the signature of the trusted getter and returns an error - /// if it's invalid. - fn get_state(getter: G, state: &mut StateType) -> Result>>; -} - -pub struct StfStateGetter { - _phantom: PhantomData, -} - -impl GetState for StfStateGetter -where - Stf: StateGetterInterface, - G: PartialEq + Decode + GetterAuthorization, -{ - fn get_state(getter: G, state: &mut SgxExternalities) -> Result>> { - if !getter.is_authorized() { - error!("getter authorization failed"); - return Err(Error::GetterIsNotAuthorized) - } - debug!("getter authorized. calling into STF to get state"); - Ok(Stf::execute_getter(state, getter)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use core::assert_matches::assert_matches; - - use itp_test::mock::stf_mock::{ - GetterMock, StfMock, TrustedGetterMock, TrustedGetterSignedMock, - }; - - type TestStateGetter = StfStateGetter; - - #[test] - fn upon_false_signature_get_stf_state_errs() { - let getter = - TrustedGetterSignedMock { getter: TrustedGetterMock::some_value, signature: false }; - let mut state = SgxExternalities::default(); - - assert_matches!( - TestStateGetter::get_state(GetterMock::trusted(getter), &mut state), - Err(Error::GetterIsNotAuthorized) - ); - } - - #[test] - fn state_getter_is_executed_if_signature_is_correct() { - let getter = - TrustedGetterSignedMock { getter: TrustedGetterMock::some_value, signature: true }; - let mut state = SgxExternalities::default(); - assert!(TestStateGetter::get_state(GetterMock::trusted(getter), &mut state).is_ok()); - } -} diff --git a/tee-worker/bitacross/core-primitives/stf-executor/src/traits.rs b/tee-worker/bitacross/core-primitives/stf-executor/src/traits.rs deleted file mode 100644 index 62e788141a..0000000000 --- a/tee-worker/bitacross/core-primitives/stf-executor/src/traits.rs +++ /dev/null @@ -1,87 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{error::Result, BatchExecutionResult}; -use codec::{Decode, Encode}; -use core::fmt::Debug; -use itp_sgx_externalities::SgxExternalitiesTrait; -use itp_stf_primitives::{ - traits::TrustedCallSigning, - types::{AccountId, ShardIdentifier, TrustedOperation}, -}; -use itp_types::{MrEnclave, H256}; -use sp_runtime::traits::Header as HeaderTrait; -use std::{time::Duration, vec::Vec}; - -/// Post-processing steps after executing STF -pub enum StatePostProcessing { - None, - Prune, -} - -/// Allows signing of a trusted call or a raw bytes with the enclave account that is registered in the STF. -/// -/// The signing key is derived from the shielding key, which guarantees that all enclaves sign the same key. -pub trait StfEnclaveSigning -where - TCS: PartialEq + Encode + Debug, -{ - fn get_enclave_account(&self) -> Result; - - fn get_mrenclave(&self) -> Result; - - fn sign_call_with_self>( - &self, - trusted_call: &TC, - shard: &ShardIdentifier, - ) -> Result; - - // litentry: sign an opaque payload - fn sign(&self, payload: &[u8]) -> Result>; -} - -/// Proposes a state update to `Externalities`. -pub trait StateUpdateProposer -where - TCS: PartialEq + Encode + Decode + Debug + Send + Sync, - G: PartialEq + Encode + Decode + Debug + Send + Sync, -{ - type Externalities: SgxExternalitiesTrait + Encode; - - /// Executes trusted calls within a given time frame without permanent state mutation. - /// - /// All executed call hashes and the mutated state are returned. - /// If the time expires, any remaining trusted calls within the batch will be ignored. - fn propose_state_update( - &self, - trusted_calls: &[TrustedOperation], - header: &PH, - shard: &ShardIdentifier, - max_exec_duration: Duration, - prepare_state_function: F, - ) -> Result> - where - PH: HeaderTrait, - F: FnOnce(Self::Externalities) -> Self::Externalities; -} - -/// Updates the STF state for a specific header. -/// -/// Cannot be implemented for a generic header currently, because the runtime expects a ParentchainHeader. -pub trait StfUpdateState { - fn update_states(&self, header: &PCH, parentchain_id: &PCID) -> Result<()>; -} diff --git a/tee-worker/bitacross/core-primitives/top-pool-author/Cargo.toml b/tee-worker/bitacross/core-primitives/top-pool-author/Cargo.toml deleted file mode 100644 index dbfa28d14f..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool-author/Cargo.toml +++ /dev/null @@ -1,68 +0,0 @@ -[package] -name = "bc-itp-top-pool-author" -version = "0.1.0" -authors = ['Trust Computing GmbH ', 'Integritee AG '] -edition = "2021" - -[dependencies] -sgx_tstd = { workspace = true, optional = true } - -itp-enclave-metrics = { workspace = true } -itp-ocall-api = { workspace = true } -itp-sgx-crypto = { workspace = true } -itp-stf-primitives = { workspace = true } -itp-stf-state-handler = { workspace = true } -itp-test = { workspace = true, optional = true } -itp-top-pool = { package = "bc-itp-top-pool", path = "../top-pool", default-features = false } -itp-types = { workspace = true } -itp-utils = { workspace = true } -litentry-primitives = { workspace = true } - -jsonrpc-core = { workspace = true, optional = true } -jsonrpc-core_sgx = { workspace = true, optional = true } - -codec = { package = "parity-scale-codec", workspace = true } -derive_more = { workspace = true } -lazy_static = { workspace = true, optional = true } -log = { workspace = true } - -sp-core = { workspace = true, features = ["full_crypto"] } -sp-runtime = { workspace = true } - -[dev-dependencies] -futures = { workspace = true } -itp-sgx-crypto = { workspace = true, features = ["mocks"] } -itp-test = { workspace = true, features = ["std"] } -itp-top-pool = { package = "bc-itp-top-pool", path = "../top-pool", features = ["std", "mocks"] } -sgx_crypto_helper = { workspace = true } -sp-keyring = { workspace = true } - -[features] -default = ["std"] -std = [ - "itp-sgx-crypto/std", - "itp-enclave-metrics/std", - "itp-ocall-api/std", - "itp-stf-state-handler/std", - "itp-top-pool/std", - "itp-types/std", - "jsonrpc-core", - "log/std", - "litentry-primitives/std", - "itp-utils/std", - "sgx_crypto_helper/default", -] -sgx = [ - "sgx_tstd", - "jsonrpc-core_sgx", - "itp-enclave-metrics/sgx", - "itp-sgx-crypto/sgx", - "itp-stf-state-handler/sgx", - "itp-top-pool/sgx", - "litentry-primitives/sgx", - "sgx_crypto_helper/mesalock_sgx", -] -test = ["itp-test/sgx", "itp-top-pool/mocks"] -mocks = ["lazy_static"] -sidechain = [] -offchain-worker = [] diff --git a/tee-worker/bitacross/core-primitives/top-pool-author/src/api.rs b/tee-worker/bitacross/core-primitives/top-pool-author/src/api.rs deleted file mode 100644 index 7a133dca40..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool-author/src/api.rs +++ /dev/null @@ -1,171 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Chain api required for the operation pool. - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; -use core::fmt::Debug; - -use crate::error; -use codec::Encode; -use itp_stf_primitives::{ - traits::{PoolTransactionValidation, TrustedCallVerification}, - types::ShardIdentifier, -}; -use itp_top_pool::{ - pool::{ChainApi, NumberFor}, - primitives::{TrustedOperationSource, TxHash}, -}; -use itp_types::BlockHash as SidechainBlockHash; -use jsonrpc_core::futures::future::{ready, Future, Ready}; -use log::*; -use sp_runtime::{ - generic::BlockId, - traits::{BlakeTwo256, Block as BlockT, Hash as HashT}, - transaction_validity::TransactionValidity, -}; -use std::{boxed::Box, marker::PhantomData, pin::Pin}; - -/// Future that resolves to account nonce. -pub type Result = core::result::Result; - -/// The operation pool logic for full client. -pub struct SidechainApi { - _marker: PhantomData<(Block, TCS)>, -} - -impl SidechainApi -where - TCS: PartialEq + TrustedCallVerification + Debug, -{ - /// Create new operation pool logic. - pub fn new() -> Self { - SidechainApi { _marker: Default::default() } - } -} - -impl Default for SidechainApi -where - TCS: PartialEq + TrustedCallVerification + Debug + Sync + Send, -{ - fn default() -> Self { - Self::new() - } -} - -impl ChainApi for SidechainApi -where - Block: BlockT, - TCS: PartialEq + TrustedCallVerification + Sync + Send + Debug, -{ - type Block = Block; - type Error = error::Error; - type ValidationFuture = - Pin> + Send>>; - type BodyFuture = Ready>>; - - fn validate_transaction( - &self, - _source: TrustedOperationSource, - uxt: TOP, - _shard: ShardIdentifier, - ) -> Self::ValidationFuture { - let operation = uxt.validate(); - Box::pin(ready(Ok(operation))) - } - - fn block_id_to_number( - &self, - at: &BlockId, - ) -> error::Result>> { - Ok(match at { - BlockId::Number(num) => Some(*num), - BlockId::Hash(_) => None, - }) - } - - fn block_id_to_hash( - &self, - at: &BlockId, - ) -> error::Result> { - Ok(match at { - //BlockId::Hash(x) => Some(x.clone()), - BlockId::Hash(_x) => None, - // dummy - BlockId::Number(_num) => None, - }) - } - - fn hash_and_length(&self, ex: &TOP) -> (TxHash, usize) { - debug!("[Pool] creating hash of {:?}", ex); - ex.using_encoded(|x| (BlakeTwo256::hash(x), x.len())) - } - - fn block_body(&self, _id: &BlockId) -> Self::BodyFuture { - ready(Ok(None)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use futures::executor; - use itp_stf_primitives::types::ShardIdentifier; - use itp_test::mock::stf_mock::{ - mock_top_indirect_trusted_call_signed, mock_top_public_getter, TrustedCallSignedMock, - }; - use itp_types::{AccountId, Block as ParentchainBlock}; - use sp_core::{ed25519, Pair}; - - type TestChainApi = SidechainApi; - - pub fn endowed_account() -> ed25519::Pair { - ed25519::Pair::from_seed(&[42u8; 32].into()) - } - - #[test] - fn indirect_calls_are_valid() { - let chain_api = TestChainApi::default(); - let _account: AccountId = endowed_account().public().into(); - let operation = mock_top_indirect_trusted_call_signed(); - - let validation = executor::block_on(chain_api.validate_transaction( - TrustedOperationSource::Local, - operation, - ShardIdentifier::default(), - )) - .unwrap(); - - assert!(validation.is_ok()); - } - - #[test] - fn public_getters_are_not_valid() { - let chain_api = TestChainApi::default(); - let public_getter = mock_top_public_getter(); - - let validation = executor::block_on(chain_api.validate_transaction( - TrustedOperationSource::Local, - public_getter, - ShardIdentifier::default(), - )) - .unwrap(); - - assert!(validation.is_err()); - } -} diff --git a/tee-worker/bitacross/core-primitives/top-pool-author/src/author.rs b/tee-worker/bitacross/core-primitives/top-pool-author/src/author.rs deleted file mode 100644 index 4c7b7363d2..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool-author/src/author.rs +++ /dev/null @@ -1,407 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; -use core::fmt::Debug; - -use crate::{ - client_error::Error as ClientError, - error::{Error as StateRpcError, Result}, - top_filter::Filter, - traits::{AuthorApi, OnBlockImported}, -}; -use codec::{Decode, Encode}; -use itp_sgx_crypto::{key_repository::AccessKey, ShieldingCryptoDecrypt}; -use itp_stf_primitives::{ - traits::{PoolTransactionValidation, TrustedCallVerification}, - types::{AccountId, Hash, TrustedOperation as StfTrustedOperation, TrustedOperationOrHash}, -}; -use itp_stf_state_handler::query_shard_state::QueryShardState; -use itp_top_pool::{ - error::{Error as PoolError, IntoPoolError}, - primitives::{ - BlockHash, InPoolOperation, PoolFuture, PoolStatus, TrustedOperationPool, - TrustedOperationSource, TxHash, - }, -}; -use itp_types::{BlockHash as SidechainBlockHash, DecryptableRequest, ShardIdentifier}; -use jsonrpc_core::{ - futures::future::{ready, TryFutureExt}, - Error as RpcError, -}; -use log::*; -use sp_runtime::generic; -use std::{boxed::Box, string::String, sync::Arc, vec::Vec}; - -/// Define type of TOP filter that is used in the Author -pub type AuthorTopFilter = crate::top_filter::IndirectCallsOnlyFilter; - -/// Currently we treat all RPC operations as externals. -/// -/// Possibly in the future we could allow opt-in for special treatment -/// of such operations, so that the block authors can inject -/// some unique operations via RPC and have them included in the pool. -const TX_SOURCE: TrustedOperationSource = TrustedOperationSource::External; - -// remove duplication of this type definiton ? -pub type RequestIdWithParamsAndMethod = Option<(Hash, Vec)>; - -/// Authoring API for RPC calls -/// -/// -pub struct Author -where - TopPool: TrustedOperationPool> + Sync + Send + 'static, - TopFilter: Filter>, - StateFacade: QueryShardState, - ShieldingKeyRepository: AccessKey, - ::KeyType: ShieldingCryptoDecrypt + 'static, - TCS: PartialEq + Encode + Clone + Debug + Send + Sync, - G: PartialEq + Encode + Clone + PoolTransactionValidation + Debug + Send + Sync, -{ - top_pool: Arc, - top_filter: TopFilter, - state_facade: Arc, - shielding_key_repo: Arc, -} - -impl - Author -where - TopPool: TrustedOperationPool> + Sync + Send + 'static, - TopFilter: Filter>, - StateFacade: QueryShardState, - ShieldingKeyRepository: AccessKey, - ::KeyType: ShieldingCryptoDecrypt + 'static, - TCS: PartialEq + Encode + Clone + Debug + Send + Sync, - G: PartialEq + Encode + Clone + PoolTransactionValidation + Debug + Send + Sync, -{ - /// Create new instance of Authoring API. - pub fn new( - top_pool: Arc, - top_filter: TopFilter, - state_facade: Arc, - encryption_key: Arc, - ) -> Self { - Author { top_pool, top_filter, state_facade, shielding_key_repo: encryption_key } - } -} - -enum TopSubmissionMode { - Submit, - SubmitWatch, -} - -impl - Author -where - TopPool: TrustedOperationPool> + Sync + Send + 'static, - TopFilter: Filter>, - StateFacade: QueryShardState, - ShieldingKeyRepository: AccessKey, - ::KeyType: ShieldingCryptoDecrypt + 'static, - TCS: PartialEq - + Encode - + Decode - + Clone - + Debug - + Send - + Sync - + TrustedCallVerification - + 'static, - G: PartialEq - + Encode - + Decode - + Clone - + PoolTransactionValidation - + Debug - + Send - + Sync - + 'static, -{ - fn process_top( - &self, - mut request: R, - submission_mode: TopSubmissionMode, - ) -> PoolFuture { - let shard = request.shard(); - - // check if shard exists - match self.state_facade.shard_exists(&shard) { - Err(_) => return Box::pin(ready(Err(ClientError::InvalidShard.into()))), - Ok(shard_exists) => - if !shard_exists { - return Box::pin(ready(Err(ClientError::InvalidShard.into()))) - }, - }; - - // decrypt call - let shielding_key = match self.shielding_key_repo.retrieve_key() { - Ok(k) => k, - Err(_) => return Box::pin(ready(Err(ClientError::BadFormatDecipher.into()))), - }; - let request_vec = match request.decrypt(Box::new(shielding_key)) { - Ok(req) => req, - Err(_) => return Box::pin(ready(Err(ClientError::BadFormatDecipher.into()))), - }; - // decode call - let trusted_operation = - match StfTrustedOperation::::decode(&mut request_vec.as_slice()) { - Ok(op) => op, - Err(_) => return Box::pin(ready(Err(ClientError::BadFormat.into()))), - }; - - trace!("decrypted indirect invocation: {:?}", trusted_operation); - - // apply top filter - return error if this specific type of trusted operation - // is not allowed by the filter - if !self.top_filter.filter(&trusted_operation) { - warn!("unsupported operation"); - return Box::pin(ready(Err(ClientError::UnsupportedOperation.into()))) - } - - //let best_block_hash = self.client.info().best_hash; - // dummy block hash - let best_block_hash = Default::default(); - - if let Some(trusted_call_signed) = trusted_operation.to_call() { - debug!( - "Submitting trusted call to TOP pool: {:?}, TOP hash: {:?}", - trusted_call_signed, - self.hash_of(&trusted_operation) - ); - } else if let StfTrustedOperation::::get(ref getter) = trusted_operation { - debug!( - "Submitting trusted or public getter to TOP pool: {:?}, TOP hash: {:?}", - getter, - self.hash_of(&trusted_operation) - ); - } - - match submission_mode { - TopSubmissionMode::Submit => Box::pin( - self.top_pool - .submit_one( - &generic::BlockId::hash(best_block_hash), - TX_SOURCE, - trusted_operation, - shard, - ) - .map_err(map_top_error::), - ), - - TopSubmissionMode::SubmitWatch => Box::pin( - self.top_pool - .submit_and_watch( - &generic::BlockId::hash(best_block_hash), - TX_SOURCE, - trusted_operation, - shard, - ) - .map_err(map_top_error::), - ), - } - } - - fn remove_top( - &self, - bytes_or_hash: TrustedOperationOrHash, - shard: ShardIdentifier, - inblock: bool, - ) -> Result { - let hash = match bytes_or_hash { - TrustedOperationOrHash::Hash(h) => Ok(h), - TrustedOperationOrHash::OperationEncoded(bytes) => { - match Decode::decode(&mut bytes.as_slice()) { - Ok(op) => Ok(self.top_pool.hash_of(&op)), - Err(e) => { - error!("Failed to decode trusted operation: {:?}, operation will not be removed from pool", e); - Err(StateRpcError::CodecError(e)) - }, - } - }, - TrustedOperationOrHash::Operation(op) => Ok(self.top_pool.hash_of(&op)), - }?; - - debug!("removing {:?} from top pool", hash); - - let removed_op_hash = self - .top_pool - .remove_invalid(&[hash], shard, inblock) - // Only remove a single element, so first should return Ok(). - .first() - .map(|o| o.hash()) - .ok_or(PoolError::InvalidTrustedOperation)?; - - Ok(removed_op_hash) - } -} - -fn map_top_error>, TCS, G>( - error: P::Error, -) -> RpcError -where - TCS: PartialEq + Encode + Debug, - G: PartialEq + Encode + Debug, -{ - StateRpcError::PoolError( - error - .into_pool_error() - .map(Into::into) - .unwrap_or_else(|_error| PoolError::Verification), - ) - .into() -} - -impl - AuthorApi - for Author -where - TopPool: TrustedOperationPool> + Sync + Send + 'static, - TopFilter: Filter>, - StateFacade: QueryShardState, - ShieldingKeyRepository: AccessKey, - ::KeyType: ShieldingCryptoDecrypt + 'static, - G: PartialEq - + Encode - + Decode - + Clone - + PoolTransactionValidation - + Debug - + Send - + Sync - + 'static, - TCS: PartialEq - + Encode - + Decode - + Clone - + Debug - + Send - + Sync - + TrustedCallVerification - + 'static, -{ - fn submit_top(&self, req: R) -> PoolFuture { - self.process_top(req, TopSubmissionMode::Submit) - } - - /// Get hash of TrustedOperation - fn hash_of(&self, xt: &StfTrustedOperation) -> TxHash { - self.top_pool.hash_of(xt) - } - - fn pending_tops(&self, shard: ShardIdentifier) -> Result>> { - Ok(self.top_pool.ready(shard).map(|top| top.data().encode()).collect()) - } - - fn get_pending_getters(&self, shard: ShardIdentifier) -> Vec> { - self.top_pool - .ready(shard) - .filter_map(|o| match o.data() { - StfTrustedOperation::::get(_) => Some(o.data().clone()), - StfTrustedOperation::::direct_call(_) - | StfTrustedOperation::::indirect_call(_) => None, - }) - .collect() - } - - fn get_pending_trusted_calls( - &self, - shard: ShardIdentifier, - ) -> Vec> { - self.top_pool - .ready(shard) - .filter_map(|o| match o.data() { - StfTrustedOperation::::direct_call(_) - | StfTrustedOperation::::indirect_call(_) => Some(o.data().clone()), - StfTrustedOperation::::get(_) => None, - }) - .collect() - } - - fn get_status(&self, shard: ShardIdentifier) -> PoolStatus { - self.top_pool.status(shard) - } - - fn get_pending_trusted_calls_for( - &self, - shard: ShardIdentifier, - account: &AccountId, - ) -> Vec> { - self.get_pending_trusted_calls(shard) - .into_iter() - .filter(|o| o.signed_caller_account().as_ref() == Some(account)) - .collect() - } - - fn get_shards(&self) -> Vec { - self.top_pool.shards() - } - - fn list_handled_shards(&self) -> Vec { - self.state_facade.list_shards().unwrap_or_default() - } - - fn remove_calls_from_pool( - &self, - shard: ShardIdentifier, - executed_calls: Vec<(TrustedOperationOrHash, bool)>, - ) -> Vec> { - let mut failed_to_remove = Vec::new(); - for (executed_call, inblock) in executed_calls { - if let Err(e) = self.remove_top(executed_call.clone(), shard, inblock) { - // We don't want to return here before all calls have been iterated through, - // hence log message and collect failed calls in vec. - debug!("Error removing trusted call from top pool: {:?}", e); - failed_to_remove.push(executed_call); - } - } - failed_to_remove - } - - fn watch_top( - &self, - request: R, - ) -> PoolFuture { - self.process_top(request, TopSubmissionMode::SubmitWatch) - } - - fn update_connection_state(&self, updates: Vec<(TxHash, (Vec, bool))>) { - self.top_pool.update_connection_state(updates) - } - - fn swap_rpc_connection_hash(&self, old_hash: TxHash, new_hash: TxHash) { - self.top_pool.swap_rpc_connection_hash(old_hash, new_hash) - } -} - -impl OnBlockImported - for Author -where - TopPool: TrustedOperationPool> + Sync + Send + 'static, - TopFilter: Filter>, - StateFacade: QueryShardState, - ShieldingKeyRepository: AccessKey, - ::KeyType: ShieldingCryptoDecrypt + 'static, - G: PartialEq + Encode + Clone + PoolTransactionValidation + Debug + Send + Sync, - TCS: PartialEq + Encode + Clone + Debug + Send + Sync, -{ - type Hash = TxHash; - - fn on_block_imported(&self, _hashes: &[Self::Hash], _block_hash: SidechainBlockHash) {} -} diff --git a/tee-worker/bitacross/core-primitives/top-pool-author/src/author_tests.rs b/tee-worker/bitacross/core-primitives/top-pool-author/src/author_tests.rs deleted file mode 100644 index a354ea772b..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool-author/src/author_tests.rs +++ /dev/null @@ -1,141 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - author::Author, - test_fixtures::shard_id, - test_utils::submit_operation_to_top_pool, - top_filter::{AllowAllTopsFilter, Filter, GettersOnlyFilter}, - traits::AuthorApi, -}; -use codec::{Decode, Encode}; -use itp_sgx_crypto::{mocks::KeyRepositoryMock, ShieldingCryptoDecrypt, ShieldingCryptoEncrypt}; - -use itp_stf_state_handler::handle_state::HandleState; -use itp_test::mock::{ - handle_state_mock::HandleStateMock, - shielding_crypto_mock::ShieldingCryptoMock, - stf_mock::{ - mock_top_direct_trusted_call_signed, mock_top_indirect_trusted_call_signed, - mock_top_trusted_getter_signed, GetterMock, TrustedCallSignedMock, TrustedOperationMock, - }, -}; -use itp_top_pool::mocks::trusted_operation_pool_mock::TrustedOperationPoolMock; - -use sgx_crypto_helper::{rsa3072::Rsa3072KeyPair, RsaKeyPair}; -use std::sync::Arc; - -type TestAuthor = Author< - TrustedOperationPoolMock, - Filter, - HandleStateMock, - KeyRepositoryMock, - TrustedCallSignedMock, - GetterMock, ->; - -#[test] -fn top_encryption_works() { - let top_call = mock_top_direct_trusted_call_signed(); - let top_getter = mock_top_trusted_getter_signed(); - assert_eq!(top_call, encrypt_and_decrypt_top(&top_call)); - assert_eq!(top_getter, encrypt_and_decrypt_top(&top_getter)); -} - -fn encrypt_and_decrypt_top(top: &TrustedOperationMock) -> TrustedOperationMock { - let encryption_key = Rsa3072KeyPair::new().unwrap(); - let encrypted_top = encryption_key.encrypt(top.encode().as_slice()).unwrap(); - let decrypted_top = encryption_key.decrypt(encrypted_top.as_slice()).unwrap(); - - TrustedOperationMock::decode(&mut decrypted_top.as_slice()).unwrap() -} - -#[test] -fn submitting_to_author_inserts_in_pool() { - let (author, top_pool, shielding_key) = create_author_with_filter(AllowAllTopsFilter::new()); - let top_getter = mock_top_trusted_getter_signed(); - - let submit_response = - submit_operation_to_top_pool(&author, &top_getter, &shielding_key, shard_id()).unwrap(); - - assert!(!submit_response.0.is_zero()); - - let submitted_transactions = top_pool.get_last_submitted_transactions(); - assert_eq!(1, submitted_transactions.len()); -} - -#[test] -fn submitting_call_to_author_when_top_is_filtered_returns_error() { - let (author, top_pool, shielding_key) = create_author_with_filter(GettersOnlyFilter::new()); - let top_call = mock_top_direct_trusted_call_signed(); - let submit_response = - submit_operation_to_top_pool(&author, &top_call, &shielding_key, shard_id()); - - assert!(submit_response.is_err()); - assert!(top_pool.get_last_submitted_transactions().is_empty()); -} - -#[test] -fn submitting_getter_to_author_when_top_is_filtered_inserts_in_pool() { - let (author, top_pool, shielding_key) = create_author_with_filter(GettersOnlyFilter::new()); - let top_getter = mock_top_trusted_getter_signed(); - let submit_response = - submit_operation_to_top_pool(&author, &top_getter, &shielding_key, shard_id()).unwrap(); - - assert!(!submit_response.0.is_zero()); - assert_eq!(1, top_pool.get_last_submitted_transactions().len()); -} - -#[test] -fn submitting_direct_call_works() { - let (author, top_pool, shielding_key) = create_author_with_filter(AllowAllTopsFilter::new()); - let top_call = mock_top_direct_trusted_call_signed(); - let _ = submit_operation_to_top_pool(&author, &top_call, &shielding_key, shard_id()).unwrap(); - - assert_eq!(1, top_pool.get_last_submitted_transactions().len()); - assert_eq!(1, author.get_pending_trusted_calls(shard_id()).len()); -} - -#[test] -fn submitting_indirect_call_works() { - let (author, top_pool, shielding_key) = create_author_with_filter(AllowAllTopsFilter::new()); - let top_call = mock_top_indirect_trusted_call_signed(); - let _ = submit_operation_to_top_pool(&author, &top_call, &shielding_key, shard_id()).unwrap(); - - assert_eq!(1, top_pool.get_last_submitted_transactions().len()); - assert_eq!(1, author.get_pending_trusted_calls(shard_id()).len()); -} - -fn create_author_with_filter>( - filter: F, -) -> (TestAuthor, Arc>, ShieldingCryptoMock) { - let top_pool = Arc::new(TrustedOperationPoolMock::default()); - - let shard_id = shard_id(); - let state_facade = HandleStateMock::from_shard(shard_id).unwrap(); - state_facade.load_cloned(&shard_id).unwrap(); - - let encryption_key = ShieldingCryptoMock::default(); - let shielding_key_repo = - Arc::new(KeyRepositoryMock::::new(encryption_key.clone())); - - ( - Author::new(top_pool.clone(), filter, Arc::new(state_facade), shielding_key_repo), - top_pool, - encryption_key, - ) -} diff --git a/tee-worker/bitacross/core-primitives/top-pool-author/src/client_error.rs b/tee-worker/bitacross/core-primitives/top-pool-author/src/client_error.rs deleted file mode 100644 index badd278008..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool-author/src/client_error.rs +++ /dev/null @@ -1,183 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Authoring RPC module client errors. - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; - -use derive_more::{Display, From}; -use jsonrpc_core as rpc_core; -use std::{boxed::Box, format}; - -/// Author RPC Result type. -pub type Result = core::result::Result; - -/// Author RPC errors. -#[derive(Debug, Display, From)] -pub enum Error { - /// Client error. - #[display(fmt = "Client error: {}", _0)] - #[from(ignore)] - Client(Box), - /// TrustedOperation pool error, - #[display(fmt = "TrustedOperation pool error: {}", _0)] - Pool(itp_top_pool::error::Error), - /// Verification error - #[display(fmt = "Extrinsic verification error")] - #[from(ignore)] - Verification, - /// Incorrect extrinsic format. - #[display(fmt = "Invalid trusted call format")] - BadFormat, - // Incorrect enciphered trusted call format. - #[display(fmt = "Invalid enciphered trusted call format")] - BadFormatDecipher, - /// Incorrect seed phrase. - #[display(fmt = "Invalid seed phrase/SURI")] - BadSeedPhrase, - /// Key type ID has an unknown format. - #[display(fmt = "Invalid key type ID format (should be of length four)")] - BadKeyType, - /// Key type ID has some unsupported crypto. - #[display(fmt = "The crypto of key type ID is unknown")] - UnsupportedKeyType, - /// Some random issue with the key store. Shouldn't happen. - #[display(fmt = "The key store is unavailable")] - KeyStoreUnavailable, - /// Invalid session keys encoding. - #[display(fmt = "Session keys are not encoded correctly")] - InvalidSessionKeys, - /// Shard does not exist. - #[display(fmt = "Shard does not exist")] - InvalidShard, - /// Unsupported trusted operation (in case we allow only certain types of operations, using filters) - #[display(fmt = "Unsupported operation type")] - UnsupportedOperation, -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Error::Client(ref err) => Some(&**err), - //Error::Pool(ref err) => Some(err), - //Error::Verification(ref err) => Some(&**err), - _ => None, - } - } -} - -/// Base code for all authorship errors. -const BASE_ERROR: i64 = 1000; -/// Extrinsic has an invalid format. -const BAD_FORMAT: i64 = BASE_ERROR + 1; -/// Error during operation verification in runtime. -const VERIFICATION_ERROR: i64 = BASE_ERROR + 2; - -/// Pool rejected the operation as invalid -const POOL_INVALID_TX: i64 = BASE_ERROR + 10; -/// Cannot determine operation validity. -const POOL_UNKNOWN_VALIDITY: i64 = POOL_INVALID_TX + 1; -/// The operation is temporarily banned. -const POOL_TEMPORARILY_BANNED: i64 = POOL_INVALID_TX + 2; -/// The operation is already in the pool -const POOL_ALREADY_IMPORTED: i64 = POOL_INVALID_TX + 3; -/// TrustedOperation has too low priority to replace existing one in the pool. -const POOL_TOO_LOW_PRIORITY: i64 = POOL_INVALID_TX + 4; -/// Including this operation would cause a dependency cycle. -const POOL_CYCLE_DETECTED: i64 = POOL_INVALID_TX + 5; -/// The operation was not included to the pool because of the limits. -const POOL_IMMEDIATELY_DROPPED: i64 = POOL_INVALID_TX + 6; -/// The key type crypto is not known. -const UNSUPPORTED_KEY_TYPE: i64 = POOL_INVALID_TX + 7; - -impl From for rpc_core::Error { - fn from(e: Error) -> Self { - use itp_top_pool::error::Error as PoolError; - - match e { - Error::BadFormat => rpc_core::Error { - code: rpc_core::ErrorCode::ServerError(BAD_FORMAT), - message: "Trusted operation has invalid format".into(), - data: None, - }, - Error::BadFormatDecipher => rpc_core::Error { - code: rpc_core::ErrorCode::ServerError(BAD_FORMAT), - message: "Trusted operation could not be deciphered".into(), - data: None, - }, - Error::Verification => rpc_core::Error { - code: rpc_core::ErrorCode::ServerError(VERIFICATION_ERROR), - message: "Verification Error".into(), - data: Some(format!("{:?}", e).into()), - }, - Error::InvalidShard => rpc_core::Error { - code: rpc_core::ErrorCode::ServerError(VERIFICATION_ERROR), - message: "Shard does not exist".into(), - data: Some(format!("{:?}", e).into()), - }, - Error::Pool(PoolError::InvalidTrustedOperation) => rpc_core::Error { - code: rpc_core::ErrorCode::ServerError(POOL_INVALID_TX), - message: "Invalid Trusted Operation".into(), - data: None, - }, - Error::Pool(PoolError::UnknownTrustedOperation) => rpc_core::Error { - code: rpc_core::ErrorCode::ServerError(POOL_UNKNOWN_VALIDITY), - message: "Unknown Trusted Operation Validity".into(), - data: None, - }, - Error::Pool(PoolError::TemporarilyBanned) => rpc_core::Error { - code: rpc_core::ErrorCode::ServerError(POOL_TEMPORARILY_BANNED), - message: "Trusted Operation is temporarily banned".into(), - data: None, - }, - Error::Pool(PoolError::AlreadyImported) => rpc_core::Error { - code: rpc_core::ErrorCode::ServerError(POOL_ALREADY_IMPORTED), - message: "Trusted Operation Already Imported".into(), - data: None, - }, - Error::Pool(PoolError::TooLowPriority(new)) => rpc_core::Error { - code: rpc_core::ErrorCode::ServerError(POOL_TOO_LOW_PRIORITY), - message: format!("Priority is too low: {}", new), - data: Some("The Trusted Operation has too low priority to replace another Trusted Operation already in the pool.".into()), - }, - Error::Pool(PoolError::CycleDetected) => rpc_core::Error { - code: rpc_core::ErrorCode::ServerError(POOL_CYCLE_DETECTED), - message: "Cycle Detected".into(), - data: None, - }, - Error::Pool(PoolError::ImmediatelyDropped) => rpc_core::Error { - code: rpc_core::ErrorCode::ServerError(POOL_IMMEDIATELY_DROPPED), - message: "Immediately Dropped".into(), - data: Some("The Trusted Operation couldn't enter the pool because of the limit".into()), - }, - Error::UnsupportedKeyType => rpc_core::Error { - code: rpc_core::ErrorCode::ServerError(UNSUPPORTED_KEY_TYPE), - message: "Unknown key type crypto" .into(), - data: Some( - "The crypto for the given key type is unknown, please add the public key to the \ - request to insert the key successfully.".into() - ), - }, - e => rpc_core::Error { - code: rpc_core::ErrorCode::InternalError, - message: "Unknown error occurred".into(), - data: Some(format!("{:?}", e).into()), - }, - } - } -} diff --git a/tee-worker/bitacross/core-primitives/top-pool-author/src/error.rs b/tee-worker/bitacross/core-primitives/top-pool-author/src/error.rs deleted file mode 100644 index 1c967a1b82..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool-author/src/error.rs +++ /dev/null @@ -1,111 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; - -use crate::client_error::Error as ClientError; -use core::pin::Pin; -use derive_more::{Display, From}; -use itp_top_pool::error::{Error as PoolError, IntoPoolError}; -use jsonrpc_core as rpc; -use std::{boxed::Box, error, format, string::String}; - -/// State RPC Result type. -pub type Result = core::result::Result; - -/// State RPC future Result type. -pub type FutureResult = - Pin> + Send>>; - -/// State RPC errors. -#[derive(Debug, Display, From)] -pub enum Error { - /// Client error. - #[display(fmt = "Client error: {}", _0)] - Client(Box), - /// Provided block range couldn't be resolved to a list of blocks. - #[display(fmt = "Cannot resolve a block range ['{:?}' ... '{:?}]. {}", from, to, details)] - InvalidBlockRange { - /// Beginning of the block range. - from: String, - /// End of the block range. - to: String, - /// Details of the error message. - details: String, - }, - /// Provided count exceeds maximum value. - #[display(fmt = "count exceeds maximum value. value: {}, max: {}", value, max)] - InvalidCount { - /// Provided value - value: u32, - /// Maximum allowed value - max: u32, - }, - - /// Wrapping of PoolError to RPC Error - PoolError(PoolError), - - /// Wrapping of ClientError to RPC Error - ClientError(ClientError), - - #[display(fmt = "Codec error: {}", _0)] - CodecError(codec::Error), -} - -impl error::Error for Error { - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self { - Error::Client(ref err) => Some(&**err), - _ => None, - } - } -} - -impl IntoPoolError for Error { - fn into_pool_error(self) -> std::result::Result { - match self { - Error::PoolError(e) => Ok(e), - e => Err(e), - } - } -} - -/// Base code for all state errors. -const BASE_ERROR: i64 = 4000; - -impl From for rpc::Error { - fn from(e: Error) -> Self { - match e { - Error::InvalidBlockRange { .. } => rpc::Error { - code: rpc::ErrorCode::ServerError(BASE_ERROR + 1), - message: format!("{}", e), - data: None, - }, - Error::InvalidCount { .. } => rpc::Error { - code: rpc::ErrorCode::ServerError(BASE_ERROR + 2), - message: format!("{}", e), - data: None, - }, - e => rpc::Error { - code: rpc::ErrorCode::ServerError(BASE_ERROR + 4), - message: format!("{}", e), - data: None, - }, - } - } -} diff --git a/tee-worker/bitacross/core-primitives/top-pool-author/src/lib.rs b/tee-worker/bitacross/core-primitives/top-pool-author/src/lib.rs deleted file mode 100644 index b0b84b992c..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool-author/src/lib.rs +++ /dev/null @@ -1,51 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#![feature(trait_alias)] -#![cfg_attr(feature = "mocks", feature(drain_filter))] -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -// re-export module to properly feature gate sgx and regular std environment -#[cfg(all(not(feature = "std"), feature = "sgx"))] -pub mod sgx_reexport_prelude { - pub use jsonrpc_core_sgx as jsonrpc_core; -} - -pub mod api; -pub mod author; -pub mod client_error; -pub mod error; -pub mod top_filter; -pub mod traits; - -#[cfg(test)] -mod author_tests; - -#[cfg(test)] -mod test_fixtures; - -#[cfg(any(test, feature = "test"))] -pub mod test_utils; - -#[cfg(feature = "mocks")] -pub mod mocks; diff --git a/tee-worker/bitacross/core-primitives/top-pool-author/src/mocks.rs b/tee-worker/bitacross/core-primitives/top-pool-author/src/mocks.rs deleted file mode 100644 index 061852e9f9..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool-author/src/mocks.rs +++ /dev/null @@ -1,306 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; -use core::fmt::Debug; - -#[cfg(feature = "sgx")] -use std::sync::SgxRwLock as RwLock; - -#[cfg(feature = "std")] -use std::sync::RwLock; - -use crate::{ - error::Result, - traits::{AuthorApi, OnBlockImported}, -}; -use codec::{Decode, Encode}; -use itp_stf_primitives::{ - traits::TrustedCallVerification, - types::{AccountId, TrustedOperation as StfTrustedOperation, TrustedOperationOrHash}, -}; -use itp_top_pool::primitives::{PoolFuture, PoolStatus}; -use itp_types::{DecryptableRequest, ShardIdentifier}; -use jsonrpc_core::{futures::future::ready, Error as RpcError}; -use lazy_static::lazy_static; -use sp_core::{blake2_256, H256}; -#[cfg(feature = "sgx")] -use std::sync::SgxMutex as Mutex; -use std::{ - boxed::Box, - collections::HashMap, - marker::PhantomData, - sync::{mpsc::Sender, Arc}, - vec, - vec::Vec, -}; - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -#[cfg(feature = "std")] -use std::sync::Mutex; - -lazy_static! { - pub static ref GLOBAL_MOCK_AUTHOR_API: Arc>>>> = - Arc::new(Mutex::new(None)); -} - -#[derive(Default)] -pub struct AuthorApiMock -where - TCS: PartialEq + Encode + Decode + Debug + Send + Sync + TrustedCallVerification, - G: PartialEq + Encode + Decode + Debug + Send + Sync, -{ - tops: RwLock>>>, - _phantom: PhantomData<(Hash, BlockHash, TCS, G)>, - pub remove_attempts: RwLock, -} - -impl AuthorApiMock -where - TCS: PartialEq + Encode + Decode + Debug + Send + Sync + TrustedCallVerification, - G: PartialEq + Encode + Decode + Debug + Send + Sync, -{ - fn remove_top( - &self, - bytes_or_hash: Vec>, - shard: ShardIdentifier, - _inblock: bool, - ) -> Result> { - let hashes = bytes_or_hash - .into_iter() - .map(|x| match x { - TrustedOperationOrHash::Hash(h) => h, - TrustedOperationOrHash::OperationEncoded(bytes) => { - let top: StfTrustedOperation = - StfTrustedOperation::::decode(&mut bytes.as_slice()).unwrap(); - top.hash() - }, - TrustedOperationOrHash::Operation(op) => op.hash(), - }) - .collect::>(); - - let mut tops_lock = self.tops.write().unwrap(); - - match tops_lock.get_mut(&shard) { - Some(tops_encoded) => { - let removed_tops = tops_encoded - .drain_filter(|t| hashes.contains(&blake2_256(t).into())) - .map(|t| blake2_256(&t).into()) - .collect::>(); - Ok(removed_tops) - }, - None => Ok(Vec::new()), - } - } -} - -impl AuthorApi for AuthorApiMock -where - TCS: PartialEq + Encode + Decode + Debug + Clone + TrustedCallVerification + Send + Sync, - G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, -{ - fn submit_top(&self, req: R) -> PoolFuture { - let mut write_lock = self.tops.write().unwrap(); - let extrinsics = write_lock.entry(req.shard()).or_default(); - extrinsics.push(req.payload().to_vec()); - Box::pin(ready(Ok(H256::default()))) - } - - fn hash_of(&self, xt: &StfTrustedOperation) -> H256 { - xt.hash() - } - - fn pending_tops(&self, shard: ShardIdentifier) -> Result>> { - let extrinsics = self.tops.read().unwrap().get(&shard).cloned(); - Ok(extrinsics.unwrap_or_default()) - } - - fn get_pending_getters(&self, shard: ShardIdentifier) -> Vec> { - self.tops - .read() - .unwrap() - .get(&shard) - .map(|encoded_operations| { - let mut trusted_getters: Vec> = Vec::new(); - for encoded_operation in encoded_operations { - if let Ok(g) = G::decode(&mut encoded_operation.as_slice()) { - trusted_getters.push(StfTrustedOperation::::get(g)); - } - } - trusted_getters - }) - .unwrap_or_default() - } - - fn get_pending_trusted_calls( - &self, - shard: ShardIdentifier, - ) -> Vec> { - self.tops - .read() - .unwrap() - .get(&shard) - .map(|encoded_operations| { - let mut trusted_operations: Vec> = Vec::new(); - for encoded_operation in encoded_operations { - if let Ok(o) = StfTrustedOperation::decode(&mut encoded_operation.as_slice()) { - trusted_operations.push(o); - } - } - trusted_operations - }) - .unwrap_or_default() - } - - fn get_status(&self, shard: ShardIdentifier) -> PoolStatus { - self.tops - .read() - .unwrap() - .get(&shard) - .map(|encoded_operations| { - let mut trusted_operations: Vec> = Vec::new(); - for encoded_operation in encoded_operations { - if let Ok(o) = StfTrustedOperation::decode(&mut encoded_operation.as_slice()) { - trusted_operations.push(o); - } - } - PoolStatus { - ready: trusted_operations.len(), - ready_bytes: trusted_operations.encode().len(), - future: 0, - future_bytes: 0, - } - }) - .unwrap_or_default() - } - - fn get_pending_trusted_calls_for( - &self, - shard: ShardIdentifier, - account: &AccountId, - ) -> Vec> { - self.tops - .read() - .unwrap() - .get(&shard) - .map(|encoded_operations| { - let mut trusted_operations: Vec> = Vec::new(); - for encoded_operation in encoded_operations { - if let Ok(top) = StfTrustedOperation::decode(&mut encoded_operation.as_slice()) - { - if top.signed_caller_account().as_ref() == Some(account) { - trusted_operations.push(top); - } - } - } - trusted_operations - }) - .unwrap_or_default() - } - - fn get_shards(&self) -> Vec { - self.tops.read().unwrap().keys().cloned().collect() - } - - fn list_handled_shards(&self) -> Vec { - //dummy - self.tops.read().unwrap().keys().cloned().collect() - } - - fn remove_calls_from_pool( - &self, - shard: ShardIdentifier, - executed_calls: Vec<(TrustedOperationOrHash, bool)>, - ) -> Vec> { - let mut remove_attempts_lock = self.remove_attempts.write().unwrap(); - *remove_attempts_lock += 1; - - let mut failed_to_remove = Vec::new(); - for (executed_call, inblock) in executed_calls { - if self.remove_top(vec![executed_call.clone()], shard, inblock).is_err() { - failed_to_remove.push(executed_call); - } - } - failed_to_remove - } - - fn watch_top(&self, request: R) -> PoolFuture { - // Note: The below implementation is specific for litentry/core/stf-task/receiver/test.rs - let sender_guard = GLOBAL_MOCK_AUTHOR_API.lock().unwrap(); - let sender = &*sender_guard; - sender - .as_ref() - .expect("Not yet initialized") - .send(request.payload().to_vec()) - .unwrap(); - Box::pin(ready(Ok([0u8; 32].into()))) - } - - fn update_connection_state(&self, _updates: Vec<(H256, (Vec, bool))>) {} - - fn swap_rpc_connection_hash(&self, _old_hash: H256, _new_hash: H256) {} -} - -impl OnBlockImported for AuthorApiMock -where - TCS: PartialEq + Encode + Decode + Debug + Send + Sync + TrustedCallVerification, - G: PartialEq + Encode + Decode + Debug + Send + Sync, -{ - type Hash = H256; - - fn on_block_imported(&self, _hashes: &[Self::Hash], _block_hash: H256) {} -} - -#[cfg(test)] -mod tests { - - use super::*; - use crate::test_fixtures::shard_id; - use codec::Encode; - use futures::executor::block_on; - use itp_test::mock::stf_mock::{ - mock_top_indirect_trusted_call_signed, GetterMock, TrustedCallSignedMock, - }; - use itp_types::RsaRequest; - use std::vec; - - #[test] - fn submitted_tops_can_be_removed_again() { - let author = AuthorApiMock::::default(); - let shard = shard_id(); - let trusted_operation = mock_top_indirect_trusted_call_signed(); - - let _ = block_on(author.submit_top(RsaRequest::new(shard, trusted_operation.encode()))) - .unwrap(); - - assert_eq!(1, author.pending_tops(shard).unwrap().len()); - assert_eq!(1, author.get_pending_trusted_calls(shard).len()); - assert_eq!(0, author.get_pending_getters(shard).len()); - - let trusted_operation_or_hash = - TrustedOperationOrHash::::from_top( - trusted_operation.clone(), - ); - let removed_tops = author.remove_top(vec![trusted_operation_or_hash], shard, true).unwrap(); - - assert_eq!(1, removed_tops.len()); - assert!(author.tops.read().unwrap().get(&shard).unwrap().is_empty()); - } -} diff --git a/tee-worker/bitacross/core-primitives/top-pool-author/src/test_fixtures.rs b/tee-worker/bitacross/core-primitives/top-pool-author/src/test_fixtures.rs deleted file mode 100644 index d5c83341d5..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool-author/src/test_fixtures.rs +++ /dev/null @@ -1,26 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use codec::Encode; -use itp_stf_primitives::types::ShardIdentifier; - -use sp_runtime::traits::{BlakeTwo256, Hash}; -use std::vec; - -pub(crate) fn shard_id() -> ShardIdentifier { - BlakeTwo256::hash(vec![1u8, 2u8, 3u8].as_slice().encode().as_slice()) -} diff --git a/tee-worker/bitacross/core-primitives/top-pool-author/src/test_utils.rs b/tee-worker/bitacross/core-primitives/top-pool-author/src/test_utils.rs deleted file mode 100644 index 20f8bd0dd7..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool-author/src/test_utils.rs +++ /dev/null @@ -1,49 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; - -use crate::traits::AuthorApi; -use codec::Encode; -use itp_sgx_crypto::ShieldingCryptoEncrypt; -use itp_stf_primitives::types::{ShardIdentifier, TrustedOperation as StfTrustedOperation}; -use itp_types::RsaRequest; -use jsonrpc_core::futures::executor; -use sp_core::H256; -use std::fmt::Debug; - -/// Test utility function to submit a trusted operation on an RPC author -pub fn submit_operation_to_top_pool( - author: &R, - top: &StfTrustedOperation, - shielding_key: &S, - shard: ShardIdentifier, -) -> Result<(H256, RsaRequest), jsonrpc_core::Error> -where - R: AuthorApi, - S: ShieldingCryptoEncrypt, - S::Error: Debug, - TCS: PartialEq + Encode + Debug + Send + Sync, - G: PartialEq + Encode + Debug + Send + Sync, -{ - let top_encrypted = shielding_key.encrypt(&top.encode()).unwrap(); - let submit_future = - async { author.watch_top(RsaRequest::new(shard, top_encrypted.clone())).await }; - let hash = executor::block_on(submit_future)?; - Ok((hash, RsaRequest::new(shard, top_encrypted))) -} diff --git a/tee-worker/bitacross/core-primitives/top-pool-author/src/top_filter.rs b/tee-worker/bitacross/core-primitives/top-pool-author/src/top_filter.rs deleted file mode 100644 index 25b3574870..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool-author/src/top_filter.rs +++ /dev/null @@ -1,320 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use codec::Encode; -use core::{fmt::Debug, marker::PhantomData}; -use itp_stf_primitives::types::TrustedOperation as StfTrustedOperation; - -/// Trait for filtering values -/// -/// Returns `Some` if a value should be included and `None` if discarded -pub trait Filter { - type Value; - - fn filter(&self, value: &Self::Value) -> bool; -} - -/// Filter for calls only (no getters). -pub struct CallsOnlyFilter { - _phantom: PhantomData<(TCS, G)>, -} - -impl CallsOnlyFilter { - pub fn new() -> Self { - Self { _phantom: Default::default() } - } -} - -impl Default for CallsOnlyFilter { - fn default() -> Self { - Self::new() - } -} - -impl Filter for CallsOnlyFilter -where - TCS: PartialEq + Encode + Debug, - G: PartialEq + Encode + Debug, -{ - type Value = StfTrustedOperation; - - fn filter(&self, value: &Self::Value) -> bool { - matches!(value, Self::Value::direct_call(_)) - || matches!(value, Self::Value::indirect_call(_)) - } -} - -/// Filter for direct calls only. -pub struct DirectCallsOnlyFilter { - _phantom: PhantomData<(TCS, G)>, -} - -impl DirectCallsOnlyFilter { - pub fn new() -> Self { - Self { _phantom: Default::default() } - } -} - -impl Default for DirectCallsOnlyFilter { - fn default() -> Self { - Self::new() - } -} - -impl Filter for DirectCallsOnlyFilter -where - TCS: PartialEq + Encode + Debug, - G: PartialEq + Encode + Debug, -{ - type Value = StfTrustedOperation; - - fn filter(&self, value: &Self::Value) -> bool { - matches!(value, Self::Value::direct_call(_)) - } -} - -/// Filter that allows all TOPs (i.e. not filter at all) -pub struct AllowAllTopsFilter { - _phantom: PhantomData<(TCS, G)>, -} - -impl AllowAllTopsFilter { - pub fn new() -> Self { - Self { _phantom: Default::default() } - } -} - -impl Default for AllowAllTopsFilter { - fn default() -> Self { - Self::new() - } -} - -impl Filter for AllowAllTopsFilter -where - TCS: PartialEq + Encode + Debug, - G: PartialEq + Encode + Debug, -{ - type Value = StfTrustedOperation; - - fn filter(&self, _value: &Self::Value) -> bool { - true - } -} - -/// Filter that allows only trusted getters -pub struct GettersOnlyFilter { - _phantom: PhantomData<(TCS, G)>, -} - -impl GettersOnlyFilter { - pub fn new() -> Self { - Self { _phantom: Default::default() } - } -} - -impl Default for GettersOnlyFilter { - fn default() -> Self { - Self::new() - } -} - -impl Filter for GettersOnlyFilter -where - TCS: PartialEq + Encode + Debug, - G: PartialEq + Encode + Debug, -{ - type Value = StfTrustedOperation; - - fn filter(&self, value: &Self::Value) -> bool { - matches!(value, Self::Value::get(_)) - } -} - -/// Filter for indirect calls only (no getters, no direct calls). -pub struct IndirectCallsOnlyFilter { - _phantom: PhantomData<(TCS, G)>, -} - -impl IndirectCallsOnlyFilter { - pub fn new() -> Self { - Self { _phantom: Default::default() } - } -} - -impl Default for IndirectCallsOnlyFilter { - fn default() -> Self { - Self::new() - } -} - -impl Filter for IndirectCallsOnlyFilter -where - TCS: PartialEq + Encode + Debug, - G: PartialEq + Encode + Debug, -{ - type Value = StfTrustedOperation; - - fn filter(&self, value: &Self::Value) -> bool { - matches!(value, Self::Value::indirect_call(_)) - } -} - -/// Filter that allows no direct calls, only indirect and getters. -pub struct NoDirectCallsFilter { - _phantom: PhantomData<(TCS, G)>, -} - -impl NoDirectCallsFilter { - pub fn new() -> Self { - Self { _phantom: Default::default() } - } -} - -impl Default for NoDirectCallsFilter { - fn default() -> Self { - Self::new() - } -} - -impl Filter for NoDirectCallsFilter -where - TCS: PartialEq + Encode + Debug, - G: PartialEq + Encode + Debug, -{ - type Value = StfTrustedOperation; - - fn filter(&self, value: &Self::Value) -> bool { - !matches!(value, Self::Value::direct_call(_)) - } -} - -/// Filter to deny all trusted operations. -pub struct DenyAllFilter { - _phantom: PhantomData<(TCS, G)>, -} - -impl DenyAllFilter { - pub fn new() -> Self { - Self { _phantom: Default::default() } - } -} - -impl Default for DenyAllFilter { - fn default() -> Self { - Self::new() - } -} - -impl Filter for DenyAllFilter -where - TCS: PartialEq + Encode + Debug, - G: PartialEq + Encode + Debug, -{ - type Value = StfTrustedOperation; - - fn filter(&self, _value: &Self::Value) -> bool { - false - } -} - -#[cfg(test)] -mod tests { - - use super::*; - - use itp_test::mock::stf_mock::{ - mock_top_direct_trusted_call_signed, mock_top_indirect_trusted_call_signed, - mock_top_trusted_getter_signed, - }; - - use std::string::{String, ToString}; - - #[test] - fn filter_returns_none_if_values_is_filtered_out() { - struct WorldFilter; - impl Filter for WorldFilter { - type Value = String; - - fn filter(&self, value: &Self::Value) -> bool { - if value.eq(&String::from("world")) { - return true - } - false - } - } - - let filter = WorldFilter; - - assert!(!filter.filter(&"hello".to_string())); - assert!(filter.filter(&"world".to_string())); - } - - #[test] - fn allow_all_tops_filter_works() { - let filter = AllowAllTopsFilter::new(); - - assert!(filter.filter(&mock_top_trusted_getter_signed())); - assert!(filter.filter(&mock_top_indirect_trusted_call_signed())); - assert!(filter.filter(&mock_top_direct_trusted_call_signed())); - } - - #[test] - fn getters_only_filter_works() { - let filter = GettersOnlyFilter::new(); - - assert!(filter.filter(&mock_top_trusted_getter_signed())); - assert!(!filter.filter(&mock_top_indirect_trusted_call_signed())); - assert!(!filter.filter(&mock_top_direct_trusted_call_signed())); - } - - #[test] - fn no_direct_calls_filter_works() { - let filter = NoDirectCallsFilter::new(); - - assert!(!filter.filter(&mock_top_direct_trusted_call_signed())); - assert!(filter.filter(&mock_top_indirect_trusted_call_signed())); - assert!(filter.filter(&mock_top_trusted_getter_signed())); - } - - #[test] - fn indirect_calls_only_filter_works() { - let filter = IndirectCallsOnlyFilter::new(); - - assert!(!filter.filter(&mock_top_direct_trusted_call_signed())); - assert!(filter.filter(&mock_top_indirect_trusted_call_signed())); - assert!(!filter.filter(&mock_top_trusted_getter_signed())); - } - - #[test] - fn calls_only_filter_works() { - let filter = CallsOnlyFilter::new(); - - assert!(filter.filter(&mock_top_direct_trusted_call_signed())); - assert!(filter.filter(&mock_top_indirect_trusted_call_signed())); - assert!(!filter.filter(&mock_top_trusted_getter_signed())); - } - - #[test] - fn direct_calls_only_filter_works() { - let filter = DirectCallsOnlyFilter::new(); - - assert!(filter.filter(&mock_top_direct_trusted_call_signed())); - assert!(!filter.filter(&mock_top_indirect_trusted_call_signed())); - assert!(!filter.filter(&mock_top_trusted_getter_signed())); - } -} diff --git a/tee-worker/bitacross/core-primitives/top-pool-author/src/traits.rs b/tee-worker/bitacross/core-primitives/top-pool-author/src/traits.rs deleted file mode 100644 index 132245d404..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool-author/src/traits.rs +++ /dev/null @@ -1,102 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; -use codec::Encode; -use core::fmt::Debug; - -use crate::error::Result; -use itp_stf_primitives::types::{ - AccountId, TrustedOperation as StfTrustedOperation, TrustedOperationOrHash, -}; -use itp_top_pool::primitives::{PoolFuture, PoolStatus}; -use itp_types::{BlockHash as SidechainBlockHash, DecryptableRequest, ShardIdentifier, H256}; -use jsonrpc_core::Error as RpcError; -use std::vec::Vec; - -/// Trait alias for a full STF author API -pub trait FullAuthor< - TCS: PartialEq + Encode + Debug + Send + Sync + 'static, - G: PartialEq + Encode + Debug + Send + Sync + 'static, -> = AuthorApi + OnBlockImported + Send + Sync + 'static; - -/// Authoring RPC API -pub trait AuthorApi -where - TCS: PartialEq + Encode + Debug + Send + Sync, - G: PartialEq + Encode + Debug + Send + Sync, -{ - /// Submit encoded extrinsic for inclusion in block. - fn submit_top(&self, req: R) -> PoolFuture; - - /// Return hash of Trusted Operation - fn hash_of(&self, xt: &StfTrustedOperation) -> Hash; - - /// Returns all pending operations, potentially grouped by sender. - fn pending_tops(&self, shard: ShardIdentifier) -> Result>>; - - /// Returns all pending trusted getters. - fn get_pending_getters(&self, shard: ShardIdentifier) -> Vec>; - - /// Returns all pending trusted calls (in ready state). - fn get_pending_trusted_calls(&self, shard: ShardIdentifier) - -> Vec>; - - /// Returns pool status - fn get_status(&self, shard: ShardIdentifier) -> PoolStatus; - - /// Returns all pending trusted calls for a given `account` - fn get_pending_trusted_calls_for( - &self, - shard: ShardIdentifier, - account: &AccountId, - ) -> Vec>; - - /// returns all shards which are currently present in the tops in the pool - fn get_shards(&self) -> Vec; - - /// returns all shards which are handled by our worker - fn list_handled_shards(&self) -> Vec; - - /// Remove a collection of trusted operations from the pool. - /// Return operations that were not successfully removed. - fn remove_calls_from_pool( - &self, - shard: ShardIdentifier, - executed_calls: Vec<(TrustedOperationOrHash, bool)>, - ) -> Vec>; - - /// Submit a request to watch. - /// - /// See [`TrustedOperationStatus`](sp_transaction_pool::TrustedOperationStatus) for details on transaction - /// life cycle. - fn watch_top(&self, request: R) -> PoolFuture; - - /// Litentry: set the rpc response value - fn update_connection_state(&self, updates: Vec<(Hash, (Vec, bool))>); - - /// Litentry: swap the old hash with the new one in rpc connection registry - fn swap_rpc_connection_hash(&self, old_hash: Hash, new_hash: Hash); -} - -/// Trait to notify listeners/observer of a newly created block -pub trait OnBlockImported { - type Hash; - - fn on_block_imported(&self, hashes: &[Self::Hash], block_hash: SidechainBlockHash); -} diff --git a/tee-worker/bitacross/core-primitives/top-pool/Cargo.toml b/tee-worker/bitacross/core-primitives/top-pool/Cargo.toml deleted file mode 100644 index fda8d7cc4f..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool/Cargo.toml +++ /dev/null @@ -1,55 +0,0 @@ -[package] -name = "bc-itp-top-pool" -version = "0.1.0" -authors = ['Trust Computing GmbH ', 'Integritee AG '] -edition = "2021" - -[dependencies] -sgx_tstd = { workspace = true, features = ["net", "thread", "untrusted_time"], optional = true } - -itc-direct-rpc-server = { package = "bc-itc-direct-rpc-server", path = "../../core/direct-rpc-server", default-features = false } -itp-stf-primitives = { workspace = true } -itp-types = { workspace = true } - -jsonrpc-core_sgx = { workspace = true, optional = true } -linked-hash-map_sgx = { workspace = true, optional = true } - -jsonrpc-core = { workspace = true, optional = true } -linked-hash-map = { workspace = true, optional = true } - -byteorder = { workspace = true } -codec = { package = "parity-scale-codec", workspace = true } -derive_more = { workspace = true } -log = { workspace = true } -sp-core = { workspace = true, features = ["full_crypto"] } -sp-runtime = { workspace = true } - -[dev-dependencies] -parity-util-mem = { workspace = true, features = ["primitive-types"] } -itp-test = { workspace = true } -serde = { workspace = true } -sp-application-crypto = { workspace = true } -litentry-primitives = { workspace = true } - -[features] -default = ["std"] -sgx = [ - "sgx_tstd", - "itc-direct-rpc-server/sgx", - "jsonrpc-core_sgx", - "linked-hash-map_sgx", - "litentry-primitives/sgx", -] -std = [ - "itc-direct-rpc-server/std", - "itp-types/std", - "jsonrpc-core", - "linked-hash-map", - "log/std", - "serde/std", - "sp-core/std", - "sp-runtime/std", - "sp-application-crypto/std", - "litentry-primitives/std", -] -mocks = [] diff --git a/tee-worker/bitacross/core-primitives/top-pool/src/base_pool.rs b/tee-worker/bitacross/core-primitives/top-pool/src/base_pool.rs deleted file mode 100644 index a6cb0628a0..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool/src/base_pool.rs +++ /dev/null @@ -1,1379 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2021 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! A basic version of the dependency graph. -//! -//! For a more full-featured pool, have a look at the `pool` module. - -pub extern crate alloc; -use crate::{ - error, - future::{FutureTrustedOperations, WaitingTrustedOperations}, - primitives::{InPoolOperation, PoolStatus, TrustedOperationSource as Source, TxHash}, - ready::ReadyOperations, -}; -use alloc::{fmt, sync::Arc, vec, vec::Vec}; -use core::iter; -use itp_stf_primitives::types::ShardIdentifier; -use log::{debug, trace, warn}; -use sp_core::hexdisplay::HexDisplay; -use sp_runtime::transaction_validity::{ - TransactionLongevity as Longevity, TransactionPriority as Priority, TransactionTag as Tag, -}; -use std::collections::HashSet; - -/// Successful import result. -#[derive(Debug, PartialEq, Eq)] -pub enum Imported { - /// TrustedOperation was successfully imported to Ready queue. - Ready { - /// Hash of operation that was successfully imported. - hash: TxHash, - /// operations that got promoted from the Future queue. - promoted: Vec, - /// operations that failed to be promoted from the Future queue and are now discarded. - failed: Vec, - /// operations removed from the Ready pool (replaced). - removed: Vec>>, - }, - /// TrustedOperation was successfully imported to Future queue. - Future { - /// Hash of operation that was successfully imported. - hash: TxHash, - }, -} - -impl Imported { - /// Returns the hash of imported operation. - pub fn hash(&self) -> &TxHash { - use self::Imported::*; - match *self { - Ready { ref hash, .. } => hash, - Future { ref hash, .. } => hash, - } - } -} - -/// Status of pruning the queue. -#[derive(Debug)] -pub struct PruneStatus { - /// A list of imports that satisfying the tag triggered. - pub promoted: Vec>, - /// A list of operations that failed to be promoted and now are discarded. - pub failed: Vec, - /// A list of operations that got pruned from the ready queue. - pub pruned: Vec>>, -} - -/// Immutable operation -#[derive(PartialEq, Eq, Clone)] -pub struct TrustedOperation { - /// Raw extrinsic representing that operation. - pub data: Extrinsic, - /// Number of bytes encoding of the operation requires. - pub bytes: usize, - /// TrustedOperation hash (unique) - pub hash: TxHash, - /// TrustedOperation priority (higher = better) - pub priority: Priority, - /// At which block the operation becomes invalid? - pub valid_till: Longevity, - /// Tags required by the operation. - pub requires: Vec, - /// Tags that this operation provides. - pub provides: Vec, - /// Should that operation be propagated. - pub propagate: bool, - /// Source of that operation. - pub source: Source, -} - -impl AsRef for TrustedOperation { - fn as_ref(&self) -> &Extrinsic { - &self.data - } -} - -impl InPoolOperation for TrustedOperation { - type TrustedOperation = Extrinsic; - - fn data(&self) -> &Extrinsic { - &self.data - } - - fn hash(&self) -> TxHash { - self.hash - } - - fn priority(&self) -> &Priority { - &self.priority - } - - fn longevity(&self) -> &Longevity { - &self.valid_till - } - - fn requires(&self) -> &[Tag] { - &self.requires - } - - fn provides(&self) -> &[Tag] { - &self.provides - } - - fn is_propagable(&self) -> bool { - self.propagate - } -} - -impl TrustedOperation { - /// Explicit operation clone. - /// - /// TrustedOperation should be cloned only if absolutely necessary && we want - /// every reason to be commented. That's why we `TrustedOperation` is not `Clone`, - /// but there's explicit `duplicate` method. - pub fn duplicate(&self) -> Self { - TrustedOperation { - data: self.data.clone(), - bytes: self.bytes, - hash: self.hash, - priority: self.priority, - source: self.source, - valid_till: self.valid_till, - requires: self.requires.clone(), - provides: self.provides.clone(), - propagate: self.propagate, - } - } -} - -impl fmt::Debug for TrustedOperation -where - Extrinsic: fmt::Debug, -{ - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fn print_tags(fmt: &mut fmt::Formatter, tags: &[Tag]) -> fmt::Result { - let mut it = tags.iter(); - if let Some(t) = it.next() { - write!(fmt, "{}", HexDisplay::from(t))?; - } - for t in it { - write!(fmt, ",{}", HexDisplay::from(t))?; - } - Ok(()) - } - - write!(fmt, "TrustedOperation {{ ")?; - write!(fmt, "hash: {:?}, ", &self.hash)?; - write!(fmt, "priority: {:?}, ", &self.priority)?; - write!(fmt, "valid_till: {:?}, ", &self.valid_till)?; - write!(fmt, "bytes: {:?}, ", &self.bytes)?; - write!(fmt, "propagate: {:?}, ", &self.propagate)?; - write!(fmt, "source: {:?}, ", &self.source)?; - write!(fmt, "requires: [")?; - print_tags(fmt, &self.requires)?; - write!(fmt, "], provides: [")?; - print_tags(fmt, &self.provides)?; - write!(fmt, "], ")?; - write!(fmt, "data: {:?}", &self.data)?; - write!(fmt, "}}")?; - Ok(()) - } -} - -/// Store last pruned tags for given number of invocations. -const RECENTLY_PRUNED_TAGS: usize = 2; - -/// TrustedOperation pool. -/// -/// Builds a dependency graph for all operations in the pool and returns -/// the ones that are currently ready to be executed. -/// -/// General note: -/// If function returns some operations it usually means that importing them -/// as-is for the second time will fail or produce unwanted results. -/// Most likely it is required to revalidate them and recompute set of -/// required tags. -#[derive(Debug)] -pub struct BasePool { - reject_future_operations: bool, - future: FutureTrustedOperations, - ready: ReadyOperations, - /// Store recently pruned tags (for last two invocations). - /// - /// This is used to make sure we don't accidentally put - /// operations to future in case they were just stuck in verification. - recently_pruned: [HashSet; RECENTLY_PRUNED_TAGS], - recently_pruned_index: usize, -} - -impl Default for BasePool { - fn default() -> Self { - Self::new(false) - } -} - -impl BasePool { - /// Create new pool given reject_future_operations flag. - pub fn new(reject_future_operations: bool) -> Self { - BasePool { - reject_future_operations, - future: Default::default(), - ready: Default::default(), - recently_pruned: Default::default(), - recently_pruned_index: 0, - } - } - - /// Temporary enables future operations, runs closure and then restores - /// `reject_future_operations` flag back to previous value. - /// - /// The closure accepts the mutable reference to the pool and original value - /// of the `reject_future_operations` flag. - pub(crate) fn with_futures_enabled( - &mut self, - closure: impl FnOnce(&mut Self, bool) -> T, - ) -> T { - let previous = self.reject_future_operations; - self.reject_future_operations = false; - let return_value = closure(self, previous); - self.reject_future_operations = previous; - return_value - } - - /// Returns if the operation for the given hash is already imported. - pub fn is_imported(&self, tx_hash: &TxHash, shard: ShardIdentifier) -> bool { - self.future.contains(tx_hash, shard) || self.ready.contains(tx_hash, shard) - } - - /// Imports operations to the pool. - /// - /// The pool consists of two parts: Future and Ready. - /// The former contains operations that require some tags that are not yet provided by - /// other operations in the pool. - /// The latter contains operations that have all the requirements satisfied and are - /// ready to be included in the block. - pub fn import( - &mut self, - tx: TrustedOperation, - shard: ShardIdentifier, - ) -> error::Result> { - if self.is_imported(&tx.hash, shard) { - return Err(error::Error::AlreadyImported) - } - - let tx = WaitingTrustedOperations::new( - tx, - self.ready.provided_tags(shard), - &self.recently_pruned, - ); - trace!(target: "txpool", "[{:?}] {:?}", tx.operation.hash, tx); - debug!( - target: "txpool", - "[{:?}] Importing to {}", - tx.operation.hash, - if tx.is_ready() { "ready" } else { "future" } - ); - - // If all tags are not satisfied import to future. - if !tx.is_ready() { - if self.reject_future_operations { - return Err(error::Error::RejectedFutureTrustedOperation) - } - - let hash = tx.operation.hash; - self.future.import(tx, shard); - return Ok(Imported::Future { hash }) - } - - self.import_to_ready(tx, shard) - } - - /// Imports operations to ready queue. - /// - /// NOTE the operation has to have all requirements satisfied. - fn import_to_ready( - &mut self, - tx: WaitingTrustedOperations, - shard: ShardIdentifier, - ) -> error::Result> { - let hash = tx.operation.hash; - let mut promoted = vec![]; - let mut failed = vec![]; - let mut removed = vec![]; - - let mut first = true; - let mut to_import = vec![tx]; - - while let Some(tx) = to_import.pop() { - // find operation in Future that it unlocks - to_import.append(&mut self.future.satisfy_tags(&tx.operation.provides, shard)); - - // import this operation - let current_hash = tx.operation.hash; - match self.ready.import(tx, shard) { - Ok(mut replaced) => { - if !first { - promoted.push(current_hash); - } - // The operations were removed from the ready pool. We might attempt to re-import them. - removed.append(&mut replaced); - }, - // operation failed to be imported. - Err(e) => - if first { - debug!(target: "txpool", "[{:?}] Error importing", current_hash,); - return Err(e) - } else { - failed.push(current_hash); - }, - } - first = false; - } - - // An edge case when importing operation caused - // some future operations to be imported and that - // future operations pushed out current operation. - // This means that there is a cycle and the operations should - // be moved back to future, since we can't resolve it. - if removed.iter().any(|tx| tx.hash == hash) { - // We still need to remove all operations that we promoted - // since they depend on each other and will never get to the best iterator. - self.ready.remove_subtree(&promoted, shard); - - debug!(target: "txpool", "[{:?}] Cycle detected, bailing.", hash); - return Err(error::Error::CycleDetected) - } - - Ok(Imported::Ready { hash, promoted, failed, removed }) - } - - /// Returns an iterator over ready operations in the pool. - pub fn ready(&self, shard: ShardIdentifier) -> impl Iterator>> { - self.ready.get(shard) - } - - /// Returns an iterator over all shards in the pool. - pub fn get_shards(&self) -> impl Iterator { - self.ready.get_shards() - } - - /// Returns an iterator over future operations in the pool. - pub fn futures(&self, shard: ShardIdentifier) -> impl Iterator> { - self.future.all(shard) - } - - /// Returns pool operations given list of hashes. - /// - /// Includes both ready and future pool. For every hash in the `hashes` - /// iterator an `Option` is produced (so the resulting `Vec` always have the same length). - pub fn by_hashes( - &self, - hashes: &[TxHash], - shard: ShardIdentifier, - ) -> Vec>>> { - let ready = self.ready.by_hashes(hashes, shard); - let future = self.future.by_hashes(hashes, shard); - - ready.into_iter().zip(future).map(|(a, b)| a.or(b)).collect() - } - - /// Returns pool operation by hash. - pub fn ready_by_hash( - &self, - hash: &TxHash, - shard: ShardIdentifier, - ) -> Option>> { - self.ready.by_hash(hash, shard) - } - - /// Makes sure that the operations in the queues stay within provided limits. - /// - /// Removes and returns worst operations from the queues and all operations that depend on them. - /// Technically the worst operation should be evaluated by computing the entire pending set. - /// We use a simplified approach to remove the operation that occupies the pool for the longest time. - pub fn enforce_limits( - &mut self, - ready: &Limit, - future: &Limit, - shard: ShardIdentifier, - ) -> Vec>> { - let mut removed = vec![]; - - while ready.is_exceeded(self.ready.len(shard), self.ready.bytes(shard)) { - // find the worst operation - let minimal = self.ready.fold( - |minimal, current| { - let operation = ¤t.operation; - match minimal { - None => Some(operation.clone()), - Some(ref tx) if tx.insertion_id > operation.insertion_id => - Some(operation.clone()), - other => other, - } - }, - shard, - ); - - if let Some(minimal) = minimal { - removed.append(&mut self.remove_subtree(&[minimal.operation.hash], shard)) - } else { - break - } - } - - while future.is_exceeded(self.future.len(shard), self.future.bytes(shard)) { - // find the worst operation - let minimal = self.future.fold( - |minimal, current| { - match minimal { - None => Some(current.clone()), - /*Some(ref tx) if tx.imported_at > current.imported_at => { - Some(current.clone()) - },*/ - other => other, - } - }, - shard, - ); - - if let Some(minimal) = minimal { - removed.append(&mut self.remove_subtree(&[minimal.operation.hash], shard)) - } else { - break - } - } - - removed - } - - /// Removes all operations represented by the hashes and all other operations - /// that depend on them. - /// - /// Returns a list of actually removed operations. - /// NOTE some operations might still be valid, but were just removed because - /// they were part of a chain, you may attempt to re-import them later. - /// NOTE If you want to remove ready operations that were already used - /// and you don't want them to be stored in the pool use `prune_tags` method. - pub fn remove_subtree( - &mut self, - hashes: &[TxHash], - shard: ShardIdentifier, - ) -> Vec>> { - let mut removed = self.ready.remove_subtree(hashes, shard); - removed.extend(self.future.remove(hashes, shard)); - removed - } - - /// Removes and returns all operations from the future queue. - pub fn clear_future(&mut self, shard: ShardIdentifier) -> Vec>> { - self.future.clear(shard) - } - - /// Prunes operations that provide given list of tags. - /// - /// This will cause all operations that provide these tags to be removed from the pool, - /// but unlike `remove_subtree`, dependent operations are not touched. - /// Additional operations from future queue might be promoted to ready if you satisfy tags - /// that the pool didn't previously know about. - pub fn prune_tags( - &mut self, - tags: impl IntoIterator, - shard: ShardIdentifier, - ) -> PruneStatus { - let mut to_import = vec![]; - let mut pruned = vec![]; - let recently_pruned = &mut self.recently_pruned[self.recently_pruned_index]; - self.recently_pruned_index = (self.recently_pruned_index + 1) % RECENTLY_PRUNED_TAGS; - recently_pruned.clear(); - - for tag in tags { - // make sure to promote any future operations that could be unlocked - to_import.append(&mut self.future.satisfy_tags(iter::once(&tag), shard)); - // and actually prune operations in ready queue - pruned.append(&mut self.ready.prune_tags(tag.clone(), shard)); - // store the tags for next submission - recently_pruned.insert(tag); - } - - let mut promoted = vec![]; - let mut failed = vec![]; - for tx in to_import { - let hash = tx.operation.hash; - match self.import_to_ready(tx, shard) { - Ok(res) => promoted.push(res), - Err(_e) => { - warn!(target: "txpool", "[{:?}] Failed to promote during pruning", hash); - failed.push(hash) - }, - } - } - - PruneStatus { promoted, failed, pruned } - } - - /// Get pool status. - pub fn status(&self, shard: ShardIdentifier) -> PoolStatus { - PoolStatus { - ready: self.ready.len(shard), - ready_bytes: self.ready.bytes(shard), - future: self.future.len(shard), - future_bytes: self.future.bytes(shard), - } - } -} - -/// Queue limits -#[derive(Debug, Clone)] -pub struct Limit { - /// Maximal number of operations in the queue. - pub count: usize, - /// Maximal size of encodings of all operations in the queue. - pub total_bytes: usize, -} - -impl Limit { - /// Returns true if any of the provided values exceeds the limit. - pub fn is_exceeded(&self, count: usize, bytes: usize) -> bool { - self.count < count || self.total_bytes < bytes - } -} - -#[cfg(test)] -pub mod tests { - - use super::*; - use alloc::borrow::ToOwned; - use itp_types::H256; - - fn hash(index: u8) -> H256 { - [index; 32].into() - } - - fn test_pool() -> BasePool> { - BasePool::default() - } - - #[test] - pub fn test_should_import_transaction_to_ready() { - // given - let mut pool = test_pool(); - let shard = ShardIdentifier::default(); - - // when - pool.import( - TrustedOperation { - data: vec![1u8], - bytes: 1, - hash: hash(1), - priority: 5u64, - valid_till: 64u64, - requires: vec![], - provides: vec![vec![1]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - - // then - assert_eq!(pool.ready(shard).count(), 1); - assert_eq!(pool.ready.len(shard), 1); - } - - #[test] - pub fn test_should_not_import_same_transaction_twice() { - // given - let mut pool = test_pool(); - let shard = ShardIdentifier::default(); - - // when - pool.import( - TrustedOperation { - data: vec![1u8], - bytes: 1, - hash: hash(1), - priority: 5u64, - valid_till: 64u64, - requires: vec![], - provides: vec![vec![1]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - pool.import( - TrustedOperation { - data: vec![1u8], - bytes: 1, - hash: hash(1), - priority: 5u64, - valid_till: 64u64, - requires: vec![], - provides: vec![vec![1]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap_err(); - - // then - assert_eq!(pool.ready(shard).count(), 1); - assert_eq!(pool.ready.len(shard), 1); - } - - #[test] - pub fn test_should_import_transaction_to_future_and_promote_it_later() { - // given - let mut pool = test_pool(); - let shard = ShardIdentifier::default(); - - // when - pool.import( - TrustedOperation { - data: vec![1u8], - bytes: 1, - hash: hash(1), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![0]], - provides: vec![vec![1]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - assert_eq!(pool.ready(shard).count(), 0); - assert_eq!(pool.ready.len(shard), 0); - pool.import( - TrustedOperation { - data: vec![2u8], - bytes: 1, - hash: hash(2), - priority: 5u64, - valid_till: 64u64, - requires: vec![], - provides: vec![vec![0]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - - // then - assert_eq!(pool.ready(shard).count(), 2); - assert_eq!(pool.ready.len(shard), 2); - } - - #[test] - pub fn test_should_promote_a_subgraph() { - // given - let mut pool = test_pool(); - let shard = ShardIdentifier::default(); - - // when - pool.import( - TrustedOperation { - data: vec![1u8], - bytes: 1, - hash: hash(1), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![0]], - provides: vec![vec![1]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - pool.import( - TrustedOperation { - data: vec![3u8], - bytes: 1, - hash: hash(3), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![2]], - provides: vec![], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - pool.import( - TrustedOperation { - data: vec![2u8], - bytes: 1, - hash: hash(2), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![1]], - provides: vec![vec![3], vec![2]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - pool.import( - TrustedOperation { - data: vec![4u8], - bytes: 1, - hash: hash(4), - priority: 1_000u64, - valid_till: 64u64, - requires: vec![vec![3], vec![4]], - provides: vec![], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - assert_eq!(pool.ready(shard).count(), 0); - assert_eq!(pool.ready.len(shard), 0); - - let res = pool - .import( - TrustedOperation { - data: vec![5u8], - bytes: 1, - hash: hash(5), - priority: 5u64, - valid_till: 64u64, - requires: vec![], - provides: vec![vec![0], vec![4]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - - // then - let mut it = pool.ready(shard).into_iter().map(|tx| tx.data[0]); - - assert_eq!(it.next(), Some(5)); - assert_eq!(it.next(), Some(1)); - assert_eq!(it.next(), Some(2)); - assert_eq!(it.next(), Some(4)); - assert_eq!(it.next(), Some(3)); - assert_eq!(it.next(), None); - assert_eq!( - res, - Imported::Ready { - hash: hash(5), - promoted: vec![hash(1), hash(2), hash(3), hash(4)], - failed: vec![], - removed: vec![] - } - ); - } - - #[test] - pub fn test_should_handle_a_cycle() { - // given - let shard = ShardIdentifier::default(); - let mut pool = test_pool(); - pool.import( - TrustedOperation { - data: vec![1u8], - bytes: 1, - hash: hash(1), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![0]], - provides: vec![vec![1]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - pool.import( - TrustedOperation { - data: vec![3u8], - bytes: 1, - hash: hash(3), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![1]], - provides: vec![vec![2]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - assert_eq!(pool.ready(shard).count(), 0); - assert_eq!(pool.ready.len(shard), 0); - - // when - pool.import( - TrustedOperation { - data: vec![2u8], - bytes: 1, - hash: hash(2), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![2]], - provides: vec![vec![0]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - - // then - { - let mut it = pool.ready(shard).into_iter().map(|tx| tx.data[0]); - assert_eq!(it.next(), None); - } - // all operations occupy the Future queue - it's fine - assert_eq!(pool.future.len(shard), 3); - - // let's close the cycle with one additional operation - let res = pool - .import( - TrustedOperation { - data: vec![4u8], - bytes: 1, - hash: hash(4), - priority: 50u64, - valid_till: 64u64, - requires: vec![], - provides: vec![vec![0]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - let mut it = pool.ready(shard).into_iter().map(|tx| tx.data[0]); - assert_eq!(it.next(), Some(4)); - assert_eq!(it.next(), Some(1)); - assert_eq!(it.next(), Some(3)); - assert_eq!(it.next(), None); - assert_eq!( - res, - Imported::Ready { - hash: hash(4), - promoted: vec![hash(1), hash(3)], - failed: vec![hash(2)], - removed: vec![] - } - ); - assert_eq!(pool.future.len(shard), 0); - } - - #[test] - pub fn test_should_handle_a_cycle_with_low_priority() { - // given - let mut pool = test_pool(); - let shard = ShardIdentifier::default(); - pool.import( - TrustedOperation { - data: vec![1u8], - bytes: 1, - hash: hash(1), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![0]], - provides: vec![vec![1]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - pool.import( - TrustedOperation { - data: vec![3u8], - bytes: 1, - hash: hash(3), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![1]], - provides: vec![vec![2]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - assert_eq!(pool.ready(shard).count(), 0); - assert_eq!(pool.ready.len(shard), 0); - - // when - pool.import( - TrustedOperation { - data: vec![2u8], - bytes: 1, - hash: hash(2), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![2]], - provides: vec![vec![0]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - - // then - { - let mut it = pool.ready(shard).into_iter().map(|tx| tx.data[0]); - assert_eq!(it.next(), None); - } - // all operations occupy the Future queue - it's fine - assert_eq!(pool.future.len(shard), 3); - - // let's close the cycle with one additional operation - let err = pool - .import( - TrustedOperation { - data: vec![4u8], - bytes: 1, - hash: hash(4), - priority: 1u64, // lower priority than Tx(2) - valid_till: 64u64, - requires: vec![], - provides: vec![vec![0]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap_err(); - let mut it = pool.ready(shard).into_iter().map(|tx| tx.data[0]); - assert_eq!(it.next(), None); - assert_eq!(pool.ready.len(shard), 0); - assert_eq!(pool.future.len(shard), 0); - if let error::Error::CycleDetected = err { - } else { - assert!(false, "Invalid error kind: {:?}", err); - } - } - - #[test] - pub fn test_can_track_heap_size() { - let mut pool = test_pool(); - let shard = ShardIdentifier::default(); - pool.import( - TrustedOperation { - data: vec![5u8; 1024], - bytes: 1, - hash: hash(5), - priority: 5u64, - valid_till: 64u64, - requires: vec![], - provides: vec![vec![0], vec![4]], - propagate: true, - source: Source::External, - }, - shard, - ) - .expect("import 1 should be ok"); - pool.import( - TrustedOperation { - data: vec![3u8; 1024], - bytes: 1, - hash: hash(7), - priority: 5u64, - valid_till: 64u64, - requires: vec![], - provides: vec![vec![2], vec![7]], - propagate: true, - source: Source::External, - }, - shard, - ) - .expect("import 2 should be ok"); - - //assert!(parity_util_mem::malloc_size(&pool) > 5000); - } - - #[test] - pub fn test_should_remove_invalid_transactions() { - // given - let shard = ShardIdentifier::default(); - let mut pool = test_pool(); - pool.import( - TrustedOperation { - data: vec![5u8], - bytes: 1, - hash: hash(5), - priority: 5u64, - valid_till: 64u64, - requires: vec![], - provides: vec![vec![0], vec![4]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - pool.import( - TrustedOperation { - data: vec![1u8], - bytes: 1, - hash: hash(1), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![0]], - provides: vec![vec![1]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - pool.import( - TrustedOperation { - data: vec![3u8], - bytes: 1, - hash: hash(3), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![2]], - provides: vec![], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - pool.import( - TrustedOperation { - data: vec![2u8], - bytes: 1, - hash: hash(2), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![1]], - provides: vec![vec![3], vec![2]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - pool.import( - TrustedOperation { - data: vec![4u8], - bytes: 1, - hash: hash(4), - priority: 1_000u64, - valid_till: 64u64, - requires: vec![vec![3], vec![4]], - provides: vec![], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - // future - pool.import( - TrustedOperation { - data: vec![6u8], - bytes: 1, - hash: hash(6), - priority: 1_000u64, - valid_till: 64u64, - requires: vec![vec![11]], - provides: vec![], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - assert_eq!(pool.ready(shard).count(), 5); - assert_eq!(pool.future.len(shard), 1); - - // when - pool.remove_subtree(&[hash(6), hash(1)], shard); - - // then - assert_eq!(pool.ready(shard).count(), 1); - assert_eq!(pool.future.len(shard), 0); - } - - #[test] - pub fn test_should_prune_ready_transactions() { - // given - let mut pool = test_pool(); - let shard = ShardIdentifier::default(); - // future (waiting for 0) - pool.import( - TrustedOperation { - data: vec![5u8], - bytes: 1, - hash: hash(5), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![0]], - provides: vec![vec![100]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - // ready - pool.import( - TrustedOperation { - data: vec![1u8], - bytes: 1, - hash: hash(1), - priority: 5u64, - valid_till: 64u64, - requires: vec![], - provides: vec![vec![1]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - pool.import( - TrustedOperation { - data: vec![2u8], - bytes: 1, - hash: hash(2), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![2]], - provides: vec![vec![3]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - pool.import( - TrustedOperation { - data: vec![3u8], - bytes: 1, - hash: hash(3), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![1]], - provides: vec![vec![2]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - pool.import( - TrustedOperation { - data: vec![4u8], - bytes: 1, - hash: hash(4), - priority: 1_000u64, - valid_till: 64u64, - requires: vec![vec![3], vec![2]], - provides: vec![vec![4]], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - - assert_eq!(pool.ready(shard).count(), 4); - assert_eq!(pool.future.len(shard), 1); - - // when - let result = pool.prune_tags(vec![vec![0], vec![2]], shard); - - // then - assert_eq!(result.pruned.len(), 2); - assert_eq!(result.failed.len(), 0); - assert_eq!( - result.promoted[0], - Imported::Ready { hash: hash(5), promoted: vec![], failed: vec![], removed: vec![] } - ); - assert_eq!(result.promoted.len(), 1); - assert_eq!(pool.future.len(shard), 0); - assert_eq!(pool.ready.len(shard), 3); - assert_eq!(pool.ready(shard).count(), 3); - } - - #[test] - pub fn test_transaction_debug() { - assert_eq!( - format!( - "{:?}", - TrustedOperation { - data: vec![4u8], - bytes: 1, - hash: hash(4), - priority: 1_000u64, - valid_till: 64u64, - requires: vec![vec![3], vec![2]], - provides: vec![vec![4]], - propagate: true, - source: Source::External, - } - ), - "TrustedOperation { \ -hash: 0x0404040404040404040404040404040404040404040404040404040404040404, priority: 1000, valid_till: 64, bytes: 1, propagate: true, \ -source: External, requires: [03,02], provides: [04], data: [4]}" - .to_owned() - ); - } - - #[test] - pub fn test_transaction_propagation() { - assert!(TrustedOperation { - data: vec![4u8], - bytes: 1, - hash: hash(4), - priority: 1_000u64, - valid_till: 64u64, - requires: vec![vec![3], vec![2]], - provides: vec![vec![4]], - propagate: true, - source: Source::External, - } - .is_propagable()); - - assert!(!TrustedOperation { - data: vec![4u8], - bytes: 1, - hash: hash(4), - priority: 1_000u64, - valid_till: 64u64, - requires: vec![vec![3], vec![2]], - provides: vec![vec![4]], - propagate: false, - source: Source::External, - } - .is_propagable()); - } - - #[test] - pub fn test_should_reject_future_transactions() { - // given - let mut pool = test_pool(); - let shard = ShardIdentifier::default(); - - // when - pool.reject_future_operations = true; - - // then - let err = pool.import( - TrustedOperation { - data: vec![5u8], - bytes: 1, - hash: hash(5), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![0]], - provides: vec![], - propagate: true, - source: Source::External, - }, - shard, - ); - - if let Err(error::Error::RejectedFutureTrustedOperation) = err { - } else { - assert!(false, "Invalid error kind: {:?}", err); - } - } - - #[test] - pub fn test_should_clear_future_queue() { - // given - let mut pool = test_pool(); - let shard = ShardIdentifier::default(); - - // when - pool.import( - TrustedOperation { - data: vec![5u8], - bytes: 1, - hash: hash(5), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![0]], - provides: vec![], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - - // then - assert_eq!(pool.future.len(shard), 1); - - // and then when - assert_eq!(pool.clear_future(shard).len(), 1); - - // then - assert_eq!(pool.future.len(shard), 0); - } - - #[test] - pub fn test_should_accept_future_transactions_when_explicitly_asked_to() { - // given - let mut pool = test_pool(); - pool.reject_future_operations = true; - let shard = ShardIdentifier::default(); - - // when - let flag_value = pool.with_futures_enabled(|pool, flag| { - pool.import( - TrustedOperation { - data: vec![5u8], - bytes: 1, - hash: hash(5), - priority: 5u64, - valid_till: 64u64, - requires: vec![vec![0]], - provides: vec![], - propagate: true, - source: Source::External, - }, - shard, - ) - .unwrap(); - - flag - }); - - // then - assert!(flag_value); - assert!(pool.reject_future_operations); - assert_eq!(pool.future.len(shard), 1); - } -} diff --git a/tee-worker/bitacross/core-primitives/top-pool/src/basic_pool.rs b/tee-worker/bitacross/core-primitives/top-pool/src/basic_pool.rs deleted file mode 100644 index d046fa72b6..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool/src/basic_pool.rs +++ /dev/null @@ -1,253 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pub extern crate alloc; - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use std::sync::SgxMutex as Mutex; - -#[cfg(feature = "std")] -use std::sync::Mutex; - -use crate::{ - base_pool::TrustedOperation, - error::IntoPoolError, - pool::{ChainApi, Options as PoolOptions, Pool}, - primitives::{ - ImportNotificationStream, PoolFuture, PoolStatus, TrustedOperationPool, - TrustedOperationSource, TxHash, - }, -}; -use alloc::{boxed::Box, string::String, sync::Arc}; -use codec::Encode; -use core::{marker::PhantomData, pin::Pin}; -use itc_direct_rpc_server::SendRpcResponse; -use itp_stf_primitives::{traits::PoolTransactionValidation, types::ShardIdentifier}; -use jsonrpc_core::futures::{ - channel::oneshot, - future::{ready, Future, FutureExt}, -}; -use sp_runtime::{ - generic::BlockId, - traits::{Block as BlockT, NumberFor, Zero}, -}; -use std::{collections::HashMap, vec, vec::Vec}; - -type BoxedReadyIterator = Box>> + Send>; - -type ReadyIteratorFor = BoxedReadyIterator; - -type PolledIterator = Pin> + Send>>; - -struct ReadyPoll { - updated_at: NumberFor, - pollers: Vec<(NumberFor, oneshot::Sender)>, -} - -impl Default for ReadyPoll { - fn default() -> Self { - Self { updated_at: NumberFor::::zero(), pollers: Default::default() } - } -} - -impl ReadyPoll { - #[allow(unused)] - fn trigger(&mut self, number: NumberFor, iterator_factory: impl Fn() -> T) { - self.updated_at = number; - - let mut idx = 0; - while idx < self.pollers.len() { - if self.pollers[idx].0 <= number { - let poller_sender = self.pollers.swap_remove(idx); - let _ = poller_sender.1.send(iterator_factory()); - } else { - idx += 1; - } - } - } - - fn add(&mut self, number: NumberFor) -> oneshot::Receiver { - let (sender, receiver) = oneshot::channel(); - self.pollers.push((number, sender)); - receiver - } - - fn updated_at(&self) -> NumberFor { - self.updated_at - } -} - -/// Basic implementation of operation pool that can be customized by providing PoolApi. -pub struct BasicPool -where - Block: BlockT, - PoolApi: ChainApi + 'static, - RpcResponse: SendRpcResponse, -{ - pool: Arc>, - _api: Arc, - ready_poll: Arc, Block>>>, - _phantom: PhantomData, -} - -impl BasicPool -where - Block: BlockT, - PoolApi: ChainApi + 'static, - RpcResponse: SendRpcResponse, - TOP: Clone + Encode + PoolTransactionValidation + core::fmt::Debug + Sync + Send, -{ - /// Create new basic operation pool with provided api and custom - /// revalidation type. - pub fn create( - options: PoolOptions, - pool_api: Arc, - rpc_response_sender: Arc, - //prometheus: Option<&PrometheusRegistry>, - //revalidation_type: RevalidationType, - //spawner: impl SpawnNamed, - ) -> Self - where - ::Error: IntoPoolError, - { - let pool = Arc::new(Pool::new(options, pool_api.clone(), rpc_response_sender)); - BasicPool { - _api: pool_api, - pool, - ready_poll: Default::default(), - _phantom: Default::default(), - } - } -} - -// FIXME: obey clippy -#[allow(clippy::type_complexity)] -impl TrustedOperationPool - for BasicPool -where - Block: BlockT, - PoolApi: ChainApi + 'static, - ::Error: IntoPoolError, - RpcResponse: SendRpcResponse + 'static, - TOP: Send + Sync + PoolTransactionValidation + core::fmt::Debug + Encode + Clone + 'static, -{ - type Block = PoolApi::Block; - type InPoolOperation = TrustedOperation; - type Error = PoolApi::Error; - - fn submit_at( - &self, - at: &BlockId, - source: TrustedOperationSource, - ops: Vec, - shard: ShardIdentifier, - ) -> PoolFuture>, Self::Error> { - let pool = self.pool.clone(); - let at = *at; - async move { pool.submit_at(&at, source, ops, shard).await }.boxed() - } - - fn submit_one( - &self, - at: &BlockId, - source: TrustedOperationSource, - op: TOP, - shard: ShardIdentifier, - ) -> PoolFuture { - let pool = self.pool.clone(); - let at = *at; - async move { pool.submit_one(&at, source, op, shard).await }.boxed() - } - - fn submit_and_watch( - &self, - at: &BlockId, - source: TrustedOperationSource, - xt: TOP, - shard: ShardIdentifier, - ) -> PoolFuture { - let at = *at; - let pool = self.pool.clone(); - async move { pool.submit_and_watch(&at, source, xt, shard).await }.boxed() - } - - fn ready_at(&self, at: NumberFor, shard: ShardIdentifier) -> PolledIterator { - if self.ready_poll.lock().unwrap().updated_at() >= at { - let iterator: ReadyIteratorFor = Box::new(self.pool.validated_pool().ready(shard)); - return Box::pin(ready(iterator)) - } - - Box::pin(self.ready_poll.lock().unwrap().add(at).map(|received| { - received.unwrap_or_else(|e| { - log::warn!("Error receiving pending set: {:?}", e); - Box::new(vec![].into_iter()) - }) - })) - } - - fn ready(&self, shard: ShardIdentifier) -> ReadyIteratorFor { - Box::new(self.pool.validated_pool().ready(shard)) - } - - fn shards(&self) -> Vec { - self.pool.validated_pool().shards() - } - - fn remove_invalid( - &self, - hashes: &[TxHash], - shard: ShardIdentifier, - inblock: bool, - ) -> Vec> { - self.pool.validated_pool().remove_invalid(hashes, shard, inblock) - } - - fn status(&self, shard: ShardIdentifier) -> PoolStatus { - self.pool.validated_pool().status(shard) - } - - fn import_notification_stream(&self) -> ImportNotificationStream { - self.pool.validated_pool().import_notification_stream() - } - - fn on_broadcasted(&self, propagations: HashMap>) { - self.pool.validated_pool().on_broadcasted(propagations) - } - - fn hash_of(&self, xt: &TOP) -> TxHash { - self.pool.hash_of(xt) - } - - fn ready_transaction( - &self, - hash: &TxHash, - shard: ShardIdentifier, - ) -> Option> { - self.pool.validated_pool().ready_by_hash(hash, shard) - } - - fn update_connection_state(&self, updates: Vec<(TxHash, (Vec, bool))>) { - self.pool.validated_pool().update_connection_state(updates); - } - - fn swap_rpc_connection_hash(&self, old_hash: TxHash, new_hash: TxHash) { - self.pool.validated_pool().swap_rpc_connection_hash(old_hash, new_hash); - } -} diff --git a/tee-worker/bitacross/core-primitives/top-pool/src/error.rs b/tee-worker/bitacross/core-primitives/top-pool/src/error.rs deleted file mode 100644 index 47029b30e1..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool/src/error.rs +++ /dev/null @@ -1,95 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! TrustedOperation pool errors. - -use derive_more::{Display, From}; -use sp_runtime::transaction_validity::TransactionPriority as Priority; -use std::string::String; - -/// TrustedOperation pool result. -pub type Result = std::result::Result; - -/// TrustedOperation pool error type. -#[derive(Debug, From, Display)] -#[allow(missing_docs)] -pub enum Error { - #[display(fmt = "Unknown trusted operation")] - UnknownTrustedOperation, - - #[display(fmt = "Invalid trusted operation")] - InvalidTrustedOperation, - - /// Incorrect extrinsic format. - - /// The operation validity returned no "provides" tag. - /// - /// Such operations are not accepted to the pool, since we use those tags - /// to define identity of operations (occupance of the same "slot"). - #[display(fmt = "Trusted Operation does not provide any tags, so the pool can't identify it")] - NoTagsProvided, - - #[display(fmt = "Trusted Operation temporarily Banned")] - TemporarilyBanned, - - #[display(fmt = "Already imported")] - AlreadyImported, - - #[display(fmt = "Too low priority")] - TooLowPriority(Priority), - - #[display(fmt = "TrustedOperation with cyclic dependency")] - CycleDetected, - - #[display(fmt = "TrustedOperation couldn't enter the pool because of the limit")] - ImmediatelyDropped, - - #[from(ignore)] - #[display(fmt = "Invalid Block")] - InvalidBlockId(String), - - #[display(fmt = "The pool is not accepting future trusted operations")] - RejectedFutureTrustedOperation, - - #[display(fmt = "Extrinsic verification error")] - #[from(ignore)] - Verification, - - #[display(fmt = "Failed to send result of trusted operation to RPC client")] - FailedToSendUpdateToRpcClient(String), - - #[display(fmt = "Failed to unlock pool (mutex)")] - UnlockError, -} - -/// TrustedOperation pool error conversion. -pub trait IntoPoolError: Send + Sized { - /// Try to extract original `Error` - /// - /// This implementation is optional and used only to - /// provide more descriptive error messages for end users - /// of RPC API. - fn into_pool_error(self) -> std::result::Result { - Err(self) - } -} - -impl IntoPoolError for Error { - fn into_pool_error(self) -> std::result::Result { - Ok(self) - } -} diff --git a/tee-worker/bitacross/core-primitives/top-pool/src/future.rs b/tee-worker/bitacross/core-primitives/top-pool/src/future.rs deleted file mode 100644 index 2ceb34827e..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool/src/future.rs +++ /dev/null @@ -1,316 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2021 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -pub extern crate alloc; - -use crate::{base_pool::TrustedOperation, primitives::TxHash}; -use alloc::{boxed::Box, fmt, sync::Arc, vec, vec::Vec}; - -use itp_stf_primitives::types::ShardIdentifier; -use sp_core::hexdisplay::HexDisplay; -use sp_runtime::transaction_validity::TransactionTag as Tag; -use std::{ - collections::{HashMap, HashSet}, - time::Instant, -}; - -/// TrustedOperation with partially satisfied dependencies. -pub struct WaitingTrustedOperations { - /// TrustedOperation details. - pub operation: Arc>, - /// Tags that are required and have not been satisfied yet by other operations in the pool. - pub missing_tags: HashSet, - /// Time of import to the Future Queue. - pub imported_at: Instant, -} - -impl fmt::Debug for WaitingTrustedOperations { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "WaitingTrustedOperations {{ ")?; - //write!(fmt, "imported_at: {:?}, ", self.imported_at)?; - write!(fmt, "operation: {:?}, ", self.operation)?; - write!(fmt, "missing_tags: {{")?; - let mut it = self.missing_tags.iter().map(HexDisplay::from); - if let Some(tag) = it.next() { - write!(fmt, "{}", tag)?; - } - for tag in it { - write!(fmt, ", {}", tag)?; - } - write!(fmt, " }}}}") - } -} - -impl Clone for WaitingTrustedOperations { - fn clone(&self) -> Self { - WaitingTrustedOperations { - operation: self.operation.clone(), - missing_tags: self.missing_tags.clone(), - imported_at: self.imported_at, - } - } -} - -impl WaitingTrustedOperations { - /// Creates a new `WaitingTrustedOperations`. - /// - /// Computes the set of missing tags based on the requirements and tags that - /// are provided by all operations in the ready queue. - pub fn new( - operation: TrustedOperation, - provided: Option<&HashMap>, - recently_pruned: &[HashSet], - ) -> Self { - let missing_tags = operation - .requires - .iter() - .filter(|tag| { - // is true if the tag is already satisfied either via operation in the pool - // or one that was recently included. - - let is_provided = recently_pruned.iter().any(|x| x.contains(&**tag)) - || match provided { - Some(tags) => tags.contains_key(&**tag), - None => false, - }; - - !is_provided - }) - .cloned() - .collect(); - - WaitingTrustedOperations { - operation: Arc::new(operation), - missing_tags, - imported_at: Instant::now(), - } - } - - /// Marks the tag as satisfied. - // FIXME: obey clippy - #[allow(clippy::ptr_arg)] - pub fn satisfy_tag(&mut self, tag: &Tag) { - self.missing_tags.remove(tag); - } - - /// Returns true if operation has all requirements satisfied. - pub fn is_ready(&self) -> bool { - self.missing_tags.is_empty() - } -} - -/// A pool of operations that are not yet ready to be included in the block. -/// -/// Contains operations that are still awaiting for some other operations that -/// could provide a tag that they require. -#[derive(Debug)] -pub struct FutureTrustedOperations { - /// tags that are not yet provided by any operation and we await for them - wanted_tags: HashMap>>, - /// Transactions waiting for a particular other operation - waiting: HashMap>>, -} - -impl Default for FutureTrustedOperations { - fn default() -> Self { - FutureTrustedOperations { wanted_tags: Default::default(), waiting: Default::default() } - } -} - -const WAITING_PROOF: &str = r"# -In import we always insert to `waiting` if we push to `wanted_tags`; -when removing from `waiting` we always clear `wanted_tags`; -every hash from `wanted_tags` is always present in `waiting`; -qed -#"; - -#[allow(clippy::len_without_is_empty)] -impl FutureTrustedOperations { - /// Import operation to Future queue. - /// - /// Only operations that don't have all their tags satisfied should occupy - /// the Future queue. - /// As soon as required tags are provided by some other operations that are ready - /// we should remove the operations from here and move them to the Ready queue. - pub fn import(&mut self, tx: WaitingTrustedOperations, shard: ShardIdentifier) { - assert!(!tx.is_ready(), "TrustedOperation is ready."); - if let Some(tx_pool_waiting) = self.waiting.get(&shard) { - assert!( - !tx_pool_waiting.contains_key(&tx.operation.hash), - "TrustedOperation is already imported." - ); - } - - let tx_pool_waiting_map = self.waiting.entry(shard).or_insert_with(HashMap::new); - let tx_pool_wanted_map = self.wanted_tags.entry(shard).or_insert_with(HashMap::new); - // Add all tags that are missing - for tag in &tx.missing_tags { - let entry = tx_pool_wanted_map.entry(tag.clone()).or_insert_with(HashSet::new); - entry.insert(tx.operation.hash); - } - - // Add the operation to a by-hash waiting map - tx_pool_waiting_map.insert(tx.operation.hash, tx); - } - - /// Returns true if given hash is part of the queue. - pub fn contains(&self, hash: &TxHash, shard: ShardIdentifier) -> bool { - if let Some(tx_pool_waiting) = self.waiting.get(&shard) { - return tx_pool_waiting.contains_key(hash) - } - false - } - - /// Returns a list of known operations - pub fn by_hashes( - &self, - hashes: &[TxHash], - shard: ShardIdentifier, - ) -> Vec>>> { - if let Some(tx_pool_waiting) = self.waiting.get(&shard) { - return hashes - .iter() - .map(|h| tx_pool_waiting.get(h).map(|x| x.operation.clone())) - .collect() - } - vec![] - } - - /// Satisfies provided tags in operations that are waiting for them. - /// - /// Returns (and removes) operations that became ready after their last tag got - /// satisfied and now we can remove them from Future and move to Ready queue. - pub fn satisfy_tags>( - &mut self, - tags: impl IntoIterator, - shard: ShardIdentifier, - ) -> Vec> { - let mut became_ready = vec![]; - - for tag in tags { - if let Some(tx_pool_wanted) = self.wanted_tags.get_mut(&shard) { - if let Some(hashes) = tx_pool_wanted.remove(tag.as_ref()) { - if let Some(tx_pool_waiting) = self.waiting.get_mut(&shard) { - for hash in hashes { - let is_ready = { - let tx = tx_pool_waiting.get_mut(&hash).expect(WAITING_PROOF); - tx.satisfy_tag(tag.as_ref()); - tx.is_ready() - }; - - if is_ready { - let tx = tx_pool_waiting.remove(&hash).expect(WAITING_PROOF); - became_ready.push(tx); - } - } - } - } - } - } - - became_ready - } - - /// Removes operations for given list of hashes. - /// - /// Returns a list of actually removed operations. - pub fn remove( - &mut self, - hashes: &[TxHash], - shard: ShardIdentifier, - ) -> Vec>> { - let mut removed = vec![]; - if let Some(tx_pool_waiting) = self.waiting.get_mut(&shard) { - if let Some(tx_pool_wanted) = self.wanted_tags.get_mut(&shard) { - for hash in hashes { - if let Some(waiting_tx) = tx_pool_waiting.remove(hash) { - // remove from wanted_tags as well - for tag in waiting_tx.missing_tags { - let remove = if let Some(wanted) = tx_pool_wanted.get_mut(&tag) { - wanted.remove(hash); - wanted.is_empty() - } else { - false - }; - if remove { - tx_pool_wanted.remove(&tag); - } - } - // add to result - removed.push(waiting_tx.operation) - } - } - } - } - removed - } - - /// Fold a list of future operations to compute a single value. - pub fn fold, &WaitingTrustedOperations) -> Option>( - &mut self, - f: F, - shard: ShardIdentifier, - ) -> Option { - if let Some(tx_pool) = self.waiting.get(&shard) { - return tx_pool.values().fold(None, f) - } - None - } - - /// Returns iterator over all future operations - pub fn all( - &self, - shard: ShardIdentifier, - ) -> Box> + '_> { - if let Some(tx_pool) = self.waiting.get(&shard) { - return Box::new(tx_pool.values().map(|waiting| &*waiting.operation)) - } - Box::new(core::iter::empty()) - } - - /// Removes and returns all future operations. - pub fn clear(&mut self, shard: ShardIdentifier) -> Vec>> { - if let Some(wanted_tx_pool) = self.wanted_tags.get_mut(&shard) { - wanted_tx_pool.clear(); - return self - .waiting - .get_mut(&shard) - .unwrap() - .drain() - .map(|(_, tx)| tx.operation) - .collect() - } - vec![] - } - - /// Returns number of operations in the Future queue. - pub fn len(&self, shard: ShardIdentifier) -> usize { - if let Some(tx_pool) = self.waiting.get(&shard) { - return tx_pool.len() - } - 0 - } - - /// Returns sum of encoding lengths of all operations in this queue. - pub fn bytes(&self, shard: ShardIdentifier) -> usize { - if let Some(tx_pool) = self.waiting.get(&shard) { - return tx_pool.values().fold(0, |acc, tx| acc + tx.operation.bytes) - } - 0 - } -} diff --git a/tee-worker/bitacross/core-primitives/top-pool/src/lib.rs b/tee-worker/bitacross/core-primitives/top-pool/src/lib.rs deleted file mode 100644 index fdd46ff9fe..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool/src/lib.rs +++ /dev/null @@ -1,47 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -// re-export module to properly feature gate sgx and regular std environment -#[cfg(all(not(feature = "std"), feature = "sgx"))] -pub mod sgx_reexport_prelude { - pub use jsonrpc_core_sgx as jsonrpc_core; - pub use linked_hash_map_sgx as linked_hash_map; -} - -pub mod base_pool; -pub mod basic_pool; -pub mod error; -pub mod future; -pub mod listener; -pub mod pool; -pub mod primitives; -pub mod ready; -pub mod rotator; -pub mod tracked_map; -pub mod validated_pool; -pub mod watcher; - -#[cfg(any(test, feature = "mocks"))] -pub mod mocks; diff --git a/tee-worker/bitacross/core-primitives/top-pool/src/listener.rs b/tee-worker/bitacross/core-primitives/top-pool/src/listener.rs deleted file mode 100644 index 0e069597cb..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool/src/listener.rs +++ /dev/null @@ -1,185 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; - -use crate::{primitives::TxHash, watcher::Watcher}; - -use itc_direct_rpc_server::SendRpcResponse; -use itp_types::BlockHash as SidechainBlockHash; -use linked_hash_map::LinkedHashMap; -use log::{debug, trace}; - -use std::{collections::HashMap, string::String, sync::Arc, vec, vec::Vec}; - -/// Extrinsic pool default listener. -#[derive(Default)] -pub struct Listener -where - R: SendRpcResponse, -{ - watchers: HashMap>, - finality_watchers: LinkedHashMap>, - rpc_response_sender: Arc, -} - -/// Maximum number of blocks awaiting finality at any time. -const MAX_FINALITY_WATCHERS: usize = 512; - -impl Listener -where - R: SendRpcResponse, -{ - pub fn new(rpc_response_sender: Arc) -> Self { - Listener { - watchers: Default::default(), - finality_watchers: Default::default(), - rpc_response_sender, - } - } - - fn fire(&mut self, hash: &TxHash, fun: F) - where - F: FnOnce(&mut Watcher), - { - let clean = if let Some(h) = self.watchers.get_mut(hash) { - fun(h); - h.is_done() - } else { - false - }; - - if clean { - self.watchers.remove(hash); - } - } - - /// Creates a new watcher for given verified extrinsic. - /// - /// The watcher can be used to subscribe to life-cycle events of that extrinsic. - pub fn create_watcher(&mut self, hash: TxHash) { - let new_watcher = Watcher::new_watcher(hash, self.rpc_response_sender.clone()); - self.watchers.insert(hash, new_watcher); - } - - /// Notify the listeners about extrinsic broadcast. - pub fn broadcasted(&mut self, hash: &TxHash, peers: Vec) { - trace!(target: "txpool", "[{:?}] Broadcasted", hash); - self.fire(hash, |watcher| watcher.broadcast(peers)); - } - - /// Notify listeners about top execution. - pub fn top_executed(&mut self, hash: &TxHash, response: &[u8], force_wait: bool) { - trace!(target: "txpool", "[{:?}] Top Executed", hash); - self.fire(hash, |watcher| watcher.top_executed(response, force_wait)); - } - - /// New operation was added to the ready pool or promoted from the future pool. - pub fn ready(&mut self, tx: &TxHash, old: Option<&TxHash>) { - trace!(target: "txpool", "[{:?}] Ready (replaced with {:?})", tx, old); - self.fire(tx, |watcher| watcher.ready()); - if let Some(old) = old { - self.fire(old, |watcher| watcher.usurped()); - } - } - - /// New operation was added to the future pool. - pub fn future(&mut self, tx: &TxHash) { - trace!(target: "txpool", "[{:?}] Future", tx); - self.fire(tx, |watcher| watcher.future()); - } - - /// TrustedOperation was dropped from the pool because of the limit. - pub fn dropped(&mut self, tx: &TxHash, by: Option<&TxHash>) { - trace!(target: "txpool", "[{:?}] Dropped (replaced with {:?})", tx, by); - self.fire(tx, |watcher| match by { - Some(_) => watcher.usurped(), - None => watcher.dropped(), - }) - } - - /// TrustedOperation was removed as invalid. - pub fn invalid(&mut self, tx: &TxHash) { - self.fire(tx, |watcher| watcher.invalid()); - } - - /// TrustedOperation was pruned from the pool. - #[allow(clippy::or_fun_call)] - pub fn pruned(&mut self, block_hash: SidechainBlockHash, tx: &TxHash) { - debug!(target: "txpool", "[{:?}] Pruned at {:?}", tx, block_hash); - self.fire(tx, |s| s.in_block(block_hash)); - self.finality_watchers.entry(block_hash).or_insert(vec![]).push(*tx); - - while self.finality_watchers.len() > MAX_FINALITY_WATCHERS { - if let Some((_hash, txs)) = self.finality_watchers.pop_front() { - for tx in txs { - self.fire(&tx, |s| s.finality_timeout()); - } - } - } - } - - /// TrustedOperation in block. - pub fn in_block(&mut self, tx: &TxHash, block_hash: SidechainBlockHash) { - self.fire(tx, |s| s.in_block(block_hash)); - } - - /// The block this operation was included in has been retracted. - pub fn retracted(&mut self, block_hash: SidechainBlockHash) { - if let Some(hashes) = self.finality_watchers.remove(&block_hash) { - for hash in hashes { - self.fire(&hash, |s| s.retracted()) - } - } - } - - /// Notify all watchers that operations have been finalized - pub fn finalized(&mut self, block_hash: SidechainBlockHash) { - if let Some(hashes) = self.finality_watchers.remove(&block_hash) { - for hash in hashes { - log::debug!(target: "txpool", "[{:?}] Sent finalization event (block {:?})", hash, block_hash); - self.fire(&hash, |s| s.finalized()) - } - } - } - - /// Litentry: set the rpc response value and force_wait flag for a given TrustedOperation `tx`. - pub fn update_connection_state( - &mut self, - tx: &TxHash, - encoded_value: Vec, - force_wait: bool, - ) { - self.fire(tx, |s| s.update_connection_state(encoded_value, force_wait)); - } - - /// Litentry: swap the old hash with the new one in rpc connection registry - pub fn swap_rpc_connection_hash(&mut self, old_hash: TxHash, new_hash: TxHash) { - log::debug!("Swapping connection {:?} to {:?}", &old_hash, &new_hash); - // It's possible that the old top (hash) is already removed from the pool when we - // request to swap hashes, in this case we just create one to facilitate the swap - if let Some(w) = self.watchers.get(&old_hash) { - w.swap_rpc_connection_hash(new_hash); - } else { - // do not insert it to `watchers`, will be deallocated if it goes out of scope - Watcher::new_watcher(old_hash, self.rpc_response_sender.clone()) - .swap_rpc_connection_hash(new_hash); - } - } -} diff --git a/tee-worker/bitacross/core-primitives/top-pool/src/mocks/mod.rs b/tee-worker/bitacross/core-primitives/top-pool/src/mocks/mod.rs deleted file mode 100644 index 81b1c65ebe..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool/src/mocks/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(any(test, feature = "mocks"))] -pub mod rpc_responder_mock; - -#[cfg(feature = "mocks")] -pub mod trusted_operation_pool_mock; diff --git a/tee-worker/bitacross/core-primitives/top-pool/src/mocks/rpc_responder_mock.rs b/tee-worker/bitacross/core-primitives/top-pool/src/mocks/rpc_responder_mock.rs deleted file mode 100644 index d4d99e18bf..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool/src/mocks/rpc_responder_mock.rs +++ /dev/null @@ -1,85 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use itc_direct_rpc_server::{DirectRpcResult, RpcHash, SendRpcResponse}; -use itp_types::{DirectRequestStatus, TrustedOperationStatus}; -use std::{marker::PhantomData, vec::Vec}; - -pub struct RpcResponderMock { - _hash: PhantomData, -} - -impl RpcResponderMock { - pub fn new() -> Self { - RpcResponderMock { _hash: PhantomData } - } -} - -impl Default for RpcResponderMock { - fn default() -> Self { - Self::new() - } -} - -impl SendRpcResponse for RpcResponderMock -where - Hash: RpcHash, -{ - type Hash = Hash; - - fn update_status_event( - &self, - _hash: Self::Hash, - _status_update: TrustedOperationStatus, - ) -> DirectRpcResult<()> { - Ok(()) - } - - fn send_state(&self, _hash: Self::Hash, _state_encoded: Vec) -> DirectRpcResult<()> { - Ok(()) - } - - fn send_state_with_status( - &self, - _hash: Self::Hash, - _state_encoded: Vec, - _status: DirectRequestStatus, - ) -> DirectRpcResult<()> { - Ok(()) - } - - fn update_force_wait(&self, _hash: Self::Hash, _force_wait: bool) -> DirectRpcResult<()> { - Ok(()) - } - - fn update_connection_state( - &self, - _hash: Self::Hash, - _encoded_value: Vec, - _force_wait: bool, - ) -> DirectRpcResult<()> { - Ok(()) - } - - fn swap_hash(&self, _old_hash: Self::Hash, _new_hash: Self::Hash) -> DirectRpcResult<()> { - Ok(()) - } - - fn is_force_wait(&self, _hash: Self::Hash) -> bool { - false - } -} diff --git a/tee-worker/bitacross/core-primitives/top-pool/src/mocks/trusted_operation_pool_mock.rs b/tee-worker/bitacross/core-primitives/top-pool/src/mocks/trusted_operation_pool_mock.rs deleted file mode 100644 index 1e515d612d..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool/src/mocks/trusted_operation_pool_mock.rs +++ /dev/null @@ -1,225 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; - -#[cfg(feature = "sgx")] -use std::sync::SgxRwLock as RwLock; - -#[cfg(feature = "std")] -use std::sync::RwLock; - -use crate::{ - base_pool::TrustedOperation, - error::Error, - primitives::{ - ImportNotificationStream, PoolFuture, PoolStatus, TrustedOperationPool, - TrustedOperationSource, TxHash, - }, -}; -use codec::Encode; -use core::{future::Future, pin::Pin}; - -use itp_types::{Block, ShardIdentifier, H256}; -use jsonrpc_core::futures::future::ready; -use sp_runtime::{ - generic::BlockId, - traits::{BlakeTwo256, Hash, NumberFor}, -}; -use std::{boxed::Box, collections::HashMap, string::String, sync::Arc, vec, vec::Vec}; - -/// Mock for the trusted operation pool -/// -/// To be used in unit tests -pub struct TrustedOperationPoolMock { - submitted_transactions: RwLock>>, -} - -/// Transaction payload -#[derive(Clone, PartialEq)] -pub struct TxPayload { - pub block_id: BlockId< as TrustedOperationPool>::Block>, - pub source: TrustedOperationSource, - pub xts: Vec, - pub shard: ShardIdentifier, -} - -impl Default for TrustedOperationPoolMock { - fn default() -> Self { - TrustedOperationPoolMock:: { submitted_transactions: RwLock::new(HashMap::new()) } - } -} - -impl TrustedOperationPoolMock { - pub fn get_last_submitted_transactions(&self) -> HashMap> { - let transactions = self.submitted_transactions.read().unwrap(); - transactions.clone() - } - - fn map_stf_top_to_tx(stf_top: &TOP) -> Arc> { - Arc::new(TrustedOperation:: { - data: stf_top.clone(), - bytes: 0, - hash: hash_of_top(stf_top), - priority: 0u64, - valid_till: 0u64, - requires: vec![], - provides: vec![], - propagate: false, - source: TrustedOperationSource::External, - }) - } -} - -impl TrustedOperationPool for TrustedOperationPoolMock -where - TOP: Encode + Clone + Sync + Send + 'static, -{ - type Block = Block; - type InPoolOperation = TrustedOperation; - type Error = Error; - - #[allow(clippy::type_complexity)] - fn submit_at( - &self, - at: &BlockId, - source: TrustedOperationSource, - xts: Vec, - shard: ShardIdentifier, - ) -> PoolFuture>, Self::Error> { - let mut transactions = self.submitted_transactions.write().unwrap(); - transactions.insert(shard, TxPayload { block_id: *at, source, xts: xts.clone(), shard }); - - let top_hashes: Vec> = - xts.iter().map(|top| Ok(hash_of_top(top))).collect(); - - Box::pin(ready(Ok(top_hashes))) - } - - fn submit_one( - &self, - at: &BlockId, - source: TrustedOperationSource, - xt: TOP, - shard: ShardIdentifier, - ) -> PoolFuture { - let mut transactions = self.submitted_transactions.write().unwrap(); - transactions - .insert(shard, TxPayload { block_id: *at, source, xts: vec![xt.clone()], shard }); - - let top_hash = hash_of_top(&xt); - - Box::pin(ready(Ok(top_hash))) - } - - fn submit_and_watch( - &self, - at: &BlockId, - source: TrustedOperationSource, - xt: TOP, - shard: ShardIdentifier, - ) -> PoolFuture { - self.submit_one(at, source, xt, shard) - } - - #[allow(clippy::type_complexity)] - fn ready_at( - &self, - _at: NumberFor, - _shard: ShardIdentifier, - ) -> Pin< - Box< - dyn Future> + Send>> + Send, - >, - > { - unimplemented!() - } - - #[allow(clippy::type_complexity)] - fn ready( - &self, - shard: ShardIdentifier, - ) -> Box> + Send> { - let transactions = self.submitted_transactions.read().unwrap(); - let ready_transactions = transactions - .get(&shard) - .map(|payload| payload.xts.iter().map(Self::map_stf_top_to_tx).collect()) - .unwrap_or_else(Vec::new); - Box::new(ready_transactions.into_iter()) - } - - fn shards(&self) -> Vec { - let transactions = self.submitted_transactions.read().unwrap(); - transactions.iter().map(|(shard, _)| *shard).collect() - } - - fn remove_invalid( - &self, - _hashes: &[TxHash], - _shard: ShardIdentifier, - _inblock: bool, - ) -> Vec> { - Vec::new() - } - - fn status(&self, shard: ShardIdentifier) -> PoolStatus { - let transactions = self.submitted_transactions.read().unwrap(); - transactions - .get(&shard) - .map(|payload| PoolStatus { - ready: payload.xts.len(), - ready_bytes: 0, - future: 0, - future_bytes: 0, - }) - .unwrap_or_else(default_pool_status) - } - - fn import_notification_stream(&self) -> ImportNotificationStream { - unimplemented!() - } - - fn on_broadcasted(&self, _propagations: HashMap>) { - unimplemented!() - } - - fn hash_of(&self, xt: &TOP) -> TxHash { - hash_of_top(xt) - } - - fn ready_transaction( - &self, - _hash: &TxHash, - _shard: ShardIdentifier, - ) -> Option> { - unimplemented!() - } - - fn update_connection_state(&self, _updates: Vec<(TxHash, (Vec, bool))>) {} - - fn swap_rpc_connection_hash(&self, _old_hash: TxHash, _new_hash: TxHash) {} -} - -fn default_pool_status() -> PoolStatus { - PoolStatus { ready: 0, ready_bytes: 0, future: 0, future_bytes: 0 } -} - -fn hash_of_top(top: &TOP) -> H256 { - top.using_encoded(|x| BlakeTwo256::hash(x)) -} diff --git a/tee-worker/bitacross/core-primitives/top-pool/src/pool.rs b/tee-worker/bitacross/core-primitives/top-pool/src/pool.rs deleted file mode 100644 index 42f43c645b..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool/src/pool.rs +++ /dev/null @@ -1,810 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; -use core::{fmt::Debug, marker::PhantomData}; - -use crate::{ - base_pool as base, error, - primitives::{TrustedOperationSource, TxHash}, - validated_pool::{ValidatedOperation, ValidatedPool}, -}; -use codec::Encode; -use core::matches; -use itc_direct_rpc_server::SendRpcResponse; -use itp_stf_primitives::{traits::PoolTransactionValidation, types::ShardIdentifier}; -use itp_types::BlockHash as SidechainBlockHash; -use jsonrpc_core::futures::{channel::mpsc::Receiver, future, Future}; -use sp_runtime::{ - generic::BlockId, - traits::{self, Block as BlockT, SaturatedConversion}, - transaction_validity::{TransactionTag as Tag, TransactionValidity, TransactionValidityError}, -}; -use std::{collections::HashMap, format, sync::Arc, time::Instant, vec::Vec}; - -/// Modification notification event stream type; -pub type EventStream = Receiver; - -/// Block hash type for a pool. -pub type BlockHash = <::Block as traits::Block>::Hash; -/// Extrinsic hash type for a pool. -pub type ExtrinsicHash = <::Block as traits::Block>::Hash; -/// Extrinsic type for a pool. -//pub type ExtrinsicFor = <::Block as traits::Block>::Extrinsic; -/// Block number type for the ChainApi -pub type NumberFor = traits::NumberFor<::Block>; -/// A type of operation stored in the pool -pub type TransactionFor = Arc>; -/// A type of validated operation stored in the pool. -pub type ValidatedOperationFor = ValidatedOperation::Error>; - -/// Concrete extrinsic validation and query logic. -pub trait ChainApi: Send + Sync { - /// Block type. - type Block: BlockT; - /// Error type. - type Error: From; - /// Validate operation future. - type ValidationFuture: Future> + Send + Unpin; - /// Body future (since block body might be remote) - type BodyFuture: Future, Self::Error>> + Unpin + Send + 'static; - - /// Verify extrinsic at given block. - fn validate_transaction( - &self, - source: TrustedOperationSource, - uxt: TOP, - shard: ShardIdentifier, - ) -> Self::ValidationFuture; - - /// Returns a block number given the block id. - fn block_id_to_number( - &self, - at: &BlockId, - ) -> Result>, Self::Error>; - - /// Returns a block hash given the block id. - fn block_id_to_hash( - &self, - at: &BlockId, - ) -> Result, Self::Error>; - - /// Returns hash and encoding length of the extrinsic. - fn hash_and_length(&self, uxt: &TOP) -> (TxHash, usize); - - /// Returns a block body given the block id. - fn block_body(&self, at: &BlockId) -> Self::BodyFuture; -} - -/// Pool configuration options. -#[derive(Debug, Clone)] -pub struct Options { - /// Ready queue limits. - pub ready: base::Limit, - /// Future queue limits. - pub future: base::Limit, - /// Reject future operations. - pub reject_future_operations: bool, -} - -impl Default for Options { - fn default() -> Self { - Options { - ready: base::Limit { count: 8192, total_bytes: 20 * 1024 * 1024 }, - future: base::Limit { count: 512, total_bytes: 1024 * 1024 }, - reject_future_operations: false, - } - } -} - -/// Should we check that the operation is banned -/// in the pool, before we verify it? -#[derive(Copy, Clone)] -enum CheckBannedBeforeVerify { - Yes, - No, -} - -/// Extrinsics pool that performs validation. -pub struct Pool -where - R: SendRpcResponse, -{ - validated_pool: Arc>, - _phantom: PhantomData, -} - -impl Pool -where - ::Error: error::IntoPoolError, - R: SendRpcResponse, - TOP: Encode + Clone + PoolTransactionValidation + core::fmt::Debug + Send + Sync, -{ - /// Create a new operation pool. - pub fn new(options: Options, api: Arc, rpc_response_sender: Arc) -> Self { - Pool { - validated_pool: Arc::new(ValidatedPool::new(options, api, rpc_response_sender)), - _phantom: Default::default(), - } - } - - /// Imports a bunch of unverified extrinsics to the pool - pub async fn submit_at( - &self, - at: &BlockId, - source: TrustedOperationSource, - xts: impl IntoIterator, - shard: ShardIdentifier, - ) -> Result>, B::Error> { - let xts = xts.into_iter().map(|xt| (source, xt)); - let validated_transactions = - self.verify(at, xts, CheckBannedBeforeVerify::Yes, shard).await?; - Ok(self.validated_pool.submit(validated_transactions.into_values(), shard)) - } - - /// Resubmit the given extrinsics to the pool. - /// - /// This does not check if a operation is banned, before we verify it again. - pub async fn resubmit_at( - &self, - at: &BlockId, - source: TrustedOperationSource, - xts: impl IntoIterator, - shard: ShardIdentifier, - ) -> Result>, B::Error> { - let xts = xts.into_iter().map(|xt| (source, xt)); - let validated_transactions = - self.verify(at, xts, CheckBannedBeforeVerify::No, shard).await?; - Ok(self.validated_pool.submit(validated_transactions.into_values(), shard)) - } - - /// Imports one unverified extrinsic to the pool - pub async fn submit_one( - &self, - at: &BlockId, - source: TrustedOperationSource, - xt: TOP, - shard: ShardIdentifier, - ) -> Result { - let res = self.submit_at(at, source, std::iter::once(xt), shard).await?.pop(); - res.expect("One extrinsic passed; one result returned; qed") - } - - /// Import a single extrinsic and starts to watch their progress in the pool. - pub async fn submit_and_watch( - &self, - at: &BlockId, - source: TrustedOperationSource, - xt: TOP, - shard: ShardIdentifier, - ) -> Result { - //TODO - //let block_number = self.resolve_block_number(at)?; - // dummy value: - let block_number = 0; - let (_, tx) = self - .verify_one(at, block_number, source, xt, CheckBannedBeforeVerify::Yes, shard) - .await; - self.validated_pool.submit_and_watch(tx, shard) - } - - /// Resubmit some operation that were validated elsewhere. - pub fn resubmit( - &self, - revalidated_transactions: HashMap>, - shard: ShardIdentifier, - ) { - let now = Instant::now(); - self.validated_pool.resubmit(revalidated_transactions, shard); - log::debug!(target: "txpool", - "Resubmitted. Took {} ms. Status: {:?}", - now.elapsed().as_millis(), - self.validated_pool.status(shard) - ); - } - - /// Prunes known ready operations. - /// - /// Used to clear the pool from operations that were part of recently imported block. - /// The main difference from the `prune` is that we do not revalidate any operations - /// and ignore unknown passed hashes. - pub fn prune_known( - &self, - at: &BlockId, - hashes: &[TxHash], - shard: ShardIdentifier, - ) -> Result<(), B::Error> { - // Get details of all extrinsics that are already in the pool - #[allow(clippy::filter_map_identity)] - // false positive. Filter map does filter because x is an option - let in_pool_tags = self - .validated_pool - .extrinsics_tags(hashes, shard) - .into_iter() - .filter_map(|x| x) - .flatten(); - - // Prune all operations that provide given tags - let prune_status = self.validated_pool.prune_tags(in_pool_tags, shard)?; - let pruned_transactions = - hashes.iter().cloned().chain(prune_status.pruned.iter().map(|tx| tx.hash)); - self.validated_pool.fire_pruned(at, pruned_transactions) - } - - /// Prunes ready operations. - /// - /// Used to clear the pool from operations that were part of recently imported block. - /// To perform pruning we need the tags that each extrinsic provides and to avoid calling - /// into runtime too often we first lookup all extrinsics that are in the pool and get - /// their provided tags from there. Otherwise we query the runtime at the `parent` block. - pub async fn prune( - &self, - at: &BlockId, - _parent: &BlockId, - extrinsics: &[TOP], - shard: ShardIdentifier, - ) -> Result<(), B::Error> { - log::debug!( - target: "txpool", - "Starting pruning of block {:?} (extrinsics: {})", - at, - extrinsics.len() - ); - // Get details of all extrinsics that are already in the pool - let in_pool_hashes = - extrinsics.iter().map(|extrinsic| self.hash_of(extrinsic)).collect::>(); - let in_pool_tags = self.validated_pool.extrinsics_tags(&in_pool_hashes, shard); - - // Zip the ones from the pool with the full list (we get pairs `(Extrinsic, Option>)`) - let all = extrinsics.iter().zip(in_pool_tags.into_iter()); - - let mut future_tags = Vec::new(); - for (extrinsic, in_pool_tags) in all { - match in_pool_tags { - // reuse the tags for extrinsics that were found in the pool - Some(tags) => future_tags.extend(tags), - // if it's not found in the pool query the runtime at parent block - // to get validity info and tags that the extrinsic provides. - None => { - let validity = self - .validated_pool - .api() - .validate_transaction( - TrustedOperationSource::InBlock, - extrinsic.clone(), - shard, - ) - .await; - - if let Ok(Ok(validity)) = validity { - future_tags.extend(validity.provides); - } - }, - } - } - - self.prune_tags(at, future_tags, in_pool_hashes, shard).await - } - - /// Prunes ready operations that provide given list of tags. - /// - /// Given tags are assumed to be always provided now, so all operations - /// in the Future Queue that require that particular tag (and have other - /// requirements satisfied) are promoted to Ready Queue. - /// - /// Moreover for each provided tag we remove operations in the pool that: - /// 1. Provide that tag directly - /// 2. Are a dependency of pruned operation. - /// - /// Returns operations that have been removed from the pool and must be reverified - /// before reinserting to the pool. - /// - /// By removing predecessor operations as well we might actually end up - /// pruning too much, so all removed operations are reverified against - /// the runtime (`validate_transaction`) to make sure they are invalid. - /// - /// However we avoid revalidating operations that are contained within - /// the second parameter of `known_imported_hashes`. These operations - /// (if pruned) are not revalidated and become temporarily banned to - /// prevent importing them in the (near) future. - pub async fn prune_tags( - &self, - at: &BlockId, - tags: impl IntoIterator, - known_imported_hashes: impl IntoIterator + Clone, - shard: ShardIdentifier, - ) -> Result<(), B::Error> { - log::debug!(target: "txpool", "Pruning at {:?}", at); - // Prune all operations that provide given tags - let prune_status = match self.validated_pool.prune_tags(tags, shard) { - Ok(prune_status) => prune_status, - Err(e) => return Err(e), - }; - - // Make sure that we don't revalidate extrinsics that were part of the recently - // imported block. This is especially important for UTXO-like chains cause the - // inputs are pruned so such operation would go to future again. - self.validated_pool - .ban(&Instant::now(), known_imported_hashes.clone().into_iter()); - - // Try to re-validate pruned operations since some of them might be still valid. - // note that `known_imported_hashes` will be rejected here due to temporary ban. - let pruned_hashes = prune_status.pruned.iter().map(|tx| tx.hash).collect::>(); - let pruned_transactions = - prune_status.pruned.into_iter().map(|tx| (tx.source, tx.data.clone())); - - let reverified_transactions = self - .verify(at, pruned_transactions, CheckBannedBeforeVerify::Yes, shard) - .await?; - - log::trace!(target: "txpool", "Pruning at {:?}. Resubmitting operations.", at); - // And finally - submit reverified operations back to the pool - - self.validated_pool.resubmit_pruned( - at, - known_imported_hashes, - pruned_hashes, - reverified_transactions.into_values().collect(), - shard, - ) - } - - /// Returns operation hash - pub fn hash_of(&self, xt: &TOP) -> TxHash { - self.validated_pool.api().hash_and_length(xt).0 - } - - /// Resolves block number by id. - fn _resolve_block_number(&self, at: &BlockId) -> Result, B::Error> { - self.validated_pool.api().block_id_to_number(at).and_then(|number| { - number.ok_or_else(|| error::Error::InvalidBlockId(format!("{:?}", at)).into()) - }) - } - - /// Returns future that validates a bunch of operations at given block. - async fn verify( - &self, - at: &BlockId, - xts: impl IntoIterator, - check: CheckBannedBeforeVerify, - shard: ShardIdentifier, - ) -> Result>, B::Error> { - //FIXME: Nicer verify - // we need a block number to compute tx validity - //let block_number = self.resolve_block_number(at)?; - // dummy blocknumber - //pub type NumberFor = traits::NumberFor<::Block>; - let block_number = 0; - - let res = future::join_all( - xts.into_iter() - .map(|(source, xt)| self.verify_one(at, block_number, source, xt, check, shard)), - ) - .await - .into_iter() - .collect::>(); - - Ok(res) - } - - /// Returns future that validates single operation at given block. - async fn verify_one( - &self, - _block_id: &BlockId, - //block_number: NumberFor, - block_number: i8, - source: TrustedOperationSource, - xt: TOP, - check: CheckBannedBeforeVerify, - shard: ShardIdentifier, - ) -> (TxHash, ValidatedOperationFor) { - let (hash, bytes) = self.validated_pool.api().hash_and_length(&xt); - - let ignore_banned = matches!(check, CheckBannedBeforeVerify::No); - if let Err(err) = self.validated_pool.check_is_known(&hash, ignore_banned, shard) { - return (hash, ValidatedOperation::Invalid(hash, err)) - } - - //FIXME: - // no runtime validation check for now. - let validation_result = - self.validated_pool.api().validate_transaction(source, xt.clone(), shard).await; - - let status = match validation_result { - Ok(status) => status, - Err(e) => return (hash, ValidatedOperation::Invalid(hash, e)), - }; - - let validity = match status { - Ok(validity) => - if validity.provides.is_empty() { - ValidatedOperation::Invalid(hash, error::Error::NoTagsProvided.into()) - } else { - ValidatedOperation::valid_at( - block_number.saturated_into::(), - hash, - source, - xt, - bytes, - validity, - ) - }, - Err(TransactionValidityError::Invalid(_e)) => - ValidatedOperation::Invalid(hash, error::Error::InvalidTrustedOperation.into()), - Err(TransactionValidityError::Unknown(_e)) => - ValidatedOperation::Unknown(hash, error::Error::UnknownTrustedOperation.into()), - }; - - (hash, validity) - } - - /// get a reference to the underlying validated pool. - pub fn validated_pool(&self) -> &ValidatedPool { - &self.validated_pool - } -} - -impl Clone for Pool -where - ::Error: error::IntoPoolError, - R: SendRpcResponse, -{ - fn clone(&self) -> Self { - Self { validated_pool: self.validated_pool.clone(), _phantom: Default::default() } - } -} - -#[cfg(test)] -pub mod tests { - use super::*; - use crate::{ - base_pool::Limit, mocks::rpc_responder_mock::RpcResponderMock, - primitives::from_low_u64_to_be_h256, - }; - use codec::{Decode, Encode}; - use itp_test::mock::stf_mock::{ - mock_top_direct_trusted_call_signed, mock_trusted_call_signed, TrustedOperationMock, - }; - use itp_types::Header; - use jsonrpc_core::{ - futures, - futures::{executor::block_on, future::ready}, - }; - use parity_util_mem::MallocSizeOf; - use serde::Serialize; - use sp_application_crypto::ed25519; - use sp_core::hash::H256; - use sp_runtime::traits::{BlakeTwo256, Extrinsic as ExtrinsicT, Hash, Verify}; - - #[derive(Clone, PartialEq, Eq, Encode, Decode, core::fmt::Debug, Serialize, MallocSizeOf)] - pub enum Extrinsic { - #[codec(index = 0)] - IncludeData(Vec), - #[codec(index = 1)] - StorageChange(Vec, Option>), - #[codec(index = 2)] - OffchainIndexSet(Vec, Vec), - #[codec(index = 3)] - OffchainIndexClear(Vec), - } - - impl ExtrinsicT for Extrinsic { - type Call = Extrinsic; - type SignaturePayload = (); - - fn is_signed(&self) -> Option { - if let Extrinsic::IncludeData(_) = *self { - Some(false) - } else { - Some(true) - } - } - - fn new( - call: Self::Call, - _signature_payload: Option, - ) -> Option { - Some(call) - } - } - - /// The signature type used by accounts/transactions. - pub type AccountSignature = ed25519::Signature; - /// An identifier for an account on this system. - pub type AccountId = ::Signer; - /// The hashing algorithm used. - pub type Hashing = BlakeTwo256; - /// The item of a block digest. - pub type DigestItem = sp_runtime::generic::DigestItem; - /// The digest of a block. - pub type Digest = sp_runtime::generic::Digest; - /// A test block. - pub type Block = sp_runtime::generic::Block; - /// Test RPC responder - pub type TestRpcResponder = RpcResponderMock; - - const SOURCE: TrustedOperationSource = TrustedOperationSource::External; - - #[derive(Clone, Debug, Default)] - struct TestApi {} - - impl ChainApi for TestApi { - type Block = tests::Block; - type Error = error::Error; - type ValidationFuture = futures::future::Ready>; - type BodyFuture = futures::future::Ready>>; - - /// Verify extrinsic at given block. - fn validate_transaction( - &self, - _source: TrustedOperationSource, - uxt: TOP, - _shard: ShardIdentifier, - ) -> Self::ValidationFuture { - let operation = uxt.validate(); - ready(Ok(operation)) - } - - /// Returns a block number given the block id. - fn block_id_to_number( - &self, - at: &BlockId, - ) -> Result>, Self::Error> { - Ok(match at { - BlockId::Number(num) => Some(*num), - BlockId::Hash(_) => None, - }) - } - - /// Returns a block hash given the block id. - fn block_id_to_hash( - &self, - at: &BlockId, - ) -> Result, Self::Error> { - Ok(match at { - BlockId::Number(num) => Some(from_low_u64_to_be_h256((*num).into())), - BlockId::Hash(_) => None, - }) - } - - /// Hash the extrinsic. - fn hash_and_length(&self, uxt: &TOP) -> (SidechainBlockHash, usize) { - let encoded = uxt.encode(); - let len = encoded.len(); - (tests::Hashing::hash_of(&encoded), len) - } - - fn block_body(&self, _id: &BlockId) -> Self::BodyFuture { - futures::future::ready(Ok(None)) - } - } - - fn test_pool() -> Pool, TrustedOperationMock> { - Pool::new( - Default::default(), - TestApi::default().into(), - Arc::new(RpcResponderMock::::new()), - ) - } - - #[test] - pub fn test_should_validate_and_import_transaction() { - // given - let pool = test_pool(); - let shard = ShardIdentifier::default(); - - // when - let hash = block_on(pool.submit_one( - &BlockId::Number(0), - SOURCE, - mock_top_direct_trusted_call_signed(), - shard, - )) - .unwrap(); - - // then - assert_eq!( - pool.validated_pool().ready(shard).map(|v| v.hash).collect::>(), - vec![hash] - ); - } - - #[test] - pub fn test_should_reject_if_temporarily_banned() { - // given - let pool = test_pool(); - let shard = ShardIdentifier::default(); - let top = mock_top_direct_trusted_call_signed(); - - // when - pool.validated_pool.rotator().ban(&Instant::now(), vec![pool.hash_of(&top)]); - let res = block_on(pool.submit_one(&BlockId::Number(0), SOURCE, top, shard)); - assert_eq!(pool.validated_pool().status(shard).ready, 0); - assert_eq!(pool.validated_pool().status(shard).future, 0); - - // then - assert!(matches!(res.unwrap_err(), error::Error::TemporarilyBanned)); - } - - #[test] - pub fn test_should_notify_about_pool_events() { - let (stream, hash0, hash1) = { - // given - let pool = test_pool(); - let shard = ShardIdentifier::default(); - let stream = pool.validated_pool().import_notification_stream(); - - // when - let hash0 = block_on(pool.submit_one( - &BlockId::Number(0), - SOURCE, - TrustedOperationMock::direct_call(mock_trusted_call_signed(0)), - shard, - )) - .unwrap(); - let hash1 = block_on(pool.submit_one( - &BlockId::Number(0), - SOURCE, - TrustedOperationMock::direct_call(mock_trusted_call_signed(1)), - shard, - )) - .unwrap(); - /* this fails because of #1488 - // future doesn't count - let _hash = block_on(pool.submit_one( - &BlockId::Number(0), - SOURCE, - TrustedOperationMock::direct_call(mock_trusted_call_signed(3)), - shard, - )) - .unwrap(); - assert_eq!(pool.validated_pool().status(shard).future, 1); - */ - assert_eq!(pool.validated_pool().status(shard).ready, 2); - - (stream, hash0, hash1) - }; - - // then - let mut it = futures::executor::block_on_stream(stream); - assert_eq!(it.next(), Some(hash0)); - assert_eq!(it.next(), Some(hash1)); - assert_eq!(it.next(), None); - } - - #[test] - pub fn test_should_clear_stale_transactions() { - // given - let pool = test_pool(); - let shard = ShardIdentifier::default(); - let hash1 = block_on(pool.submit_one( - &BlockId::Number(0), - SOURCE, - TrustedOperationMock::direct_call(mock_trusted_call_signed(0)), - shard, - )) - .unwrap(); - let hash2 = block_on(pool.submit_one( - &BlockId::Number(0), - SOURCE, - TrustedOperationMock::direct_call(mock_trusted_call_signed(1)), - shard, - )) - .unwrap(); - let hash3 = block_on(pool.submit_one( - &BlockId::Number(0), - SOURCE, - TrustedOperationMock::direct_call(mock_trusted_call_signed(3)), - shard, - )) - .unwrap(); - // when - pool.validated_pool.clear_stale(&BlockId::Number(65), shard).unwrap(); - - // then - assert_eq!(pool.validated_pool().ready(shard).count(), 0); - assert_eq!(pool.validated_pool().status(shard).future, 0); - assert_eq!(pool.validated_pool().status(shard).ready, 0); - // make sure they are temporarily banned as well - assert!(pool.validated_pool.rotator().is_banned(&hash1)); - assert!(pool.validated_pool.rotator().is_banned(&hash2)); - assert!(pool.validated_pool.rotator().is_banned(&hash3)); - } - - #[test] - pub fn test_should_ban_mined_transactions() { - // given - let pool = test_pool(); - let shard = ShardIdentifier::default(); - let hash1 = block_on(pool.submit_one( - &BlockId::Number(0), - SOURCE, - TrustedOperationMock::direct_call(mock_trusted_call_signed(0)), - shard, - )) - .unwrap(); - - // when - block_on(pool.prune_tags(&BlockId::Number(1), vec![vec![0]], vec![hash1], shard)).unwrap(); - - // then - assert!(pool.validated_pool.rotator().is_banned(&hash1)); - } - - #[test] - #[ignore] // flaky, fails sometimes - pub fn test_should_limit_futures() { - // given - let shard = ShardIdentifier::default(); - let limit = Limit { count: 100, total_bytes: 300 }; - let pool = Pool::new( - Options { ready: limit.clone(), future: limit, ..Default::default() }, - TestApi::default().into(), - Arc::new(TestRpcResponder::new()), - ); - - let hash1 = block_on(pool.submit_one( - &BlockId::Number(0), - SOURCE, - TrustedOperationMock::direct_call(mock_trusted_call_signed(1)), - shard, - )) - .unwrap(); - assert_eq!(pool.validated_pool().status(shard).future, 1); - - // when - let hash2 = block_on(pool.submit_one( - &BlockId::Number(0), - SOURCE, - TrustedOperationMock::direct_call(mock_trusted_call_signed(10)), - shard, - )) - .unwrap(); - - // then - assert_eq!(pool.validated_pool().status(shard).future, 1); - assert!(pool.validated_pool.rotator().is_banned(&hash1)); - assert!(!pool.validated_pool.rotator().is_banned(&hash2)); - } - - #[test] - pub fn test_should_error_if_reject_immediately() { - // given - let shard = ShardIdentifier::default(); - let limit = Limit { count: 100, total_bytes: 10 }; - let pool = Pool::new( - Options { ready: limit.clone(), future: limit, ..Default::default() }, - TestApi::default().into(), - Arc::new(TestRpcResponder::new()), - ); - - // when - block_on(pool.submit_one( - &BlockId::Number(0), - SOURCE, - TrustedOperationMock::direct_call(mock_trusted_call_signed(1)), - shard, - )) - .unwrap_err(); - - // then - assert_eq!(pool.validated_pool().status(shard).ready, 0); - assert_eq!(pool.validated_pool().status(shard).future, 0); - } -} diff --git a/tee-worker/bitacross/core-primitives/top-pool/src/primitives.rs b/tee-worker/bitacross/core-primitives/top-pool/src/primitives.rs deleted file mode 100644 index acd24bb317..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool/src/primitives.rs +++ /dev/null @@ -1,346 +0,0 @@ -// File replacing substrate crate sp_transaction_pool::{error, PoolStatus}; - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; - -extern crate alloc; -use crate::error; -use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec}; -use byteorder::{BigEndian, ByteOrder}; -use codec::{Decode, Encode}; -use core::pin::Pin; -use itp_stf_primitives::types::ShardIdentifier; -use jsonrpc_core::futures::{channel::mpsc::Receiver, Future, Stream}; -use sp_core::H256; -use sp_runtime::{ - generic::BlockId, - traits::{Block as BlockT, NumberFor}, - transaction_validity::{TransactionLongevity, TransactionPriority, TransactionTag}, -}; -use std::collections::HashMap; - -/// TrustedOperation pool status. -#[derive(Debug, Default)] -pub struct PoolStatus { - /// Number of operations in the ready queue. - pub ready: usize, - /// Sum of bytes of ready operation encodings. - pub ready_bytes: usize, - /// Number of operations in the future queue. - pub future: usize, - /// Sum of bytes of ready operation encodings. - pub future_bytes: usize, -} - -impl PoolStatus { - /// Returns true if the are no operations in the pool. - pub fn is_empty(&self) -> bool { - self.ready == 0 && self.future == 0 - } -} - -/// Possible operation status events. -/// -/// This events are being emitted by `TrustedOperationPool` watchers, -/// which are also exposed over RPC. -/// -/// The status events can be grouped based on their kinds as: -/// 1. Entering/Moving within the pool: -/// - `Future` -/// - `Ready` -/// 2. Inside `Ready` queue: -/// - `Broadcast` -/// 3. Leaving the pool: -/// - `InBlock` -/// - `Invalid` -/// - `Usurped` -/// - `Dropped` -/// 4. Re-entering the pool: -/// - `Retracted` -/// 5. Block finalized: -/// - `Finalized` -/// - `FinalityTimeout` -/// -/// The events will always be received in the order described above, however -/// there might be cases where operations alternate between `Future` and `Ready` -/// pool, and are `Broadcast` in the meantime. -/// -/// There is also only single event causing the operation to leave the pool. -/// I.e. only one of the listed ones should be triggered. -/// -/// Note that there are conditions that may cause operations to reappear in the pool. -/// 1. Due to possible forks, the operation that ends up being in included -/// in one block, may later re-enter the pool or be marked as invalid. -/// 2. TrustedOperation `Dropped` at one point, may later re-enter the pool if some other -/// operations are removed. -/// 3. `Invalid` operation may become valid at some point in the future. -/// (Note that runtimes are encouraged to use `UnknownValidity` to inform the pool about -/// such case). -/// 4. `Retracted` operations might be included in some next block. -/// -/// The stream is considered finished only when either `Finalized` or `FinalityTimeout` -/// event is triggered. You are however free to unsubscribe from notifications at any point. -/// The first one will be emitted when the block, in which operation was included gets -/// finalized. The `FinalityTimeout` event will be emitted when the block did not reach finality -/// within 512 blocks. This either indicates that finality is not available for your chain, -/// or that finality gadget is lagging behind. If you choose to wait for finality longer, you can -/// re-subscribe for a particular operation hash manually again. -#[derive(Debug, Clone, PartialEq)] -pub enum TrustedOperationStatus { - /// TrustedOperation is part of the future queue. - Future, - /// TrustedOperation is part of the ready queue. - Ready, - /// The operation has been broadcast to the given peers. - Broadcast(Vec), - /// TrustedOperation has been included in block with given hash. - InBlock(BlockHash), - /// The block this operation was included in has been retracted. - Retracted(BlockHash), - /// Maximum number of finality watchers has been reached, - /// old watchers are being removed. - FinalityTimeout(BlockHash), - /// TrustedOperation has been finalized by a finality-gadget, e.g GRANDPA - Finalized(BlockHash), - /// TrustedOperation has been replaced in the pool, by another operation - /// that provides the same tags. (e.g. same (sender, nonce)). - Usurped(Hash), - /// TrustedOperation has been dropped from the pool because of the limit. - Dropped, - /// TrustedOperation is no longer valid in the current state. - Invalid, -} - -/// The stream of operation events. -pub type TrustedOperationStatusStream = - dyn Stream> + Send + Unpin; - -/// The import notification event stream. -pub type ImportNotificationStream = Receiver; - -/// TrustedOperation hash type for a pool. -pub type TxHash = H256; -/// Block hash type for a pool. -pub type BlockHash = H256; -/// Type of operations event stream for a pool. -pub type TrustedOperationStatusStreamFor = TrustedOperationStatusStream; - -/// Typical future type used in operation pool api. -pub type PoolFuture = Pin> + Send>>; - -/// In-pool operation interface. -/// -/// The pool is container of operations that are implementing this trait. -/// See `sp_runtime::ValidTransaction` for details about every field. -pub trait InPoolOperation { - /// TrustedOperation type. - type TrustedOperation; - - /// Get the reference to the operation data. - fn data(&self) -> &Self::TrustedOperation; - /// Get hash of the operation. - fn hash(&self) -> TxHash; - /// Get priority of the operation. - fn priority(&self) -> &TransactionPriority; - /// Get longevity of the operation. - fn longevity(&self) -> &TransactionLongevity; - /// Get operation dependencies. - fn requires(&self) -> &[TransactionTag]; - /// Get tags that operation provides. - fn provides(&self) -> &[TransactionTag]; - /// Return a flag indicating if the operation should be propagated to other peers. - fn is_propagable(&self) -> bool; -} - -/// TrustedOperation pool interface. -pub trait TrustedOperationPool: Send + Sync { - /// Block type. - type Block: BlockT; - /// In-pool operation type. - type InPoolOperation: InPoolOperation; - /// Error type. - type Error: From + error::IntoPoolError; - - // *** RPC - - /// Returns a future that imports a bunch of unverified operations to the pool. - // FIXME: obey clippy - #[allow(clippy::type_complexity)] - fn submit_at( - &self, - at: &BlockId, - source: TrustedOperationSource, - xts: Vec, - shard: ShardIdentifier, - ) -> PoolFuture>, Self::Error>; - - /// Returns a future that imports one unverified operation to the pool. - fn submit_one( - &self, - at: &BlockId, - source: TrustedOperationSource, - xt: TOP, - shard: ShardIdentifier, - ) -> PoolFuture; - - /// Returns a future that import a single operation and starts to watch their progress in the pool. - fn submit_and_watch( - &self, - at: &BlockId, - source: TrustedOperationSource, - xt: TOP, - shard: ShardIdentifier, - ) -> PoolFuture; - - // *** Block production / Networking - /// Get an iterator for ready operations ordered by priority. - /// - /// Guarantees to return only when operation pool got updated at `at` block. - /// Guarantees to return immediately when `None` is passed. - // FIXME: obey clippy - #[allow(clippy::type_complexity)] - fn ready_at( - &self, - at: NumberFor, - shard: ShardIdentifier, - ) -> Pin< - Box< - dyn Future> + Send>> + Send, - >, - >; - - /// Get an iterator for ready operations ordered by priority. - fn ready( - &self, - shard: ShardIdentifier, - ) -> Box> + Send>; - - /// Get an iterator over all shards. - fn shards(&self) -> Vec; - - // *** Block production - /// Remove operations identified by given hashes (and dependent operations) from the pool. - fn remove_invalid( - &self, - hashes: &[TxHash], - shard: ShardIdentifier, - inblock: bool, - ) -> Vec>; - - // *** logging - /// Returns pool status. - fn status(&self, shard: ShardIdentifier) -> PoolStatus; - - // *** logging / RPC / networking - /// Return an event stream of operations imported to the pool. - fn import_notification_stream(&self) -> ImportNotificationStream; - - // *** networking - /// Notify the pool about operations broadcast. - fn on_broadcasted(&self, propagations: HashMap>); - - /// Returns operation hash - fn hash_of(&self, xt: &TOP) -> TxHash; - - /// Return specific ready operation by hash, if there is one. - fn ready_transaction( - &self, - hash: &TxHash, - shard: ShardIdentifier, - ) -> Option>; - - /// Litentry: set the rpc response value - #[allow(clippy::type_complexity)] - fn update_connection_state(&self, updates: Vec<(TxHash, (Vec, bool))>); - - /// Litentry: swap the old hash with the new one in rpc connection registry - fn swap_rpc_connection_hash(&self, old_hash: TxHash, new_hash: TxHash); -} - -/// The source of the transaction. -/// -/// Depending on the source we might apply different validation schemes. -/// For instance we can disallow specific kinds of transactions if they were not produced -/// by our local node (for instance off-chain workers). -#[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, Debug)] -pub enum TrustedOperationSource { - /// Transaction is already included in block. - /// - /// This means that we can't really tell where the transaction is coming from, - /// since it's already in the received block. Note that the custom validation logic - /// using either `Local` or `External` should most likely just allow `InBlock` - /// transactions as well. - #[codec(index = 0)] - InBlock, - - /// Transaction is coming from a local source. - /// - /// This means that the transaction was produced internally by the node - /// (for instance an Off-Chain Worker, or an Off-Chain Call), as opposed - /// to being received over the network. - #[codec(index = 1)] - Local, - - /// Transaction has been received externally. - /// - /// This means the transaction has been received from (usually) "untrusted" source, - /// for instance received over the network or RPC. - #[codec(index = 2)] - External, -} - -// Replacement of primitive function from_low_u64_be -pub fn from_low_u64_to_be_h256(val: u64) -> H256 { - let mut buf = [0x0; 8]; - BigEndian::write_u64(&mut buf, val); - let capped = core::cmp::min(H256::len_bytes(), 8); - let mut bytes = [0x0; core::mem::size_of::()]; - bytes[(H256::len_bytes() - capped)..].copy_from_slice(&buf[..capped]); - H256::from_slice(&bytes) -} - -#[cfg(test)] -pub mod tests { - - use super::*; - use alloc::string::ToString; - - #[test] - pub fn test_h256() { - let tests = vec![ - ( - from_low_u64_to_be_h256(0), - "0x0000000000000000000000000000000000000000000000000000000000000000", - ), - ( - from_low_u64_to_be_h256(2), - "0x0000000000000000000000000000000000000000000000000000000000000002", - ), - ( - from_low_u64_to_be_h256(15), - "0x000000000000000000000000000000000000000000000000000000000000000f", - ), - ( - from_low_u64_to_be_h256(16), - "0x0000000000000000000000000000000000000000000000000000000000000010", - ), - ( - from_low_u64_to_be_h256(1_000), - "0x00000000000000000000000000000000000000000000000000000000000003e8", - ), - ( - from_low_u64_to_be_h256(100_000), - "0x00000000000000000000000000000000000000000000000000000000000186a0", - ), - ( - from_low_u64_to_be_h256(u64::max_value()), - "0x000000000000000000000000000000000000000000000000ffffffffffffffff", - ), - ]; - - for (number, expected) in tests { - // workaround, as H256 in no_std does not implement (de)serialize - assert_eq!(expected.to_string(), format!("{:?}", number)); - } - } -} diff --git a/tee-worker/bitacross/core-primitives/top-pool/src/ready.rs b/tee-worker/bitacross/core-primitives/top-pool/src/ready.rs deleted file mode 100644 index c3dbf5afbb..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool/src/ready.rs +++ /dev/null @@ -1,800 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2021 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -pub extern crate alloc; -use crate::{ - base_pool::TrustedOperation, - error, - future::WaitingTrustedOperations, - primitives::TxHash, - tracked_map::{self, ReadOnlyTrackedMap, TrackedMap}, -}; -use alloc::{boxed::Box, collections::BTreeSet, sync::Arc, vec, vec::Vec}; -use core::{cmp, cmp::Ord, default::Default}; -use itp_stf_primitives::types::ShardIdentifier; -use log::trace; -use sp_runtime::transaction_validity::TransactionTag as Tag; -use std::collections::{HashMap, HashSet}; - -type TopErrorResult = error::Result<(Vec>>, Vec)>; - -/// An in-pool operation reference. -/// -/// Should be cheap to clone. -#[derive(Debug)] -pub struct OperationRef { - /// The actual operation data. - pub operation: Arc>, - /// Unique id when operation was inserted into the pool. - pub insertion_id: u64, -} - -impl Clone for OperationRef { - fn clone(&self) -> Self { - OperationRef { operation: self.operation.clone(), insertion_id: self.insertion_id } - } -} - -impl Ord for OperationRef { - fn cmp(&self, other: &Self) -> cmp::Ordering { - self.operation - .priority - .cmp(&other.operation.priority) - .then_with(|| other.operation.valid_till.cmp(&self.operation.valid_till)) - .then_with(|| other.insertion_id.cmp(&self.insertion_id)) - } -} - -impl PartialOrd for OperationRef { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl PartialEq for OperationRef { - fn eq(&self, other: &Self) -> bool { - self.cmp(other) == cmp::Ordering::Equal - } -} -impl Eq for OperationRef {} - -#[derive(Debug)] -pub struct ReadyTx { - /// A reference to a operation - pub operation: OperationRef, - /// A list of operations that get unlocked by this one - pub unlocks: Vec, - /// How many required tags are provided inherently - /// - /// Some operations might be already pruned from the queue, - /// so when we compute ready set we may consider this operations ready earlier. - pub requires_offset: usize, -} - -impl Clone for ReadyTx { - fn clone(&self) -> Self { - ReadyTx { - operation: self.operation.clone(), - unlocks: self.unlocks.clone(), - requires_offset: self.requires_offset, - } - } -} - -const HASH_READY: &str = r#" -Every time operation is imported its hash is placed in `ready` map and tags in `provided_tags`; -Every time operation is removed from the queue we remove the hash from `ready` map and from `provided_tags`; -Hence every hash retrieved from `provided_tags` is always present in `ready`; -qed -"#; - -#[derive(Debug)] -pub struct ReadyOperations { - /// Insertion id - insertion_id: HashMap, - /// tags that are provided by Ready operations - provided_tags: HashMap>, - /// Trusted Operations that are ready (i.e. don't have any requirements external to the pool) - ready: HashMap>>, - /// Best operations that are ready to be included to the block without any other previous operation. - best: HashMap>>, -} - -impl tracked_map::Size for ReadyTx { - fn size(&self) -> usize { - self.operation.operation.bytes - } -} - -impl Default for ReadyOperations { - fn default() -> Self { - ReadyOperations { - insertion_id: Default::default(), - provided_tags: Default::default(), - ready: Default::default(), - best: Default::default(), - } - } -} - -impl ReadyOperations { - /// Borrows a map of tags that are provided by operations in this queue. - pub fn provided_tags(&self, shard: ShardIdentifier) -> Option<&HashMap> { - if let Some(tag_pool) = &self.provided_tags.get(&shard) { - return Some(tag_pool) - } - None - } - - /// Returns an iterator of ready operations. - /// - /// Trusted Operations are returned in order: - /// 1. First by the dependencies: - /// - never return operation that requires a tag, which was not provided by one of the previously returned operations - /// 2. Then by priority: - /// - If there are two operations with all requirements satisfied the one with higher priority goes first. - /// 3. Then by the ttl that's left - /// - operations that are valid for a shorter time go first - /// 4. Lastly we sort by the time in the queue - /// - operations that are longer in the queue go first - pub fn get(&self, shard: ShardIdentifier) -> impl Iterator>> { - // check if shard tx pool exists - if let Some(ready_map) = self.ready.get(&shard) { - return BestIterator { - all: ready_map.get_read_only_clone(), - best: self.best.get(&shard).unwrap().clone(), - awaiting: Default::default(), - } - } - let tracked_map: TrackedMap> = Default::default(); - BestIterator { - all: tracked_map.get_read_only_clone(), - best: Default::default(), - awaiting: Default::default(), - } - } - /// Returns an iterator over all shards - pub fn get_shards(&self) -> Box + '_> { - // check if shard tx pool exists - Box::new(self.ready.keys()) - } - - /// Imports operations to the pool of ready operations. - /// - /// The operation needs to have all tags satisfied (be ready) by operations - /// that are in this queue. - /// Returns operations that were replaced by the one imported. - pub fn import( - &mut self, - tx: WaitingTrustedOperations, - shard: ShardIdentifier, - ) -> error::Result>>> { - assert!( - tx.is_ready(), - "Only ready operations can be imported. Missing: {:?}", - tx.missing_tags - ); - if let Some(ready_map) = &self.ready.get(&shard) { - assert!( - !ready_map.read().contains_key(&tx.operation.hash), - "TrustedOperation is already imported." - ); - } - // Get shard pool or create if not yet existing - let current_insertion_id = self.insertion_id.entry(shard).or_insert_with(|| { - let x: u64 = Default::default(); - x - }); - - *current_insertion_id += 1; - let insertion_id = *current_insertion_id; - let hash = tx.operation.hash; - let operation = tx.operation; - - let (replaced, unlocks) = self.replace_previous(&operation, shard)?; - - let mut goes_to_best = true; - let tracked_ready = self.ready.entry(shard).or_insert_with(|| { - let x: TrackedMap> = Default::default(); - x - }); - let mut ready = tracked_ready.write(); - let mut requires_offset = 0; - // Add links to operations that unlock the current one - let tag_map = self.provided_tags.entry(shard).or_insert_with(|| { - let x: HashMap = Default::default(); - x - }); - for tag in &operation.requires { - // Check if the operation that satisfies the tag is still in the queue. - if let Some(other) = tag_map.get(tag) { - let tx = ready.get_mut(other).expect(HASH_READY); - tx.unlocks.push(hash); - // this operation depends on some other, so it doesn't go to best directly. - goes_to_best = false; - } else { - requires_offset += 1; - } - } - - // update provided_tags - // call to replace_previous guarantees that we will be overwriting - // only entries that have been removed. - - for tag in &operation.provides { - tag_map.insert(tag.clone(), hash); - } - - let operation = OperationRef { operation, insertion_id }; - - // insert to best if it doesn't require any other operation to be included before it - let best_set = self.best.entry(shard).or_insert_with(|| { - let x: BTreeSet> = Default::default(); - x - }); - if goes_to_best { - best_set.insert(operation.clone()); - } - - // insert to Ready - ready.insert(hash, ReadyTx { operation, unlocks, requires_offset }); - - Ok(replaced) - } - - /// Fold a list of ready operations to compute a single value. - pub fn fold, &ReadyTx) -> Option>( - &mut self, - f: F, - shard: ShardIdentifier, - ) -> Option { - if let Some(ready_map) = self.ready.get(&shard) { - return ready_map.read().values().fold(None, f) - } - None - } - - /// Returns true if given hash is part of the queue. - pub fn contains(&self, hash: &TxHash, shard: ShardIdentifier) -> bool { - if let Some(ready_map) = self.ready.get(&shard) { - return ready_map.read().contains_key(hash) - } - false - } - - /// Retrive operation by hash - pub fn by_hash( - &self, - hash: &TxHash, - shard: ShardIdentifier, - ) -> Option>> { - self.by_hashes(&[*hash], shard).into_iter().next().unwrap_or(None) - } - - /// Retrieve operations by hash - pub fn by_hashes( - &self, - hashes: &[TxHash], - shard: ShardIdentifier, - ) -> Vec>>> { - if let Some(ready_map) = self.ready.get(&shard) { - let ready = ready_map.read(); - return hashes - .iter() - .map(|hash| ready.get(hash).map(|x| x.operation.operation.clone())) - .collect() - } - vec![] - } - - /// Removes a subtree of operations from the ready pool. - /// - /// NOTE removing a operation will also cause a removal of all operations that depend on that one - /// (i.e. the entire subgraph that this operation is a start of will be removed). - /// All removed operations are returned. - pub fn remove_subtree( - &mut self, - hashes: &[TxHash], - shard: ShardIdentifier, - ) -> Vec>> { - let to_remove = hashes.to_vec(); - self.remove_subtree_with_tag_filter(to_remove, None, shard) - } - - /// Removes a subtrees of operations trees starting from roots given in `to_remove`. - /// - /// We proceed with a particular branch only if there is at least one provided tag - /// that is not part of `provides_tag_filter`. I.e. the filter contains tags - /// that will stay in the pool, so that we can early exit and avoid descending. - fn remove_subtree_with_tag_filter( - &mut self, - mut to_remove: Vec, - provides_tag_filter: Option>, - shard: ShardIdentifier, - ) -> Vec>> { - let mut removed = vec![]; - if let Some(ready_map) = self.ready.get_mut(&shard) { - let mut ready = ready_map.write(); - while let Some(hash) = to_remove.pop() { - if let Some(mut tx) = ready.remove(&hash) { - let invalidated = tx.operation.operation.provides.iter().filter(|tag| { - provides_tag_filter - .as_ref() - .map(|filter| !filter.contains(&**tag)) - .unwrap_or(true) - }); - - let mut removed_some_tags = false; - // remove entries from provided_tags - for tag in invalidated { - removed_some_tags = true; - self.provided_tags.get_mut(&shard).unwrap().remove(tag); - } - - // remove from unlocks - for tag in &tx.operation.operation.requires { - if let Some(hash) = self.provided_tags.get(&shard).unwrap().get(tag) { - if let Some(tx) = ready.get_mut(hash) { - remove_item(&mut tx.unlocks, hash); - } - } - } - - // remove from best - self.best.get_mut(&shard).unwrap().remove(&tx.operation); - - if removed_some_tags { - // remove all operations that the current one unlocks - to_remove.append(&mut tx.unlocks); - } - - // add to removed - trace!(target: "txpool", "[{:?}] Removed as part of the subtree.", hash); - removed.push(tx.operation.operation); - } - } - } - - removed - } - - /// Removes operations that provide given tag. - /// - /// All operations that lead to a operation, which provides this tag - /// are going to be removed from the queue, but no other operations are touched - - /// i.e. all other subgraphs starting from given tag are still considered valid & ready. - pub fn prune_tags( - &mut self, - tag: Tag, - shard: ShardIdentifier, - ) -> Vec>> { - let mut removed = vec![]; - let mut to_remove = vec![tag]; - - if self.provided_tags.contains_key(&shard) { - while let Some(tag) = to_remove.pop() { - let res = self - .provided_tags - .get_mut(&shard) - .unwrap() - .remove(&tag) - .and_then(|hash| self.ready.get_mut(&shard).unwrap().write().remove(&hash)); - - if let Some(tx) = res { - let unlocks = tx.unlocks; - - // Make sure we remove it from best txs - self.best.get_mut(&shard).unwrap().remove(&tx.operation); - - let tx = tx.operation.operation; - - // prune previous operations as well - { - let hash = &tx.hash; - let mut find_previous = |tag| -> Option> { - let prev_hash = self.provided_tags.get(&shard).unwrap().get(tag)?; - let mut ready = self.ready.get_mut(&shard).unwrap().write(); - let tx2 = ready.get_mut(prev_hash)?; - remove_item(&mut tx2.unlocks, hash); - // We eagerly prune previous operations as well. - // But it might not always be good. - // Possible edge case: - // - tx provides two tags - // - the second tag enables some subgraph we don't know of yet - // - we will prune the operation - // - when we learn about the subgraph it will go to future - // - we will have to wait for re-propagation of that operation - // Alternatively the caller may attempt to re-import these operations. - if tx2.unlocks.is_empty() { - Some(tx2.operation.operation.provides.clone()) - } else { - None - } - }; - - // find previous operations - for tag in &tx.requires { - if let Some(mut tags_to_remove) = find_previous(tag) { - to_remove.append(&mut tags_to_remove); - } - } - } - - // add the operations that just got unlocked to `best` - for hash in unlocks { - if let Some(tx) = self.ready.get_mut(&shard).unwrap().write().get_mut(&hash) - { - tx.requires_offset += 1; - // this operation is ready - if tx.requires_offset == tx.operation.operation.requires.len() { - self.best.get_mut(&shard).unwrap().insert(tx.operation.clone()); - } - } - } - - // we also need to remove all other tags that this operation provides, - // but since all the hard work is done, we only clear the provided_tag -> hash - // mapping. - let current_tag = &tag; - for tag in &tx.provides { - let removed = self.provided_tags.get_mut(&shard).unwrap().remove(tag); - assert_eq!( - removed.as_ref(), - if current_tag == tag { None } else { Some(&tx.hash) }, - "The pool contains exactly one operation providing given tag; the removed operation - claims to provide that tag, so it has to be mapped to it's hash; qed" - ); - } - - removed.push(tx); - } - } - } - - removed - } - - /// Checks if the operation is providing the same tags as other operations. - /// - /// In case that's true it determines if the priority of operations that - /// we are about to replace is lower than the priority of the replacement operation. - /// We remove/replace old operations in case they have lower priority. - /// - /// In case replacement is successful returns a list of removed operations - /// and a list of hashes that are still in pool and gets unlocked by the new operation. - fn replace_previous( - &mut self, - tx: &TrustedOperation, - shard: ShardIdentifier, - ) -> TopErrorResult { - if let Some(provided_tag_map) = self.provided_tags.get(&shard) { - let (to_remove, unlocks) = { - // check if we are replacing a operation - let replace_hashes = tx - .provides - .iter() - .filter_map(|tag| provided_tag_map.get(tag)) - .collect::>(); - - // early exit if we are not replacing anything. - if replace_hashes.is_empty() { - return Ok((vec![], vec![])) - } - - // now check if collective priority is lower than the replacement operation. - let old_priority = { - let ready = self.ready.get(&shard).unwrap().read(); - replace_hashes - .iter() - .filter_map(|hash| ready.get(hash)) - .fold(0u64, |total, tx| { - total.saturating_add(tx.operation.operation.priority) - }) - }; - - // bail - the operation has too low priority to replace the old ones - if old_priority >= tx.priority { - return Err(error::Error::TooLowPriority(tx.priority)) - } - - // construct a list of unlocked operations - let unlocks = { - let ready = self.ready.get(&shard).unwrap().read(); - replace_hashes.iter().filter_map(|hash| ready.get(hash)).fold( - vec![], - |mut list, tx| { - list.extend(tx.unlocks.iter().cloned()); - list - }, - ) - }; - - (replace_hashes.into_iter().cloned().collect::>(), unlocks) - }; - - let new_provides = tx.provides.iter().cloned().collect::>(); - let removed = self.remove_subtree_with_tag_filter(to_remove, Some(new_provides), shard); - - return Ok((removed, unlocks)) - } - Ok((vec![], vec![])) - } - - /// Returns number of operations in this queue. - #[allow(clippy::len_without_is_empty)] - pub fn len(&self, shard: ShardIdentifier) -> usize { - self.ready.get(&shard).map_or(0, |ready_map| ready_map.len()) - } - - /// Returns sum of encoding lengths of all operations in this queue. - pub fn bytes(&self, shard: ShardIdentifier) -> usize { - self.ready.get(&shard).map_or(0, |ready_map| ready_map.bytes()) - } -} - -/// Iterator of ready operations ordered by priority. -pub struct BestIterator { - all: ReadOnlyTrackedMap>, - awaiting: HashMap)>, - best: BTreeSet>, -} - -/*impl Default for BestIterator { - let insertion_id = 0; - let operation = Arc::new(with_priority(3, 3)) - let tx_default = OperationRef { - insertion_id, - operation - }; - fn default() -> self.awaiting.insert("NA", (0, tx_default)) -}*/ - -impl BestIterator { - /// Depending on number of satisfied requirements insert given ref - /// either to awaiting set or to best set. - fn best_or_awaiting(&mut self, satisfied: usize, tx_ref: OperationRef) { - if satisfied >= tx_ref.operation.requires.len() { - // If we have satisfied all deps insert to best - self.best.insert(tx_ref); - } else { - // otherwise we're still awaiting for some deps - self.awaiting.insert(tx_ref.operation.hash, (satisfied, tx_ref)); - } - } -} - -impl Iterator for BestIterator { - type Item = Arc>; - - fn next(&mut self) -> Option { - loop { - let best = self.best.iter().next_back()?.clone(); - let best = self.best.take(&best)?; - - let next = self.all.read().get(&best.operation.hash).cloned(); - let ready = match next { - Some(ready) => ready, - // The operation is not in all, maybe it was removed in the meantime? - None => continue, - }; - - // Insert operations that just got unlocked. - for hash in &ready.unlocks { - // first check local awaiting operations - let res = if let Some((mut satisfied, tx_ref)) = self.awaiting.remove(hash) { - satisfied += 1; - Some((satisfied, tx_ref)) - // then get from the pool - } else { - self.all - .read() - .get(hash) - .map(|next| (next.requires_offset + 1, next.operation.clone())) - }; - - if let Some((satisfied, tx_ref)) = res { - self.best_or_awaiting(satisfied, tx_ref) - } - } - - return Some(best.operation) - } - } -} - -// See: https://github.com/rust-lang/rust/issues/40062 -fn remove_item(vec: &mut Vec, item: &T) { - if let Some(idx) = vec.iter().position(|i| i == item) { - vec.swap_remove(idx); - } -} - -#[cfg(test)] -pub mod tests { - use super::*; - use crate::primitives::TrustedOperationSource as Source; - use codec::Encode; - use sp_core::blake2_256; - - fn hash(index: u64) -> TxHash { - blake2_256(index.encode().as_slice()).into() - } - - fn tx(id: u8) -> TrustedOperation> { - TrustedOperation { - data: vec![id], - bytes: 1, - hash: hash(id as u64), - priority: 1, - valid_till: 2, - requires: vec![vec![1], vec![2]], - provides: vec![vec![3], vec![4]], - propagate: true, - source: Source::External, - } - } - - fn import( - ready: &mut ReadyOperations, - tx: TrustedOperation, - shard: ShardIdentifier, - ) -> error::Result>>> { - let x = WaitingTrustedOperations::new(tx, ready.provided_tags(shard), &[]); - ready.import(x, shard) - } - - #[test] - pub fn test_should_replace_transaction_that_provides_the_same_tag() { - // given - let shard = ShardIdentifier::default(); - let mut ready = ReadyOperations::default(); - let mut tx1 = tx(1); - tx1.requires.clear(); - let mut tx2 = tx(2); - tx2.requires.clear(); - tx2.provides = vec![vec![3]]; - let mut tx3 = tx(3); - tx3.requires.clear(); - tx3.provides = vec![vec![4]]; - - // when - import(&mut ready, tx2, shard).unwrap(); - import(&mut ready, tx3, shard).unwrap(); - assert_eq!(ready.get(shard).count(), 2); - - // too low priority - import(&mut ready, tx1.clone(), shard).unwrap_err(); - - tx1.priority = 10; - import(&mut ready, tx1, shard).unwrap(); - - // then - assert_eq!(ready.get(shard).count(), 1); - } - - #[test] - pub fn test_should_replace_multiple_transactions_correctly() { - // given - let shard = ShardIdentifier::default(); - let mut ready = ReadyOperations::default(); - let mut tx0 = tx(0); - tx0.requires = vec![]; - tx0.provides = vec![vec![0]]; - let mut tx1 = tx(1); - tx1.requires = vec![]; - tx1.provides = vec![vec![1]]; - let mut tx2 = tx(2); - tx2.requires = vec![vec![0], vec![1]]; - tx2.provides = vec![vec![2], vec![3]]; - let mut tx3 = tx(3); - tx3.requires = vec![vec![2]]; - tx3.provides = vec![vec![4]]; - let mut tx4 = tx(4); - tx4.requires = vec![vec![3]]; - tx4.provides = vec![vec![5]]; - // replacement - let mut tx2_2 = tx(5); - tx2_2.requires = vec![vec![0], vec![1]]; - tx2_2.provides = vec![vec![2]]; - tx2_2.priority = 10; - - for tx in vec![tx0, tx1, tx2, tx3, tx4] { - import(&mut ready, tx, shard).unwrap(); - } - assert_eq!(ready.get(shard).count(), 5); - - // when - import(&mut ready, tx2_2, shard).unwrap(); - - // then - assert_eq!(ready.get(shard).count(), 3); - } - - #[test] - pub fn test_should_return_best_transactions_in_correct_order() { - // given - let shard = ShardIdentifier::default(); - let mut ready = ReadyOperations::default(); - let mut tx1 = tx(1); - tx1.requires.clear(); - let mut tx2 = tx(2); - tx2.requires = tx1.provides.clone(); - tx2.provides = vec![vec![106]]; - let mut tx3 = tx(3); - tx3.requires = vec![tx1.provides[0].clone(), vec![106]]; - tx3.provides = vec![]; - let mut tx4 = tx(4); - tx4.requires = vec![tx1.provides[0].clone()]; - tx4.provides = vec![]; - let tx5 = TrustedOperation { - data: vec![5], - bytes: 1, - hash: hash(5), - priority: 1, - valid_till: u64::max_value(), // use the max_value() here for testing. - requires: vec![tx1.provides[0].clone()], - provides: vec![], - propagate: true, - source: Source::External, - }; - - // when - for tx in vec![tx1, tx2, tx3, tx4, tx5] { - import(&mut ready, tx, shard).unwrap(); - } - - // then - assert_eq!(ready.best.len(), 1); - - let mut it = ready.get(shard).map(|tx| tx.data[0]); - - assert_eq!(it.next(), Some(1)); - assert_eq!(it.next(), Some(2)); - assert_eq!(it.next(), Some(3)); - assert_eq!(it.next(), Some(4)); - assert_eq!(it.next(), Some(5)); - assert_eq!(it.next(), None); - } - - #[test] - pub fn test_should_order_refs() { - let mut id = 1; - let mut with_priority = |priority, longevity| { - id += 1; - let mut tx = tx(id); - tx.priority = priority; - tx.valid_till = longevity; - tx - }; - // higher priority = better - assert!( - OperationRef { operation: Arc::new(with_priority(3, 3)), insertion_id: 1 } - > OperationRef { operation: Arc::new(with_priority(2, 3)), insertion_id: 2 } - ); - // lower validity = better - assert!( - OperationRef { operation: Arc::new(with_priority(3, 2)), insertion_id: 1 } - > OperationRef { operation: Arc::new(with_priority(3, 3)), insertion_id: 2 } - ); - // lower insertion_id = better - assert!( - OperationRef { operation: Arc::new(with_priority(3, 3)), insertion_id: 1 } - > OperationRef { operation: Arc::new(with_priority(3, 3)), insertion_id: 2 } - ); - } -} diff --git a/tee-worker/bitacross/core-primitives/top-pool/src/rotator.rs b/tee-worker/bitacross/core-primitives/top-pool/src/rotator.rs deleted file mode 100644 index 6cfec05fa7..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool/src/rotator.rs +++ /dev/null @@ -1,221 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! Rotate extrinsic inside the pool. -//! -//! Keeps only recent extrinsic and discard the ones kept for a significant amount of time. -//! Discarded extrinsics are banned so that they don't get re-imported again. - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use std::sync::SgxRwLock as RwLock; - -#[cfg(feature = "std")] -use std::sync::RwLock; - -use crate::{base_pool::TrustedOperation, primitives::TxHash}; -use std::{ - collections::HashMap, - iter, - time::{Duration, Instant}, -}; - -/// Expected size of the banned extrinsics cache. -const EXPECTED_SIZE: usize = 2048; - -/// Pool rotator is responsible to only keep fresh extrinsics in the pool. -/// -/// Extrinsics that occupy the pool for too long are culled and temporarily banned from entering -/// the pool again. -pub struct PoolRotator { - /// How long the extrinsic is banned for. - ban_time: Duration, - /// Currently banned extrinsics. - banned_until: RwLock>, -} - -impl Default for PoolRotator { - fn default() -> Self { - PoolRotator { ban_time: Duration::from_secs(60 * 30), banned_until: Default::default() } - } -} - -impl PoolRotator { - /// Returns `true` if extrinsic hash is currently banned. - pub fn is_banned(&self, hash: &TxHash) -> bool { - self.banned_until.read().unwrap().contains_key(hash) - } - - /// Bans given set of hashes. - pub fn ban(&self, now: &Instant, hashes: impl IntoIterator) { - let mut banned = self.banned_until.write().unwrap(); - - for hash in hashes { - banned.insert(hash, *now + self.ban_time); - } - - if banned.len() > 2 * EXPECTED_SIZE { - while banned.len() > EXPECTED_SIZE { - if let Some(key) = banned.keys().next().cloned() { - banned.remove(&key); - } - } - } - } - - /// Bans extrinsic if it's stale. - /// - /// Returns `true` if extrinsic is stale and got banned. - pub fn ban_if_stale( - &self, - now: &Instant, - current_block: u64, - xt: &TrustedOperation, - ) -> bool { - if xt.valid_till > current_block { - return false - } - - self.ban(now, iter::once(xt.hash)); - true - } - - /// Removes timed bans. - pub fn clear_timeouts(&self, now: &Instant) { - let mut banned = self.banned_until.write().unwrap(); - - banned.retain(|_, &mut v| v >= *now); - } -} - -#[cfg(test)] -pub mod tests { - use super::*; - use crate::primitives::TrustedOperationSource; - use codec::Encode; - use sp_core::blake2_256; - - type Ex = (); - - fn rotator() -> PoolRotator { - PoolRotator { ban_time: Duration::from_millis(1000), ..Default::default() } - } - - fn hash(index: u64) -> TxHash { - blake2_256(index.encode().as_slice()).into() - } - - fn tx() -> (TxHash, TrustedOperation) { - let hash = hash(5); - let tx = TrustedOperation { - data: (), - bytes: 1, - hash, - priority: 5, - valid_till: 1, - requires: vec![], - provides: vec![], - propagate: true, - source: TrustedOperationSource::External, - }; - - (hash, tx) - } - - #[test] - pub fn test_should_not_ban_if_not_stale() { - // given - let (hash, tx) = tx(); - let rotator = rotator(); - assert!(!rotator.is_banned(&hash)); - let now = Instant::now(); - let past_block = 0; - - // when - assert!(!rotator.ban_if_stale(&now, past_block, &tx)); - - // then - assert!(!rotator.is_banned(&hash)); - } - - #[test] - pub fn test_should_ban_stale_extrinsic() { - // given - let (hash, tx) = tx(); - let rotator = rotator(); - assert!(!rotator.is_banned(&hash)); - - // when - assert!(rotator.ban_if_stale(&Instant::now(), 1, &tx)); - - // then - assert!(rotator.is_banned(&hash)); - } - - #[test] - pub fn test_should_clear_banned() { - // given - let (hash, tx) = tx(); - let rotator = rotator(); - assert!(rotator.ban_if_stale(&Instant::now(), 1, &tx)); - assert!(rotator.is_banned(&hash)); - - // when - let future = Instant::now() + rotator.ban_time + rotator.ban_time; - rotator.clear_timeouts(&future); - - // then - assert!(!rotator.is_banned(&hash)); - } - - #[test] - pub fn test_should_garbage_collect() { - // given - fn tx_with(i: u64, valid_till: u64) -> TrustedOperation { - let hash = hash(i); - TrustedOperation { - data: (), - bytes: 2, - hash, - priority: 5, - valid_till, - requires: vec![], - provides: vec![], - propagate: true, - source: TrustedOperationSource::External, - } - } - - let rotator = rotator(); - - let now = Instant::now(); - let past_block = 0; - - // when - for i in 0..2 * EXPECTED_SIZE { - let tx = tx_with(i as u64, past_block); - assert!(rotator.ban_if_stale(&now, past_block, &tx)); - } - assert_eq!(rotator.banned_until.read().unwrap().len(), 2 * EXPECTED_SIZE); - - // then - let tx = tx_with(2 * EXPECTED_SIZE as u64, past_block); - // trigger a garbage collection - assert!(rotator.ban_if_stale(&now, past_block, &tx)); - assert_eq!(rotator.banned_until.read().unwrap().len(), EXPECTED_SIZE); - } -} diff --git a/tee-worker/bitacross/core-primitives/top-pool/src/tracked_map.rs b/tee-worker/bitacross/core-primitives/top-pool/src/tracked_map.rs deleted file mode 100644 index dacbe841dd..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool/src/tracked_map.rs +++ /dev/null @@ -1,198 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -pub extern crate alloc; -use alloc::sync::Arc; -use core::{ - clone::Clone, - cmp, hash, - sync::atomic::{AtomicIsize, Ordering as AtomicOrdering}, -}; -use std::collections::{hash_map::Values, HashMap}; - -//use parking_lot::{RwLock, RwLockWriteGuard, RwLockReadGuard}; - -/// Something that can report it's size. -pub trait Size { - fn size(&self) -> usize; -} - -/// Map with size tracking. -/// -/// Size reported might be slightly off and only approximately true. -#[derive(Debug)] -pub struct TrackedMap { - index: Arc>, - bytes: AtomicIsize, - length: AtomicIsize, -} - -impl Default for TrackedMap { - fn default() -> Self { - Self { index: Arc::new(HashMap::new()), bytes: 0.into(), length: 0.into() } - } -} - -impl TrackedMap { - /// Current tracked length of the content. - pub fn len(&self) -> usize { - cmp::max(self.length.load(AtomicOrdering::Relaxed), 0) as usize - } - - /// Returns true if Map is empty - pub fn is_empty(&self) -> bool { - self.length.load(AtomicOrdering::Relaxed) == 0 - } - - /// Current sum of content length. - pub fn bytes(&self) -> usize { - cmp::max(self.bytes.load(AtomicOrdering::Relaxed), 0) as usize - } - - /// Read-only clone of the interior. - pub fn get_read_only_clone(&self) -> ReadOnlyTrackedMap { - ReadOnlyTrackedMap(self.index.clone()) - } - - /// Read Access - no data race safety - pub fn read(&self) -> TrackedMapReadAccess { - TrackedMapReadAccess { inner_guard: self.index.clone() } - } - - /// Write Access - no data race safety - pub fn write(&mut self) -> TrackedMapWriteAccess { - TrackedMapWriteAccess { - //inner_guard: self.index.make_mut(&self), - inner_guard: Arc::make_mut(&mut self.index), - bytes: &self.bytes, - length: &self.length, - } - } -} - -/// Read-only access to map. -/// -/// The only thing can be done is .read(). -pub struct ReadOnlyTrackedMap(Arc>); - -impl ReadOnlyTrackedMap -where - K: Eq + hash::Hash, -{ - /// Lock map for read. - pub fn read(&self) -> TrackedMapReadAccess { - TrackedMapReadAccess { inner_guard: self.0.clone() } - } -} - -pub struct TrackedMapReadAccess { - inner_guard: Arc>, -} - -impl TrackedMapReadAccess -where - K: Eq + hash::Hash, -{ - /// Returns true if map contains key. - pub fn contains_key(&self, key: &K) -> bool { - self.inner_guard.contains_key(key) - } - - /// Returns reference to the contained value by key, if exists. - pub fn get(&self, key: &K) -> Option<&V> { - self.inner_guard.get(key) - } - - /// Returns iterator over all values. - pub fn values(&self) -> Values { - self.inner_guard.values() - } -} - -pub struct TrackedMapWriteAccess<'a, K, V> { - bytes: &'a AtomicIsize, - length: &'a AtomicIsize, - inner_guard: &'a mut HashMap, -} - -impl<'a, K, V> TrackedMapWriteAccess<'a, K, V> -where - K: Eq + hash::Hash, - V: Size, -{ - /// Insert value and return previous (if any). - pub fn insert(&mut self, key: K, val: V) -> Option { - let new_bytes = val.size(); - self.bytes.fetch_add(new_bytes as isize, AtomicOrdering::Relaxed); - self.length.fetch_add(1, AtomicOrdering::Relaxed); - self.inner_guard.insert(key, val).map(|old_val| { - self.bytes.fetch_sub(old_val.size() as isize, AtomicOrdering::Relaxed); - self.length.fetch_sub(1, AtomicOrdering::Relaxed); - old_val - }) - } - - /// Remove value by key. - pub fn remove(&mut self, key: &K) -> Option { - let val = self.inner_guard.remove(key); - if let Some(size) = val.as_ref().map(Size::size) { - self.bytes.fetch_sub(size as isize, AtomicOrdering::Relaxed); - self.length.fetch_sub(1, AtomicOrdering::Relaxed); - } - val - } - - /// Returns mutable reference to the contained value by key, if exists. - pub fn get_mut(&mut self, key: &K) -> Option<&mut V> { - self.inner_guard.get_mut(key) - } -} - -#[cfg(test)] -pub mod tests { - - use super::*; - - impl Size for i32 { - fn size(&self) -> usize { - *self as usize / 10 - } - } - - #[test] - pub fn test_basic() { - let mut map = TrackedMap::default(); - - assert!(map.is_empty()); - - map.write().insert(5, 10); - map.write().insert(6, 20); - - assert_eq!(map.bytes(), 3); - assert_eq!(map.len(), 2); - - map.write().insert(6, 30); - - assert_eq!(map.bytes(), 4); - assert_eq!(map.len(), 2); - - map.write().remove(&6); - assert_eq!(map.bytes(), 1); - assert_eq!(map.len(), 1); - } -} diff --git a/tee-worker/bitacross/core-primitives/top-pool/src/validated_pool.rs b/tee-worker/bitacross/core-primitives/top-pool/src/validated_pool.rs deleted file mode 100644 index 0d66fca8b8..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool/src/validated_pool.rs +++ /dev/null @@ -1,738 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use std::sync::SgxMutex as Mutex; -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use std::sync::SgxRwLock as RwLock; - -#[cfg(feature = "std")] -use std::sync::Mutex; -#[cfg(feature = "std")] -use std::sync::RwLock; - -use crate::{ - base_pool as base, - base_pool::PruneStatus, - error, - listener::Listener, - pool::{ChainApi, EventStream, Options, TransactionFor}, - primitives::{PoolStatus, TrustedOperationSource, TxHash}, - rotator::PoolRotator, -}; -use core::{marker::PhantomData, result::Result}; -use itc_direct_rpc_server::SendRpcResponse; -use itp_stf_primitives::types::ShardIdentifier; -use itp_types::BlockHash as SidechainBlockHash; -use jsonrpc_core::futures::channel::mpsc::{channel, Sender}; -use sp_runtime::{ - generic::BlockId, - traits::SaturatedConversion, - transaction_validity::{TransactionTag as Tag, ValidTransaction}, -}; -use std::{ - collections::{HashMap, HashSet}, - format, - string::String, - sync::Arc, - time::Instant, - vec, - vec::Vec, -}; - -/// Pre-validated operation. Validated pool only accepts operations wrapped in this enum. -#[derive(Debug)] -pub enum ValidatedOperation { - /// TrustedOperation that has been validated successfully. - Valid(base::TrustedOperation), - /// TrustedOperation that is invalid. - Invalid(TxHash, Error), - /// TrustedOperation which validity can't be determined. - /// - /// We're notifying watchers about failure, if 'unknown' operation is submitted. - Unknown(TxHash, Error), -} - -impl ValidatedOperation { - /// Consume validity result, operation data and produce ValidTransaction. - pub fn valid_at( - at: u64, - hash: TxHash, - source: TrustedOperationSource, - data: Ex, - bytes: usize, - validity: ValidTransaction, - ) -> Self { - Self::Valid(base::TrustedOperation { - data, - bytes, - hash, - source, - priority: validity.priority, - requires: validity.requires, - provides: validity.provides, - propagate: validity.propagate, - valid_till: at.saturated_into::().saturating_add(validity.longevity), - }) - } -} - -/// A type of validated operation stored in the pool. -pub type ValidatedOperationFor = ValidatedOperation::Error>; - -/// Pool that deals with validated operations. -pub struct ValidatedPool -where - R: SendRpcResponse, -{ - api: Arc, - options: Options, - listener: RwLock>, - pool: RwLock>, - import_notification_sinks: Mutex>>, - rotator: PoolRotator, - _phantom: PhantomData, -} - -impl ValidatedPool -where - R: SendRpcResponse, - TOP: core::fmt::Debug + Send + Sync + Clone, -{ - /// Create a new operation pool. - pub fn new(options: Options, api: Arc, rpc_response_sender: Arc) -> Self { - let base_pool = base::BasePool::new(options.reject_future_operations); - ValidatedPool { - options, - listener: RwLock::new(Listener::new(rpc_response_sender)), - api, - pool: RwLock::new(base_pool), - import_notification_sinks: Default::default(), - rotator: Default::default(), - _phantom: Default::default(), - } - } - - /// Bans given set of hashes. - pub fn ban(&self, now: &Instant, hashes: impl IntoIterator) { - self.rotator.ban(now, hashes) - } - - /// Returns true if operation with given hash is currently banned from the pool. - pub fn is_banned(&self, hash: &TxHash) -> bool { - self.rotator.is_banned(hash) - } - - /// A fast check before doing any further processing of a operation, like validation. - /// - /// If `ingore_banned` is `true`, it will not check if the operation is banned. - /// - /// It checks if the operation is already imported or banned. If so, it returns an error. - pub fn check_is_known( - &self, - tx_hash: &TxHash, - ignore_banned: bool, - shard: ShardIdentifier, - ) -> Result<(), B::Error> { - if !ignore_banned && self.is_banned(tx_hash) { - Err(error::Error::TemporarilyBanned.into()) - } else if self.pool.read().unwrap().is_imported(tx_hash, shard) { - Err(error::Error::AlreadyImported.into()) - } else { - Ok(()) - } - } - - /// Imports a bunch of pre-validated operations to the pool. - pub fn submit( - &self, - txs: impl IntoIterator>, - shard: ShardIdentifier, - ) -> Vec> { - let results = txs - .into_iter() - .map(|validated_tx| self.submit_one(validated_tx, shard)) - .collect::>(); - - // only enforce limits if there is at least one imported operation - let removed = if results.iter().any(|res| res.is_ok()) { - self.enforce_limits(shard) - } else { - Default::default() - }; - - results - .into_iter() - .map(|res| match res { - Ok(ref hash) if removed.contains(hash) => - Err(error::Error::ImmediatelyDropped.into()), - other => other, - }) - .collect() - } - - /// Submit single pre-validated operation to the pool. - fn submit_one( - &self, - tx: ValidatedOperationFor, - shard: ShardIdentifier, - ) -> Result { - match tx { - ValidatedOperation::Valid(tx) => { - let imported = - self.pool.write().map_err(|_| error::Error::UnlockError)?.import(tx, shard)?; - - if let base::Imported::Ready { ref hash, .. } = imported { - self.import_notification_sinks - .lock() - .map_err(|_| error::Error::UnlockError)? - .retain_mut(|sink| match sink.try_send(*hash) { - Ok(()) => true, - Err(e) => - if e.is_full() { - log::warn!(target: "txpool", "[{:?}] Trying to notify an import but the channel is full", hash); - true - } else { - false - }, - }); - } - - let mut listener = self.listener.write().map_err(|_| error::Error::UnlockError)?; - fire_events(&mut listener, &imported); - Ok(*imported.hash()) - }, - ValidatedOperation::Invalid(hash, err) => { - self.rotator.ban(&Instant::now(), core::iter::once(hash)); - Err(err) - }, - ValidatedOperation::Unknown(hash, err) => { - self.listener.write().unwrap().invalid(&hash); - Err(err) - }, - } - } - - fn enforce_limits(&self, shard: ShardIdentifier) -> HashSet { - let status = self.pool.read().unwrap().status(shard); - let ready_limit = &self.options.ready; - let future_limit = &self.options.future; - - log::debug!(target: "txpool", "Pool Status: {:?}", status); - if ready_limit.is_exceeded(status.ready, status.ready_bytes) - || future_limit.is_exceeded(status.future, status.future_bytes) - { - log::debug!( - target: "txpool", - "Enforcing limits ({}/{}kB ready, {}/{}kB future", - ready_limit.count, ready_limit.total_bytes / 1024, - future_limit.count, future_limit.total_bytes / 1024, - ); - - // clean up the pool - let removed = { - let mut pool = self.pool.write().unwrap(); - let removed = pool - .enforce_limits(ready_limit, future_limit, shard) - .into_iter() - .map(|x| x.hash) - .collect::>(); - // ban all removed operations - self.rotator.ban(&Instant::now(), removed.iter().copied()); - removed - }; - if !removed.is_empty() { - log::debug!(target: "txpool", "Enforcing limits: {} dropped", removed.len()); - } - - // run notifications - let mut listener = self.listener.write().unwrap(); - for h in &removed { - listener.dropped(h, None); - } - - removed - } else { - Default::default() - } - } - - /// Import a single extrinsic and starts to watch their progress in the pool. - pub fn submit_and_watch( - &self, - tx: ValidatedOperationFor, - shard: ShardIdentifier, - ) -> Result { - match tx { - ValidatedOperation::Valid(tx) => { - let hash_result = self - .submit(core::iter::once(ValidatedOperation::Valid(tx)), shard) - .pop() - .expect("One extrinsic passed; one result returned; qed"); - // TODO: How to return / notice if Future or Ready queue? - if let Ok(hash) = hash_result { - self.listener.write().unwrap().create_watcher(hash); - } - hash_result - }, - ValidatedOperation::Invalid(hash, err) => { - self.rotator.ban(&Instant::now(), core::iter::once(hash)); - Err(err) - }, - ValidatedOperation::Unknown(_, err) => Err(err), - } - } - - /// Resubmits revalidated operations back to the pool. - /// - /// Removes and then submits passed operations and all dependent operations. - /// Transactions that are missing from the pool are not submitted. - pub fn resubmit( - &self, - mut updated_transactions: HashMap>, - shard: ShardIdentifier, - ) { - #[derive(Debug, Clone, Copy, PartialEq)] - enum Status { - Future, - Ready, - Failed, - Dropped, - } - - let (mut initial_statuses, final_statuses) = { - let mut pool = self.pool.write().unwrap(); - - // remove all passed operations from the ready/future queues - // (this may remove additional operations as well) - // - // for every operation that has an entry in the `updated_transactions`, - // we store updated validation result in txs_to_resubmit - // for every operation that has no entry in the `updated_transactions`, - // we store last validation result (i.e. the pool entry) in txs_to_resubmit - let mut initial_statuses = HashMap::new(); - let mut txs_to_resubmit = Vec::with_capacity(updated_transactions.len()); - while !updated_transactions.is_empty() { - let hash = updated_transactions - .keys() - .next() - .cloned() - .expect("operations is not empty; qed"); - - // note we are not considering tx with hash invalid here - we just want - // to remove it along with dependent operations and `remove_subtree()` - // does exactly what we need - let removed = pool.remove_subtree(&[hash], shard); - for removed_tx in removed { - let removed_hash = removed_tx.hash; - let updated_transaction = updated_transactions.remove(&removed_hash); - let tx_to_resubmit = if let Some(updated_tx) = updated_transaction { - updated_tx - } else { - // in most cases we'll end up in successful `try_unwrap`, but if not - // we still need to reinsert operation back to the pool => duplicate call - let operation = match Arc::try_unwrap(removed_tx) { - Ok(operation) => operation, - Err(operation) => operation.duplicate(), - }; - ValidatedOperation::Valid(operation) - }; - - initial_statuses.insert(removed_hash, Status::Ready); - txs_to_resubmit.push((removed_hash, tx_to_resubmit)); - } - // make sure to remove the hash even if it's not present in the pool any more. - updated_transactions.remove(&hash); - } - - // if we're rejecting future operations, then insertion order matters here: - // if tx1 depends on tx2, then if tx1 is inserted before tx2, then it goes - // to the future queue and gets rejected immediately - // => let's temporary stop rejection and clear future queue before return - pool.with_futures_enabled(|pool, reject_future_operations| { - // now resubmit all removed operations back to the pool - let mut final_statuses = HashMap::new(); - for (hash, tx_to_resubmit) in txs_to_resubmit { - match tx_to_resubmit { - ValidatedOperation::Valid(tx) => match pool.import(tx, shard) { - Ok(imported) => match imported { - base::Imported::Ready { promoted, failed, removed, .. } => { - final_statuses.insert(hash, Status::Ready); - for hash in promoted { - final_statuses.insert(hash, Status::Ready); - } - for hash in failed { - final_statuses.insert(hash, Status::Failed); - } - for tx in removed { - final_statuses.insert(tx.hash, Status::Dropped); - } - }, - base::Imported::Future { .. } => { - final_statuses.insert(hash, Status::Future); - }, - }, - Err(err) => { - // we do not want to fail if single operation import has failed - // nor we do want to propagate this error, because it could tx unknown to caller - // => let's just notify listeners (and issue debug message) - log::warn!( - target: "txpool", - "[{:?}] Removing invalid operation from update: {:?}", - hash, - err, - ); - final_statuses.insert(hash, Status::Failed); - }, - }, - ValidatedOperation::Invalid(_, _) | ValidatedOperation::Unknown(_, _) => { - final_statuses.insert(hash, Status::Failed); - }, - } - } - - // if the pool is configured to reject future operations, let's clear the future - // queue, updating final statuses as required - if reject_future_operations { - for future_tx in pool.clear_future(shard) { - final_statuses.insert(future_tx.hash, Status::Dropped); - } - } - - (initial_statuses, final_statuses) - }) - }; - - // and now let's notify listeners about status changes - let mut listener = self.listener.write().unwrap(); - for (hash, final_status) in final_statuses { - let initial_status = initial_statuses.remove(&hash); - if initial_status.is_none() || Some(final_status) != initial_status { - match final_status { - Status::Future => listener.future(&hash), - Status::Ready => listener.ready(&hash, None), - Status::Dropped => listener.dropped(&hash, None), - Status::Failed => listener.invalid(&hash), - } - } - } - } - - /// For each extrinsic, returns tags that it provides (if known), or None (if it is unknown). - pub fn extrinsics_tags( - &self, - hashes: &[TxHash], - shard: ShardIdentifier, - ) -> Vec>> { - self.pool - .read() - .unwrap() - .by_hashes(hashes, shard) - .into_iter() - .map(|existing_in_pool| existing_in_pool.map(|operation| operation.provides.to_vec())) - .collect() - } - - /// Get ready operation by hash - pub fn ready_by_hash( - &self, - hash: &TxHash, - shard: ShardIdentifier, - ) -> Option> { - self.pool.read().unwrap().ready_by_hash(hash, shard) - } - - /// Prunes ready operations that provide given list of tags. - pub fn prune_tags( - &self, - tags: impl IntoIterator, - shard: ShardIdentifier, - ) -> Result, B::Error> { - // Perform tag-based pruning in the base pool - let status = self.pool.write().unwrap().prune_tags(tags, shard); - // Notify event listeners of all operations - // that were promoted to `Ready` or were dropped. - { - let mut listener = self.listener.write().unwrap(); - for promoted in &status.promoted { - fire_events(&mut *listener, promoted); - } - for f in &status.failed { - listener.dropped(f, None); - } - } - - Ok(status) - } - - /// Resubmit operations that have been revalidated after prune_tags call. - pub fn resubmit_pruned( - &self, - at: &BlockId, - known_imported_hashes: impl IntoIterator + Clone, - pruned_hashes: Vec, - pruned_xts: Vec>, - shard: ShardIdentifier, - ) -> Result<(), B::Error> - where - ::Error: error::IntoPoolError, - { - debug_assert_eq!(pruned_hashes.len(), pruned_xts.len()); - - // Resubmit pruned operations - let results = self.submit(pruned_xts, shard); - - // Collect the hashes of operations that now became invalid (meaning that they are successfully pruned). - let hashes = results.into_iter().enumerate().filter_map(|(idx, r)| { - match r.map_err(error::IntoPoolError::into_pool_error) { - Err(Ok(error::Error::InvalidTrustedOperation)) => Some(pruned_hashes[idx]), - _ => None, - } - }); - // Fire `pruned` notifications for collected hashes and make sure to include - // `known_imported_hashes` since they were just imported as part of the block. - let hashes = hashes.chain(known_imported_hashes.into_iter()); - self.fire_pruned(at, hashes)?; - - // perform regular cleanup of old operations in the pool - // and update temporary bans. - self.clear_stale(at, shard)?; - Ok(()) - } - - /// Fire notifications for pruned operations. - pub fn fire_pruned( - &self, - at: &BlockId, - hashes: impl Iterator, - ) -> Result<(), B::Error> { - let header_hash = self - .api - .block_id_to_hash(at)? - .ok_or_else(|| error::Error::InvalidBlockId(format!("{:?}", at)))?; - let mut listener = self.listener.write().unwrap(); - let mut set = HashSet::with_capacity(hashes.size_hint().0); - for h in hashes { - // `hashes` has possibly duplicate hashes. - // we'd like to send out the `InBlock` notification only once. - if !set.contains(&h) { - listener.pruned(header_hash, &h); - set.insert(h); - } - } - Ok(()) - } - - /// Removes stale operations from the pool. - /// - /// Stale operations are operation beyond their longevity period. - /// Note this function does not remove operations that are already included in the chain. - /// See `prune_tags` if you want this. - pub fn clear_stale( - &self, - at: &BlockId, - shard: ShardIdentifier, - ) -> Result<(), B::Error> { - let block_number = self - .api - .block_id_to_number(at)? - .ok_or_else(|| error::Error::InvalidBlockId(format!("{:?}", at)))? - .saturated_into::(); - let now = Instant::now(); - let to_remove = { - self.ready(shard) - .filter(|tx| self.rotator.ban_if_stale(&now, block_number, tx)) - .map(|tx| tx.hash) - .collect::>() - }; - let futures_to_remove: Vec = { - let p = self.pool.read().unwrap(); - let mut hashes = Vec::new(); - for tx in p.futures(shard) { - if self.rotator.ban_if_stale(&now, block_number, tx) { - hashes.push(tx.hash); - } - } - hashes - }; - // removing old operations - self.remove_invalid(&to_remove, shard, false); - self.remove_invalid(&futures_to_remove, shard, false); - // clear banned operations timeouts - self.rotator.clear_timeouts(&now); - - Ok(()) - } - - /// Get rotator reference. - /// only used for test - pub fn rotator(&self) -> &PoolRotator { - &self.rotator - } - - /// Get api reference. - pub fn api(&self) -> &B { - &self.api - } - - /// Return an event stream of notifications for when operations are imported to the pool. - /// - /// Consumers of this stream should use the `ready` method to actually get the - /// pending operations in the right order. - pub fn import_notification_stream(&self) -> EventStream { - const CHANNEL_BUFFER_SIZE: usize = 1024; - - let (sink, stream) = channel(CHANNEL_BUFFER_SIZE); - self.import_notification_sinks.lock().unwrap().push(sink); - stream - } - - /// Invoked when extrinsics are broadcasted. - pub fn on_broadcasted(&self, propagated: HashMap>) { - let mut listener = self.listener.write().unwrap(); - for (hash, peers) in propagated.into_iter() { - listener.broadcasted(&hash, peers); - } - } - - /// Remove a subtree of operations from the pool and mark them invalid. - /// - /// The operations passed as an argument will be additionally banned - /// to prevent them from entering the pool right away. - /// Note this is not the case for the dependent operations - those may - /// still be valid so we want to be able to re-import them. - pub fn remove_invalid( - &self, - hashes: &[TxHash], - shard: ShardIdentifier, - inblock: bool, - ) -> Vec> { - // early exit in case there is no invalid operations. - if hashes.is_empty() { - return vec![] - } - - let invalid = self.pool.write().unwrap().remove_subtree(hashes, shard); - - log::debug!(target: "txpool", "Removed invalid operations: {:?}", invalid); - - let mut listener = self.listener.write().unwrap(); - if inblock { - for _tx in &invalid { - //listener.in_block(&tx.hash); - } - } else { - // temporarily ban invalid operations - self.rotator.ban(&Instant::now(), hashes.iter().cloned()); - for tx in &invalid { - listener.invalid(&tx.hash); - } - } - - invalid - } - - /// Get an iterator for ready operations ordered by priority - pub fn ready( - &self, - shard: ShardIdentifier, - ) -> impl Iterator> + Send { - self.pool.read().unwrap().ready(shard) - } - - /// Get an iterator for all shards - pub fn shards(&self) -> Vec { - let mut shards = vec![]; - let base_pool = self.pool.read().unwrap(); - let shard_iterator = base_pool.get_shards(); - for shard in shard_iterator { - shards.push(*shard); - } - shards - } - - /// Returns pool status. - pub fn status(&self, shard: ShardIdentifier) -> PoolStatus { - self.pool.read().unwrap().status(shard) - } - - /// Notify all watchers that operations in the block with hash have been finalized - pub async fn on_block_finalized(&self, block_hash: SidechainBlockHash) -> Result<(), B::Error> - where - <::Block as sp_runtime::traits::Block>::Hash: core::fmt::Display, - { - log::trace!(target: "txpool", "Attempting to notify watchers of finalization for {}", block_hash); - self.listener.write().unwrap().finalized(block_hash); - Ok(()) - } - - /// Notify the listener of retracted blocks - pub fn on_block_retracted(&self, block_hash: SidechainBlockHash) { - self.listener.write().unwrap().retracted(block_hash) - } - - /// Notify the listener of top inclusion in sidechain block - pub fn on_block_imported(&self, hashes: &[TxHash], block_hash: SidechainBlockHash) { - for top_hash in hashes.iter() { - self.listener.write().unwrap().in_block(top_hash, block_hash); - } - } - - #[allow(clippy::type_complexity)] - pub fn update_connection_state(&self, updates: Vec<(TxHash, (Vec, bool))>) { - for (top_hash, (encoded_value, force_wait)) in updates { - self.listener.write().unwrap().update_connection_state( - &top_hash, - encoded_value.clone(), - force_wait, - ); - self.listener - .write() - .unwrap() - .top_executed(&top_hash, &encoded_value, force_wait); - } - } - - pub fn swap_rpc_connection_hash(&self, old_hash: TxHash, new_hash: TxHash) { - self.listener.write().unwrap().swap_rpc_connection_hash(old_hash, new_hash); - } -} - -fn fire_events(listener: &mut Listener, imported: &base::Imported) -where - R: SendRpcResponse, -{ - match *imported { - base::Imported::Ready { ref promoted, ref failed, ref removed, ref hash } => { - listener.ready(hash, None); - for f in failed { - listener.invalid(f); - } - for r in removed { - listener.dropped(&r.hash, Some(hash)); - } - for p in promoted { - listener.ready(p, None); - } - }, - base::Imported::Future { ref hash } => listener.future(hash), - } -} diff --git a/tee-worker/bitacross/core-primitives/top-pool/src/watcher.rs b/tee-worker/bitacross/core-primitives/top-pool/src/watcher.rs deleted file mode 100644 index 018bdf82a8..0000000000 --- a/tee-worker/bitacross/core-primitives/top-pool/src/watcher.rs +++ /dev/null @@ -1,176 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! Extrinsics status updates. - -extern crate alloc; -use crate::primitives::TxHash; -use alloc::{string::String, sync::Arc, vec::Vec}; - -use itc_direct_rpc_server::{DirectRpcError, SendRpcResponse}; -use itp_types::{BlockHash as SidechainBlockHash, TrustedOperationStatus}; -use log::*; - -/// Extrinsic watcher. -/// -/// Represents a stream of status updates for particular extrinsic. -#[derive(Debug)] -pub struct Watcher { - //receiver: TracingUnboundedReceiver>, - hash: TxHash, - is_in_block: bool, - rpc_response_sender: Arc, -} - -impl Watcher -where - S: SendRpcResponse, -{ - /// Returns the operation hash. - pub fn hash(&self) -> &TxHash { - &self.hash - } - - pub fn new_watcher(hash: TxHash, rpc_response_sender: Arc) -> Self { - Watcher { hash, is_in_block: false, rpc_response_sender } - } - - /// TrustedOperation became ready. - pub fn ready(&mut self) { - self.send(TrustedOperationStatus::Ready) - } - - /// TrustedOperation was moved to future. - pub fn future(&mut self) { - self.send(TrustedOperationStatus::Future) - } - - /// Some state change (perhaps another extrinsic was included) rendered this extrinsic invalid. - pub fn usurped(&mut self) { - //self.send(TrustedOperationStatus::Usurped(hash)); - self.send(TrustedOperationStatus::Usurped); - self.is_in_block = true; - } - - /// Extrinsic has been included in block with given hash. - pub fn in_block(&mut self, block_hash: SidechainBlockHash) { - self.send(TrustedOperationStatus::InSidechainBlock(block_hash)); - self.is_in_block = true; - } - - /// Extrinsic has been finalized by a finality gadget. - pub fn finalized(&mut self) { - //self.send(TrustedOperationStatus::Finalized(hash)); - self.send(TrustedOperationStatus::Finalized); - self.is_in_block = true; - } - - /// The block this extrinsic was included in has been retracted - pub fn finality_timeout(&mut self) { - //self.send(TrustedOperationStatus::FinalityTimeout(hash)); - self.send(TrustedOperationStatus::FinalityTimeout); - self.is_in_block = true; - } - - /// The block this extrinsic was included in has been retracted - pub fn retracted(&mut self) { - //self.send(TrustedOperationStatus::Retracted(hash)); - self.send(TrustedOperationStatus::Retracted); - } - - /// Extrinsic has been marked as invalid by the block builder. - pub fn invalid(&mut self) { - self.send(TrustedOperationStatus::Invalid); - // we mark as finalized as there are no more notifications - self.is_in_block = true; - } - - /// TrustedOperation has been dropped from the pool because of the limit. - pub fn dropped(&mut self) { - self.send(TrustedOperationStatus::Dropped); - self.is_in_block = true; - } - - /// The extrinsic has been broadcast to the given peers. - pub fn broadcast(&mut self, _peers: Vec) { - //self.send(TrustedOperationStatus::Broadcast(peers)) - self.send(TrustedOperationStatus::Broadcast) - } - - /// The extrinsic has been executed. - pub fn top_executed(&mut self, response: &[u8], force_wait: bool) { - self.send(TrustedOperationStatus::TopExecuted(response.to_vec(), force_wait)) - } - - /// Returns true if the are no more listeners for this extrinsic or it was finalized. - pub fn is_done(&self) -> bool { - self.is_in_block // || self.receivers.is_empty() - } - - fn send(&mut self, status: TrustedOperationStatus) { - if let Err(e) = self.rpc_response_sender.update_status_event(*self.hash(), status) { - match e { - DirectRpcError::InvalidConnectionHash => { - warn!("Client connection interrupted while sending status update"); - }, - _ => error!("Failed to send status update to RPC client: {:?}", e), - } - } - } - - // Litentry: set the new rpc response value and force_wait flag - pub fn update_connection_state(&mut self, encoded_value: Vec, force_wait: bool) { - if let Err(e) = self.rpc_response_sender.update_connection_state( - *self.hash(), - encoded_value, - force_wait, - ) { - warn!("failed to update connection state: {:?}", e); - } - } - - // Litentry: swap the old hash with the new one in rpc connection registry - pub fn swap_rpc_connection_hash(&self, new_hash: TxHash) { - if let Err(e) = self.rpc_response_sender.swap_hash(*self.hash(), new_hash) { - warn!("failed to swap rpc connection hash: {:?}", e); - } - } -} - -/* /// Sender part of the watcher. Exposed only for testing purposes. -#[derive(Debug)] -pub struct Sender { - //receivers: Vec>>, - //receivers: Vec, - is_in_block: bool, -} - */ -/* impl Default for Watcher { - fn default() -> Self { - Watcher { - //receivers: Default::default(), - hash: , - is_in_block: false, - } - } -} */ - -/* impl Sender { - /// Add a new watcher to this sender object. - -} */ diff --git a/tee-worker/bitacross/core/direct-rpc-client/Cargo.toml b/tee-worker/bitacross/core/direct-rpc-client/Cargo.toml deleted file mode 100644 index 0205299ad8..0000000000 --- a/tee-worker/bitacross/core/direct-rpc-client/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -[package] -name = "bc-itc-direct-rpc-client" -version = "0.1.0" -authors = ['Trust Computing GmbH '] -edition = "2021" - -[dependencies] -rustls_sgx = { workspace = true, features = ["dangerous_configuration"], optional = true } -sgx_tstd = { workspace = true, optional = true } -tungstenite_sgx = { workspace = true, optional = true } -webpki_sgx = { workspace = true, optional = true } - -log = { workspace = true } -serde_json = { workspace = true } -url = { workspace = true } - -rustls = { workspace = true, features = ["dangerous_configuration"], optional = true } -tungstenite = { workspace = true, features = ["rustls-tls-webpki-roots"], optional = true } -webpki = { workspace = true, optional = true } - -itp-rpc = { workspace = true } -itp-types = { workspace = true } -itp-utils = { workspace = true } - -[features] -default = ["std"] -sgx = [ - "webpki_sgx", - "tungstenite_sgx", - "rustls_sgx", - "sgx_tstd", - "itp-rpc/sgx", -] -std = [ - "rustls", - "webpki", - "tungstenite", - "url/std", - "itp-rpc/std", - "itp-types/std", - "itp-utils/std", - "log/std", -] diff --git a/tee-worker/bitacross/core/direct-rpc-client/src/lib.rs b/tee-worker/bitacross/core/direct-rpc-client/src/lib.rs deleted file mode 100644 index dbc07484f0..0000000000 --- a/tee-worker/bitacross/core/direct-rpc-client/src/lib.rs +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -// re-export module to properly feature gate sgx and regular std environment -#[cfg(all(not(feature = "std"), feature = "sgx"))] -pub mod sgx_reexport_prelude { - pub use rustls_sgx as rustls; - pub use tungstenite_sgx as tungstenite; - pub use webpki_sgx as webpki; -} - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; - -extern crate alloc; - -use alloc::format; - -use core::str::FromStr; - -use log::{debug, error}; - -use itp_rpc::{Id, RpcRequest, RpcReturnValue}; - -use std::{ - boxed::Box, - error::Error, - net::TcpStream, - string::String, - sync::{ - mpsc::{channel, Sender}, - Arc, - }, - time::Duration, - vec::Vec, -}; -use tungstenite::{client_tls_with_config, stream::MaybeTlsStream, Connector, Message, WebSocket}; -use url::Url; -use webpki::{DNSName, DNSNameRef}; - -pub type Response = (Id, RpcReturnValue); - -pub struct IgnoreCertVerifier {} - -impl rustls::ServerCertVerifier for IgnoreCertVerifier { - fn verify_server_cert( - &self, - _: &rustls::RootCertStore, - _: &[rustls::Certificate], - _: DNSNameRef<'_>, - _: &[u8], - ) -> Result { - log::warn!("Using NoCertVerifier"); - Ok(rustls::ServerCertVerified::assertion()) - } -} - -impl rustls::ClientCertVerifier for IgnoreCertVerifier { - fn client_auth_root_subjects( - &self, - _sni: Option<&DNSName>, - ) -> Option { - None - } - - fn verify_client_cert( - &self, - _presented_certs: &[rustls::Certificate], - _sni: Option<&DNSName>, - ) -> Result { - Ok(rustls::ClientCertVerified::assertion()) - } -} - -pub trait RpcClientFactory { - type Client: RpcClient + Send + Clone; - fn create(&self, url: &str) -> Result>; -} - -pub struct DirectRpcClientFactory {} - -impl RpcClientFactory for DirectRpcClientFactory { - type Client = DirectRpcClient; - - fn create(&self, url: &str) -> Result> { - DirectRpcClient::new(url) - } -} - -pub trait RpcClient { - fn send(&mut self, request: &RpcRequest) -> Result<(), Box>; -} - -#[derive(Clone)] -pub struct DirectRpcClient { - request_sink: Sender<(String, Sender)>, -} - -impl DirectRpcClient { - pub fn new(url: &str) -> Result> { - let server_url = - Url::from_str(url).map_err(|e| format!("Could not connect, reason: {:?}", e))?; - let mut config = rustls::ClientConfig::new(); - // we need to set this cert verifier or client will fail to connect with following error - // HandshakeError::Failure(Io(Custom { kind: InvalidData, error: WebPKIError(UnknownIssuer) })) - config.dangerous().set_certificate_verifier(Arc::new(IgnoreCertVerifier {})); - let connector = Connector::Rustls(Arc::new(config)); - let stream = TcpStream::connect(server_url.authority()) - .map_err(|e| format!("Could not connect to {:?}, reason: {:?}", url, e))?; - - let (mut socket, _response) = - client_tls_with_config(server_url.as_str(), stream, None, Some(connector)) - .map_err(|e| format!("Could not open websocket connection: {:?}", e))?; - - let (request_sender, request_receiver) = channel::<(String, Sender)>(); - - //it fails to perform handshake in non_blocking mode so we are setting it up after the handshake is performed - Self::switch_to_non_blocking(&mut socket); - - std::thread::spawn(move || { - while let Ok((request, result_sender)) = request_receiver.recv() { - let mut result = true; - if let Err(e) = socket.write_message(Message::Text(request)) { - error!("Could not write message to socket, reason: {:?}", e); - result = false; - } - if let Err(e) = result_sender.send(result) { - log::error!("Could not send rpc result back, reason: {:?}", e); - } - } - }); - debug!("Connected to peer: {}", url); - Ok(Self { request_sink: request_sender }) - } - - fn switch_to_non_blocking(socket: &mut WebSocket>) { - match socket.get_ref() { - MaybeTlsStream::Plain(stream) => { - stream.set_nonblocking(true).expect("set_nonblocking call failed"); - stream - .set_read_timeout(Some(Duration::from_millis(5))) - .expect("set_read_timeout call failed"); - }, - MaybeTlsStream::Rustls(stream) => { - stream.get_ref().set_nonblocking(true).expect("set_nonblocking call failed"); - stream - .get_ref() - .set_read_timeout(Some(Duration::from_millis(1))) - .expect("set_read_timeout call failed"); - }, - _ => {}, - } - } -} - -#[derive(Clone)] -pub enum RequestParams { - Rsa(Vec), - Aes(Vec), -} - -impl RpcClient for DirectRpcClient { - fn send(&mut self, request: &RpcRequest) -> Result<(), Box> { - let request = serde_json::to_string(request) - .map_err(|e| format!("Could not parse RpcRequest {:?}", e))?; - let (sender, receiver) = channel(); - self.request_sink - .send((request, sender)) - .map_err(|e| format!("Could not parse RpcRequest {:?}", e))?; - - if receiver.recv()? { - Ok(()) - } else { - Err("Could not send request".into()) - } - } -} diff --git a/tee-worker/bitacross/core/direct-rpc-server/Cargo.toml b/tee-worker/bitacross/core/direct-rpc-server/Cargo.toml deleted file mode 100644 index c913c9e6b6..0000000000 --- a/tee-worker/bitacross/core/direct-rpc-server/Cargo.toml +++ /dev/null @@ -1,49 +0,0 @@ -[package] -name = "bc-itc-direct-rpc-server" -version = "0.1.0" -authors = ['Trust Computing GmbH ', 'Integritee AG '] -edition = "2021" - -[dependencies] -sgx_tstd = { workspace = true, features = ["net", "thread"], optional = true } - -codec = { package = "parity-scale-codec", workspace = true } -log = { workspace = true } -serde_json = { workspace = true } -sp-runtime = { workspace = true } - -itc-tls-websocket-server = { workspace = true } -itp-rpc = { workspace = true } -itp-types = { workspace = true } -itp-utils = { workspace = true } - -jsonrpc-core = { workspace = true, optional = true } -jsonrpc-core_sgx = { workspace = true, optional = true } -thiserror = { workspace = true, optional = true } -thiserror_sgx = { workspace = true, optional = true } - -[features] -default = ["std"] -std = [ - # no-std dependencies - "codec/std", - "log/std", - "serde_json/std", - "sp-runtime/std", - # integritee dependencies - "itp-types/std", - # local - "itc-tls-websocket-server/std", - "itp-rpc/std", - # optional ones - "jsonrpc-core", - "thiserror", -] -sgx = [ - "itc-tls-websocket-server/sgx", - "itp-rpc/sgx", - "jsonrpc-core_sgx", - "sgx_tstd", - "thiserror_sgx", -] -mocks = [] diff --git a/tee-worker/bitacross/core/direct-rpc-server/src/builders/mod.rs b/tee-worker/bitacross/core/direct-rpc-server/src/builders/mod.rs deleted file mode 100644 index ea028434c4..0000000000 --- a/tee-worker/bitacross/core/direct-rpc-server/src/builders/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pub mod rpc_response_builder; -pub mod rpc_return_value_builder; diff --git a/tee-worker/bitacross/core/direct-rpc-server/src/builders/rpc_response_builder.rs b/tee-worker/bitacross/core/direct-rpc-server/src/builders/rpc_response_builder.rs deleted file mode 100644 index 9cc85cf369..0000000000 --- a/tee-worker/bitacross/core/direct-rpc-server/src/builders/rpc_response_builder.rs +++ /dev/null @@ -1,64 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::builders::rpc_return_value_builder::RpcReturnValueBuilder; -use itp_rpc::{Id, RpcResponse, RpcReturnValue}; -use itp_utils::ToHexPrefixed; - -/// builder pattern for RpcResponse -pub struct RpcResponseBuilder { - maybe_id: Option, - maybe_json_rpc: Option, - maybe_result: Option, -} - -impl RpcResponseBuilder { - #[allow(unused)] - pub fn new() -> Self { - RpcResponseBuilder { maybe_id: None, maybe_json_rpc: None, maybe_result: None } - } - - #[allow(unused)] - pub fn with_id(mut self, id: u32) -> Self { - self.maybe_id = Some(id); - self - } - - #[allow(unused)] - pub fn with_json_rpc(mut self, json_rpc: String) -> Self { - self.maybe_json_rpc = Some(json_rpc); - self - } - - #[allow(unused)] - pub fn with_result(mut self, result: RpcReturnValue) -> Self { - self.maybe_result = Some(result); - self - } - - #[allow(unused)] - pub fn build(self) -> RpcResponse { - let id = Id::Number(self.maybe_id.unwrap_or(1u32)); - let json_rpc = self.maybe_json_rpc.unwrap_or(String::from("json_rpc")); - let result = self - .maybe_result - .unwrap_or_else(|| RpcReturnValueBuilder::new().build()) - .to_hex(); - - RpcResponse { result, jsonrpc: json_rpc, id } - } -} diff --git a/tee-worker/bitacross/core/direct-rpc-server/src/builders/rpc_return_value_builder.rs b/tee-worker/bitacross/core/direct-rpc-server/src/builders/rpc_return_value_builder.rs deleted file mode 100644 index 126d58e985..0000000000 --- a/tee-worker/bitacross/core/direct-rpc-server/src/builders/rpc_return_value_builder.rs +++ /dev/null @@ -1,62 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use codec::Encode; -use itp_rpc::RpcReturnValue; -use itp_types::DirectRequestStatus; -use std::{string::String, vec::Vec}; - -/// Builder pattern for a RpcReturnValue -pub struct RpcReturnValueBuilder { - maybe_do_watch: Option, - maybe_status: Option, - maybe_value: Option>, -} - -impl RpcReturnValueBuilder { - #[allow(unused)] - pub fn new() -> Self { - RpcReturnValueBuilder { maybe_do_watch: None, maybe_status: None, maybe_value: None } - } - - #[allow(unused)] - pub fn with_do_watch(mut self, do_watch: bool) -> Self { - self.maybe_do_watch = Some(do_watch); - self - } - - #[allow(unused)] - pub fn with_status(mut self, status: DirectRequestStatus) -> Self { - self.maybe_status = Some(status); - self - } - - #[allow(unused)] - pub fn with_value(mut self, value: Vec) -> Self { - self.maybe_value = Some(value); - self - } - - #[allow(unused)] - pub fn build(self) -> RpcReturnValue { - let do_watch = self.maybe_do_watch.unwrap_or(false); - let status = self.maybe_status.unwrap_or(DirectRequestStatus::Ok); - let value = self.maybe_value.unwrap_or(String::from("value").encode()); - - RpcReturnValue { value, do_watch, status } - } -} diff --git a/tee-worker/bitacross/core/direct-rpc-server/src/lib.rs b/tee-worker/bitacross/core/direct-rpc-server/src/lib.rs deleted file mode 100644 index 8b779064eb..0000000000 --- a/tee-worker/bitacross/core/direct-rpc-server/src/lib.rs +++ /dev/null @@ -1,165 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -#![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(test, feature(assert_matches))] - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -extern crate alloc; - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -// re-export module to properly feature gate sgx and regular std environment -#[cfg(all(not(feature = "std"), feature = "sgx"))] -pub mod sgx_reexport_prelude { - pub use jsonrpc_core_sgx as jsonrpc_core; - pub use thiserror_sgx as thiserror; -} - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; - -use crate::rpc_watch_extractor::RpcWatchExtractor; -use codec::{Encode, Error as CodecError}; -use itc_tls_websocket_server::error::WebSocketError; -use itp_rpc::RpcResponse; -use itp_types::{DirectRequestStatus, TrustedOperationStatus, H256}; -use serde_json::error::Error as SerdeJsonError; -use sp_runtime::traits; -use std::{boxed::Box, fmt::Debug, vec::Vec}; - -#[cfg(any(test, feature = "mocks"))] -pub mod mocks; - -#[cfg(test)] -mod builders; - -pub mod response_channel; -pub mod rpc_connection_registry; -pub mod rpc_responder; -pub mod rpc_watch_extractor; -pub mod rpc_ws_handler; - -/// General web-socket error type -#[derive(Debug, thiserror::Error)] -pub enum DirectRpcError { - #[error("Invalid connection hash")] - InvalidConnectionHash, - #[error("RPC serialization error: {0}")] - SerializationError(SerdeJsonError), - #[error("Web socket error: {0}")] - WebSocketError(#[from] WebSocketError), - #[error("Encoding error: {0}")] - EncodingError(CodecError), - #[error("Other error: {0}")] - Other(Box), - // Litentry - #[error("Hash conversion error")] - HashConversionError, -} - -pub type DirectRpcResult = Result; - -/// trait helper to mix-in all necessary traits for a hash -pub trait RpcHash: std::hash::Hash + traits::Member + Encode { - fn maybe_h256(&self) -> Option; -} -impl RpcHash for T { - fn maybe_h256(&self) -> Option { - let enc = self.encode(); - if enc.len() == 32 { - let mut inner = [0u8; 32]; - inner.copy_from_slice(&enc); - Some(inner.into()) - } else { - None - } - } -} - -pub type ForceWait = bool; - -/// Registry for RPC connections (i.e. connections that are kept alive to send updates). -pub trait RpcConnectionRegistry: Send + Sync { - type Hash: RpcHash; - type Connection: Copy + Debug; - - fn store( - &self, - hash: Self::Hash, - connection: Self::Connection, - rpc_response: RpcResponse, - force_wait: ForceWait, - ); - - fn withdraw(&self, hash: &Self::Hash) -> Option<(Self::Connection, RpcResponse, ForceWait)>; - - fn is_force_wait(&self, hash: &Self::Hash) -> bool; -} - -/// Sends an RPC response back to the client. -pub trait SendRpcResponse: Send + Sync { - type Hash: RpcHash; - - fn update_status_event( - &self, - hash: Self::Hash, - status_update: TrustedOperationStatus, - ) -> DirectRpcResult<()>; - - fn send_state(&self, hash: Self::Hash, state_encoded: Vec) -> DirectRpcResult<()>; - - fn send_state_with_status( - &self, - hash: Self::Hash, - state_encoded: Vec, - status: DirectRequestStatus, - ) -> DirectRpcResult<()>; - - fn update_force_wait(&self, hash: Self::Hash, force_wait: bool) -> DirectRpcResult<()>; - - // Litentry: update the `value` field in the returning structure and connection force_wait flag - fn update_connection_state( - &self, - hash: Self::Hash, - encoded_value: Vec, - force_wait: bool, - ) -> DirectRpcResult<()>; - - // Litentry: swap the old hash with the new one in rpc connection registry - fn swap_hash(&self, old_hash: Self::Hash, new_hash: Self::Hash) -> DirectRpcResult<()>; - - fn is_force_wait(&self, hash: Self::Hash) -> bool; -} - -/// Determines if a given connection must be watched (i.e. kept alive), -/// based on the information in the RpcResponse. -pub trait DetermineWatch: Send + Sync { - type Hash: RpcHash; - - fn must_be_watched(&self, rpc_response: &RpcResponse) -> DirectRpcResult>; -} - -/// Convenience method to create a do_watch extractor. -pub fn create_determine_watch() -> RpcWatchExtractor -where - Hash: RpcHash, -{ - RpcWatchExtractor::::new() -} diff --git a/tee-worker/bitacross/core/direct-rpc-server/src/mocks/determine_watch_mock.rs b/tee-worker/bitacross/core/direct-rpc-server/src/mocks/determine_watch_mock.rs deleted file mode 100644 index c01730390d..0000000000 --- a/tee-worker/bitacross/core/direct-rpc-server/src/mocks/determine_watch_mock.rs +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{DetermineWatch, DirectRpcResult, RpcHash}; -use itp_rpc::RpcResponse; - -pub struct DetermineWatchMock -where - Hash: RpcHash, -{ - watch_next: Option, -} - -impl DetermineWatchMock -where - Hash: RpcHash, -{ - #[allow(unused)] - pub fn do_watch(hash: Hash) -> Self { - DetermineWatchMock { watch_next: Some(hash) } - } - - #[allow(unused)] - pub fn no_watch() -> Self { - DetermineWatchMock { watch_next: None } - } -} - -impl DetermineWatch for DetermineWatchMock -where - Hash: RpcHash, -{ - type Hash = Hash; - - fn must_be_watched(&self, _rpc_response: &RpcResponse) -> DirectRpcResult> { - Ok(self.watch_next.clone()) - } -} diff --git a/tee-worker/bitacross/core/direct-rpc-server/src/mocks/mod.rs b/tee-worker/bitacross/core/direct-rpc-server/src/mocks/mod.rs deleted file mode 100644 index 011b4d9905..0000000000 --- a/tee-worker/bitacross/core/direct-rpc-server/src/mocks/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pub mod determine_watch_mock; -pub mod response_channel_mock; -pub mod send_rpc_response_mock; diff --git a/tee-worker/bitacross/core/direct-rpc-server/src/mocks/response_channel_mock.rs b/tee-worker/bitacross/core/direct-rpc-server/src/mocks/response_channel_mock.rs deleted file mode 100644 index 6a612d6766..0000000000 --- a/tee-worker/bitacross/core/direct-rpc-server/src/mocks/response_channel_mock.rs +++ /dev/null @@ -1,55 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(feature = "sgx")] -use std::sync::SgxRwLock as RwLock; - -#[cfg(feature = "std")] -use std::sync::RwLock; - -use crate::{response_channel::ResponseChannel, DirectRpcError}; -use std::vec::Vec; - -#[derive(Default)] -pub struct ResponseChannelMock -where - Token: Copy + Send + Sync, -{ - sent_messages: RwLock>, -} - -impl ResponseChannelMock -where - Token: Copy + Send + Sync, -{ - pub fn number_of_updates(&self) -> usize { - self.sent_messages.read().unwrap().len() - } -} - -impl ResponseChannel for ResponseChannelMock -where - Token: Copy + Send + Sync, -{ - type Error = DirectRpcError; - - fn respond(&self, token: Token, message: String) -> Result<(), Self::Error> { - let mut messages_lock = self.sent_messages.write().unwrap(); - messages_lock.push((token, message)); - Ok(()) - } -} diff --git a/tee-worker/bitacross/core/direct-rpc-server/src/mocks/send_rpc_response_mock.rs b/tee-worker/bitacross/core/direct-rpc-server/src/mocks/send_rpc_response_mock.rs deleted file mode 100644 index 671ee22f4b..0000000000 --- a/tee-worker/bitacross/core/direct-rpc-server/src/mocks/send_rpc_response_mock.rs +++ /dev/null @@ -1,83 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(feature = "sgx")] -use std::sync::SgxRwLock as RwLock; - -#[cfg(feature = "std")] -use std::sync::RwLock; - -use crate::{DirectRpcResult, RpcHash, SendRpcResponse}; -use itp_types::{DirectRequestStatus, TrustedOperationStatus}; -use std::vec::Vec; - -/// Send RPC response mock. -#[derive(Default)] -pub struct SendRpcResponseMock { - pub sent_states: RwLock)>>, -} - -impl SendRpcResponse for SendRpcResponseMock -where - HashType: RpcHash, -{ - type Hash = HashType; - - fn update_status_event( - &self, - _hash: Self::Hash, - _status_update: TrustedOperationStatus, - ) -> DirectRpcResult<()> { - unimplemented!() - } - - fn send_state(&self, hash: Self::Hash, state_encoded: Vec) -> DirectRpcResult<()> { - let mut states_lock = self.sent_states.write().unwrap(); - states_lock.push((hash, state_encoded)); - Ok(()) - } - - fn send_state_with_status( - &self, - _hash: Self::Hash, - _state_encoded: Vec, - _status: DirectRequestStatus, - ) -> DirectRpcResult<()> { - Ok(()) - } - - fn update_force_wait(&self, _hash: Self::Hash, _force_wait: bool) -> DirectRpcResult<()> { - Ok(()) - } - - fn update_connection_state( - &self, - _hash: Self::Hash, - _encoded_value: Vec, - _force_wait: bool, - ) -> DirectRpcResult<()> { - Ok(()) - } - - fn swap_hash(&self, _old_hash: Self::Hash, _new_hash: Self::Hash) -> DirectRpcResult<()> { - Ok(()) - } - - fn is_force_wait(&self, _hash: Self::Hash) -> bool { - false - } -} diff --git a/tee-worker/bitacross/core/direct-rpc-server/src/response_channel.rs b/tee-worker/bitacross/core/direct-rpc-server/src/response_channel.rs deleted file mode 100644 index b1fe6a3fea..0000000000 --- a/tee-worker/bitacross/core/direct-rpc-server/src/response_channel.rs +++ /dev/null @@ -1,26 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::DirectRpcError; -use std::string::String; - -/// Response / status update channel for an RPC call. -pub trait ResponseChannel: Send + Sync { - type Error: Into; - - fn respond(&self, token: Token, message: String) -> Result<(), Self::Error>; -} diff --git a/tee-worker/bitacross/core/direct-rpc-server/src/rpc_connection_registry.rs b/tee-worker/bitacross/core/direct-rpc-server/src/rpc_connection_registry.rs deleted file mode 100644 index 2c83986fe5..0000000000 --- a/tee-worker/bitacross/core/direct-rpc-server/src/rpc_connection_registry.rs +++ /dev/null @@ -1,140 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use std::sync::SgxRwLock as RwLock; - -#[cfg(feature = "std")] -use std::sync::RwLock; - -use crate::{ForceWait, RpcConnectionRegistry, RpcHash}; -use itp_rpc::RpcResponse; -use std::{collections::HashMap, fmt::Debug}; - -type HashMapLock = RwLock>; - -pub struct ConnectionRegistry -where - Hash: RpcHash, - Token: Copy + Send + Sync + Debug, -{ - connection_map: - HashMapLock<::Hash, (Token, RpcResponse, ForceWait)>, -} - -impl ConnectionRegistry -where - Hash: RpcHash, - Token: Copy + Send + Sync + Debug, -{ - pub fn new() -> Self { - Self::default() - } - - #[cfg(test)] - pub fn is_empty(&self) -> bool { - self.connection_map.read().unwrap().is_empty() - } -} - -impl Default for ConnectionRegistry -where - Hash: RpcHash, - Token: Copy + Send + Sync + Debug, -{ - fn default() -> Self { - ConnectionRegistry { connection_map: RwLock::new(HashMap::default()) } - } -} - -impl RpcConnectionRegistry for ConnectionRegistry -where - Hash: RpcHash, - Token: Copy + Send + Sync + Debug, -{ - type Hash = Hash; - type Connection = Token; - - fn store( - &self, - hash: Self::Hash, - connection: Self::Connection, - rpc_response: RpcResponse, - force_wait: ForceWait, - ) { - let mut map = self.connection_map.write().expect("Lock poisoning"); - map.insert(hash, (connection, rpc_response, force_wait)); - } - - fn withdraw(&self, hash: &Self::Hash) -> Option<(Self::Connection, RpcResponse, ForceWait)> { - let mut map = self.connection_map.write().expect("Lock poisoning"); - map.remove(hash) - } - - fn is_force_wait(&self, hash: &Self::Hash) -> bool { - if let Some(v) = self.connection_map.read().unwrap().get(hash) { - v.2 - } else { - false - } - } -} - -#[cfg(test)] -pub mod tests { - use super::*; - use itp_rpc::Id; - - type TestRegistry = ConnectionRegistry; - - #[test] - pub fn adding_element_with_same_hash_overwrite() { - let registry = TestRegistry::new(); - - let hash = "first".to_string(); - - registry.store(hash.clone(), 1, dummy_rpc_response(), false); - registry.store(hash.clone(), 2, dummy_rpc_response(), false); - - let connection_token = registry.withdraw(&hash).unwrap().0; - assert_eq!(2, connection_token); - } - - #[test] - pub fn withdrawing_from_empty_registry_returns_none() { - let registry = TestRegistry::new(); - - assert!(registry.withdraw(&"hash".to_string()).is_none()); - } - - #[test] - pub fn withdrawing_only_element_clears_registry() { - let registry = TestRegistry::new(); - let hash = "first".to_string(); - - registry.store(hash.clone(), 1, dummy_rpc_response(), false); - - let connection = registry.withdraw(&hash); - - assert!(connection.is_some()); - assert!(registry.is_empty()); - } - - fn dummy_rpc_response() -> RpcResponse { - RpcResponse { jsonrpc: String::new(), result: Default::default(), id: Id::Number(1u32) } - } -} diff --git a/tee-worker/bitacross/core/direct-rpc-server/src/rpc_responder.rs b/tee-worker/bitacross/core/direct-rpc-server/src/rpc_responder.rs deleted file mode 100644 index 053fc05c56..0000000000 --- a/tee-worker/bitacross/core/direct-rpc-server/src/rpc_responder.rs +++ /dev/null @@ -1,389 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - response_channel::ResponseChannel, DirectRpcError, DirectRpcResult, RpcConnectionRegistry, - RpcHash, SendRpcResponse, -}; -use alloc::format; -use itp_rpc::{RpcResponse, RpcReturnValue}; -use itp_types::{DirectRequestStatus, TrustedOperationStatus}; -use itp_utils::{FromHexPrefixed, ToHexPrefixed}; -use log::*; -use std::{sync::Arc, vec::Vec}; - -pub struct RpcResponder -where - Registry: RpcConnectionRegistry, - Hash: RpcHash, - ResponseChannelType: ResponseChannel, -{ - connection_registry: Arc, - response_channel: Arc, -} - -impl RpcResponder -where - Registry: RpcConnectionRegistry, - Hash: RpcHash, - ResponseChannelType: ResponseChannel, -{ - pub fn new( - connection_registry: Arc, - web_socket_responder: Arc, - ) -> Self { - RpcResponder { connection_registry, response_channel: web_socket_responder } - } - - fn encode_and_send_response( - &self, - connection: Registry::Connection, - rpc_response: &RpcResponse, - ) -> DirectRpcResult<()> { - let string_response = - serde_json::to_string(&rpc_response).map_err(DirectRpcError::SerializationError)?; - - self.response_channel.respond(connection, string_response).map_err(|e| e.into()) - } -} - -impl SendRpcResponse - for RpcResponder -where - Registry: RpcConnectionRegistry, - Hash: RpcHash, - ResponseChannelType: ResponseChannel, -{ - type Hash = Hash; - - fn update_status_event( - &self, - hash: Hash, - status_update: TrustedOperationStatus, - ) -> DirectRpcResult<()> { - debug!("updating status event, hash: {}, status: {:?}", hash.to_hex(), status_update); - - // withdraw removes it from the registry - let (connection_token, rpc_response, force_wait) = self - .connection_registry - .withdraw(&hash) - .ok_or(DirectRpcError::InvalidConnectionHash)?; - - let mut new_response = rpc_response.clone(); - - let mut result = RpcReturnValue::from_hex(&rpc_response.result) - .map_err(|e| DirectRpcError::Other(format!("{:?}", e).into()))?; - - // Litentry: - // connections are per trusted call, but if we expect trusted call to have a side effect of creating another trusted call (callback) - // we force connection to wait for potential TOP execution - let do_watch = continue_watching(&status_update) || force_wait; - - // update response - result.do_watch = do_watch; - result.status = DirectRequestStatus::TrustedOperationStatus( - status_update, - hash.maybe_h256().ok_or(DirectRpcError::HashConversionError)?, - ); - new_response.result = result.to_hex(); - - self.encode_and_send_response(connection_token, &new_response)?; - - if do_watch { - self.connection_registry.store(hash, connection_token, new_response, force_wait); - } - - debug!("updating status event successful"); - Ok(()) - } - - // TODO(Litentry): it seems that this fn is only used in tests? - fn send_state(&self, hash: Hash, state_encoded: Vec) -> DirectRpcResult<()> { - debug!("sending state"); - - // withdraw removes it from the registry - let (connection_token, mut response, _force_wait) = self - .connection_registry - .withdraw(&hash) - .ok_or(DirectRpcError::InvalidConnectionHash)?; - - // create return value - // TODO: Signature? - let submitted = DirectRequestStatus::TrustedOperationStatus( - TrustedOperationStatus::Submitted, - hash.maybe_h256().ok_or(DirectRpcError::HashConversionError)?, - ); - let result = RpcReturnValue::new(state_encoded, false, submitted); - - // update response - response.result = result.to_hex(); - - self.encode_and_send_response(connection_token, &response)?; - - debug!("sending state successful"); - Ok(()) - } - - fn update_force_wait(&self, hash: Self::Hash, force_wait: bool) -> DirectRpcResult<()> { - let (connection_token, rpc_response, _) = self - .connection_registry - .withdraw(&hash) - .ok_or(DirectRpcError::InvalidConnectionHash)?; - self.connection_registry.store(hash, connection_token, rpc_response, force_wait); - - Ok(()) - } - - fn is_force_wait(&self, hash: Self::Hash) -> bool { - self.connection_registry.is_force_wait(&hash) - } - - fn update_connection_state( - &self, - hash: Self::Hash, - encoded_value: Vec, - force_wait: bool, - ) -> DirectRpcResult<()> { - info!( - "updating connection state for hash {:?}: encoded_value {:?}, force_wait: {:?}", - hash, encoded_value, force_wait - ); - - // withdraw removes it from the registry - let (connection_token, rpc_response, _) = self - .connection_registry - .withdraw(&hash) - .ok_or(DirectRpcError::InvalidConnectionHash)?; - - let mut new_response = rpc_response.clone(); - - let mut result = RpcReturnValue::from_hex(&rpc_response.result) - .map_err(|e| DirectRpcError::Other(format!("{:?}", e).into()))?; - - result.value = encoded_value; - new_response.result = result.to_hex(); - self.connection_registry.store(hash, connection_token, new_response, force_wait); - - debug!("set response value OK"); - Ok(()) - } - - fn swap_hash(&self, old_hash: Self::Hash, new_hash: Self::Hash) -> DirectRpcResult<()> { - debug!("swap hash, old: {:?}, new: {:?}", old_hash, new_hash); - - let (connection_token, rpc_response, force_wait) = self - .connection_registry - .withdraw(&old_hash) - .ok_or(DirectRpcError::InvalidConnectionHash)?; - - // leave `rpc_response` untouched - it should be overwritten later anyway and keep on force waiting - self.connection_registry - .store(new_hash, connection_token, rpc_response, force_wait); - debug!("swap hash OK"); - Ok(()) - } - - fn send_state_with_status( - &self, - hash: Self::Hash, - state_encoded: Vec, - status: DirectRequestStatus, - ) -> DirectRpcResult<()> { - debug!("sending state with status for hash {:?}", hash); - - // withdraw removes it from the registry - let (connection_token, mut response, _force_wait) = self - .connection_registry - .withdraw(&hash) - .ok_or(DirectRpcError::InvalidConnectionHash)?; - - // create return value - let result = RpcReturnValue::new(state_encoded, false, status); - - // update response - response.result = result.to_hex(); - - self.encode_and_send_response(connection_token, &response)?; - - debug!("sending state successful"); - Ok(()) - } -} - -fn continue_watching(status: &TrustedOperationStatus) -> bool { - !matches!( - status, - TrustedOperationStatus::Invalid - | TrustedOperationStatus::InSidechainBlock(_) - | TrustedOperationStatus::Finalized - | TrustedOperationStatus::Usurped - ) -} - -#[cfg(test)] -pub mod tests { - - use super::*; - use crate::{ - builders::rpc_response_builder::RpcResponseBuilder, - mocks::response_channel_mock::ResponseChannelMock, - rpc_connection_registry::ConnectionRegistry, - }; - use codec::Encode; - use itp_types::H256; - use std::assert_matches::assert_matches; - - type TestConnectionToken = u64; - type TestResponseChannel = ResponseChannelMock; - type TestConnectionRegistry = ConnectionRegistry; - - #[test] - fn given_empty_registry_when_updating_status_event_then_return_error() { - let connection_registry = Arc::new(TestConnectionRegistry::new()); - let websocket_responder = Arc::new(TestResponseChannel::default()); - let rpc_responder = RpcResponder::new(connection_registry, websocket_responder); - - assert_matches!( - rpc_responder.update_status_event([1u8; 32].into(), TrustedOperationStatus::Broadcast), - Err(DirectRpcError::InvalidConnectionHash) - ); - } - - #[test] - fn given_empty_registry_when_sending_state_then_return_error() { - let connection_registry = Arc::new(TestConnectionRegistry::new()); - let websocket_responder = Arc::new(TestResponseChannel::default()); - let rpc_responder = RpcResponder::new(connection_registry, websocket_responder); - - assert_matches!( - rpc_responder.send_state([1u8; 32].into(), vec![1u8, 2u8]), - Err(DirectRpcError::InvalidConnectionHash) - ); - } - - #[test] - fn updating_status_event_with_finalized_state_removes_connection() { - let connection_hash = H256::random(); - let connection_registry = create_registry_with_single_connection(connection_hash.clone()); - - let websocket_responder = Arc::new(TestResponseChannel::default()); - let rpc_responder = - RpcResponder::new(connection_registry.clone(), websocket_responder.clone()); - - let result = rpc_responder - .update_status_event(connection_hash.clone(), TrustedOperationStatus::Finalized); - - assert!(result.is_ok()); - - verify_closed_connection(&connection_hash, connection_registry); - assert_eq!(1, websocket_responder.number_of_updates()); - } - - #[test] - fn updating_status_event_with_finalized_state_doesnt_remove_connection_if_force_watch_set() { - let connection_hash = H256::random(); - let connection_registry = create_registry_with_single_connection(connection_hash.clone()); - - let websocket_responder = Arc::new(TestResponseChannel::default()); - let rpc_responder = - RpcResponder::new(connection_registry.clone(), websocket_responder.clone()); - rpc_responder - .update_connection_state(connection_hash.clone(), vec![], true) - .unwrap(); - - let result = rpc_responder - .update_status_event(connection_hash.clone(), TrustedOperationStatus::Finalized); - - assert!(result.is_ok()); - - verify_open_connection(&connection_hash, connection_registry); - assert_eq!(1, websocket_responder.number_of_updates()); - } - - #[test] - fn updating_status_event_with_ready_state_keeps_connection_and_sends_update() { - let connection_hash = H256::random(); - let connection_registry: Arc> = - create_registry_with_single_connection(connection_hash.clone()); - - let websocket_responder = Arc::new(TestResponseChannel::default()); - let rpc_responder = - RpcResponder::new(connection_registry.clone(), websocket_responder.clone()); - - let first_result = rpc_responder - .update_status_event(connection_hash.clone(), TrustedOperationStatus::Ready); - - let second_result = rpc_responder - .update_status_event(connection_hash.clone(), TrustedOperationStatus::Submitted); - - assert!(first_result.is_ok()); - assert!(second_result.is_ok()); - - verify_open_connection(&connection_hash, connection_registry); - assert_eq!(2, websocket_responder.number_of_updates()); - } - - #[test] - fn sending_state_successfully_sends_update_and_removes_connection_token() { - let connection_hash = H256::random(); - let connection_registry = create_registry_with_single_connection(connection_hash.clone()); - - let websocket_responder = Arc::new(TestResponseChannel::default()); - let rpc_responder = - RpcResponder::new(connection_registry.clone(), websocket_responder.clone()); - - let result = rpc_responder.send_state(connection_hash.clone(), "new_state".encode()); - assert!(result.is_ok()); - - verify_closed_connection(&connection_hash, connection_registry); - assert_eq!(1, websocket_responder.number_of_updates()); - } - - #[test] - fn test_continue_watching() { - assert!(!continue_watching(&TrustedOperationStatus::Invalid)); - assert!(!continue_watching(&TrustedOperationStatus::Usurped)); - assert!(continue_watching(&TrustedOperationStatus::Future)); - assert!(continue_watching(&TrustedOperationStatus::Broadcast)); - assert!(continue_watching(&TrustedOperationStatus::Dropped)); - } - - fn verify_open_connection( - connection_hash: &H256, - connection_registry: Arc, - ) { - let maybe_connection = connection_registry.withdraw(&connection_hash); - assert!(maybe_connection.is_some()); - } - - fn verify_closed_connection( - connection_hash: &H256, - connection_registry: Arc, - ) { - assert!(connection_registry.withdraw(&connection_hash).is_none()); - } - - fn create_registry_with_single_connection( - connection_hash: H256, - ) -> Arc { - let connection_registry = TestConnectionRegistry::new(); - let rpc_response = RpcResponseBuilder::new().with_id(2).build(); - - connection_registry.store(connection_hash.clone(), 1, rpc_response, false); - Arc::new(connection_registry) - } -} diff --git a/tee-worker/bitacross/core/direct-rpc-server/src/rpc_watch_extractor.rs b/tee-worker/bitacross/core/direct-rpc-server/src/rpc_watch_extractor.rs deleted file mode 100644 index cbc69bdfd0..0000000000 --- a/tee-worker/bitacross/core/direct-rpc-server/src/rpc_watch_extractor.rs +++ /dev/null @@ -1,133 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{DetermineWatch, DirectRpcError, DirectRpcResult, RpcHash}; -use alloc::format; -use codec::Decode; -use itp_rpc::{RpcResponse, RpcReturnValue}; -use itp_types::DirectRequestStatus; -use itp_utils::FromHexPrefixed; -use std::marker::PhantomData; - -pub struct RpcWatchExtractor -where - Hash: RpcHash, -{ - phantom_data: PhantomData, -} - -impl RpcWatchExtractor -where - Hash: RpcHash, -{ - pub fn new() -> Self { - Self::default() - } -} - -impl Default for RpcWatchExtractor -where - Hash: RpcHash, -{ - fn default() -> Self { - RpcWatchExtractor { phantom_data: PhantomData } - } -} - -impl DetermineWatch for RpcWatchExtractor -where - Hash: RpcHash + Decode, -{ - type Hash = Hash; - - fn must_be_watched(&self, rpc_response: &RpcResponse) -> DirectRpcResult> { - let rpc_return_value = RpcReturnValue::from_hex(&rpc_response.result) - .map_err(|e| DirectRpcError::Other(format!("{:?}", e).into()))?; - - if !rpc_return_value.do_watch { - return Ok(None) - } - - match rpc_return_value.status { - DirectRequestStatus::TrustedOperationStatus(_, top_hash) => - Self::Hash::decode::<_>(&mut top_hash.as_ref()) - .map(Some) - .map_err(DirectRpcError::EncodingError), - DirectRequestStatus::Processing(hash) => Self::Hash::decode::<_>(&mut hash.as_ref()) - .map(Some) - .map_err(DirectRpcError::EncodingError), - _ => Ok(None), - } - } -} - -#[cfg(test)] -pub mod tests { - use super::*; - use crate::builders::{ - rpc_response_builder::RpcResponseBuilder, rpc_return_value_builder::RpcReturnValueBuilder, - }; - use itp_rpc::Id; - use itp_types::{TrustedOperationStatus, H256}; - - #[test] - fn invalid_rpc_response_returns_error() { - let watch_extractor = RpcWatchExtractor::::new(); - let rpc_response = RpcResponse { - id: Id::Number(1u32), - jsonrpc: String::from("json"), - result: "hello".to_string(), - }; - - assert!(watch_extractor.must_be_watched(&rpc_response).is_err()); - } - - #[test] - fn rpc_response_without_watch_flag_must_not_be_watched() { - let watch_extractor = RpcWatchExtractor::::new(); - let rpc_result = RpcReturnValueBuilder::new() - .with_do_watch(false) - .with_status(DirectRequestStatus::TrustedOperationStatus( - TrustedOperationStatus::Ready, - Default::default(), - )) - .build(); - let rpc_response = RpcResponseBuilder::new().with_result(rpc_result).build(); - - let do_watch = watch_extractor.must_be_watched(&rpc_response).unwrap(); - - assert_eq!(None, do_watch); - } - - #[test] - fn rpc_response_with_watch_flag_must_be_watched() { - let hash = H256::random(); - let watch_extractor = RpcWatchExtractor::::new(); - let rpc_return_value = RpcReturnValueBuilder::new() - .with_do_watch(true) - .with_status(DirectRequestStatus::TrustedOperationStatus( - TrustedOperationStatus::Ready, - hash, - )) - .build(); - let rpc_response = RpcResponseBuilder::new().with_result(rpc_return_value).build(); - - let do_watch = watch_extractor.must_be_watched(&rpc_response).unwrap(); - - assert_eq!(Some(hash), do_watch); - } -} diff --git a/tee-worker/bitacross/core/direct-rpc-server/src/rpc_ws_handler.rs b/tee-worker/bitacross/core/direct-rpc-server/src/rpc_ws_handler.rs deleted file mode 100644 index f585258e84..0000000000 --- a/tee-worker/bitacross/core/direct-rpc-server/src/rpc_ws_handler.rs +++ /dev/null @@ -1,241 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; - -use crate::{DetermineWatch, RpcConnectionRegistry, RpcHash}; -use itc_tls_websocket_server::{error::WebSocketResult, ConnectionToken, WebSocketMessageHandler}; -use itp_rpc::{RpcResponse, RpcReturnValue}; -use itp_types::DirectRequestStatus; -use itp_utils::FromHexPrefixed; -use jsonrpc_core::IoHandler; -use log::*; -use std::{string::String, sync::Arc}; - -pub struct RpcWsHandler -where - Watcher: DetermineWatch, - Registry: RpcConnectionRegistry, - Hash: RpcHash, -{ - rpc_io_handler: IoHandler, - connection_watcher: Arc, - connection_registry: Arc, -} - -impl RpcWsHandler -where - Watcher: DetermineWatch, - Registry: RpcConnectionRegistry, - Hash: RpcHash, -{ - pub fn new( - rpc_io_handler: IoHandler, - connection_watcher: Arc, - connection_registry: Arc, - ) -> Self { - RpcWsHandler { rpc_io_handler, connection_watcher, connection_registry } - } -} - -impl WebSocketMessageHandler for RpcWsHandler -where - Watcher: DetermineWatch, - Registry: RpcConnectionRegistry, - Registry::Connection: From, - Hash: RpcHash, -{ - fn handle_message( - &self, - connection_token: ConnectionToken, - message: String, - ) -> WebSocketResult> { - let maybe_rpc_response = self.rpc_io_handler.handle_request_sync(message.as_str()); - - debug!("RPC response string: {:?}", maybe_rpc_response); - - if let Ok(rpc_response) = serde_json::from_str::( - maybe_rpc_response.clone().unwrap_or_default().as_str(), - ) { - let ignore_response = - if let Ok(rpc_return_value) = RpcReturnValue::from_hex(&rpc_response.result) { - //in order to silence it - matches!(rpc_return_value.status, DirectRequestStatus::Processing(_)) - } else { - false - }; - - if let Ok(Some(connection_hash)) = - self.connection_watcher.must_be_watched(&rpc_response) - { - self.connection_registry.store( - connection_hash, - connection_token.into(), - rpc_response, - false, - ); - } - - if ignore_response { - return Ok(None) - } - } - - Ok(maybe_rpc_response) - } -} - -#[cfg(test)] -pub mod tests { - - use super::*; - use crate::{ - mocks::determine_watch_mock::DetermineWatchMock, - rpc_connection_registry::ConnectionRegistry, - }; - use codec::Encode; - use itc_tls_websocket_server::ConnectionToken; - use itp_rpc::RpcReturnValue; - use itp_types::DirectRequestStatus; - use itp_utils::ToHexPrefixed; - use jsonrpc_core::Params; - use serde_json::json; - - type TestConnectionRegistry = ConnectionRegistry; - type TestConnectionWatcher = DetermineWatchMock; - type TestWsHandler = RpcWsHandler; - - const RPC_METHOD_NAME: &str = "test_call"; - - #[test] - fn valid_rpc_call_without_watch_runs_successfully() { - let io_handler = create_io_handler_with_method(RPC_METHOD_NAME); - - let (connection_token, message) = create_message_to_handle(RPC_METHOD_NAME); - - let (ws_handler, connection_registry) = create_ws_handler(io_handler, None); - - let handle_result = ws_handler.handle_message(connection_token, message); - - assert!(handle_result.is_ok()); - assert!(connection_registry.is_empty()); - } - - #[test] - fn valid_rpc_call_with_watch_runs_successfully_and_stores_connection() { - let io_handler = create_io_handler_with_method(RPC_METHOD_NAME); - - let connection_hash = String::from("connection_hash"); - let (connection_token, message) = create_message_to_handle(RPC_METHOD_NAME); - - let (ws_handler, connection_registry) = - create_ws_handler(io_handler, Some(connection_hash.clone())); - - let handle_result = ws_handler.handle_message(connection_token, message); - - assert!(handle_result.is_ok()); - assert!(connection_registry.withdraw(&connection_hash).is_some()); - } - - #[test] - fn when_rpc_returns_error_then_return_ok_but_status_is_set_to_error() { - let io_handler = create_io_handler_with_error(RPC_METHOD_NAME); - - let connection_hash = String::from("connection_hash"); - let (connection_token, message) = create_message_to_handle(RPC_METHOD_NAME); - - let (ws_handler, connection_registry) = - create_ws_handler(io_handler, Some(connection_hash.clone())); - - let handle_result = ws_handler.handle_message(connection_token, message); - - assert!(handle_result.is_ok()); - assert!(connection_registry.withdraw(&connection_hash).is_some()); - } - - #[test] - fn when_rpc_method_does_not_match_anything_return_json_error_message() { - let io_handler = create_io_handler_with_error(RPC_METHOD_NAME); - let (connection_token, message) = create_message_to_handle("not_a_valid_method"); - - let (ws_handler, connection_registry) = create_ws_handler(io_handler, None); - - let handle_result = ws_handler.handle_message(connection_token, message).unwrap().unwrap(); - - assert_eq!(handle_result, "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32601,\"message\":\"Method not found\"},\"id\":1}"); - assert!(connection_registry.is_empty()); - } - - fn create_message_to_handle(method_name: &str) -> (ConnectionToken, String) { - let json_rpc_pre_method = r#"{"jsonrpc": "2.0", "method": ""#; - let json_rpc_post_method = r#"", "params": {}, "id": 1}"#; - - let json_string = format!("{}{}{}", json_rpc_pre_method, method_name, json_rpc_post_method); - debug!("JSON input: {}", json_string); - - (ConnectionToken(23), json_string) - } - - fn create_ws_handler( - io_handler: IoHandler, - watch_connection: Option, - ) -> (TestWsHandler, Arc) { - let watcher = match watch_connection { - Some(hash) => TestConnectionWatcher::do_watch(hash), - None => TestConnectionWatcher::no_watch(), - }; - - let connection_registry = Arc::new(TestConnectionRegistry::new()); - - ( - TestWsHandler::new(io_handler, Arc::new(watcher), connection_registry.clone()), - connection_registry, - ) - } - - fn create_io_handler_with_method(method_name: &str) -> IoHandler { - create_io_handler( - method_name, - RpcReturnValue { - do_watch: false, - value: String::from("value").encode(), - status: DirectRequestStatus::Ok, - }, - ) - } - - fn create_io_handler_with_error(method_name: &str) -> IoHandler { - create_io_handler( - method_name, - RpcReturnValue { - value: "error!".encode(), - do_watch: false, - status: DirectRequestStatus::Error, - }, - ) - } - - fn create_io_handler(method_name: &str, return_value: ReturnValue) -> IoHandler - where - ReturnValue: Encode + Send + Sync + 'static, - { - let mut io_handler = IoHandler::new(); - io_handler.add_sync_method(method_name, move |_: Params| Ok(json!(return_value.to_hex()))); - io_handler - } -} diff --git a/tee-worker/bitacross/core/offchain-worker-executor/Cargo.toml b/tee-worker/bitacross/core/offchain-worker-executor/Cargo.toml deleted file mode 100644 index db752e7e20..0000000000 --- a/tee-worker/bitacross/core/offchain-worker-executor/Cargo.toml +++ /dev/null @@ -1,61 +0,0 @@ -[package] -name = "bc-itc-offchain-worker-executor" -version = "0.1.0" -authors = ['Trust Computing GmbH ', 'Integritee AG '] -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -codec = { package = "parity-scale-codec", workspace = true } -log = { workspace = true } -sgx_tstd = { workspace = true, optional = true } - -thiserror = { workspace = true, optional = true } -thiserror_sgx = { workspace = true, optional = true } - -itc-parentchain-light-client = { workspace = true } -itp-extrinsics-factory = { workspace = true } -itp-stf-executor = { package = "bc-itp-stf-executor", path = "../../core-primitives/stf-executor", default-features = false } -itp-stf-interface = { workspace = true } -itp-stf-primitives = { workspace = true } -itp-stf-state-handler = { workspace = true } -itp-top-pool-author = { package = "bc-itp-top-pool-author", path = "../../core-primitives/top-pool-author", default-features = false } -itp-types = { workspace = true } - -sp-runtime = { workspace = true } - -[dev-dependencies] -codec = { package = "parity-scale-codec", workspace = true, features = ["std"] } -itc-parentchain-light-client = { workspace = true, features = ["std", "mocks"] } -itp-extrinsics-factory = { workspace = true, features = ["std", "mocks"] } -itp-stf-executor = { package = "bc-itp-stf-executor", path = "../../core-primitives/stf-executor", features = ["std", "mocks"] } -itp-test = { workspace = true, features = ["std"] } -itp-top-pool-author = { package = "bc-itp-top-pool-author", path = "../../core-primitives/top-pool-author", features = ["std", "mocks"] } -itp-stf-interface = { workspace = true, features = ["std", "mocks"] } -itp-sgx-externalities = { workspace = true } -sp-core = { workspace = true, features = ["full_crypto"] } - -[features] -default = ["std"] -std = [ - "itc-parentchain-light-client/std", - "itp-extrinsics-factory/std", - "itp-stf-executor/std", - "itp-stf-interface/std", - "itp-stf-primitives/std", - "itp-stf-state-handler/std", - "itp-top-pool-author/std", - "itp-types/std", - "sp-runtime/std", - "thiserror", -] -sgx = [ - "itc-parentchain-light-client/sgx", - "itp-extrinsics-factory/sgx", - "itp-stf-executor/sgx", - "itp-stf-state-handler/sgx", - "itp-top-pool-author/sgx", - "sgx_tstd", - "thiserror_sgx", -] diff --git a/tee-worker/bitacross/core/offchain-worker-executor/src/error.rs b/tee-worker/bitacross/core/offchain-worker-executor/src/error.rs deleted file mode 100644 index 2c955d3e00..0000000000 --- a/tee-worker/bitacross/core/offchain-worker-executor/src/error.rs +++ /dev/null @@ -1,40 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; - -use std::boxed::Box; - -pub type Result = core::result::Result; - -/// General offchain-worker error type -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("STF state handler error: {0}")] - StfStateHandler(#[from] itp_stf_state_handler::error::Error), - #[error("STF executor error: {0}")] - StfExecutor(#[from] itp_stf_executor::error::Error), - #[error("TOP pool author error: {0}")] - TopPoolAuthor(#[from] itp_top_pool_author::error::Error), - #[error("Light-client error: {0}")] - LightClient(#[from] itc_parentchain_light_client::error::Error), - #[error("Extrinsics factory error: {0}")] - ExtrinsicsFactory(#[from] itp_extrinsics_factory::error::Error), - #[error("{0}")] - Other(Box), -} diff --git a/tee-worker/bitacross/core/offchain-worker-executor/src/executor.rs b/tee-worker/bitacross/core/offchain-worker-executor/src/executor.rs deleted file mode 100644 index 5cf3e778b8..0000000000 --- a/tee-worker/bitacross/core/offchain-worker-executor/src/executor.rs +++ /dev/null @@ -1,373 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::error::Result; -use codec::{Decode, Encode}; -use core::fmt::Debug; -use itc_parentchain_light_client::{ - concurrent_access::ValidatorAccess, BlockNumberOps, ExtrinsicSender, LightClientState, - NumberFor, -}; -use itp_extrinsics_factory::CreateExtrinsics; -use itp_stf_executor::{traits::StateUpdateProposer, ExecutedOperation}; -use itp_stf_interface::system_pallet::SystemPalletEventInterface; -use itp_stf_primitives::{traits::TrustedCallVerification, types::TrustedOperationOrHash}; -use itp_stf_state_handler::{handle_state::HandleState, query_shard_state::QueryShardState}; -use itp_top_pool_author::traits::AuthorApi; -use itp_types::{parentchain::ParentchainCall, OpaqueCall, ShardIdentifier, H256}; -use log::*; -use sp_runtime::traits::Block; -use std::{marker::PhantomData, sync::Arc, time::Duration, vec::Vec}; - -/// Off-chain worker executor implementation. -/// -/// Executes calls found in the top-pool and immediately applies the corresponding state diffs. -/// - Sends confirmations for all executed calls (TODO) -/// - Sends extrinsics for any parentchain effects (such as unshield calls). -/// -/// The trigger to start executing calls is given when the parentchain block imported event is -/// signaled (event listener). -pub struct Executor< - ParentchainBlock, - TopPoolAuthor, - StfExecutor, - StateHandler, - ValidatorAccessor, - ExtrinsicsFactory, - Stf, - TCS, - G, -> { - top_pool_author: Arc, - stf_executor: Arc, - state_handler: Arc, - validator_accessor: Arc, - extrinsics_factory: Arc, - _phantom: PhantomData<(ParentchainBlock, Stf, TCS, G)>, -} - -impl< - ParentchainBlock, - TopPoolAuthor, - StfExecutor, - StateHandler, - ValidatorAccessor, - ExtrinsicsFactory, - Stf, - TCS, - G, - > - Executor< - ParentchainBlock, - TopPoolAuthor, - StfExecutor, - StateHandler, - ValidatorAccessor, - ExtrinsicsFactory, - Stf, - TCS, - G, - > where - ParentchainBlock: Block, - StfExecutor: StateUpdateProposer, - TopPoolAuthor: AuthorApi, - StateHandler: QueryShardState + HandleState, - ValidatorAccessor: ValidatorAccess + Send + Sync + 'static, - ExtrinsicsFactory: CreateExtrinsics, - NumberFor: BlockNumberOps, - Stf: SystemPalletEventInterface, - TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, - G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, -{ - pub fn new( - top_pool_author: Arc, - stf_executor: Arc, - state_handler: Arc, - validator_accessor: Arc, - extrinsics_factory: Arc, - ) -> Self { - Self { - top_pool_author, - stf_executor, - state_handler, - validator_accessor, - extrinsics_factory, - _phantom: Default::default(), - } - } - - pub fn execute(&self) -> Result<()> { - let max_duration = Duration::from_secs(5); - let latest_parentchain_header = self.get_latest_parentchain_header()?; - - let mut parentchain_effects: Vec = Vec::new(); - - let shards = self.state_handler.list_shards()?; - trace!("Executing calls on {} shard(s)", shards.len()); - - for shard in shards { - debug!( - "executing pending tops in top pool with status: {:?}", - self.top_pool_author.get_status(shard) - ); - let trusted_calls = self.top_pool_author.get_pending_trusted_calls(shard); - trace!("Executing {} trusted calls on shard {:?}", trusted_calls.len(), shard); - - let batch_execution_result = self.stf_executor.propose_state_update( - &trusted_calls, - &latest_parentchain_header, - &shard, - max_duration, - |mut state| { - Stf::reset_events(&mut state); - state - }, - )?; - - parentchain_effects - .append(&mut batch_execution_result.get_extrinsic_callbacks().clone()); - - let failed_operations = batch_execution_result.get_failed_operations(); - let successful_operations: Vec> = batch_execution_result - .get_executed_operation_hashes() - .into_iter() - .map(|h| { - ExecutedOperation::success( - h, - TrustedOperationOrHash::Hash(h), - Vec::new(), - Vec::new(), - false, - ) - }) - .collect(); - - // Remove all not successfully executed operations from the top pool. - self.remove_calls_from_pool(&shard, failed_operations); - - // Apply the state update - self.apply_state_update(&shard, batch_execution_result.state_after_execution)?; - - // Remove successful operations from pool - self.remove_calls_from_pool(&shard, successful_operations); - - // TODO: notify parentchain about executed operations? -> add to parentchain effects - } - - if !parentchain_effects.is_empty() { - self.send_parentchain_effects(parentchain_effects)?; - } - - Ok(()) - } - - fn get_latest_parentchain_header(&self) -> Result { - let header = self.validator_accessor.execute_on_validator(|v| { - let latest_parentchain_header = v.latest_finalized_header()?; - Ok(latest_parentchain_header) - })?; - Ok(header) - } - - fn apply_state_update( - &self, - shard: &ShardIdentifier, - updated_state: >::Externalities, - ) -> Result<()> { - self.state_handler.reset(updated_state, shard)?; - Ok(()) - } - - fn send_parentchain_effects(&self, parentchain_effects: Vec) -> Result<()> { - let integritee_calls: Vec = parentchain_effects - .iter() - .filter_map(|parentchain_call| parentchain_call.as_litentry()) - .collect(); - let target_a_calls: Vec = parentchain_effects - .iter() - .filter_map(|parentchain_call| parentchain_call.as_target_a()) - .collect(); - let target_b_calls: Vec = parentchain_effects - .iter() - .filter_map(|parentchain_call| parentchain_call.as_target_b()) - .collect(); - debug!( - "stf wants to send calls to parentchains: Integritee: {} TargetA: {} TargetB: {}", - integritee_calls.len(), - target_a_calls.len(), - target_b_calls.len() - ); - if !target_a_calls.is_empty() { - warn!("sending extrinsics to target A unimplemented") - }; - if !target_b_calls.is_empty() { - warn!("sending extrinsics to target B unimplemented") - }; - - let extrinsics = - self.extrinsics_factory.create_extrinsics(integritee_calls.as_slice(), None)?; - self.validator_accessor - .execute_mut_on_validator(|v| v.send_extrinsics(extrinsics))?; - Ok(()) - } - - fn remove_calls_from_pool( - &self, - shard: &ShardIdentifier, - executed_calls: Vec>, - ) -> Vec> { - let executed_calls_tuple: Vec<_> = executed_calls - .iter() - .map(|e| (e.trusted_operation_or_hash.clone(), e.is_success())) - .collect(); - let failed_to_remove_hashes = - self.top_pool_author.remove_calls_from_pool(*shard, executed_calls_tuple); - - let failed_executed_calls: Vec<_> = executed_calls - .into_iter() - .filter(|e| failed_to_remove_hashes.contains(&e.trusted_operation_or_hash)) - .collect(); - - failed_executed_calls - } -} - -#[cfg(test)] -mod tests { - - use super::*; - use codec::{Decode, Encode}; - use itc_parentchain_light_client::mocks::validator_access_mock::ValidatorAccessMock; - use itp_extrinsics_factory::mock::ExtrinsicsFactoryMock; - use itp_sgx_externalities::SgxExternalitiesTrait; - use itp_stf_executor::mocks::StfExecutorMock; - - use itp_test::mock::{ - handle_state_mock::HandleStateMock, - stf_mock::{GetterMock, TrustedCallSignedMock}, - }; - use itp_top_pool_author::mocks::AuthorApiMock; - use itp_types::{Block as ParentchainBlock, RsaRequest}; - - use itp_test::mock::stf_mock::mock_top_indirect_trusted_call_signed; - use std::boxed::Box; - - type TestStateHandler = HandleStateMock; - type TestStfInterface = SystemPalletEventInterfaceMock; - type State = ::StateT; - type TestTopPoolAuthor = AuthorApiMock; - type TestStfExecutor = StfExecutorMock; - type TestValidatorAccess = ValidatorAccessMock; - type TestExtrinsicsFactory = ExtrinsicsFactoryMock; - type TestExecutor = Executor< - ParentchainBlock, - TestTopPoolAuthor, - TestStfExecutor, - TestStateHandler, - TestValidatorAccess, - TestExtrinsicsFactory, - TestStfInterface, - TrustedCallSignedMock, - GetterMock, - >; - - const EVENT_COUNT_KEY: &[u8] = b"event_count"; - - struct SystemPalletEventInterfaceMock; - - impl SystemPalletEventInterface for SystemPalletEventInterfaceMock { - type EventRecord = String; - type EventIndex = u32; - type BlockNumber = u32; - type Hash = String; - - fn get_events(_state: &mut State) -> Vec> { - unimplemented!(); - } - - fn get_event_count(state: &mut State) -> Self::EventIndex { - let encoded_value = state.get(EVENT_COUNT_KEY).unwrap(); - Self::EventIndex::decode(&mut encoded_value.as_slice()).unwrap() - } - - fn get_event_topics( - _state: &mut State, - _topic: &Self::Hash, - ) -> Vec<(Self::BlockNumber, Self::EventIndex)> { - unimplemented!() - } - - fn reset_events(state: &mut State) { - state.insert(EVENT_COUNT_KEY.to_vec(), 0u32.encode()); - } - } - - #[test] - fn executing_tops_from_pool_works_and_empties_pool() { - let stf_executor = Arc::new(TestStfExecutor::new(State::default())); - let top_pool_author = Arc::new(TestTopPoolAuthor::default()); - top_pool_author - .submit_top(RsaRequest::new(shard(), mock_top_indirect_trusted_call_signed().encode())); - - assert_eq!(1, top_pool_author.pending_tops(shard()).unwrap().len()); - - let executor = create_executor(top_pool_author.clone(), stf_executor); - - assert!(executor.execute().is_ok()); - - assert_eq!(0, top_pool_author.pending_tops(shard()).unwrap().len()); - } - - #[test] - fn reset_events_is_called() { - let mut state = State::default(); - let event_count = 5; - state.insert(EVENT_COUNT_KEY.to_vec(), event_count.encode()); - - let stf_executor = Arc::new(TestStfExecutor::new(state)); - assert_eq!(TestStfInterface::get_event_count(&mut stf_executor.get_state()), event_count); - - let top_pool_author = Arc::new(TestTopPoolAuthor::default()); - - let executor = create_executor(top_pool_author, stf_executor.clone()); - - executor.execute().unwrap(); - - assert_eq!(TestStfInterface::get_event_count(&mut stf_executor.get_state()), 0); - } - - fn create_executor( - top_pool_author: Arc, - stf_executor: Arc, - ) -> TestExecutor { - let state_handler = Arc::new(TestStateHandler::from_shard(shard()).unwrap()); - let validator_access = Arc::new(TestValidatorAccess::default()); - let extrinsics_factory = Arc::new(TestExtrinsicsFactory::default()); - - TestExecutor::new( - top_pool_author, - stf_executor, - state_handler, - validator_access, - extrinsics_factory, - ) - } - - fn shard() -> ShardIdentifier { - ShardIdentifier::default() - } -} diff --git a/tee-worker/bitacross/core/offchain-worker-executor/src/lib.rs b/tee-worker/bitacross/core/offchain-worker-executor/src/lib.rs deleted file mode 100644 index d30a11ba0b..0000000000 --- a/tee-worker/bitacross/core/offchain-worker-executor/src/lib.rs +++ /dev/null @@ -1,33 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -// re-export module to properly feature gate sgx and regular std environment -#[cfg(all(not(feature = "std"), feature = "sgx"))] -pub mod sgx_reexport_prelude { - pub use thiserror_sgx as thiserror; -} - -pub mod error; -pub mod executor; diff --git a/tee-worker/bitacross/core/parentchain/block-import-dispatcher/Cargo.toml b/tee-worker/bitacross/core/parentchain/block-import-dispatcher/Cargo.toml deleted file mode 100644 index 28261ceb76..0000000000 --- a/tee-worker/bitacross/core/parentchain/block-import-dispatcher/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "bc-itc-parentchain-block-import-dispatcher" -version = "0.1.0" -authors = ['Trust Computing GmbH ', 'Integritee AG '] -edition = "2021" - -[dependencies] -sgx_tstd = { workspace = true, optional = true } -sgx_types = { workspace = true } - -itc-parentchain-block-importer = { package = "bc-itc-parentchain-block-importer", path = "../block-importer", default-features = false } -itp-import-queue = { workspace = true } - -thiserror = { workspace = true, optional = true } -thiserror_sgx = { workspace = true, optional = true } - -log = { workspace = true } - -[dev-dependencies] -itc-parentchain-block-importer = { package = "bc-itc-parentchain-block-importer", path = "../block-importer", features = ["mocks"] } - -[features] -default = ["std"] -std = [ - "itc-parentchain-block-importer/std", - "itp-import-queue/std", - "log/std", - "thiserror", -] -sgx = [ - "sgx_tstd", - "itc-parentchain-block-importer/sgx", - "itp-import-queue/sgx", - "thiserror_sgx", -] - -# feature to export mock implementations, only to be used for dev-dependencies! -mocks = [] diff --git a/tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/error.rs b/tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/error.rs deleted file mode 100644 index b5d73ffe54..0000000000 --- a/tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/error.rs +++ /dev/null @@ -1,47 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; - -use sgx_types::sgx_status_t; -use std::boxed::Box; - -pub type Result = core::result::Result; - -/// Parentchain block importer error. -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("SGX error, status: {0}")] - Sgx(sgx_status_t), - #[error("Two Dispatcher types assigned. Please double check the initialization process.")] - CanNotAssignTwoDispatcher, - #[error("Even though there is no dispatcher assigned, the dispatch function is called.")] - NoDispatcherAssigned, - #[error("Block import queue error: {0}")] - ImportQueue(#[from] itp_import_queue::error::Error), - #[error("Block import error: {0}")] - BlockImport(#[from] itc_parentchain_block_importer::error::Error), - #[error(transparent)] - Other(#[from] Box), -} - -impl From for Error { - fn from(sgx_status: sgx_status_t) -> Self { - Self::Sgx(sgx_status) - } -} diff --git a/tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/immediate_dispatcher.rs b/tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/immediate_dispatcher.rs deleted file mode 100644 index 080f158144..0000000000 --- a/tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/immediate_dispatcher.rs +++ /dev/null @@ -1,107 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{error::Result, DispatchBlockImport}; -use itc_parentchain_block_importer::ImportParentchainBlocks; -use log::*; -use std::{boxed::Box, vec::Vec}; - -/// Block import dispatcher that immediately imports the blocks, without any processing or queueing. -pub struct ImmediateDispatcher { - pub block_importer: BlockImporter, - import_event_observers: Vec>, -} - -impl ImmediateDispatcher { - pub fn new(block_importer: BlockImporter) -> Self { - ImmediateDispatcher { block_importer, import_event_observers: Vec::new() } - } - - pub fn with_observer(self, callback: F) -> Self - where - F: Fn() + Send + Sync + 'static, - { - let mut updated_observers = self.import_event_observers; - updated_observers.push(Box::new(callback)); - - Self { block_importer: self.block_importer, import_event_observers: updated_observers } - } -} - -impl DispatchBlockImport - for ImmediateDispatcher -where - BlockImporter: ImportParentchainBlocks, -{ - fn dispatch_import( - &self, - blocks: Vec, - events: Vec>, - _immediate_import: bool, - ) -> Result<()> { - // _immediate_import does not matter for the immediate dispatcher, behavoiur is the same. Immediate block import. - - debug!("Importing {} parentchain blocks", blocks.len()); - self.block_importer.import_parentchain_blocks(blocks, events)?; - debug!("Notifying {} observers of import", self.import_event_observers.len()); - self.import_event_observers.iter().for_each(|callback| callback()); - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use itc_parentchain_block_importer::block_importer_mock::ParentchainBlockImporterMock; - use std::{ - sync::{Arc, RwLock}, - vec, - }; - - type SignedBlockType = u32; - type TestBlockImporter = ParentchainBlockImporterMock; - type TestDispatcher = ImmediateDispatcher; - - #[derive(Default)] - struct NotificationCounter { - counter: RwLock, - } - - impl NotificationCounter { - fn increment(&self) { - *self.counter.write().unwrap() += 1; - } - - pub fn get_counter(&self) -> usize { - *self.counter.read().unwrap() - } - } - - #[test] - fn listeners_get_notified_upon_import() { - let block_importer = TestBlockImporter::default(); - let notification_counter = Arc::new(NotificationCounter::default()); - let counter_clone = notification_counter.clone(); - let dispatcher = TestDispatcher::new(block_importer).with_observer(move || { - counter_clone.increment(); - }); - - dispatcher.dispatch_import(vec![1u32, 2u32], vec![], false).unwrap(); - - assert_eq!(1, notification_counter.get_counter()); - } -} diff --git a/tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/lib.rs b/tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/lib.rs deleted file mode 100644 index 34e94523d0..0000000000 --- a/tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/lib.rs +++ /dev/null @@ -1,125 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -//! Dispatching of block imports. - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -// re-export module to properly feature gate sgx and regular std environment -#[cfg(all(not(feature = "std"), feature = "sgx"))] -pub mod sgx_reexport_prelude { - pub use thiserror_sgx as thiserror; -} - -pub mod error; -pub mod immediate_dispatcher; -pub mod triggered_dispatcher; - -#[cfg(feature = "mocks")] -pub mod trigger_parentchain_block_import_mock; - -use error::{Error, Result}; -use std::{sync::Arc, vec::Vec}; - -/// Trait to dispatch blocks for import into the local light-client. -pub trait DispatchBlockImport { - /// Dispatch blocks to be imported. - /// - /// The blocks may be imported immediately, get queued, delayed or grouped. - fn dispatch_import( - &self, - blocks: Vec, - events: Vec>, - immediate_import: bool, - ) -> Result<()>; -} - -/// Wrapper for the actual dispatchers. Allows to define one global type for -/// both dispatchers without changing the global variable when switching -/// the dispatcher type. It also allows for empty dispatchers, for use cases that -/// do not need block syncing for a specific parentchain type. -pub enum BlockImportDispatcher { - TriggeredDispatcher(Arc), - ImmediateDispatcher(Arc), - EmptyDispatcher, -} - -impl - BlockImportDispatcher -{ - pub fn new_triggered_dispatcher(triggered_dispatcher: Arc) -> Self { - BlockImportDispatcher::TriggeredDispatcher(triggered_dispatcher) - } - - pub fn new_immediate_dispatcher(immediate_dispatcher: Arc) -> Self { - BlockImportDispatcher::ImmediateDispatcher(immediate_dispatcher) - } - - pub fn new_empty_dispatcher() -> Self { - BlockImportDispatcher::EmptyDispatcher - } - - pub fn triggered_dispatcher(&self) -> Option> { - match self { - BlockImportDispatcher::TriggeredDispatcher(triggered_dispatcher) => - Some(triggered_dispatcher.clone()), - _ => None, - } - } - - pub fn immediate_dispatcher(&self) -> Option> { - match self { - BlockImportDispatcher::ImmediateDispatcher(immediate_dispatcher) => - Some(immediate_dispatcher.clone()), - _ => None, - } - } -} - -impl DispatchBlockImport - for BlockImportDispatcher -where - TriggeredDispatcher: DispatchBlockImport, - ImmediateDispatcher: DispatchBlockImport, -{ - fn dispatch_import( - &self, - blocks: Vec, - events: Vec>, - immediate_import: bool, - ) -> Result<()> { - match self { - BlockImportDispatcher::TriggeredDispatcher(dispatcher) => { - log::trace!("TRIGGERED DISPATCHER MATCH"); - dispatcher.dispatch_import(blocks, events, immediate_import) - }, - BlockImportDispatcher::ImmediateDispatcher(dispatcher) => { - log::trace!("IMMEDIATE DISPATCHER MATCH"); - dispatcher.dispatch_import(blocks, events, immediate_import) - }, - BlockImportDispatcher::EmptyDispatcher => { - log::trace!("EMPTY DISPATCHER DISPATCHER MATCH"); - Err(Error::NoDispatcherAssigned) - }, - } - } -} diff --git a/tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/trigger_parentchain_block_import_mock.rs b/tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/trigger_parentchain_block_import_mock.rs deleted file mode 100644 index a4953a4fbb..0000000000 --- a/tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/trigger_parentchain_block_import_mock.rs +++ /dev/null @@ -1,102 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(feature = "sgx")] -use std::sync::SgxRwLock as RwLock; - -#[cfg(feature = "std")] -use std::sync::RwLock; - -use crate::{error::Result, triggered_dispatcher::TriggerParentchainBlockImport}; - -/// Mock for `TriggerParentchainBlockImport`, to be used in unit tests. -/// -/// Allows setting the latest imported block, which is returned upon calling -/// the import methods. -pub struct TriggerParentchainBlockImportMock { - latest_imported: Option, - import_has_been_called: RwLock, -} - -impl TriggerParentchainBlockImportMock { - pub fn with_latest_imported(mut self, maybe_block: Option) -> Self { - self.latest_imported = maybe_block; - self - } - - pub fn has_import_been_called(&self) -> bool { - let import_flag = self.import_has_been_called.read().unwrap(); - *import_flag - } -} - -impl Default for TriggerParentchainBlockImportMock { - fn default() -> Self { - TriggerParentchainBlockImportMock { - latest_imported: None, - import_has_been_called: RwLock::new(false), - } - } -} - -impl TriggerParentchainBlockImport - for TriggerParentchainBlockImportMock -where - SignedBlockType: Clone, -{ - type SignedBlockType = SignedBlockType; - - fn import_all(&self) -> Result> { - let mut import_flag = self.import_has_been_called.write().unwrap(); - *import_flag = true; - Ok(self.latest_imported.clone()) - } - - fn import_all_but_latest(&self) -> Result<()> { - let mut import_flag = self.import_has_been_called.write().unwrap(); - *import_flag = true; - Ok(()) - } - - fn import_until( - &self, - _predicate: impl Fn(&SignedBlockType) -> bool, - ) -> Result> { - let mut import_flag = self.import_has_been_called.write().unwrap(); - *import_flag = true; - Ok(self.latest_imported.clone()) - } - - fn peek( - &self, - predicate: impl Fn(&SignedBlockType) -> bool, - ) -> Result> { - match &self.latest_imported { - None => Ok(None), - Some(block) => { - if predicate(block) { - return Ok(Some(block.clone())) - } - Ok(None) - }, - } - } - - fn peek_latest(&self) -> Result> { - Ok(self.latest_imported.clone()) - } -} diff --git a/tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/triggered_dispatcher.rs b/tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/triggered_dispatcher.rs deleted file mode 100644 index 712fcc724d..0000000000 --- a/tee-worker/bitacross/core/parentchain/block-import-dispatcher/src/triggered_dispatcher.rs +++ /dev/null @@ -1,374 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! A block import dispatcher that retains all blocks in a queue until import is triggered. - -use crate::{ - error::{Error, Result}, - DispatchBlockImport, -}; -use itc_parentchain_block_importer::ImportParentchainBlocks; -use itp_import_queue::{PeekQueue, PopFromQueue, PushToQueue}; -use log::trace; -use std::vec::Vec; - -pub type RawEventsPerBlock = Vec; - -/// Trait to specifically trigger the import of parentchain blocks. -pub trait TriggerParentchainBlockImport { - type SignedBlockType; - /// Trigger the import of all queued block, **including** the latest one. - /// - /// Returns the latest imported block (if any). - fn import_all(&self) -> Result>; - - /// Trigger import of all queued blocks, **except** the latest one. - fn import_all_but_latest(&self) -> Result<()>; - - /// Trigger import of all blocks up to **and including** a specific block. - /// - /// If no block in the queue matches, then no blocks will be imported. - /// Returns the latest imported block (if any). - fn import_until( - &self, - predicate: impl Fn(&Self::SignedBlockType) -> bool, - ) -> Result>; - - /// Search the import queue with a given predicate and return a reference - /// to the first element that matches the predicate. - fn peek( - &self, - predicate: impl Fn(&Self::SignedBlockType) -> bool, - ) -> Result>; - - /// Peek the latest block in the import queue. Returns None if queue is empty. - fn peek_latest(&self) -> Result>; -} - -/// Dispatcher for block imports that retains blocks until the import is triggered, using the -/// `TriggerParentchainBlockImport` trait implementation. -pub struct TriggeredDispatcher { - pub block_importer: BlockImporter, - import_queue: BlockImportQueue, - events_queue: EventsImportQueue, -} - -impl - TriggeredDispatcher -where - BlockImporter: ImportParentchainBlocks, - BlockImportQueue: PushToQueue - + PopFromQueue, - EventsImportQueue: PushToQueue + PopFromQueue, -{ - pub fn new( - block_importer: BlockImporter, - block_import_queue: BlockImportQueue, - events_import_queue: EventsImportQueue, - ) -> Self { - TriggeredDispatcher { - block_importer, - import_queue: block_import_queue, - events_queue: events_import_queue, - } - } -} - -impl - DispatchBlockImport - for TriggeredDispatcher -where - BlockImporter: ImportParentchainBlocks, - BlockImportQueue: PushToQueue + PopFromQueue, - EventsImportQueue: PushToQueue + PopFromQueue, -{ - fn dispatch_import( - &self, - blocks: Vec, - events: Vec, - immediate_import: bool, - ) -> Result<()> { - let parentchain_id = self.block_importer.parentchain_id(); - trace!( - "[{:?}] Triggered dispatcher received block(s) and event(s) ({}) ({})", - parentchain_id, - blocks.len(), - events.len() - ); - if immediate_import { - trace!( - "[{:?}] Triggered is in sync mode, immediately importing blocks and events", - parentchain_id - ); - self.block_importer - .import_parentchain_blocks(blocks, events) - .map_err(Error::BlockImport) - } else { - trace!("[{:?}] pushing blocks and events to import queues", parentchain_id); - self.events_queue.push_multiple(events).map_err(Error::ImportQueue)?; - self.import_queue.push_multiple(blocks).map_err(Error::ImportQueue) - } - } -} - -impl TriggerParentchainBlockImport - for TriggeredDispatcher -where - BlockImporter: ImportParentchainBlocks, - BlockImportQueue: PushToQueue - + PopFromQueue - + PeekQueue, - EventsImportQueue: PushToQueue - + PopFromQueue - + PeekQueue, -{ - type SignedBlockType = BlockImporter::SignedBlockType; - - fn import_all(&self) -> Result> { - let blocks_to_import = self.import_queue.pop_all().map_err(Error::ImportQueue)?; - let events_to_import = self.events_queue.pop_all().map_err(Error::ImportQueue)?; - - let latest_imported_block = blocks_to_import.last().map(|b| (*b).clone()); - let parentchain_id = self.block_importer.parentchain_id(); - trace!( - "[{:?}] Trigger import of all parentchain blocks and events in queue ({}) ({})", - parentchain_id, - blocks_to_import.len(), - events_to_import.len() - ); - - self.block_importer - .import_parentchain_blocks(blocks_to_import, events_to_import) - .map_err(Error::BlockImport)?; - - Ok(latest_imported_block) - } - - fn import_all_but_latest(&self) -> Result<()> { - let blocks_to_import = self.import_queue.pop_all_but_last().map_err(Error::ImportQueue)?; - let events_to_import = self.events_queue.pop_all_but_last().map_err(Error::ImportQueue)?; - let parentchain_id = self.block_importer.parentchain_id(); - trace!( - "[{:?}] Trigger import of all parentchain blocks and events, except the latest, from queue ({}) ({})", - parentchain_id, - blocks_to_import.len(), - events_to_import.len() - ); - - self.block_importer - .import_parentchain_blocks(blocks_to_import, events_to_import) - .map_err(Error::BlockImport) - } - - fn import_until( - &self, - predicate: impl Fn(&BlockImporter::SignedBlockType) -> bool, - ) -> Result> { - trace!("Import of parentchain blocks and events has been triggered"); - let blocks_to_import = - self.import_queue.pop_until(predicate).map_err(Error::ImportQueue)?; - - let events_to_import = self - .events_queue - .pop_from_front_until(blocks_to_import.len()) - .map_err(Error::ImportQueue)?; - - let latest_imported_block = blocks_to_import.last().map(|b| (*b).clone()); - let parentchain_id = self.block_importer.parentchain_id(); - trace!( - "[{:?}] Import of parentchain blocks and events has been triggered, importing {} blocks and {} events from queue", - parentchain_id, - blocks_to_import.len(), - events_to_import.len(), - ); - - self.block_importer - .import_parentchain_blocks(blocks_to_import, events_to_import) - .map_err(Error::BlockImport)?; - - Ok(latest_imported_block) - } - - fn peek( - &self, - predicate: impl Fn(&BlockImporter::SignedBlockType) -> bool, - ) -> Result> { - let parentchain_id = self.block_importer.parentchain_id(); - trace!( - "[{:?}] Peek find parentchain import queue (currently has {} elements)", - parentchain_id, - self.import_queue.peek_queue_size().unwrap_or(0) - ); - self.import_queue.peek_find(predicate).map_err(Error::ImportQueue) - } - - fn peek_latest(&self) -> Result> { - let parentchain_id = self.block_importer.parentchain_id(); - trace!( - "[{:?}] Peek latest parentchain import queue (currently has {} elements)", - parentchain_id, - self.import_queue.peek_queue_size().unwrap_or(0) - ); - self.import_queue.peek_last().map_err(Error::ImportQueue) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use itc_parentchain_block_importer::block_importer_mock::ParentchainBlockImporterMock; - use itp_import_queue::{ImportQueue, PopFromQueue}; - - type SignedBlockType = u32; - type TestBlockImporter = ParentchainBlockImporterMock; - type TestQueue = ImportQueue; - type TestEventsQueue = ImportQueue; - type TestDispatcher = TriggeredDispatcher; - - #[test] - fn dispatching_blocks_imports_none_if_not_triggered() { - let dispatcher = test_fixtures(); - - dispatcher - .dispatch_import( - vec![1, 2, 3, 4, 5], - vec![vec![1], vec![2], vec![3], vec![4], vec![5]], - false, - ) - .unwrap(); - - assert!(dispatcher.block_importer.get_all_imported_blocks().is_empty()); - assert_eq!(dispatcher.import_queue.pop_all().unwrap(), vec![1, 2, 3, 4, 5]); - assert_eq!( - dispatcher.events_queue.pop_all().unwrap(), - vec![vec![1], vec![2], vec![3], vec![4], vec![5]] - ); - } - - #[test] - fn dispatching_blocks_multiple_times_add_all_to_queue() { - let dispatcher = test_fixtures(); - - dispatcher - .dispatch_import( - vec![1, 2, 3, 4, 5], - vec![vec![1], vec![2], vec![3], vec![4], vec![5]], - false, - ) - .unwrap(); - dispatcher - .dispatch_import(vec![6, 7, 8], vec![vec![6], vec![7], vec![8]], false) - .unwrap(); - - assert!(dispatcher.block_importer.get_all_imported_blocks().is_empty()); - assert_eq!(dispatcher.import_queue.pop_all().unwrap(), vec![1, 2, 3, 4, 5, 6, 7, 8]); - assert_eq!( - dispatcher.events_queue.pop_all().unwrap(), - vec![vec![1], vec![2], vec![3], vec![4], vec![5], vec![6], vec![7], vec![8]] - ); - } - - #[test] - fn triggering_import_all_empties_queue() { - let dispatcher = test_fixtures(); - - dispatcher.dispatch_import(vec![1, 2, 3, 4, 5], vec![], false).unwrap(); - let latest_imported = dispatcher.import_all().unwrap().unwrap(); - - assert_eq!(latest_imported, 5); - assert_eq!(dispatcher.block_importer.get_all_imported_blocks(), vec![1, 2, 3, 4, 5]); - assert!(dispatcher.import_queue.is_empty().unwrap()); - } - - #[test] - fn triggering_import_all_on_empty_queue_imports_none() { - let dispatcher = test_fixtures(); - - dispatcher.dispatch_import(vec![], vec![], false).unwrap(); - let maybe_latest_imported = dispatcher.import_all().unwrap(); - - assert!(maybe_latest_imported.is_none()); - assert_eq!( - dispatcher.block_importer.get_all_imported_blocks(), - Vec::::default() - ); - assert!(dispatcher.import_queue.is_empty().unwrap()); - assert!(dispatcher.events_queue.is_empty().unwrap()); - } - - #[test] - fn triggering_import_until_leaves_remaining_in_queue() { - let dispatcher = test_fixtures(); - - dispatcher - .dispatch_import( - vec![1, 2, 3, 4, 5], - vec![vec![1], vec![2], vec![3], vec![4], vec![5]], - false, - ) - .unwrap(); - let latest_imported = - dispatcher.import_until(|i: &SignedBlockType| i == &4).unwrap().unwrap(); - - assert_eq!(latest_imported, 4); - assert_eq!(dispatcher.block_importer.get_all_imported_blocks(), vec![1, 2, 3, 4]); - assert_eq!(dispatcher.import_queue.pop_all().unwrap(), vec![5]); - assert_eq!(dispatcher.events_queue.pop_all().unwrap(), vec![vec![5]]); - } - - #[test] - fn triggering_import_until_with_no_match_imports_nothing() { - let dispatcher = test_fixtures(); - - dispatcher - .dispatch_import( - vec![1, 2, 3, 4, 5], - vec![vec![1], vec![2], vec![3], vec![4], vec![5]], - false, - ) - .unwrap(); - let maybe_latest_imported = dispatcher.import_until(|i: &SignedBlockType| i == &8).unwrap(); - - assert!(maybe_latest_imported.is_none()); - assert!(dispatcher.block_importer.get_all_imported_blocks().is_empty()); - assert_eq!(dispatcher.import_queue.pop_all().unwrap(), vec![1, 2, 3, 4, 5]); - assert_eq!( - dispatcher.events_queue.pop_all().unwrap(), - vec![vec![1], vec![2], vec![3], vec![4], vec![5]] - ); - } - - #[test] - fn trigger_import_all_but_latest_works() { - let dispatcher = test_fixtures(); - - dispatcher.dispatch_import(vec![1, 2, 3, 4, 5], vec![], false).unwrap(); - dispatcher.import_all_but_latest().unwrap(); - - assert_eq!(dispatcher.block_importer.get_all_imported_blocks(), vec![1, 2, 3, 4]); - assert_eq!(dispatcher.import_queue.pop_all().unwrap(), vec![5]); - } - - fn test_fixtures() -> TestDispatcher { - let events_import_queue = ImportQueue::::default(); - let import_queue = ImportQueue::::default(); - let block_importer = ParentchainBlockImporterMock::::default(); - let dispatcher = - TriggeredDispatcher::new(block_importer, import_queue, events_import_queue); - dispatcher - } -} diff --git a/tee-worker/bitacross/core/parentchain/block-importer/Cargo.toml b/tee-worker/bitacross/core/parentchain/block-importer/Cargo.toml deleted file mode 100644 index 9ae50d53f7..0000000000 --- a/tee-worker/bitacross/core/parentchain/block-importer/Cargo.toml +++ /dev/null @@ -1,57 +0,0 @@ -[package] -name = "bc-itc-parentchain-block-importer" -version = "0.1.0" -authors = ['Trust Computing GmbH ', 'Integritee AG '] -edition = "2021" - -[dependencies] -sgx_tstd = { workspace = true, optional = true } -sgx_types = { workspace = true } - -ita-stf = { package = "bc-ita-stf", path = "../../../app-libs/stf", default-features = false } -itc-parentchain-indirect-calls-executor = { package = "bc-itc-parentchain-indirect-calls-executor", path = "../indirect-calls-executor", default-features = false } -itc-parentchain-light-client = { workspace = true } -itp-enclave-metrics = { workspace = true } -itp-extrinsics-factory = { workspace = true } -itp-ocall-api = { workspace = true } -itp-stf-executor = { package = "bc-itp-stf-executor", path = "../../../core-primitives/stf-executor", default-features = false } -itp-stf-interface = { workspace = true } -itp-types = { workspace = true } - -thiserror = { workspace = true, optional = true } -thiserror_sgx = { workspace = true, optional = true } - -codec = { package = "parity-scale-codec", workspace = true } -log = { workspace = true } -sp-runtime = { workspace = true } - -[features] -default = ["std"] -std = [ - "ita-stf/std", - "itc-parentchain-indirect-calls-executor/std", - "itc-parentchain-light-client/std", - "itp-enclave-metrics/std", - "itp-extrinsics-factory/std", - "itp-stf-executor/std", - "itp-stf-interface/std", - "itp-types/std", - "codec/std", - "log/std", - "sp-runtime/std", - "thiserror", - "itp-ocall-api/std", -] -sgx = [ - "sgx_tstd", - "ita-stf/sgx", - "itc-parentchain-indirect-calls-executor/sgx", - "itc-parentchain-light-client/sgx", - "itp-enclave-metrics/sgx", - "itp-extrinsics-factory/sgx", - "itp-stf-executor/sgx", - "thiserror_sgx", -] - -# feature to export mock implementations, only to be used for dev-dependencies! -mocks = [] diff --git a/tee-worker/bitacross/core/parentchain/block-importer/src/block_importer.rs b/tee-worker/bitacross/core/parentchain/block-importer/src/block_importer.rs deleted file mode 100644 index 6ca5c4866a..0000000000 --- a/tee-worker/bitacross/core/parentchain/block-importer/src/block_importer.rs +++ /dev/null @@ -1,221 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Imports parentchain blocks and executes any indirect calls found in the extrinsics. - -use crate::{error::Result, ImportParentchainBlocks}; - -use ita_stf::ParentchainHeader; -use itc_parentchain_indirect_calls_executor::ExecuteIndirectCalls; -use itc_parentchain_light_client::{ - concurrent_access::ValidatorAccess, BlockNumberOps, ExtrinsicSender, Validator, -}; -use itp_enclave_metrics::EnclaveMetric; -use itp_extrinsics_factory::CreateExtrinsics; -use itp_ocall_api::EnclaveMetricsOCallApi; -use itp_stf_executor::traits::StfUpdateState; -use itp_stf_interface::ShardCreationInfo; -use itp_types::{ - parentchain::{IdentifyParentchain, ParentchainId}, - OpaqueCall, H256, -}; -use log::*; -use sp_runtime::{ - generic::SignedBlock as SignedBlockG, - traits::{Block as ParentchainBlockTrait, Header as HeaderT, NumberFor}, -}; -use std::{marker::PhantomData, sync::Arc, vec, vec::Vec}; - -/// Parentchain block import implementation. -pub struct ParentchainBlockImporter< - ParentchainBlock, - ValidatorAccessor, - StfExecutor, - ExtrinsicsFactory, - IndirectCallsExecutor, - OCallApi, -> { - pub validator_accessor: Arc, - stf_executor: Arc, - extrinsics_factory: Arc, - pub indirect_calls_executor: Arc, - ocall_api: Arc, - shard_creation_info: ShardCreationInfo, - pub parentchain_id: ParentchainId, - _phantom: PhantomData, -} - -impl< - ParentchainBlock, - ValidatorAccessor, - StfExecutor, - ExtrinsicsFactory, - IndirectCallsExecutor, - OCallApi, - > - ParentchainBlockImporter< - ParentchainBlock, - ValidatorAccessor, - StfExecutor, - ExtrinsicsFactory, - IndirectCallsExecutor, - OCallApi, - > -{ - pub fn new( - validator_accessor: Arc, - stf_executor: Arc, - extrinsics_factory: Arc, - indirect_calls_executor: Arc, - ocall_api: Arc, - shard_creation_info: ShardCreationInfo, - parentchain_id: ParentchainId, - ) -> Self { - ParentchainBlockImporter { - validator_accessor, - stf_executor, - extrinsics_factory, - indirect_calls_executor, - ocall_api, - shard_creation_info, - parentchain_id, - _phantom: Default::default(), - } - } -} - -impl< - ParentchainBlock, - ValidatorAccessor, - StfExecutor, - ExtrinsicsFactory, - IndirectCallsExecutor, - OcallApi, - > ImportParentchainBlocks - for ParentchainBlockImporter< - ParentchainBlock, - ValidatorAccessor, - StfExecutor, - ExtrinsicsFactory, - IndirectCallsExecutor, - OcallApi, - > where - ParentchainBlock: ParentchainBlockTrait, - NumberFor: BlockNumberOps, - ValidatorAccessor: ValidatorAccess + IdentifyParentchain, - StfExecutor: StfUpdateState, - ExtrinsicsFactory: CreateExtrinsics, - IndirectCallsExecutor: ExecuteIndirectCalls, - OcallApi: EnclaveMetricsOCallApi, -{ - type SignedBlockType = SignedBlockG; - - fn import_parentchain_blocks( - &self, - blocks_to_import: Vec, - events_to_import: Vec>, - ) -> Result<()> { - let mut calls = Vec::::new(); - let id = self.validator_accessor.parentchain_id(); - - debug!( - "[{:?}] Import {} blocks to light-client. event blocks {}", - id, - blocks_to_import.len(), - events_to_import.len() - ); - let events_to_import_aligned: Vec> = if events_to_import.is_empty() { - vec![vec![]; blocks_to_import.len()] - } else { - events_to_import - }; - for (signed_block, raw_events) in - blocks_to_import.into_iter().zip(events_to_import_aligned.into_iter()) - { - let started = std::time::Instant::now(); - if let Err(e) = self - .validator_accessor - .execute_mut_on_validator(|v| v.submit_block(&signed_block)) - { - error!("[{:?}] Header submission to light client failed for block number {} and hash {:?}: {:?}", id, signed_block.block.header().number(), signed_block.block.hash(), e); - - return Err(e.into()) - } - - // check if we can fast-sync - if let Some(creation_block) = self.shard_creation_info.for_parentchain(id) { - if signed_block.block.header().number < creation_block.number { - trace!( - "fast-syncing block import, ignoring any invocations before block {:}", - creation_block.number - ); - continue - } - } - - let block = signed_block.block; - // Perform state updates. - if let Err(e) = self - .stf_executor - .update_states(block.header(), &self.validator_accessor.parentchain_id()) - { - error!("[{:?}] Error performing state updates upon block import", id); - return Err(e.into()) - } - - // Execute indirect calls that were found in the extrinsics of the block, - // incl. shielding and unshielding. - match self - .indirect_calls_executor - .execute_indirect_calls_in_block(&block, &raw_events) - { - Ok(Some(confirm_processed_parentchain_block_call)) => { - calls.push(confirm_processed_parentchain_block_call); - }, - Ok(None) => trace!("omitting confirmation call to non-integritee parentchain"), - Err(e) => error!("[{:?}] Error executing relevant events: {:?}", id, e), - }; - if let Err(e) = self - .ocall_api - .update_metric(EnclaveMetric::ParentchainBlockImportTime(started.elapsed())) - { - warn!("Failed to update metric for parentchain block import: {:?}", e); - }; - - info!( - "[{:?}] Successfully imported parentchain block (number: {}, hash: {})", - id, - block.header().number, - block.header().hash() - ); - } - - // Create extrinsics for all `unshielding` and `block processed` calls we've gathered. - let parentchain_extrinsics = - self.extrinsics_factory.create_extrinsics(calls.as_slice(), None)?; - - // Sending the extrinsic requires mut access because the validator caches the sent extrinsics internally. - self.validator_accessor - .execute_mut_on_validator(|v| v.send_extrinsics(parentchain_extrinsics))?; - - Ok(()) - } - - fn parentchain_id(&self) -> ParentchainId { - self.validator_accessor.parentchain_id() - } -} diff --git a/tee-worker/bitacross/core/parentchain/block-importer/src/block_importer_mock.rs b/tee-worker/bitacross/core/parentchain/block-importer/src/block_importer_mock.rs deleted file mode 100644 index aae92293e7..0000000000 --- a/tee-worker/bitacross/core/parentchain/block-importer/src/block_importer_mock.rs +++ /dev/null @@ -1,65 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Block importer mock. - -use crate::{ - error::{Error, Result}, - ImportParentchainBlocks, -}; -use itp_types::parentchain::ParentchainId; -use std::{sync::RwLock, vec::Vec}; - -/// Mock implementation for the block importer. -/// -/// Just stores all the blocks that were sent to import internally. -#[derive(Default)] -pub struct ParentchainBlockImporterMock { - imported_blocks: RwLock>, -} - -impl ParentchainBlockImporterMock -where - SignedBlockT: Clone, -{ - pub fn get_all_imported_blocks(&self) -> Vec { - let imported_blocks_lock = self.imported_blocks.read().unwrap(); - (*imported_blocks_lock).clone() - } -} - -impl ImportParentchainBlocks for ParentchainBlockImporterMock -where - SignedBlockT: Clone, -{ - type SignedBlockType = SignedBlockT; - - fn import_parentchain_blocks( - &self, - blocks_to_import: Vec, - _events: Vec>, - ) -> Result<()> { - let mut imported_blocks_lock = self.imported_blocks.write().map_err(|e| { - Error::Other(format!("failed to acquire lock for imported blocks vec: {:?}", e).into()) - })?; - imported_blocks_lock.extend(blocks_to_import); - Ok(()) - } - fn parentchain_id(&self) -> ParentchainId { - ParentchainId::Litentry - } -} diff --git a/tee-worker/bitacross/core/parentchain/block-importer/src/error.rs b/tee-worker/bitacross/core/parentchain/block-importer/src/error.rs deleted file mode 100644 index 856aa84ef2..0000000000 --- a/tee-worker/bitacross/core/parentchain/block-importer/src/error.rs +++ /dev/null @@ -1,51 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; - -use sgx_types::sgx_status_t; -use std::{boxed::Box, format}; - -pub type Result = core::result::Result; - -/// Parentchain block importer error. -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("SGX error, status: {0}")] - Sgx(sgx_status_t), - #[error("Extrinsics factory error: {0}")] - ExtrinsicsFactory(#[from] itp_extrinsics_factory::error::Error), - #[error("STF execution error: {0}")] - StfExecution(#[from] itp_stf_executor::error::Error), - #[error("Light-client error: {0}")] - LightClient(#[from] itc_parentchain_light_client::error::Error), - #[error(transparent)] - Other(#[from] Box), -} - -impl From for Error { - fn from(sgx_status: sgx_status_t) -> Self { - Self::Sgx(sgx_status) - } -} - -impl From for Error { - fn from(e: codec::Error) -> Self { - Self::Other(format!("{:?}", e).into()) - } -} diff --git a/tee-worker/bitacross/core/parentchain/block-importer/src/lib.rs b/tee-worker/bitacross/core/parentchain/block-importer/src/lib.rs deleted file mode 100644 index 3f2fd695bc..0000000000 --- a/tee-worker/bitacross/core/parentchain/block-importer/src/lib.rs +++ /dev/null @@ -1,61 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -//! Parentchain block importing logic. -#![feature(trait_alias)] -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -// re-export module to properly feature gate sgx and regular std environment -#[cfg(all(not(feature = "std"), feature = "sgx"))] -pub mod sgx_reexport_prelude { - pub use thiserror_sgx as thiserror; -} - -pub mod block_importer; -pub mod error; - -#[cfg(feature = "mocks")] -pub mod block_importer_mock; - -pub use block_importer::*; - -use error::Result; -use itp_types::parentchain::ParentchainId; -use std::vec::Vec; - -/// Block import from the parentchain. -pub trait ImportParentchainBlocks { - type SignedBlockType: Clone; - - /// Import parentchain blocks to the light-client (validator): - /// * Scans the blocks for relevant extrinsics - /// * Validates and execute those extrinsics, mutating state - /// * Includes block headers into the light client - /// * Sends `PROCESSED_PARENTCHAIN_BLOCK` extrinsics that include the merkle root of all processed calls - fn import_parentchain_blocks( - &self, - blocks_to_import: Vec, - events_to_import: Vec>, - ) -> Result<()>; - - fn parentchain_id(&self) -> ParentchainId; -} diff --git a/tee-worker/bitacross/core/parentchain/indirect-calls-executor/Cargo.toml b/tee-worker/bitacross/core/parentchain/indirect-calls-executor/Cargo.toml deleted file mode 100644 index 9ff5496a13..0000000000 --- a/tee-worker/bitacross/core/parentchain/indirect-calls-executor/Cargo.toml +++ /dev/null @@ -1,88 +0,0 @@ -[package] -name = "bc-itc-parentchain-indirect-calls-executor" -version = "0.1.0" -authors = ['Trust Computing GmbH ', 'Integritee AG '] -edition = "2021" - -[dependencies] -sgx_tstd = { workspace = true, optional = true } -sgx_types = { workspace = true } - -itp-api-client-types = { workspace = true } -itp-node-api = { workspace = true } -itp-sgx-crypto = { workspace = true } -itp-sgx-runtime-primitives = { workspace = true } -itp-stf-executor = { package = "bc-itp-stf-executor", path = "../../../core-primitives/stf-executor", default-features = false } -itp-stf-primitives = { workspace = true } -itp-test = { workspace = true } -itp-top-pool-author = { package = "bc-itp-top-pool-author", path = "../../../core-primitives/top-pool-author", default-features = false } -itp-types = { workspace = true } - -futures_sgx = { workspace = true, optional = true } -thiserror_sgx = { workspace = true, optional = true } - -futures = { workspace = true, optional = true } -thiserror = { workspace = true, optional = true } - -bs58 = { version = "0.4.0", default-features = false, features = ["alloc"] } -codec = { package = "parity-scale-codec", workspace = true } -log = { workspace = true } - -binary-merkle-tree = { workspace = true } -sp-runtime = { workspace = true } - -# litentry -bc-enclave-registry = { path = "../../../bitacross/core/bc-enclave-registry", default-features = false } -bc-relayer-registry = { path = "../../../bitacross/core/bc-relayer-registry", default-features = false } -bc-signer-registry = { path = "../../../bitacross/core/bc-signer-registry", default-features = false } -litentry-primitives = { workspace = true } - -[dev-dependencies] -env_logger = { workspace = true } -itp-node-api = { workspace = true, features = ["std", "mocks"] } -itp-sgx-crypto = { workspace = true, features = ["std", "mocks"] } -itp-stf-executor = { package = "bc-itp-stf-executor", path = "../../../core-primitives/stf-executor", features = ["std", "mocks"] } -itp-test = { workspace = true, features = ["std"] } -itp-top-pool-author = { package = "bc-itp-top-pool-author", path = "../../../core-primitives/top-pool-author", features = ["std", "mocks"] } -itc-parentchain-test = { workspace = true, features = ["std"] } - -[features] -default = ["std"] -std = [ - "bs58/std", - "codec/std", - "futures", - "itp-node-api/std", - "itp-sgx-crypto/std", - "itp-stf-executor/std", - "itp-top-pool-author/std", - "itp-api-client-types/std", - "itp-test/std", - "itp-types/std", - "itp-sgx-runtime-primitives/std", - "log/std", - #substrate - "binary-merkle-tree/std", - "sp-runtime/std", - "thiserror", - # litentry - "litentry-primitives/std", - "bc-relayer-registry/std", - "bc-signer-registry/std", - "bc-enclave-registry/std", -] -sgx = [ - "sgx_tstd", - "futures_sgx", - "itp-node-api/sgx", - "itp-sgx-crypto/sgx", - "itp-stf-executor/sgx", - "itp-top-pool-author/sgx", - "itp-test/sgx", - "thiserror_sgx", - # litentry - "litentry-primitives/sgx", - "bc-relayer-registry/sgx", - "bc-signer-registry/sgx", - "bc-enclave-registry/sgx", -] diff --git a/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/error.rs b/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/error.rs deleted file mode 100644 index e1c243507c..0000000000 --- a/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/error.rs +++ /dev/null @@ -1,81 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; - -use itp_types::parentchain::ParentchainEventProcessingError; -use sgx_types::sgx_status_t; -use sp_runtime::traits::LookupError; -use std::{boxed::Box, format}; - -pub type Result = core::result::Result; - -/// Indirect calls execution error. -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("SGX error, status: {0}")] - Sgx(sgx_status_t), - #[error("STF execution error: {0}")] - StfExecution(#[from] itp_stf_executor::error::Error), - #[error("Node Metadata error: {0:?}")] - NodeMetadata(itp_node_api::metadata::Error), - #[error("Node metadata provider error: {0:?}")] - NodeMetadataProvider(#[from] itp_node_api::metadata::provider::Error), - #[error("Crypto error: {0}")] - Crypto(itp_sgx_crypto::Error), - #[error(transparent)] - Other(#[from] Box), - #[error("AccountId lookup error")] - AccountIdLookup, -} - -impl From for Error { - fn from(e: ParentchainEventProcessingError) -> Self { - Self::Other(format!("{:?}", e).into()) - } -} - -impl From for Error { - fn from(sgx_status: sgx_status_t) -> Self { - Self::Sgx(sgx_status) - } -} - -impl From for Error { - fn from(e: itp_sgx_crypto::Error) -> Self { - Self::Crypto(e) - } -} - -impl From for Error { - fn from(e: codec::Error) -> Self { - Self::Other(format!("{:?}", e).into()) - } -} - -impl From for Error { - fn from(e: itp_node_api::metadata::Error) -> Self { - Self::NodeMetadata(e) - } -} - -impl From for Error { - fn from(_: LookupError) -> Self { - Self::AccountIdLookup - } -} diff --git a/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/event_filter.rs b/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/event_filter.rs deleted file mode 100644 index ffb9882f58..0000000000 --- a/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/event_filter.rs +++ /dev/null @@ -1,33 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -//! Various way to filter Parentchain events - -use crate::error::Error; - -use itp_stf_primitives::error::StfError; - -use std::format; - -impl From for Error { - fn from(a: StfError) -> Self { - Error::Other(format!("Error when shielding for privacy sidechain {:?}", a).into()) - } -} - -pub trait ToEvents { - fn to_events(&self) -> &E; -} diff --git a/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/executor.rs b/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/executor.rs deleted file mode 100644 index 216391bda8..0000000000 --- a/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/executor.rs +++ /dev/null @@ -1,436 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -//! Execute indirect calls, i.e. extrinsics extracted from parentchain blocks - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -use crate::sgx_reexport_prelude::*; - -use crate::{ - error::{Error, Result}, - filter_metadata::EventsFromMetadata, - traits::ExecuteIndirectCalls, -}; -use bc_enclave_registry::EnclaveRegistryUpdater; -use bc_relayer_registry::RelayerRegistryUpdater; -use bc_signer_registry::SignerRegistryUpdater; -use binary_merkle_tree::merkle_root; -use codec::{Decode, Encode}; -use core::marker::PhantomData; -use itp_node_api::metadata::{ - pallet_teebag::TeebagCallIndexes, provider::AccessNodeMetadata, NodeMetadataTrait, -}; -use itp_sgx_crypto::{key_repository::AccessKey, ShieldingCryptoDecrypt, ShieldingCryptoEncrypt}; -use itp_stf_executor::traits::StfEnclaveSigning; -use itp_stf_primitives::{ - traits::{IndirectExecutor, TrustedCallSigning, TrustedCallVerification}, - types::AccountId, -}; -use itp_top_pool_author::traits::AuthorApi; -use itp_types::{ - parentchain::{HandleParentchainEvents, ParentchainId}, - MrEnclave, OpaqueCall, RsaRequest, ShardIdentifier, H256, -}; -use log::*; -use sp_runtime::traits::{Block as ParentchainBlockTrait, Header, Keccak256}; -use std::{fmt::Debug, sync::Arc, vec::Vec}; - -pub struct IndirectCallsExecutor< - ShieldingKeyRepository, - StfEnclaveSigner, - TopPoolAuthor, - NodeMetadataProvider, - EventCreator, - ParentchainEventHandler, - TCS, - G, - RRU, - SRU, - ERU, -> where - RRU: RelayerRegistryUpdater, - SRU: SignerRegistryUpdater, - ERU: EnclaveRegistryUpdater, -{ - pub(crate) shielding_key_repo: Arc, - pub stf_enclave_signer: Arc, - pub(crate) top_pool_author: Arc, - pub(crate) node_meta_data_provider: Arc, - pub parentchain_id: ParentchainId, - parentchain_event_handler: ParentchainEventHandler, - pub relayer_registry_updater: Arc, - pub signer_registry_updater: Arc, - pub enclave_registry_updater: Arc, - _phantom: PhantomData<(EventCreator, ParentchainEventHandler, TCS, G)>, -} -impl< - ShieldingKeyRepository, - StfEnclaveSigner, - TopPoolAuthor, - NodeMetadataProvider, - EventCreator, - ParentchainEventHandler, - TCS, - G, - RRU, - SRU, - ERU, - > - IndirectCallsExecutor< - ShieldingKeyRepository, - StfEnclaveSigner, - TopPoolAuthor, - NodeMetadataProvider, - EventCreator, - ParentchainEventHandler, - TCS, - G, - RRU, - SRU, - ERU, - > where - RRU: RelayerRegistryUpdater, - SRU: SignerRegistryUpdater, - ERU: EnclaveRegistryUpdater, -{ - #[allow(clippy::too_many_arguments)] - pub fn new( - shielding_key_repo: Arc, - stf_enclave_signer: Arc, - top_pool_author: Arc, - node_meta_data_provider: Arc, - parentchain_id: ParentchainId, - parentchain_event_handler: ParentchainEventHandler, - relayer_registry_updater: Arc, - signer_registry_updater: Arc, - enclave_registry_updater: Arc, - ) -> Self { - IndirectCallsExecutor { - shielding_key_repo, - stf_enclave_signer, - top_pool_author, - node_meta_data_provider, - parentchain_id, - parentchain_event_handler, - relayer_registry_updater, - signer_registry_updater, - enclave_registry_updater, - _phantom: Default::default(), - } - } -} - -impl< - ShieldingKeyRepository, - StfEnclaveSigner, - TopPoolAuthor, - NodeMetadataProvider, - EventCreator, - ParentchainEventHandler, - TCS, - G, - RRU, - SRU, - ERU, - > ExecuteIndirectCalls - for IndirectCallsExecutor< - ShieldingKeyRepository, - StfEnclaveSigner, - TopPoolAuthor, - NodeMetadataProvider, - EventCreator, - ParentchainEventHandler, - TCS, - G, - RRU, - SRU, - ERU, - > where - ShieldingKeyRepository: AccessKey, - ::KeyType: ShieldingCryptoDecrypt - + ShieldingCryptoEncrypt, - StfEnclaveSigner: StfEnclaveSigning, - TopPoolAuthor: AuthorApi + Send + Sync + 'static, - NodeMetadataProvider: AccessNodeMetadata, - NodeMetadataProvider::MetadataType: NodeMetadataTrait + Clone, - EventCreator: EventsFromMetadata, - ParentchainEventHandler: - HandleParentchainEvents>, - TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, - G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, - RRU: RelayerRegistryUpdater, - SRU: SignerRegistryUpdater, - ERU: EnclaveRegistryUpdater, -{ - fn execute_indirect_calls_in_block( - &self, - block: &ParentchainBlock, - events: &[u8], - ) -> Result> - where - ParentchainBlock: ParentchainBlockTrait, - { - let block_number = *block.header().number(); - let block_hash = block.hash(); - - trace!("Scanning block {:?} for relevant events", block_number); - - let events = self - .node_meta_data_provider - .get_from_metadata(|metadata| { - EventCreator::create_from_metadata(metadata.clone(), block_hash, events) - })? - .ok_or_else(|| Error::Other("Could not create events from metadata".into()))?; - - let processed_events = self.parentchain_event_handler.handle_events::( - self, - events, - block_number, - )?; - - if self.parentchain_id == ParentchainId::Litentry { - // Include a processed parentchain block confirmation for each block. - Ok(Some(self.create_processed_parentchain_block_call::( - block_hash, - processed_events, - block_number, - )?)) - } else { - // fixme: send other type of confirmation here: https://github.com/integritee-network/worker/issues/1567 - Ok(None) - } - } - - fn create_processed_parentchain_block_call( - &self, - block_hash: H256, - events: Vec, - block_number: <::Header as Header>::Number, - ) -> Result - where - ParentchainBlock: ParentchainBlockTrait, - { - let call = self.node_meta_data_provider.get_from_metadata(|meta_data| { - meta_data.parentchain_block_processed_call_indexes() - })??; - let root: H256 = merkle_root::(events); - trace!("prepared parentchain_block_processed() call for block {:?} with index {:?} and merkle root {}", block_number, call, root); - // Litentry: we don't include `shard` in the extrinsic parameter to be backwards compatible, - // however, we should not forget it in case we need it later - Ok(OpaqueCall::from_tuple(&(call, block_hash, block_number, root))) - } -} - -impl< - ShieldingKeyRepository, - StfEnclaveSigner, - TopPoolAuthor, - NodeMetadataProvider, - EventFilter, - PrivacySidechain, - TCS, - G, - RRU, - SRU, - ERU, - > IndirectExecutor - for IndirectCallsExecutor< - ShieldingKeyRepository, - StfEnclaveSigner, - TopPoolAuthor, - NodeMetadataProvider, - EventFilter, - PrivacySidechain, - TCS, - G, - RRU, - SRU, - ERU, - > where - ShieldingKeyRepository: AccessKey, - ::KeyType: ShieldingCryptoDecrypt - + ShieldingCryptoEncrypt, - StfEnclaveSigner: StfEnclaveSigning, - TopPoolAuthor: AuthorApi + Send + Sync + 'static, - TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, - G: PartialEq + Encode + Decode + Debug + Clone + Send + Sync, - RRU: RelayerRegistryUpdater, - SRU: SignerRegistryUpdater, - ERU: EnclaveRegistryUpdater, -{ - fn submit_trusted_call(&self, shard: ShardIdentifier, encrypted_trusted_call: Vec) { - if let Err(e) = futures::executor::block_on( - self.top_pool_author.submit_top(RsaRequest::new(shard, encrypted_trusted_call)), - ) { - error!("Error adding indirect trusted call to TOP pool: {:?}", e); - } - } - - fn decrypt(&self, encrypted: &[u8]) -> Result> { - let key = self.shielding_key_repo.retrieve_key()?; - Ok(key.decrypt(encrypted)?) - } - - fn encrypt(&self, value: &[u8]) -> Result> { - let key = self.shielding_key_repo.retrieve_key()?; - Ok(key.encrypt(value)?) - } - - fn get_enclave_account(&self) -> Result { - Ok(self.stf_enclave_signer.get_enclave_account()?) - } - - fn get_mrenclave(&self) -> Result { - Ok(self.stf_enclave_signer.get_mrenclave()?) - } - - fn get_default_shard(&self) -> ShardIdentifier { - self.top_pool_author.list_handled_shards().first().copied().unwrap_or_default() - } - - fn sign_call_with_self>( - &self, - trusted_call: &TC, - shard: &ShardIdentifier, - ) -> Result { - Ok(self.stf_enclave_signer.sign_call_with_self(trusted_call, shard)?) - } - - fn get_relayer_registry_updater(&self) -> &RRU { - self.relayer_registry_updater.as_ref() - } - - fn get_signer_registry_updater(&self) -> &SRU { - self.signer_registry_updater.as_ref() - } - - fn get_enclave_registry_updater(&self) -> &ERU { - self.enclave_registry_updater.as_ref() - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::mock::*; - use bc_enclave_registry::EnclaveRegistry; - use bc_relayer_registry::RelayerRegistry; - use bc_signer_registry::SignerRegistry; - use codec::Encode; - - use itp_node_api::metadata::{ - metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository, - }; - use itp_sgx_crypto::mocks::KeyRepositoryMock; - use itp_stf_executor::mocks::StfEnclaveSignerMock; - use itp_test::mock::{ - shielding_crypto_mock::ShieldingCryptoMock, - stf_mock::{GetterMock, TrustedCallSignedMock}, - }; - use itp_top_pool_author::mocks::AuthorApiMock; - use itp_types::Block; - - type TestShieldingKeyRepo = KeyRepositoryMock; - type TestStfEnclaveSigner = StfEnclaveSignerMock; - type TestTopPoolAuthor = AuthorApiMock; - type TestNodeMetadataRepository = NodeMetadataRepository; - type TestIndirectCallExecutor = IndirectCallsExecutor< - TestShieldingKeyRepo, - TestStfEnclaveSigner, - TestTopPoolAuthor, - TestNodeMetadataRepository, - TestEventCreator, - MockParentchainEventHandler, - TrustedCallSignedMock, - GetterMock, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, - >; - - #[test] - fn ensure_empty_events_vec_triggers_zero_filled_merkle_root() { - // given - let dummy_metadata = NodeMetadataMock::new(); - let (indirect_calls_executor, _, _) = test_fixtures([38u8; 32], dummy_metadata.clone()); - - let block_hash = H256::from([1; 32]); - let events = Vec::new(); - let parentchain_block_processed_call_indexes = - dummy_metadata.parentchain_block_processed_call_indexes().unwrap(); - let expected_call = - (parentchain_block_processed_call_indexes, block_hash, 1u32, H256::default()).encode(); - - // when - let call = indirect_calls_executor - .create_processed_parentchain_block_call::(block_hash, events, 1u32) - .unwrap(); - - // then - assert_eq!(call.0, expected_call); - } - - #[test] - fn ensure_non_empty_events_vec_triggers_non_zero_merkle_root() { - // given - let dummy_metadata = NodeMetadataMock::new(); - let (indirect_calls_executor, _, _) = test_fixtures([39u8; 32], dummy_metadata.clone()); - - let block_hash = H256::from([1; 32]); - let events = vec![H256::from([4; 32]), H256::from([9; 32])]; - let parentchain_block_processed_call_indexes = - dummy_metadata.parentchain_block_processed_call_indexes().unwrap(); - - let zero_root_call = - (parentchain_block_processed_call_indexes, block_hash, 1u32, H256::default()).encode(); - - // when - let call = indirect_calls_executor - .create_processed_parentchain_block_call::(block_hash, events, 1u32) - .unwrap(); - - // then - assert_ne!(call.0, zero_root_call); - } - - fn test_fixtures( - mr_enclave: [u8; 32], - metadata: NodeMetadataMock, - ) -> (TestIndirectCallExecutor, Arc, Arc) { - let shielding_key_repo = Arc::new(TestShieldingKeyRepo::default()); - let stf_enclave_signer = Arc::new(TestStfEnclaveSigner::new(mr_enclave)); - let top_pool_author = Arc::new(TestTopPoolAuthor::default()); - let node_metadata_repo = Arc::new(NodeMetadataRepository::new(metadata)); - let parentchain_event_handler = MockParentchainEventHandler {}; - let relayer_registry = Arc::new(RelayerRegistry::new(Default::default())); - let signer_registry = Arc::new(SignerRegistry::new(Default::default())); - let enclave_registry = Arc::new(EnclaveRegistry::new(Default::default())); - - let executor = IndirectCallsExecutor::new( - shielding_key_repo.clone(), - stf_enclave_signer, - top_pool_author.clone(), - node_metadata_repo, - ParentchainId::Litentry, - parentchain_event_handler, - relayer_registry, - signer_registry, - enclave_registry, - ); - - (executor, top_pool_author, shielding_key_repo) - } -} diff --git a/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/filter_metadata.rs b/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/filter_metadata.rs deleted file mode 100644 index 25edc70ed7..0000000000 --- a/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/filter_metadata.rs +++ /dev/null @@ -1,118 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{error::Result, IndirectDispatch}; -use codec::{Decode, Encode}; -use core::marker::PhantomData; -use itp_api_client_types::{Events, Metadata}; -use itp_node_api::metadata::NodeMetadata; -use itp_stf_primitives::traits::IndirectExecutor; -use itp_types::{parentchain::FilterEvents, H256}; - -pub trait EventsFromMetadata { - type Output: FilterEvents; - - fn create_from_metadata( - metadata: NodeMetadata, - block_hash: H256, - events: &[u8], - ) -> Option; -} - -pub struct EventCreator { - _phantom: PhantomData, -} - -impl + Clone, FilterableEvents> EventsFromMetadata - for EventCreator -where - FilterableEvents: From> + FilterEvents, -{ - type Output = FilterableEvents; - - fn create_from_metadata( - metadata: NodeMetadata, - block_hash: H256, - events: &[u8], - ) -> Option { - let raw_metadata: Metadata = metadata.try_into().ok()?; - Some(Events::::new(raw_metadata, block_hash, events.to_vec()).into()) - } -} - -/// Trait to filter an indirect call and decode into it, where the decoding -/// is based on the metadata provided. -pub trait FilterIntoDataFrom { - /// Type to decode into. - type Output; - - /// Knows how to parse the parentchain metadata. - type ParseParentchainMetadata; - - /// Filters some bytes and returns `Some(Self::Output)` if the filter matches some criteria. - fn filter_into_from_metadata( - encoded_data: &[u8], - metadata: &NodeMetadata, - ) -> Option; -} - -/// Indirect calls filter denying all indirect calls. -pub struct DenyAll; - -mod seal { - use super::*; - use crate::Error; - use bc_enclave_registry::EnclaveRegistry; - use bc_relayer_registry::RelayerRegistry; - use bc_signer_registry::SignerRegistry; - use core::fmt::Debug; - use itp_stf_primitives::traits::TrustedCallVerification; - - /// Stub struct for the `DenyAll` filter that never executes anything. - #[derive(Debug, Encode)] - pub struct CantExecute; - - impl FilterIntoDataFrom for DenyAll { - type Output = CantExecute; - type ParseParentchainMetadata = (); - - fn filter_into_from_metadata(_: &[u8], _: &NodeMetadata) -> Option { - None - } - } - - impl< - Executor: IndirectExecutor, - TCS, - > IndirectDispatch for CantExecute - where - TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, - { - type Args = (); - fn dispatch(&self, _: &Executor, _args: Self::Args) -> Result<()> { - // We should never get here because `CantExecute` is in a private module and the trait - // implementation is sealed and always returns `None` instead of a `CantExecute` instance. - // Regardless, we never want the enclave to panic, this is why we take this extra safety - // measure. - log::warn!( - "Executed indirect dispatch for 'CantExecute'\ - this means there is some logic error." - ); - Ok(()) - } - } -} diff --git a/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/lib.rs b/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/lib.rs deleted file mode 100644 index 57b0911e87..0000000000 --- a/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/lib.rs +++ /dev/null @@ -1,52 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -//! Execute indirect calls, i.e. extrinsics extracted from parentchain blocks. -//! -//! The core struct of this crate is the [IndirectCallsExecutor] executor. It scans parentchain -//! blocks for relevant extrinsics, derives an indirect call for those and dispatches the -//! indirect call. - -#![feature(trait_alias)] -#![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(test, feature(assert_matches))] - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -extern crate alloc; - -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -// re-export module to properly feature gate sgx and regular std environment -#[cfg(all(not(feature = "std"), feature = "sgx"))] -pub mod sgx_reexport_prelude { - pub use futures_sgx as futures; - pub use thiserror_sgx as thiserror; -} - -mod executor; -pub mod mock; -pub mod traits; - -pub mod error; -pub mod event_filter; -pub mod filter_metadata; - -pub use error::{Error, Result}; -pub use executor::IndirectCallsExecutor; -pub use traits::{ExecuteIndirectCalls, IndirectDispatch}; diff --git a/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/mock.rs b/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/mock.rs deleted file mode 100644 index 7b17d920fb..0000000000 --- a/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/mock.rs +++ /dev/null @@ -1,317 +0,0 @@ -use crate::{ - error::{Error, Result as ICResult}, - filter_metadata::EventsFromMetadata, - IndirectDispatch, -}; -use bc_relayer_registry::RelayerRegistry; -use bc_signer_registry::SignerRegistry; -use codec::{Decode, Encode}; -use core::marker::PhantomData; -use litentry_primitives::DecryptableRequest; - -use bc_enclave_registry::EnclaveRegistry; -use itp_node_api::api_client::{CallIndex, PairSignature, UncheckedExtrinsicV4}; -use itp_sgx_runtime_primitives::types::{AccountId, Balance}; -use itp_stf_primitives::{traits::IndirectExecutor, types::Signature}; -use itp_test::mock::stf_mock::{GetterMock, TrustedCallMock, TrustedCallSignedMock}; -use itp_types::{ - parentchain::{events::*, FilterEvents, HandleParentchainEvents}, - Address, RsaRequest, ShardIdentifier, H256, -}; -use log::*; -use sp_runtime::traits::{Block as ParentchainBlock, Header as ParentchainHeader}; -use std::vec::Vec; - -pub struct ExtrinsicParser { - _phantom: PhantomData, -} -use itp_api_client_types::ParentchainSignedExtra; -use itp_stf_primitives::types::TrustedOperation; - -/// Parses the extrinsics corresponding to the parentchain. -pub type MockParentchainExtrinsicParser = ExtrinsicParser; - -/// Partially interpreted extrinsic containing the `signature` and the `call_index` whereas -/// the `call_args` remain in encoded form. -/// -/// Intended for usage, where the actual `call_args` form is unknown. -pub struct SemiOpaqueExtrinsic<'a> { - /// Signature of the Extrinsic. - pub signature: Signature, - /// Call index of the dispatchable. - pub call_index: CallIndex, - /// Encoded arguments of the dispatchable corresponding to the `call_index`. - pub call_args: &'a [u8], -} - -/// Trait to extract signature and call indexes of an encoded [UncheckedExtrinsicV4]. -pub trait ParseExtrinsic { - /// Signed extra of the extrinsic. - type SignedExtra; - - fn parse(encoded_call: &[u8]) -> Result; -} - -impl ParseExtrinsic for ExtrinsicParser -where - SignedExtra: Decode + Encode, -{ - type SignedExtra = SignedExtra; - - /// Extract a call index of an encoded call. - fn parse(encoded_call: &[u8]) -> Result { - let call_mut = &mut &encoded_call[..]; - - // `()` is a trick to stop decoding after the call index. So the remaining bytes - // of `call` after decoding only contain the parentchain's dispatchable's arguments. - let xt = UncheckedExtrinsicV4::< - Address, - (CallIndex, ()), - PairSignature, - Self::SignedExtra, - >::decode(call_mut)?; - - Ok(SemiOpaqueExtrinsic { - signature: xt.signature.unwrap().1, - call_index: xt.function.0, - call_args: call_mut, - }) - } -} -/// The default indirect call (extrinsic-triggered) of the Integritee-Parachain. -#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] -pub enum IndirectCall { - ShieldFunds(ShieldFundsArgs), - Invoke(InvokeArgs), -} - -impl< - Executor: IndirectExecutor< - TrustedCallSignedMock, - Error, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, - >, - > - IndirectDispatch< - Executor, - TrustedCallSignedMock, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, - > for IndirectCall -{ - type Args = (); - fn dispatch(&self, executor: &Executor, args: Self::Args) -> ICResult<()> { - trace!("dispatching indirect call {:?}", self); - match self { - IndirectCall::ShieldFunds(shieldfunds_args) => - shieldfunds_args.dispatch(executor, args), - IndirectCall::Invoke(invoke_args) => invoke_args.dispatch(executor, args), - } - } -} - -pub struct TestEventCreator; - -impl EventsFromMetadata for TestEventCreator { - type Output = MockEvents; - - fn create_from_metadata( - _metadata: NodeMetadata, - _block_hash: H256, - _events: &[u8], - ) -> Option { - Some(MockEvents) - } -} - -pub struct MockEvents; - -impl FilterEvents for MockEvents { - type Error = (); - - fn get_link_identity_events(&self) -> Result, Self::Error> { - Ok(Vec::new()) - } - - fn get_vc_requested_events(&self) -> Result, Self::Error> { - Ok(Vec::new()) - } - - fn get_deactivate_identity_events( - &self, - ) -> Result, Self::Error> { - Ok(Vec::new()) - } - - fn get_activate_identity_events(&self) -> Result, Self::Error> { - Ok(Vec::new()) - } - - fn get_enclave_unauthorized_events(&self) -> Result, Self::Error> { - Ok(Vec::new()) - } - - fn get_opaque_task_posted_events(&self) -> Result, Self::Error> { - Ok(Vec::new()) - } - - fn get_assertion_created_events(&self) -> Result, Self::Error> { - Ok(Vec::new()) - } - - fn get_parentchain_block_proccessed_events( - &self, - ) -> Result, Self::Error> { - Ok(Vec::new()) - } - - fn get_relayer_added_events( - &self, - ) -> Result, Self::Error> { - Ok(Vec::new()) - } - - fn get_relayers_removed_events( - &self, - ) -> Result, Self::Error> { - Ok(Vec::new()) - } - - fn get_enclave_added_events( - &self, - ) -> Result, Self::Error> { - Ok(Vec::new()) - } - - fn get_enclave_removed_events( - &self, - ) -> Result, Self::Error> { - Ok(Vec::new()) - } - - fn get_btc_wallet_generated_events( - &self, - ) -> Result, Self::Error> { - Ok(Vec::new()) - } - - fn get_account_store_updated_events(&self) -> Result, Self::Error> { - Ok(Vec::new()) - } -} - -pub struct MockParentchainEventHandler {} - -impl - HandleParentchainEvents< - Executor, - TrustedCallSignedMock, - Error, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, - > for MockParentchainEventHandler -where - Executor: IndirectExecutor< - TrustedCallSignedMock, - Error, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, - >, -{ - type Output = Vec; - - fn handle_events( - &self, - _: &Executor, - _: impl itp_types::parentchain::FilterEvents, - _block_number: <::Header as ParentchainHeader>::Number, - ) -> core::result::Result, Error> - where - Block: ParentchainBlock, - { - Ok(Vec::from([H256::default()])) - } -} - -/// Arguments of the Integritee-Parachain's shield fund dispatchable. -#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] -pub struct ShieldFundsArgs { - account_encrypted: Vec, - amount: Balance, - shard: ShardIdentifier, -} - -impl< - Executor: IndirectExecutor< - TrustedCallSignedMock, - Error, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, - >, - > - IndirectDispatch< - Executor, - TrustedCallSignedMock, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, - > for ShieldFundsArgs -{ - type Args = (); - fn dispatch(&self, executor: &Executor, _args: Self::Args) -> ICResult<()> { - info!("Found ShieldFunds extrinsic in block: \nAccount Encrypted {:?} \nAmount: {} \nShard: {}", - self.account_encrypted, self.amount, bs58::encode(self.shard.encode()).into_string()); - - debug!("decrypt the account id"); - let account_vec = executor.decrypt(&self.account_encrypted)?; - let _account = AccountId::decode(&mut account_vec.as_slice())?; - - let enclave_account_id = executor.get_enclave_account()?; - let trusted_call = TrustedCallMock::noop(enclave_account_id.into()); - let signed_trusted_call = executor.sign_call_with_self(&trusted_call, &self.shard)?; - let trusted_operation = - TrustedOperation::::indirect_call( - signed_trusted_call, - ); - - let encrypted_trusted_call = executor.encrypt(&trusted_operation.encode())?; - executor.submit_trusted_call(self.shard, encrypted_trusted_call); - Ok(()) - } -} - -#[derive(Debug, Clone, Encode, Decode, Eq, PartialEq)] -pub struct InvokeArgs { - request: RsaRequest, -} - -impl< - Executor: IndirectExecutor< - TrustedCallSignedMock, - Error, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, - >, - > - IndirectDispatch< - Executor, - TrustedCallSignedMock, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, - > for InvokeArgs -{ - type Args = (); - fn dispatch(&self, executor: &Executor, _args: Self::Args) -> ICResult<()> { - log::debug!("Found trusted call extrinsic, submitting it to the top pool"); - executor.submit_trusted_call(self.request.shard(), self.request.payload().to_vec()); - Ok(()) - } -} diff --git a/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/traits.rs b/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/traits.rs deleted file mode 100644 index e2a5c04f17..0000000000 --- a/tee-worker/bitacross/core/parentchain/indirect-calls-executor/src/traits.rs +++ /dev/null @@ -1,65 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{error::Result, Error}; -use bc_enclave_registry::EnclaveRegistryUpdater; -use bc_relayer_registry::RelayerRegistryUpdater; -use bc_signer_registry::SignerRegistryUpdater; -use codec::{Decode, Encode}; -use core::fmt::Debug; -use itp_stf_primitives::traits::{IndirectExecutor, TrustedCallVerification}; -use itp_types::{OpaqueCall, H256}; -use sp_runtime::traits::{Block as ParentchainBlockTrait, Header}; -use std::vec::Vec; - -/// Trait to execute the indirect calls found in the extrinsics of a block. -pub trait ExecuteIndirectCalls { - /// Scans blocks for extrinsics that ask the enclave to execute some actions. - /// Executes indirect invocation calls, including shielding and unshielding calls. - /// Returns all unshielding call confirmations as opaque calls and the hashes of executed shielding calls. - fn execute_indirect_calls_in_block( - &self, - block: &ParentchainBlock, - events: &[u8], - ) -> Result> - where - ParentchainBlock: ParentchainBlockTrait; - - /// Creates a processed_parentchain_block extrinsic for a given parentchain block hash and the merkle executed extrinsics. - /// - /// Calculates the merkle root of the extrinsics. In case no extrinsics are supplied, the root will be a hash filled with zeros. - fn create_processed_parentchain_block_call( - &self, - block_hash: H256, - extrinsics: Vec, - block_number: <::Header as Header>::Number, - ) -> Result - where - ParentchainBlock: ParentchainBlockTrait; -} - -/// Trait that should be implemented on indirect calls to be executed. -pub trait IndirectDispatch, TCS, RRU, SRU, ERU> -where - TCS: PartialEq + Encode + Decode + Debug + Clone + Send + Sync + TrustedCallVerification, - RRU: RelayerRegistryUpdater, - SRU: SignerRegistryUpdater, - ERU: EnclaveRegistryUpdater, -{ - type Args; - fn dispatch(&self, executor: &E, args: Self::Args) -> Result<()>; -} diff --git a/tee-worker/bitacross/core/parentchain/parentchain-crate/Cargo.toml b/tee-worker/bitacross/core/parentchain/parentchain-crate/Cargo.toml deleted file mode 100644 index 1f2146be92..0000000000 --- a/tee-worker/bitacross/core/parentchain/parentchain-crate/Cargo.toml +++ /dev/null @@ -1,42 +0,0 @@ -[package] -name = "bc-itc-parentchain" -version = "0.1.0" -authors = ['Trust Computing GmbH ', 'Integritee AG '] -edition = "2021" - -[dependencies] -codec = { package = "parity-scale-codec", workspace = true, features = ["chain-error"] } - -sp-runtime = { workspace = true } - -itc-parentchain-block-import-dispatcher = { package = "bc-itc-parentchain-block-import-dispatcher", path = "../block-import-dispatcher", default-features = false } -itc-parentchain-block-importer = { package = "bc-itc-parentchain-block-importer", path = "../block-importer", default-features = false } -itc-parentchain-indirect-calls-executor = { package = "bc-itc-parentchain-indirect-calls-executor", path = "../indirect-calls-executor", default-features = false } -itc-parentchain-light-client = { workspace = true } -itp-types = { workspace = true } - -[features] -default = ["std"] -std = [ - "codec/std", - "sp-runtime/std", - "itc-parentchain-block-import-dispatcher/std", - "itc-parentchain-block-importer/std", - "itc-parentchain-indirect-calls-executor/std", - "itc-parentchain-light-client/std", - "itp-types/std", -] -sgx = [ - "itc-parentchain-block-import-dispatcher/sgx", - "itc-parentchain-block-importer/sgx", - "itc-parentchain-indirect-calls-executor/sgx", - "itc-parentchain-light-client/sgx", -] -mocks = [ - "itc-parentchain-block-import-dispatcher/mocks", - "itc-parentchain-light-client/mocks", -] -test = [ - "mocks", - "itc-parentchain-light-client/test", -] diff --git a/tee-worker/bitacross/core/parentchain/parentchain-crate/src/lib.rs b/tee-worker/bitacross/core/parentchain/parentchain-crate/src/lib.rs deleted file mode 100644 index d9e4e07f55..0000000000 --- a/tee-worker/bitacross/core/parentchain/parentchain-crate/src/lib.rs +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Reexport all the parentchain components in one crate - -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(all(feature = "std", feature = "sgx"))] -compile_error!("feature \"std\" and feature \"sgx\" cannot be enabled at the same time"); - -pub use itc_parentchain_block_import_dispatcher as block_import_dispatcher; - -pub use itc_parentchain_block_importer as block_importer; - -pub use itc_parentchain_indirect_calls_executor as indirect_calls_executor; - -pub use itc_parentchain_light_client as light_client; diff --git a/tee-worker/bitacross/docker/README.md b/tee-worker/bitacross/docker/README.md deleted file mode 100644 index 09ee9bb415..0000000000 --- a/tee-worker/bitacross/docker/README.md +++ /dev/null @@ -1,106 +0,0 @@ -# How to run the multi-validateer docker setup - -## Prerequisite - -* Make sure you have installed Docker (version >= `2.0.0`) with [Docker Compose](https://docs.docker.com/compose/install/). On Windows, this can be Docker Desktop with WSL 2 integration. -* In case you also build the worker directly, without docker (e.g. on a dev machine, running `make`), you should run `make clean` before running the docker build. Otherwise, it can occasionally lead to build errors. -* The node image version that is loaded in the `docker-compose.yml`, (e.g. `image: "integritee/integritee-node:1.1.3"`) needs to be compatible with the worker you're trying to build. -* Set export VERSION=dev -* `envsubst` should be installed, it is needed to replace the $VERSION in yaml files as docker compose doesn't support variables on service names. - -## Building the Docker containers - -Run -``` -COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose -f <(envsubst < docker-compose.yml) build -``` -in this folder to build the worker image. This will build the worker from source and tag it in an image called `integritee-worker:dev`. - -## Running the docker setup - -``` -docker compose -f <(envsubst < docker-compose.yml) up -``` -Starts all services (node and workers), using the `integritee-worker:dev` images you've built in the previous step. - -## Run the demos - -### Demo indirect invocation (M6) -Build -``` -COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < demo-shielding-unshielding-multiworker.yml) build --build-arg WORKER_MODE_ARG=offchain-worker -``` -Run -``` -FLAVOR_ID=offchain-worker docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < demo-shielding-unshielding-multiworker.yml) up demo-shielding-unshielding-multiworker --exit-code-from demo-shielding-unshielding-multiworker -``` -### Demo direct call (M8) - -Build -``` -COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < demo-direct-call.yml) build --build-arg WORKER_MODE_ARG=sidechain -``` -Run -``` -docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < demo-direct-call.yml) up demo-direct-call --exit-code-from demo-direct-call -``` - -### Demo sidechain -Build -``` -COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < demo-sidechain.yml) build --build-arg WORKER_MODE_ARG=sidechain -``` -Run -``` -docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < demo-sidechain.yml) up demo-sidechain --exit-code-from demo-sidechain -``` - - -## Run the benchmarks -Build with -``` -COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < sidechain-benchmark.yml) build -``` -and then run with -``` -docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < sidechain-benchmark.yml) up sidechain-benchmark --exit-code-from sidechain-benchmark -``` - -## Run the fork simulator -The fork simulation uses `pumba` which in turn uses the Linux traffic control (TC). This is only available on Linux hosts, not on Windows with WSL unfortunately. -Build the docker compose setup with -``` -COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < fork-inducer.yml) -f <(envsubst < demo-sidechain.yml) build --build-arg WORKER_MODE_ARG=sidechain -``` - -This requires the docker BuildKit (docker version >= 18.09) and support for it in docker compose (version >= 1.25.0) - -Run the 2-worker setup with a fork inducer (pumba) that delays the traffic on worker 2 -``` -docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < fork-inducer.yml) -f <(envsubst < integration-test.yml) up --exit-code-from demo-sidechain -``` - -This should show that the integration test fails, because we had an unhandled fork in the sidechain. Clean up the containers after each run with: -``` -docker compose -f <(envsubst < docker-compose.yml) -f <(envsubst < fork-inducer.yml) -f <(envsubst < demo-sidechain.yml) down -``` - -We need these different compose files to separate the services that we're using. E.g. we want the integration test and fork simulator to be optional. The same could be solved using `profiles` - but that requires a more up-to-date version of `docker compose`. - -## FAQ -### What do I have to do to stop everything properly? -With `Ctrl-C` you stop the containers and with `docker compose down` you clean up/remove the containers. Note that `docker compose down` will also remove any logs docker has saved, since it will remove all the container context. - -### What do I have to do if I make changes to the code? -You need to re-build the worker image, using `docker compose build`. - -### How can I change the log level? -You can change the environment variable `RUST_LOG=` in the `docker-compose.yml` for each worker individually. - -### The log from the node are quite a nuisance. Why are they all together. -You can suppress the log output for a container by setting the logging driver. This can be set to either `none` (completely disables all logs), or `local` (docker will record the logs, depending on your docker compose version, it will also log to `stdout`) in the `docker-compose.yml`: -``` -logging: - driver: local -``` -Mind the indent. Explanations for all the logging drivers in `docker compose` can be found [here](https://docs.docker.com/config/containers/logging/local/). diff --git a/tee-worker/bitacross/docker/docker-compose.yml b/tee-worker/bitacross/docker/docker-compose.yml deleted file mode 100644 index 87ed9b1292..0000000000 --- a/tee-worker/bitacross/docker/docker-compose.yml +++ /dev/null @@ -1,42 +0,0 @@ -services: - litentry-node: # just traffic forwarding, the node network should be up already at this point - image: qoomon/docker-host - cap_add: [ 'NET_ADMIN', 'NET_RAW' ] - mem_limit: 8M - restart: on-failure - container_name: litentry-node - networks: - - litentry-test-network - bitacross-worker-1: - image: litentry/bitacross-worker:latest - container_name: bitacross-worker-1 - build: - context: ${PWD}/.. - dockerfile: build.Dockerfile - target: deployed-worker - depends_on: - litentry-node: - condition: service_started - devices: - - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" - - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" - volumes: - - "${AESMD:-/dev/null}:/var/run/aesmd" - - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" - environment: - - RUST_LOG=info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,itc_parentchain_light_client=info,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,itp_attestation_handler=debug,http_req=debug,lc_mock_server=warn,itc_rest_client=debug,lc_credentials=debug,lc_identity_verification=debug,lc_stf_task_receiver=debug,lc_stf_task_sender=debug,lc_data_providers=debug,itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug, - networks: - - litentry-test-network - healthcheck: - test: curl -s -f http://bitacross-worker-1:4645/is_initialized || exit 1 - interval: 30s - timeout: 10s - retries: 20 - entrypoint: - "/usr/local/bin/bitacross-worker --clean-reset --ws-external -M bitacross-worker-1 -T wss://bitacross-worker-1 - -u ws://litentry-node -U ws://bitacross-worker-1 -P 2011 -w 2101 -p 9944 -h 4645 - run --dev --skip-ra" - restart: "no" -networks: - litentry-test-network: - driver: bridge diff --git a/tee-worker/bitacross/docker/entrypoint.sh b/tee-worker/bitacross/docker/entrypoint.sh deleted file mode 100755 index cfbefaf9c4..0000000000 --- a/tee-worker/bitacross/docker/entrypoint.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -set -e - -# Check if the first argument is "mrenclave" -if [ "$1" = "mrenclave" ]; then - # If "mrenclave" is provided, execute the corresponding command - $SGX_ENCLAVE_SIGNER dump \ - -enclave /usr/local/bin/enclave.signed.so \ - -dumpfile df.out && \ - /usr/local/bin/extract_identity < df.out && rm df.out | grep -oP ':\s*\K[a-fA-F0-9]+' - -else - # If no specific command is provided, execute the default unnamed command - - # run aesmd in the background - /opt/intel/sgx-aesm-service/aesm/aesm_service - - exec /usr/local/bin/bitacross-worker "${@}" -fi diff --git a/tee-worker/bitacross/docker/fork.Dockerfile b/tee-worker/bitacross/docker/fork.Dockerfile deleted file mode 100644 index e92c8d129a..0000000000 --- a/tee-worker/bitacross/docker/fork.Dockerfile +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2021 Integritee AG -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -### Build Pumba image with dockerize -################################################## -FROM scratch AS fork-simulator-deployed -LABEL maintainer="zoltan@integritee.network" - -COPY --from=gaiaadm/pumba /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt -COPY --from=gaiaadm/pumba /pumba /usr/local/bin/pumba -COPY --from=powerman/dockerize /usr/local/bin/dockerize /usr/local/bin/dockerize - -ENV PATH="$PATH:/usr/local/bin" - -ENTRYPOINT ["/usr/local/bin/dockerize"] \ No newline at end of file diff --git a/tee-worker/bitacross/docker/lit-parentchain-nonce.yml b/tee-worker/bitacross/docker/lit-parentchain-nonce.yml deleted file mode 100644 index 728728e544..0000000000 --- a/tee-worker/bitacross/docker/lit-parentchain-nonce.yml +++ /dev/null @@ -1,22 +0,0 @@ -services: - lit-parentchain-nonce: - image: litentry/bitacross-cli:latest - container_name: litentry-parentchain-nonce - volumes: - - ../cli:/usr/local/worker-cli - build: - context: .. - dockerfile: build.Dockerfile - target: deployed-client - depends_on: - bitacross-worker-1: - condition: service_healthy - networks: - - litentry-test-network - entrypoint: - "/usr/local/worker-cli/lit_parentchain_nonce.sh -p 9944 -u ws://litentry-node - -V wss://bitacross-worker-1 -A 2011 -C /usr/local/bin/bitacross-cli 2>&1" - restart: "no" -networks: - litentry-test-network: - driver: bridge \ No newline at end of file diff --git a/tee-worker/bitacross/docker/lit-sign-bitcoin.yml b/tee-worker/bitacross/docker/lit-sign-bitcoin.yml deleted file mode 100644 index e1b8621a30..0000000000 --- a/tee-worker/bitacross/docker/lit-sign-bitcoin.yml +++ /dev/null @@ -1,21 +0,0 @@ -services: - lit-sign-bitcoin: - image: litentry/bitacross-cli:latest - container_name: litentry-sign-bitcoin-test - volumes: - - ../ts-tests:/ts-tests - - ../cli:/usr/local/worker-cli - build: - context: .. - dockerfile: build.Dockerfile - target: deployed-client - depends_on: - bitacross-worker-3: - condition: service_healthy - networks: - - litentry-test-network - entrypoint: "bash -c '/usr/local/worker-cli/lit_ts_integration_test.sh sign_bitcoin.test.ts 2>&1' " - restart: "no" -networks: - litentry-test-network: - driver: bridge diff --git a/tee-worker/bitacross/docker/multiworker-docker-compose.yml b/tee-worker/bitacross/docker/multiworker-docker-compose.yml deleted file mode 100644 index e4142a4ee5..0000000000 --- a/tee-worker/bitacross/docker/multiworker-docker-compose.yml +++ /dev/null @@ -1,102 +0,0 @@ -services: - litentry-node: # just traffic forwarding, the node network should be up already at this point - image: qoomon/docker-host - cap_add: [ 'NET_ADMIN', 'NET_RAW' ] - mem_limit: 8M - restart: on-failure - container_name: litentry-node - networks: - - litentry-test-network - bitacross-worker-1: - image: litentry/bitacross-worker:latest - container_name: bitacross-worker-1 - build: - context: ${PWD}/.. - dockerfile: build.Dockerfile - target: deployed-worker - depends_on: - litentry-node: - condition: service_started - devices: - - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" - - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" - volumes: - - "${AESMD:-/dev/null}:/var/run/aesmd" - - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" - environment: - - RUST_LOG=info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,itc_parentchain_light_client=info,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,itp_attestation_handler=debug,http_req=debug,lc_mock_server=warn,itc_rest_client=debug,lc_credentials=debug,lc_identity_verification=debug,lc_stf_task_receiver=debug,lc_stf_task_sender=debug,lc_data_providers=debug,itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug, - networks: - - litentry-test-network - healthcheck: - test: curl -s -f http://bitacross-worker-1:4645/is_initialized || exit 1 - interval: 30s - timeout: 10s - retries: 20 - entrypoint: - "/usr/local/bin/bitacross-worker --clean-reset --ws-external -M bitacross-worker-1 -T wss://bitacross-worker-1 - -u ws://litentry-node -U ws://bitacross-worker-1 -P 2011 -w 2101 -p 9944 -h 4645 - run --dev --skip-ra" - restart: "no" - bitacross-worker-2: - image: litentry/bitacross-worker:latest - container_name: bitacross-worker-2 - build: - context: ${PWD}/.. - dockerfile: build.Dockerfile - target: deployed-worker - depends_on: - bitacross-worker-1: - condition: service_healthy - devices: - - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" - - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" - volumes: - - "${AESMD:-/dev/null}:/var/run/aesmd" - - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" - environment: - - RUST_LOG=info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,itc_parentchain_light_client=info,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,itp_attestation_handler=debug,http_req=debug,lc_mock_server=warn,itc_rest_client=debug,lc_credentials=debug,lc_identity_verification=debug,lc_stf_task_receiver=debug,lc_stf_task_sender=debug,lc_data_providers=debug,itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug, - networks: - - litentry-test-network - healthcheck: - test: curl -s -f http://bitacross-worker-2:4645/is_initialized || exit 1 - interval: 30s - timeout: 10s - retries: 20 - entrypoint: - "/usr/local/bin/bitacross-worker --clean-reset --ws-external -M bitacross-worker-2 -T wss://bitacross-worker-2 - -u ws://litentry-node -U ws://bitacross-worker-2 -P 2011 -w 2101 -p 9944 -h 4645 - run --dev --skip-ra --request-state" - restart: "no" - bitacross-worker-3: - image: litentry/bitacross-worker:latest - container_name: bitacross-worker-3 - build: - context: ${PWD}/.. - dockerfile: build.Dockerfile - target: deployed-worker - depends_on: - bitacross-worker-2: - condition: service_healthy - devices: - - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" - - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" - volumes: - - "${AESMD:-/dev/null}:/var/run/aesmd" - - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" - environment: - - RUST_LOG=info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,itc_parentchain_light_client=info,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,itp_attestation_handler=debug,http_req=debug,lc_mock_server=warn,itc_rest_client=debug,lc_credentials=debug,lc_identity_verification=debug,lc_stf_task_receiver=debug,lc_stf_task_sender=debug,lc_data_providers=debug,itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug, - networks: - - litentry-test-network - healthcheck: - test: curl -s -f http://bitacross-worker-3:4645/is_initialized || exit 1 - interval: 30s - timeout: 10s - retries: 20 - entrypoint: - "/usr/local/bin/bitacross-worker --clean-reset --ws-external -M bitacross-worker-3 -T wss://bitacross-worker-3 - -u ws://litentry-node -U ws://bitacross-worker-3 -P 2011 -w 2101 -p 9944 -h 4645 - run --dev --skip-ra --request-state" - restart: "no" -networks: - litentry-test-network: - driver: bridge diff --git a/tee-worker/bitacross/docker/ping.Dockerfile b/tee-worker/bitacross/docker/ping.Dockerfile deleted file mode 100644 index 50ea4b7723..0000000000 --- a/tee-worker/bitacross/docker/ping.Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2021 Integritee AG -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -FROM alpine:latest - -RUN apk add --update iproute2 - -ENTRYPOINT ping \ No newline at end of file diff --git a/tee-worker/bitacross/docker/sidechain-benchmark.yml b/tee-worker/bitacross/docker/sidechain-benchmark.yml deleted file mode 100644 index 5c20f94831..0000000000 --- a/tee-worker/bitacross/docker/sidechain-benchmark.yml +++ /dev/null @@ -1,25 +0,0 @@ -services: - sidechain-benchmark: - image: bitacross-cli:${VERSION:-dev} - devices: - - "${SGX_PROVISION:-/dev/null}:/dev/sgx/provision" - - "${SGX_ENCLAVE:-/dev/null}:/dev/sgx/enclave" - volumes: - - "${AESMD:-/dev/null}:/var/run/aesmd" - - "${SGX_QCNL:-/dev/null}:/etc/sgx_default_qcnl.conf" - build: - context: ${PWD}/.. - dockerfile: build.Dockerfile - target: deployed-client - depends_on: - bitacross-worker-1-${VERSION}: - condition: service_healthy - networks: - - litentry-test-network - entrypoint: - "/usr/local/worker-cli/benchmark.sh -p 9944 -A 2011 -u ws://litentry-node - -V wss://bitacross-worker-1 -C /usr/local/bin/bitacross-cli 2>&1" - restart: "no" -networks: - litentry-test-network: - driver: bridge \ No newline at end of file diff --git a/tee-worker/bitacross/docs/README.md b/tee-worker/bitacross/docs/README.md deleted file mode 100644 index c0e42e94fa..0000000000 --- a/tee-worker/bitacross/docs/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Knowhow Dump - -This folder contains documents and links that contain some (potentially outdated) information about the worker. -Use with caution, as this is work in progress. Hence, the code is most likely progressing faster than this documentation. - -## Useful links: -### O- / Ecalls -- Ocall Bridge: https://github.com/integritee-network/worker/pull/293 & https://github.com/integritee-network/worker/pull/299 -- Enclave ecalls / ocalls: https://github.com/integritee-network/worker/issues/279 -- Abstract ecalls in enclave: https://github.com/integritee-network/worker/issues/286 -- Abstract ocalls in enclave: https://github.com/integritee-network/worker/issues/279 - -### Sidechain -- Sidechain functionality: https://polkadot.polkassembly.io/post/111 -- Sidechain flow: https://github.com/integritee-network/worker/pull/627 -- Simplified sidechain sequence, of a user call and the STF: https://raw.githubusercontent.com/haerdib/substraTEE_diagramms/main/sidechain-sequence.svg -- Top_pool sequence: https://raw.githubusercontent.com/haerdib/substraTEE_diagramms/main/submit_and_watch_sequence.svg -### Parentchain -- A rough overview of the architecture surrounding the parentchain block import dispatching: https://github.com/integritee-network/worker/pull/530 - -### Runtime -- Enclave runtime: https://github.com/integritee-network/worker/pull/472 - -### Non-worker related graphics -- substrate related graphics: https://github.com/brenzi/substrate-doc diff --git a/tee-worker/bitacross/docs/diagramms/block_import_sequence.svg b/tee-worker/bitacross/docs/diagramms/block_import_sequence.svg deleted file mode 100644 index 369cecb4ab..0000000000 --- a/tee-worker/bitacross/docs/diagramms/block_import_sequence.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - -
    For every
    sidechain block
    For every...
    For every
    parentchain block
    For every...
    For every
    extrinsic
    For every...
    For every
    shard
    For every...
    Parentchain BlockImport Queue
    pop queue until()
    pop queue until()
    Light Client
    verify block
    verify block
    import block
    import block
    ! state update
    ! state update
    Node
    Validateer / Worker
    Validateer / Worker
    Substrate Node
    Substrate Node
    Event: New Finalized Blockget_blocks(last_synced_header)
    finalized blocks
    finalized blocks
    Parentchain BlockImporter
    push_to_
    import_queue
    push_to_...
    sync_parentchain(finalized blocks)
    last_synced_header
    last_synced_header
    Sgx Runtime
    Sgx Runtime
    Sidechain BlockImport Queue
    new block
    new block
    import_block
    import_block
    Sidechain BlockProducer
    create 
    sidechain
    block
    create...
    create proposed_sidechain_block
    extrinsic
    create proposed_sidechain_block...
    Top PoolState
    calculate state diff
    (no state update!)
    calculate state diff...
    import_parentchain_block(import_until(sidechain block -> parentchain block))Untrusted Listenersubmit_simple_header
    Ok()
    Ok()
    send parentchain extrinsics
    send parentchai...
    check time
    check time
    (if_author == self)remove tops (shard, hashes)
    Ok()
    Ok()
    retrieve sidechain blocks
    parentchain header
    parentchain header
    pop until(parentchain header)
    blocks
    blocks
    peek assosciated parentchain header
    sidechain blocks
    sidechain blocks
    latest imported parentchain header
    latest imported parentchain header
    Sidechain BlockImporter
    verify sidechain
    block
    verify sidechain...
    load_state(shard)
    load_state(shard)
    trigger sidechainblock import
    latest parentchain header
    latest parentchain header
    trusted_calls(shard)
    trusted_calls(shard)
    get_trusted_calls(shard)Top Pool Execution Loop
    intervall trigger
    intervall t...
    claim_slot
    claim_slot
    list_shards
    shards
    shards
    exec_aura_on_slot(shards,parentchain header)execute trusted calls(trusted calls)
    state_diff, executed hashes
    state_diff, executed hashes
    sidechain blocks,
    extrinsics
    sidechain blocks,...
    broadcast sidechain block
    broadcast sidechai...
    Stf::execute(state)
    updated state
    updated state
    Executor
    write
    (updated state)
    write...
    execute_indirect_calls_extrinsic(block)
    Ok()
    Ok()
    write(updated_state)
    write(updated_state)
    For every
    parentchain block
    For every...
    For every
    extrinsic
    For every...
    pop queue until()
    pop queue until()
    verify block
    verify block
    import block
    import block
    ! state update
    ! state update
    submit_simple_header
    Ok()
    Ok()
    pop until(parentchain header)
    blocks
    blocks
    latest imported parentchain header
    latest imported parentchain header
    write
    (updated state)
    write...
    execute_indirect_calls_extrinsic(block)
    Ok()
    Ok()
    import_latest_parentchain_block(parentchain_hedaer)Stf::execute(state)
    updated state
    updated state
    apply_state_update(state, state_diff)+ set_last_block
    updated state
    updated state
    remove invalid tops
    Ok()
    Ok()
    Viewer does not support full SVG 1.1
    \ No newline at end of file diff --git a/tee-worker/bitacross/enclave-runtime/Cargo.lock b/tee-worker/bitacross/enclave-runtime/Cargo.lock deleted file mode 100644 index 3cf9f542f2..0000000000 --- a/tee-worker/bitacross/enclave-runtime/Cargo.lock +++ /dev/null @@ -1,5313 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" -dependencies = [ - "lazy_static", - "regex 1.9.5", -] - -[[package]] -name = "ac-compose-macros" -version = "0.4.2" -source = "git+https://github.com/Kailai-Wang/substrate-api-client?branch=polkadot-v0.9.42-litentry#f867fea44a3de5352d419a605afdd7bf22859e78" -dependencies = [ - "ac-primitives", - "log 0.4.21", - "maybe-async", -] - -[[package]] -name = "ac-node-api" -version = "0.5.1" -source = "git+https://github.com/Kailai-Wang/substrate-api-client?branch=polkadot-v0.9.42-litentry#f867fea44a3de5352d419a605afdd7bf22859e78" -dependencies = [ - "ac-primitives", - "bitvec", - "derive_more", - "either", - "frame-metadata", - "hex", - "log 0.4.21", - "parity-scale-codec", - "scale-bits", - "scale-decode", - "scale-encode", - "scale-info", - "serde 1.0.204", - "serde_json 1.0.120", - "sp-application-crypto", - "sp-core", - "sp-runtime", - "sp-runtime-interface", -] - -[[package]] -name = "ac-primitives" -version = "0.9.0" -source = "git+https://github.com/Kailai-Wang/substrate-api-client?branch=polkadot-v0.9.42-litentry#f867fea44a3de5352d419a605afdd7bf22859e78" -dependencies = [ - "impl-serde", - "parity-scale-codec", - "primitive-types", - "scale-info", - "serde 1.0.204", - "serde_json 1.0.120", - "sp-application-crypto", - "sp-core", - "sp-core-hashing", - "sp-runtime", - "sp-runtime-interface", - "sp-staking", - "sp-version", - "sp-weights", -] - -[[package]] -name = "aes" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" -dependencies = [ - "aes-soft", - "aesni", - "cipher", -] - -[[package]] -name = "aes-soft" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" -dependencies = [ - "cipher", - "opaque-debug 0.3.0", -] - -[[package]] -name = "aesni" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" -dependencies = [ - "cipher", - "opaque-debug 0.3.0", -] - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom 0.2.12", - "once_cell 1.18.0", - "version_check", -] - -[[package]] -name = "ahash" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if 1.0.0", - "once_cell 1.18.0", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "0.7.10" -source = "git+https://github.com/mesalock-linux/aho-corasick-sgx#7558a97cdf02804f38ec4edd1c0bb0dc2866267f" -dependencies = [ - "memchr 2.2.1", - "sgx_tstd", -] - -[[package]] -name = "aho-corasick" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" -dependencies = [ - "memchr 2.6.3", -] - -[[package]] -name = "array-bytes" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52f63c5c1316a16a4b35eaac8b76a98248961a533f061684cb2a7cb0eafb6c6" - -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - -[[package]] -name = "async-trait" -version = "0.1.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "syn 2.0.72", -] - -[[package]] -name = "auto_impl" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "autocfg" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base-x" -version = "0.2.6" -source = "git+https://github.com/whalelephant/base-x-rs?branch=no_std#906c9ac59282ff5a2eec86efd25d50ad9927b147" - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base58" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6107fe1be6682a68940da878d9e9f5e90ca5745b3dec9fd1bb393c8777d4f581" - -[[package]] -name = "base64" -version = "0.13.0" -source = "git+https://github.com/mesalock-linux/rust-base64-sgx?tag=sgx_1.1.3#dc7389e10817b078f289386b3b6a852ab6c4c021" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "base64" -version = "0.13.0" -source = "git+https://github.com/mesalock-linux/rust-base64-sgx?rev=sgx_1.1.3#dc7389e10817b078f289386b3b6a852ab6c4c021" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "base64" -version = "0.13.0" -source = "git+https://github.com/mesalock-linux/rust-base64-sgx#dc7389e10817b078f289386b3b6a852ab6c4c021" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bc-enclave-registry" -version = "0.1.0" -dependencies = [ - "itp-settings", - "itp-sgx-io", - "litentry-primitives", - "log 0.4.21", - "parity-scale-codec", - "sgx_tstd", - "sp-std", - "thiserror", -] - -[[package]] -name = "bc-ita-parentchain-interface" -version = "0.1.0" -dependencies = [ - "bc-enclave-registry", - "bc-ita-sgx-runtime", - "bc-ita-stf", - "bc-itc-parentchain-indirect-calls-executor", - "bc-relayer-registry", - "bc-signer-registry", - "itp-api-client-types", - "itp-node-api", - "itp-stf-primitives", - "itp-types", - "litentry-primitives", - "log 0.4.21", - "parity-scale-codec", - "sgx_tstd", - "sp-core", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "bc-ita-sgx-runtime" -version = "0.1.0" -dependencies = [ - "frame-executive", - "frame-support", - "frame-system", - "itp-sgx-runtime-primitives", - "pallet-balances", - "pallet-parentchain", - "pallet-sudo", - "pallet-timestamp", - "pallet-transaction-payment", - "parity-scale-codec", - "scale-info", - "sp-api", - "sp-core", - "sp-runtime", - "sp-std", - "sp-version", -] - -[[package]] -name = "bc-ita-stf" -version = "0.1.0" -dependencies = [ - "bc-ita-sgx-runtime", - "frame-support", - "frame-system", - "hex", - "hex-literal", - "itp-hashing", - "itp-node-api", - "itp-sgx-externalities", - "itp-stf-interface", - "itp-stf-primitives", - "itp-storage", - "itp-types", - "itp-utils", - "litentry-macros", - "litentry-primitives", - "log 0.4.21", - "pallet-balances", - "pallet-parentchain", - "pallet-sudo", - "parity-scale-codec", - "sgx_tstd", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "bc-itc-direct-rpc-client" -version = "0.1.0" -dependencies = [ - "itp-rpc", - "itp-types", - "itp-utils", - "log 0.4.21", - "rustls 0.19.0 (git+https://github.com/mesalock-linux/rustls?tag=sgx_1.1.3)", - "serde_json 1.0.120", - "sgx_tstd", - "tungstenite", - "url 2.5.0", - "webpki", -] - -[[package]] -name = "bc-itc-direct-rpc-server" -version = "0.1.0" -dependencies = [ - "itc-tls-websocket-server", - "itp-rpc", - "itp-types", - "itp-utils", - "jsonrpc-core", - "log 0.4.21", - "parity-scale-codec", - "serde_json 1.0.120", - "sgx_tstd", - "sp-runtime", - "thiserror", -] - -[[package]] -name = "bc-itc-offchain-worker-executor" -version = "0.1.0" -dependencies = [ - "bc-itp-stf-executor", - "bc-itp-top-pool-author", - "itc-parentchain-light-client", - "itp-extrinsics-factory", - "itp-stf-interface", - "itp-stf-primitives", - "itp-stf-state-handler", - "itp-types", - "log 0.4.21", - "parity-scale-codec", - "sgx_tstd", - "sp-runtime", - "thiserror", -] - -[[package]] -name = "bc-itc-parentchain" -version = "0.1.0" -dependencies = [ - "bc-itc-parentchain-block-import-dispatcher", - "bc-itc-parentchain-block-importer", - "bc-itc-parentchain-indirect-calls-executor", - "itc-parentchain-light-client", - "itp-types", - "parity-scale-codec", - "sp-runtime", -] - -[[package]] -name = "bc-itc-parentchain-block-import-dispatcher" -version = "0.1.0" -dependencies = [ - "bc-itc-parentchain-block-importer", - "itp-import-queue", - "log 0.4.21", - "sgx_tstd", - "sgx_types", - "thiserror", -] - -[[package]] -name = "bc-itc-parentchain-block-importer" -version = "0.1.0" -dependencies = [ - "bc-ita-stf", - "bc-itc-parentchain-indirect-calls-executor", - "bc-itp-stf-executor", - "itc-parentchain-light-client", - "itp-enclave-metrics", - "itp-extrinsics-factory", - "itp-ocall-api", - "itp-stf-interface", - "itp-types", - "log 0.4.21", - "parity-scale-codec", - "sgx_tstd", - "sgx_types", - "sp-runtime", - "thiserror", -] - -[[package]] -name = "bc-itc-parentchain-indirect-calls-executor" -version = "0.1.0" -dependencies = [ - "bc-enclave-registry", - "bc-itp-stf-executor", - "bc-itp-top-pool-author", - "bc-relayer-registry", - "bc-signer-registry", - "binary-merkle-tree", - "bs58", - "futures 0.3.8", - "itp-api-client-types", - "itp-node-api", - "itp-sgx-crypto", - "itp-sgx-runtime-primitives", - "itp-stf-primitives", - "itp-test", - "itp-types", - "litentry-primitives", - "log 0.4.21", - "parity-scale-codec", - "sgx_tstd", - "sgx_types", - "sp-runtime", - "thiserror", -] - -[[package]] -name = "bc-itp-stf-executor" -version = "0.1.0" -dependencies = [ - "bc-itp-top-pool-author", - "hex", - "itc-parentchain-test", - "itp-enclave-metrics", - "itp-node-api", - "itp-ocall-api", - "itp-sgx-crypto", - "itp-sgx-externalities", - "itp-stf-interface", - "itp-stf-primitives", - "itp-stf-state-handler", - "itp-stf-state-observer", - "itp-test", - "itp-time-utils", - "itp-types", - "log 0.4.21", - "parity-scale-codec", - "sgx_tstd", - "sgx_types", - "sp-core", - "sp-runtime", - "thiserror", -] - -[[package]] -name = "bc-itp-top-pool" -version = "0.1.0" -dependencies = [ - "bc-itc-direct-rpc-server", - "byteorder 1.4.3", - "derive_more", - "itp-stf-primitives", - "itp-types", - "jsonrpc-core", - "linked-hash-map", - "log 0.4.21", - "parity-scale-codec", - "sgx_tstd", - "sp-core", - "sp-runtime", -] - -[[package]] -name = "bc-itp-top-pool-author" -version = "0.1.0" -dependencies = [ - "bc-itp-top-pool", - "derive_more", - "itp-enclave-metrics", - "itp-ocall-api", - "itp-sgx-crypto", - "itp-stf-primitives", - "itp-stf-state-handler", - "itp-test", - "itp-types", - "itp-utils", - "jsonrpc-core", - "lazy_static", - "litentry-primitives", - "log 0.4.21", - "parity-scale-codec", - "sgx_tstd", - "sp-core", - "sp-runtime", -] - -[[package]] -name = "bc-musig2-ceremony" -version = "0.1.0" -dependencies = [ - "itp-sgx-crypto", - "k256", - "litentry-primitives", - "log 0.4.21", - "musig2", - "parity-scale-codec", - "sgx_rand", - "sgx_tstd", -] - -[[package]] -name = "bc-musig2-event" -version = "0.1.0" -dependencies = [ - "bc-enclave-registry", - "bc-itc-direct-rpc-client", - "bc-itc-direct-rpc-server", - "bc-musig2-ceremony", - "itp-ocall-api", - "itp-rpc", - "itp-sgx-crypto", - "itp-types", - "itp-utils", - "lc-direct-call", - "litentry-primitives", - "log 0.4.21", - "parity-scale-codec", - "sgx_rand", - "sgx_tstd", - "sp-core", - "threadpool", -] - -[[package]] -name = "bc-relayer-registry" -version = "0.1.0" -dependencies = [ - "itp-settings", - "itp-sgx-io", - "litentry-primitives", - "log 0.4.21", - "parity-scale-codec", - "sgx_tstd", - "sp-std", - "thiserror", -] - -[[package]] -name = "bc-signer-registry" -version = "0.1.0" -dependencies = [ - "itp-settings", - "itp-sgx-io", - "litentry-primitives", - "log 0.4.21", - "parity-scale-codec", - "sgx_tstd", - "sp-std", - "thiserror", -] - -[[package]] -name = "bc-task-processor" -version = "0.1.0" -dependencies = [ - "bc-enclave-registry", - "bc-ita-stf", - "bc-itc-direct-rpc-client", - "bc-itc-direct-rpc-server", - "bc-itp-stf-executor", - "bc-musig2-ceremony", - "bc-musig2-event", - "bc-relayer-registry", - "bc-signer-registry", - "bc-task-sender", - "frame-support", - "futures 0.3.8", - "itp-enclave-metrics", - "itp-ocall-api", - "itp-sgx-crypto", - "itp-sgx-externalities", - "itp-stf-state-handler", - "lc-direct-call", - "litentry-primitives", - "log 0.4.21", - "parity-scale-codec", - "sgx_crypto_helper", - "sgx_tstd", - "sp-core", - "thiserror", - "threadpool", -] - -[[package]] -name = "bc-task-sender" -version = "0.1.0" -dependencies = [ - "futures 0.3.8", - "lazy_static", - "litentry-primitives", - "parity-scale-codec", - "sgx_tstd", -] - -[[package]] -name = "bech32" -version = "0.10.0-beta" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea" - -[[package]] -name = "binary-merkle-tree" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "hash-db 0.16.0", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitcoin" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5973a027b341b462105675962214dfe3c938ad9afd395d84b28602608bdcec7b" -dependencies = [ - "bech32", - "bitcoin-internals", - "bitcoin_hashes", - "core2", - "hex-conservative", - "hex_lit", - "secp256k1 0.28.0", -] - -[[package]] -name = "bitcoin-internals" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" - -[[package]] -name = "bitcoin_hashes" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" -dependencies = [ - "bitcoin-internals", - "core2", - "hex-conservative", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "blake2b_simd" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "constant_time_eq 0.1.5", -] - -[[package]] -name = "blake2b_simd" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780" -dependencies = [ - "arrayref", - "arrayvec 0.7.4", - "constant_time_eq 0.3.0", -] - -[[package]] -name = "blake2s_simd" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e461a7034e85b211a4acb57ee2e6730b32912b06c08cc242243c39fc21ae6a2" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "constant_time_eq 0.1.5", -] - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding 0.1.5", - "byte-tools", - "byteorder 1.4.3", - "generic-array 0.12.4", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "block-padding 0.2.1", - "generic-array 0.14.7", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "block-padding" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] - -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - -[[package]] -name = "bounded-collections" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b05133427c07c4776906f673ccf36c21b102c9829c641a5b56bd151d44fd6" -dependencies = [ - "log 0.4.21", - "parity-scale-codec", - "scale-info", -] - -[[package]] -name = "bs58" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" - -[[package]] -name = "byte-slice-cast" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - -[[package]] -name = "byteorder" -version = "1.3.4" -source = "git+https://github.com/mesalock-linux/byteorder-sgx?tag=sgx_1.1.3#325f392dcd294109eb05f0a3c45e4141514c7784" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.0.1" -source = "git+https://github.com/integritee-network/bytes-sgx?branch=sgx-experimental#62ed3082be2e23cb9bc8cc7ee9983a523de69292" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "bytes" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" - -[[package]] -name = "cargo_toml" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dc9f7a067415ab5058020f04c60ec7b557084dbec0e021217bbabc7a8d38d14" -dependencies = [ - "serde 1.0.204", - "toml", -] - -[[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] - -[[package]] -name = "cfg-expr" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db" -dependencies = [ - "smallvec 1.11.1", -] - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.11" -source = "git+https://github.com/mesalock-linux/chrono-sgx#f964ae7f5f65bd2c9cd6f44a067e7980afc08ca0" -dependencies = [ - "num-integer", - "num-traits 0.2.10", - "sgx_tstd", -] - -[[package]] -name = "chrono" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" -dependencies = [ - "num-traits 0.2.16", - "serde 1.0.204", -] - -[[package]] -name = "cid" -version = "0.5.1" -source = "git+https://github.com/whalelephant/rust-cid?branch=nstd#cca87467c46106c801ca3727500477258b0f13b0" -dependencies = [ - "multibase", - "multihash", - "unsigned-varint", -] - -[[package]] -name = "cipher" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "const-oid" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" - -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - -[[package]] -name = "constant_time_eq" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "core-primitives" -version = "0.1.0" -dependencies = [ - "base58", - "base64 0.13.1", - "chrono 0.4.31", - "der 0.6.1", - "frame-support", - "hex", - "hex-literal", - "litentry-hex-utils", - "litentry-macros", - "litentry-proc-macros", - "pallet-evm", - "parity-scale-codec", - "ring 0.16.20", - "rustls-webpki", - "scale-info", - "serde 1.0.204", - "serde_json 1.0.120", - "sp-consensus-aura", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "strum", - "strum_macros", - "x509-cert", -] - -[[package]] -name = "core2" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239fa3ae9b63c2dc74bd3fa852d4792b8b305ae64eeede946265b6af62f1fff3" -dependencies = [ - "memchr 2.6.3", -] - -[[package]] -name = "cpufeatures" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" -dependencies = [ - "libc", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array 0.14.7", - "typenum", -] - -[[package]] -name = "curve25519-dalek" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" -dependencies = [ - "byteorder 1.4.3", - "digest 0.8.1", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder 1.4.3", - "digest 0.9.0", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "darling" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" -dependencies = [ - "fnv 1.0.7", - "ident_case", - "proc-macro2", - "quote 1.0.36", - "strsim", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" -dependencies = [ - "darling_core", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "data-encoding" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" - -[[package]] -name = "der" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" -dependencies = [ - "const-oid", - "der_derive", - "flagset", -] - -[[package]] -name = "der" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "der_derive" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ef71ddb5b3a1f53dee24817c8f70dfa1cb29e804c18d88c228d4bc9c86ee3b9" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "derive-syn-parse" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case", - "proc-macro2", - "quote 1.0.36", - "rustc_version", - "syn 1.0.109", -] - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer 0.10.4", - "const-oid", - "crypto-common", - "subtle 2.4.1", -] - -[[package]] -name = "ecdsa" -version = "0.16.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" -dependencies = [ - "der 0.7.8", - "digest 0.10.7", - "elliptic-curve", - "rfc6979", - "signature", - "spki 0.7.3", -] - -[[package]] -name = "ed25519-zebra" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" -dependencies = [ - "curve25519-dalek 3.2.0", - "hashbrown 0.12.3", - "hex", - "rand_core 0.6.4", - "sha2 0.9.9", - "zeroize", -] - -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest 0.10.7", - "ff", - "generic-array 0.14.7", - "group", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "enclave-runtime" -version = "0.0.1" -dependencies = [ - "bc-enclave-registry", - "bc-ita-parentchain-interface", - "bc-ita-sgx-runtime", - "bc-ita-stf", - "bc-itc-direct-rpc-server", - "bc-itc-offchain-worker-executor", - "bc-itc-parentchain", - "bc-itp-stf-executor", - "bc-itp-top-pool", - "bc-itp-top-pool-author", - "bc-musig2-ceremony", - "bc-relayer-registry", - "bc-signer-registry", - "bc-task-processor", - "bc-task-sender", - "cid", - "derive_more", - "env_logger", - "frame-support", - "futures 0.3.8", - "hex", - "ipfs-unixfs", - "itc-parentchain-light-client", - "itc-parentchain-test", - "itc-tls-websocket-server", - "itp-attestation-handler", - "itp-component-container", - "itp-extrinsics-factory", - "itp-import-queue", - "itp-node-api", - "itp-node-api-metadata", - "itp-node-api-metadata-provider", - "itp-nonce-cache", - "itp-ocall-api", - "itp-primitives-cache", - "itp-rpc", - "itp-settings", - "itp-sgx-crypto", - "itp-sgx-externalities", - "itp-sgx-temp-dir", - "itp-stf-interface", - "itp-stf-primitives", - "itp-stf-state-handler", - "itp-stf-state-observer", - "itp-storage", - "itp-test", - "itp-types", - "itp-utils", - "jsonrpc-core", - "lazy_static", - "lc-direct-call", - "litentry-hex-utils", - "litentry-macros", - "litentry-primitives", - "litentry-proc-macros", - "log 0.4.17", - "multibase", - "once_cell 1.4.0 (git+https://github.com/mesalock-linux/once_cell-sgx)", - "parity-scale-codec", - "primitive-types", - "rust-base58", - "rustls 0.19.0 (git+https://github.com/mesalock-linux/rustls?rev=sgx_1.1.3)", - "serde_json 1.0.60 (git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3)", - "sgx_crypto_helper", - "sgx_rand", - "sgx_serialize", - "sgx_serialize_derive", - "sgx_tcrypto", - "sgx_trts", - "sgx_tse", - "sgx_tseal", - "sgx_tstd", - "sgx_tunittest", - "sgx_types", - "sp-core", - "sp-runtime", - "url 2.5.0", - "webpki", -] - -[[package]] -name = "env_logger" -version = "0.9.0" -source = "git+https://github.com/integritee-network/env_logger-sgx#55745829b2ae8a77f0915af3671ec8a9a00cace9" -dependencies = [ - "humantime", - "log 0.4.17", - "regex 1.3.1", - "sgx_tstd", - "termcolor", -] - -[[package]] -name = "environmental" -version = "1.1.3" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "environmental" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48c92028aaa870e83d51c64e5d4e0b6981b360c522198c23959f219a4e1b15b" - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "ethbloom" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" -dependencies = [ - "crunchy", - "fixed-hash", - "impl-codec", - "impl-rlp", - "scale-info", - "tiny-keccak", -] - -[[package]] -name = "ethereum" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89fb87a9e103f71b903b80b670200b54cc67a07578f070681f1fffb7396fb7" -dependencies = [ - "bytes 1.5.0", - "ethereum-types", - "hash-db 0.15.2", - "hash256-std-hasher", - "parity-scale-codec", - "rlp", - "scale-info", - "sha3 0.10.8", - "triehash", -] - -[[package]] -name = "ethereum-types" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" -dependencies = [ - "ethbloom", - "fixed-hash", - "impl-codec", - "impl-rlp", - "primitive-types", - "scale-info", - "uint", -] - -[[package]] -name = "evm" -version = "0.39.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a49a4e11987c51220aa89dbe1a5cc877f5079fa6864c0a5b4533331db44e9365" -dependencies = [ - "auto_impl", - "ethereum", - "evm-core", - "evm-gasometer", - "evm-runtime", - "log 0.4.21", - "parity-scale-codec", - "primitive-types", - "rlp", - "scale-info", - "sha3 0.10.8", -] - -[[package]] -name = "evm-core" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1f13264b044cb66f0602180f0bc781c29accb41ff560669a3ec15858d5b606" -dependencies = [ - "parity-scale-codec", - "primitive-types", - "scale-info", -] - -[[package]] -name = "evm-gasometer" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d43eadc395bd1a52990787ca1495c26b0248165444912be075c28909a853b8c" -dependencies = [ - "evm-core", - "evm-runtime", - "primitive-types", -] - -[[package]] -name = "evm-runtime" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aa5b32f59ec582a5651978004e5c784920291263b7dcb6de418047438e37f4f" -dependencies = [ - "auto_impl", - "evm-core", - "primitive-types", - "sha3 0.10.8", -] - -[[package]] -name = "expander" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f360349150728553f92e4c997a16af8915f418d3a0f21b440d34c5632f16ed84" -dependencies = [ - "blake2", - "fs-err", - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core 0.6.4", - "subtle 2.4.1", -] - -[[package]] -name = "finality-grandpa" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36530797b9bf31cd4ff126dcfee8170f86b00cfdcea3269d73133cc0415945c3" -dependencies = [ - "either", - "futures 0.3.28", - "num-traits 0.2.16", - "parity-scale-codec", - "scale-info", -] - -[[package]] -name = "fixed-hash" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" -dependencies = [ - "byteorder 1.4.3", - "rustc-hex", - "static_assertions", -] - -[[package]] -name = "flagset" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec" - -[[package]] -name = "fnv" -version = "1.0.6" -source = "git+https://github.com/mesalock-linux/rust-fnv-sgx#c3bd6153c1403c1fa32fa54be5544d91f5efb017" -dependencies = [ - "hashbrown 0.3.1", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.2.1" -source = "git+https://github.com/domenukk/rust-url?rev=316c868#316c8683206f3cb741163779bb30963fa05b3612" -dependencies = [ - "percent-encoding 2.3.1", -] - -[[package]] -name = "fp-account" -version = "1.0.0-dev" -source = "git+https://github.com/polkadot-evm/frontier?branch=bar/polkadot-v0.9.42#a5a5e1e6ec08cd542a6084c310863150fb8841b1" -dependencies = [ - "hex", - "libsecp256k1", - "log 0.4.21", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "fp-evm" -version = "3.0.0-dev" -source = "git+https://github.com/polkadot-evm/frontier?branch=bar/polkadot-v0.9.42#a5a5e1e6ec08cd542a6084c310863150fb8841b1" -dependencies = [ - "evm", - "frame-support", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "frame-executive" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "frame-support", - "frame-system", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "sp-tracing", -] - -[[package]] -name = "frame-metadata" -version = "15.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "878babb0b136e731cc77ec2fd883ff02745ff21e6fb662729953d44923df009c" -dependencies = [ - "cfg-if 1.0.0", - "parity-scale-codec", - "scale-info", - "serde 1.0.204", -] - -[[package]] -name = "frame-support" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "bitflags", - "environmental 1.1.4", - "frame-metadata", - "frame-support-procedural", - "impl-trait-for-tuples", - "k256", - "log 0.4.21", - "parity-scale-codec", - "paste", - "scale-info", - "smallvec 1.11.1", - "sp-api", - "sp-arithmetic", - "sp-core", - "sp-core-hashing-proc-macro", - "sp-inherents", - "sp-io", - "sp-runtime", - "sp-staking", - "sp-std", - "sp-tracing", - "sp-weights", - "tt-call", -] - -[[package]] -name = "frame-support-procedural" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "Inflector", - "cfg-expr", - "derive-syn-parse", - "frame-support-procedural-tools", - "itertools 0.10.5", - "proc-macro-warning", - "proc-macro2", - "quote 1.0.36", - "syn 2.0.72", -] - -[[package]] -name = "frame-support-procedural-tools" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "frame-support-procedural-tools-derive", - "proc-macro-crate", - "proc-macro2", - "quote 1.0.36", - "syn 2.0.72", -] - -[[package]] -name = "frame-support-procedural-tools-derive" -version = "3.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "syn 2.0.72", -] - -[[package]] -name = "frame-system" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "frame-support", - "log 0.4.21", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", - "sp-version", - "sp-weights", -] - -[[package]] -name = "fs-err" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.8" -source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" -dependencies = [ - "futures-channel 0.3.8", - "futures-core 0.3.8", - "futures-executor", - "futures-io 0.3.8", - "futures-sink 0.3.8", - "futures-task 0.3.8", - "futures-util 0.3.8", - "sgx_tstd", -] - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel 0.3.28", - "futures-core 0.3.28", - "futures-io 0.3.28", - "futures-sink 0.3.28", - "futures-task 0.3.28", - "futures-util 0.3.28", -] - -[[package]] -name = "futures-channel" -version = "0.3.8" -source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" -dependencies = [ - "futures-core 0.3.8", - "futures-sink 0.3.8", - "sgx_tstd", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core 0.3.28", - "futures-sink 0.3.28", -] - -[[package]] -name = "futures-core" -version = "0.3.8" -source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.8" -source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" -dependencies = [ - "futures-core 0.3.8", - "futures-task 0.3.8", - "futures-util 0.3.8", - "sgx_tstd", -] - -[[package]] -name = "futures-io" -version = "0.3.8" -source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-macro" -version = "0.3.8" -source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "futures-sink" -version = "0.3.8" -source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.8" -source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" -dependencies = [ - "once_cell 1.4.0 (git+https://github.com/mesalock-linux/once_cell-sgx)", - "sgx_tstd", -] - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-util" -version = "0.3.8" -source = "git+https://github.com/mesalock-linux/futures-rs-sgx#d54882f24ddf7d61327a067b2f608d6940a36444" -dependencies = [ - "futures-channel 0.3.8", - "futures-core 0.3.8", - "futures-io 0.3.8", - "futures-macro", - "futures-sink 0.3.8", - "futures-task 0.3.8", - "memchr 2.2.1", - "pin-project-lite", - "pin-utils", - "proc-macro-hack", - "proc-macro-nested", - "sgx_tstd", - "slab 0.4.2", -] - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-core 0.3.28", - "futures-sink 0.3.28", - "futures-task 0.3.28", - "pin-project-lite", - "pin-utils", -] - -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "getrandom" -version = "0.1.14" -source = "git+https://github.com/mesalock-linux/getrandom-sgx#0aa9cc20c7dea713ccaac2c44430d625a395ebae" -dependencies = [ - "cfg-if 0.1.10", - "sgx_libc", - "sgx_trts", - "sgx_tstd", -] - -[[package]] -name = "getrandom" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core 0.6.4", - "subtle 2.4.1", -] - -[[package]] -name = "hash-db" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" - -[[package]] -name = "hash-db" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e7d7786361d7425ae2fe4f9e407eb0efaa0840f5212d109cc018c40c35c6ab4" - -[[package]] -name = "hash256-std-hasher" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92c171d55b98633f4ed3860808f004099b36c1cc29c42cfc53aa8591b21efcf2" -dependencies = [ - "crunchy", -] - -[[package]] -name = "hashbrown" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29fba9abe4742d586dfd0c06ae4f7e73a1c2d86b856933509b269d82cdf06e18" - -[[package]] -name = "hashbrown" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.6", -] - -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash 0.8.3", -] - -[[package]] -name = "hashbrown" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" - -[[package]] -name = "hashbrown_tstd" -version = "0.12.0" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#1b1d03376056321441ef99716aa0888bd5ef19f7" - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hex-conservative" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" -dependencies = [ - "core2", -] - -[[package]] -name = "hex-literal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" - -[[package]] -name = "hex_lit" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "http" -version = "0.2.1" -source = "git+https://github.com/integritee-network/http-sgx.git?branch=sgx-experimental#307b5421fb7a489a114bede0dc05c8d32b804f49" -dependencies = [ - "bytes 1.0.1", - "fnv 1.0.6", - "itoa 0.4.5", - "sgx_tstd", -] - -[[package]] -name = "httparse" -version = "1.4.1" -source = "git+https://github.com/integritee-network/httparse-sgx?branch=sgx-experimental#cc97e4b34d2c44a1e3df5bdebef446b9771f5cc3" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "humantime" -version = "1.3.0" -source = "git+https://github.com/mesalock-linux/humantime-sgx#c5243dfa36002c01adbc9aade288ead1b2c411cc" -dependencies = [ - "quick-error", - "sgx_tstd", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.2.0" -source = "git+https://github.com/mesalock-linux/rust-url-sgx?tag=sgx_1.1.3#23832f3191456c2d4a0faab10952e1747be58ca8" -dependencies = [ - "matches", - "sgx_tstd", - "unicode-bidi 0.3.4", - "unicode-normalization 0.1.12", -] - -[[package]] -name = "idna" -version = "0.5.0" -source = "git+https://github.com/domenukk/rust-url?rev=316c868#316c8683206f3cb741163779bb30963fa05b3612" -dependencies = [ - "unicode-bidi 0.3.15", - "unicode-normalization 0.1.23", -] - -[[package]] -name = "impl-codec" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" -dependencies = [ - "parity-scale-codec", -] - -[[package]] -name = "impl-rlp" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" -dependencies = [ - "rlp", -] - -[[package]] -name = "impl-serde" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" -dependencies = [ - "serde 1.0.204", -] - -[[package]] -name = "impl-trait-for-tuples" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "indexmap" -version = "1.6.1" -source = "git+https://github.com/mesalock-linux/indexmap-sgx#19f52458ba64dd7349a5d3a62227619a17e4db85" -dependencies = [ - "autocfg 1.1.0", - "hashbrown 0.9.1", - "sgx_tstd", -] - -[[package]] -name = "indexmap" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" -dependencies = [ - "equivalent", - "hashbrown 0.14.0", -] - -[[package]] -name = "integer-sqrt" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770" -dependencies = [ - "num-traits 0.2.16", -] - -[[package]] -name = "iovec" -version = "0.1.4" -source = "git+https://github.com/mesalock-linux/iovec-sgx#5c2f8e81925b4c06c556d856f3237461b00e27c9" -dependencies = [ - "sgx_libc", -] - -[[package]] -name = "ipfs-unixfs" -version = "0.0.1" -source = "git+https://github.com/whalelephant/rust-ipfs?branch=w-nstd#52f84dceea7065bb4ee2c24da53b3bedf162241a" -dependencies = [ - "cid", - "either", - "multihash", - "quick-protobuf", - "sha2 0.9.9", -] - -[[package]] -name = "itc-parentchain-light-client" -version = "0.1.0" -dependencies = [ - "finality-grandpa", - "itc-parentchain-test", - "itp-ocall-api", - "itp-sgx-io", - "itp-sgx-temp-dir", - "itp-storage", - "itp-test", - "itp-types", - "log 0.4.21", - "parity-scale-codec", - "sgx_tstd", - "sgx_types", - "sp-consensus-grandpa", - "sp-runtime", - "thiserror", -] - -[[package]] -name = "itc-parentchain-test" -version = "0.1.0" -dependencies = [ - "itp-types", - "sp-runtime", -] - -[[package]] -name = "itc-tls-websocket-server" -version = "0.1.0" -dependencies = [ - "bit-vec", - "chrono 0.4.31", - "log 0.4.21", - "mio", - "mio-extras", - "rcgen", - "rustls 0.19.0 (git+https://github.com/mesalock-linux/rustls?tag=sgx_1.1.3)", - "sgx_tstd", - "sp-core", - "thiserror", - "tungstenite", - "webpki", - "yasna", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.5" -source = "git+https://github.com/mesalock-linux/itoa-sgx#295ee451f5ec74f25c299552b481beb445ea3eb7" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "itp-api-client-types" -version = "0.1.0" -dependencies = [ - "itp-types", - "parity-scale-codec", - "substrate-api-client", -] - -[[package]] -name = "itp-attestation-handler" -version = "0.8.0" -dependencies = [ - "arrayvec 0.7.4", - "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx?rev=sgx_1.1.3)", - "bit-vec", - "chrono 0.4.11", - "hex", - "httparse", - "itertools 0.10.5", - "itp-ocall-api", - "itp-settings", - "itp-sgx-crypto", - "itp-sgx-io", - "itp-time-utils", - "log 0.4.21", - "num-bigint", - "parity-scale-codec", - "rustls 0.19.0 (git+https://github.com/mesalock-linux/rustls?tag=sgx_1.1.3)", - "serde_json 1.0.60 (git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3)", - "sgx_rand", - "sgx_tcrypto", - "sgx_tse", - "sgx_tstd", - "sgx_types", - "sp-core", - "thiserror", - "webpki", - "webpki-roots 0.21.0 (git+https://github.com/mesalock-linux/webpki-roots?branch=mesalock_sgx)", - "yasna", -] - -[[package]] -name = "itp-component-container" -version = "0.8.0" -dependencies = [ - "sgx_tstd", - "thiserror", -] - -[[package]] -name = "itp-enclave-metrics" -version = "0.1.0" -dependencies = [ - "litentry-primitives", - "parity-scale-codec", - "sgx_tstd", -] - -[[package]] -name = "itp-extrinsics-factory" -version = "0.1.0" -dependencies = [ - "hex", - "itp-node-api", - "itp-nonce-cache", - "itp-types", - "log 0.4.21", - "parity-scale-codec", - "sgx_tstd", - "sgx_types", - "sp-core", - "sp-runtime", - "substrate-api-client", - "thiserror", -] - -[[package]] -name = "itp-hashing" -version = "0.1.0" -dependencies = [ - "sp-core", -] - -[[package]] -name = "itp-import-queue" -version = "0.8.0" -dependencies = [ - "sgx_tstd", - "sgx_types", - "thiserror", -] - -[[package]] -name = "itp-node-api" -version = "0.1.0" -dependencies = [ - "itp-api-client-types", - "itp-node-api-metadata", - "itp-node-api-metadata-provider", -] - -[[package]] -name = "itp-node-api-metadata" -version = "0.1.0" -dependencies = [ - "derive_more", - "itp-api-client-types", - "itp-stf-primitives", - "parity-scale-codec", - "sp-core", - "sp-version", -] - -[[package]] -name = "itp-node-api-metadata-provider" -version = "0.1.0" -dependencies = [ - "itp-node-api-metadata", - "itp-stf-primitives", - "sgx_tstd", - "thiserror", -] - -[[package]] -name = "itp-nonce-cache" -version = "0.8.0" -dependencies = [ - "sgx_tstd", - "thiserror", -] - -[[package]] -name = "itp-ocall-api" -version = "0.1.0" -dependencies = [ - "derive_more", - "itp-api-client-types", - "itp-storage", - "itp-types", - "parity-scale-codec", - "sgx_types", - "sp-core", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "itp-primitives-cache" -version = "0.1.0" -dependencies = [ - "lazy_static", - "sgx_tstd", - "thiserror", -] - -[[package]] -name = "itp-rpc" -version = "0.1.0" -dependencies = [ - "itp-types", - "parity-scale-codec", - "serde 1.0.204", - "serde_json 1.0.120", - "sgx_tstd", -] - -[[package]] -name = "itp-settings" -version = "0.1.0" -dependencies = [ - "litentry-primitives", -] - -[[package]] -name = "itp-sgx-crypto" -version = "0.1.0" -dependencies = [ - "aes", - "derive_more", - "hex", - "itp-sgx-io", - "itp-sgx-temp-dir", - "k256", - "log 0.4.21", - "ofb", - "parity-scale-codec", - "rand 0.7.3", - "ring 0.16.20", - "secp256k1 0.28.0", - "serde_json 1.0.60 (git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3)", - "sgx_crypto_helper", - "sgx_rand", - "sgx_tstd", - "sgx_types", - "sp-core", - "sp-std", -] - -[[package]] -name = "itp-sgx-externalities" -version = "0.1.0" -dependencies = [ - "derive_more", - "environmental 1.1.3", - "itp-hashing", - "log 0.4.21", - "parity-scale-codec", - "postcard", - "serde 1.0.204", - "sgx_tstd", - "sp-core", -] - -[[package]] -name = "itp-sgx-io" -version = "0.8.0" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "itp-sgx-runtime-primitives" -version = "0.1.0" -dependencies = [ - "frame-system", - "pallet-balances", - "sp-core", - "sp-runtime", -] - -[[package]] -name = "itp-sgx-temp-dir" -version = "0.1.0" -dependencies = [ - "lazy_static", - "sgx_tstd", -] - -[[package]] -name = "itp-stf-interface" -version = "0.8.0" -dependencies = [ - "itp-node-api-metadata", - "itp-node-api-metadata-provider", - "itp-stf-primitives", - "itp-types", - "parity-scale-codec", -] - -[[package]] -name = "itp-stf-primitives" -version = "0.1.0" -dependencies = [ - "derive_more", - "itp-sgx-runtime-primitives", - "litentry-primitives", - "parity-scale-codec", - "sp-core", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "itp-stf-state-handler" -version = "0.1.0" -dependencies = [ - "itp-hashing", - "itp-settings", - "itp-sgx-crypto", - "itp-sgx-externalities", - "itp-sgx-io", - "itp-sgx-temp-dir", - "itp-stf-interface", - "itp-stf-state-observer", - "itp-time-utils", - "itp-types", - "log 0.4.21", - "parity-scale-codec", - "rust-base58", - "sgx_tstd", - "sgx_types", - "sp-core", - "thiserror", -] - -[[package]] -name = "itp-stf-state-observer" -version = "0.1.0" -dependencies = [ - "itp-types", - "log 0.4.21", - "sgx_tstd", - "thiserror", -] - -[[package]] -name = "itp-storage" -version = "0.1.0" -dependencies = [ - "derive_more", - "frame-metadata", - "frame-support", - "hash-db 0.15.2", - "itp-types", - "parity-scale-codec", - "sgx_tstd", - "sp-core", - "sp-runtime", - "sp-std", - "sp-trie", - "thiserror", -] - -[[package]] -name = "itp-test" -version = "0.1.0" -dependencies = [ - "hex", - "itp-node-api", - "itp-node-api-metadata-provider", - "itp-ocall-api", - "itp-sgx-crypto", - "itp-sgx-externalities", - "itp-stf-interface", - "itp-stf-primitives", - "itp-stf-state-handler", - "itp-storage", - "itp-types", - "jsonrpc-core", - "lc-teebag-storage", - "litentry-primitives", - "log 0.4.21", - "parity-scale-codec", - "sgx_crypto_helper", - "sgx_tstd", - "sgx_types", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "itp-time-utils" -version = "0.1.0" -dependencies = [ - "chrono 0.4.11", - "sgx_tstd", -] - -[[package]] -name = "itp-types" -version = "0.1.0" -dependencies = [ - "frame-system", - "itp-sgx-crypto", - "itp-sgx-runtime-primitives", - "itp-stf-primitives", - "itp-utils", - "litentry-primitives", - "pallet-balances", - "parity-scale-codec", - "serde 1.0.204", - "sp-consensus-grandpa", - "sp-core", - "sp-runtime", - "sp-std", - "substrate-api-client", -] - -[[package]] -name = "itp-utils" -version = "0.1.0" -dependencies = [ - "hex", - "litentry-hex-utils", - "parity-scale-codec", -] - -[[package]] -name = "jsonrpc-core" -version = "18.0.0" -source = "git+https://github.com/scs/jsonrpc?branch=no_std_v18#0faf53c491c3222b96242a973d902dd06e9b6674" -dependencies = [ - "futures 0.3.8", - "log 0.4.17", - "serde 1.0.118", - "serde_derive 1.0.118", - "serde_json 1.0.60 (git+https://github.com/mesalock-linux/serde-json-sgx)", -] - -[[package]] -name = "k256" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" -dependencies = [ - "cfg-if 1.0.0", - "ecdsa", - "elliptic-curve", - "sha2 0.10.7", - "signature", -] - -[[package]] -name = "keccak" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin", -] - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "lc-direct-call" -version = "0.1.0" -dependencies = [ - "bc-enclave-registry", - "bc-musig2-ceremony", - "bc-relayer-registry", - "bc-signer-registry", - "itp-sgx-crypto", - "itp-stf-primitives", - "litentry-primitives", - "log 0.4.21", - "parity-scale-codec", - "sgx_tstd", - "sp-core", - "sp-io", -] - -[[package]] -name = "lc-teebag-storage" -version = "0.1.0" -dependencies = [ - "itp-storage", - "itp-types", - "sp-std", -] - -[[package]] -name = "libc" -version = "0.2.152" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" - -[[package]] -name = "libsecp256k1" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" -dependencies = [ - "arrayref", - "base64 0.13.1", - "digest 0.9.0", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand 0.8.5", - "serde 1.0.204", -] - -[[package]] -name = "libsecp256k1-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" -dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle 2.4.1", -] - -[[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "libsecp256k1-gen-genmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.2" -source = "git+https://github.com/mesalock-linux/linked-hash-map-sgx#03e763f7c251c16e0b85e2fb058ba47be52f2a49" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "litentry-hex-utils" -version = "0.1.0" -dependencies = [ - "hex", -] - -[[package]] -name = "litentry-macros" -version = "0.1.0" - -[[package]] -name = "litentry-primitives" -version = "0.1.0" -dependencies = [ - "bitcoin", - "core-primitives", - "hex", - "itp-sgx-crypto", - "itp-sgx-runtime-primitives", - "log 0.4.21", - "parity-scale-codec", - "rand 0.7.3", - "ring 0.16.20", - "scale-info", - "secp256k1 0.28.0", - "serde 1.0.204", - "sgx_tstd", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "litentry-proc-macros" -version = "0.1.0" -dependencies = [ - "cargo_toml", - "proc-macro2", - "quote 1.0.36", - "syn 2.0.72", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "git+https://github.com/integritee-network/log-sgx#483383a9be3e2e900042eef9b6b2d0837411783f" -dependencies = [ - "cfg-if 1.0.0", - "sgx_tstd", -] - -[[package]] -name = "log" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" - -[[package]] -name = "matches" -version = "0.1.8" -source = "git+https://github.com/mesalock-linux/rust-std-candidates-sgx#5747bcf37f3e18687758838da0339ff0f2c83924" - -[[package]] -name = "maybe-async" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1b8c13cb1f814b634a96b2c725449fe7ed464a7b8781de8688be5ffbd3f305" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "memchr" -version = "2.2.1" -source = "git+https://github.com/mesalock-linux/rust-memchr-sgx#fb51ee32766cb9a2be39b7fb2b5de26bb86dcdeb" -dependencies = [ - "sgx_libc", - "sgx_tstd", - "sgx_types", -] - -[[package]] -name = "memchr" -version = "2.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "memory-db" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe" -dependencies = [ - "hash-db 0.16.0", -] - -[[package]] -name = "merlin" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e261cf0f8b3c42ded9f7d2bb59dea03aa52bc8a1cbc7482f9fc3fd1229d3b42" -dependencies = [ - "byteorder 1.4.3", - "keccak", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "zeroize", -] - -[[package]] -name = "mio" -version = "0.6.21" -source = "git+https://github.com/mesalock-linux/mio-sgx?tag=sgx_1.1.3#5b0e56a3066231c7a8d1876c7be3a19b08ffdfd5" -dependencies = [ - "iovec", - "log 0.4.17", - "net2", - "sgx_libc", - "sgx_trts", - "sgx_tstd", - "slab 0.4.2", -] - -[[package]] -name = "mio-extras" -version = "2.0.6" -source = "git+https://github.com/integritee-network/mio-extras-sgx?rev=963234b#963234bf55e44f9efff921938255126c48deef3a" -dependencies = [ - "lazycell", - "log 0.4.21", - "mio", - "sgx_tstd", - "sgx_types", - "slab 0.4.9", -] - -[[package]] -name = "multibase" -version = "0.8.0" -source = "git+https://github.com/whalelephant/rust-multibase?branch=nstd#df67fb30e86998f7c10d4eea16a1cd480d2448c0" -dependencies = [ - "base-x", - "data-encoding", - "lazy_static", -] - -[[package]] -name = "multihash" -version = "0.11.4" -source = "git+https://github.com/whalelephant/rust-multihash?branch=nstd#2c8aca8fa1fcbcba26951d925de40fa81696020a" -dependencies = [ - "blake2b_simd 0.5.11", - "blake2s_simd", - "digest 0.9.0", - "sha-1", - "sha2 0.9.9", - "sha3 0.9.1", - "unsigned-varint", -] - -[[package]] -name = "musig2" -version = "0.0.8" -source = "git+https://github.com/kailai-wang/musig2?branch=use-sha2-0.8#93857e52abbe8f9898c9ec743eecb1380132abcb" -dependencies = [ - "base16ct", - "hmac", - "k256", - "once_cell 1.4.0 (git+https://github.com/mesalock-linux/once_cell-sgx?branch=master)", - "secp", - "secp256k1 0.28.0", - "sgx_tstd", - "sha2 0.10.7", - "sha2_v08_wrapper", - "subtle 2.5.0", -] - -[[package]] -name = "net2" -version = "0.2.33" -source = "git+https://github.com/mesalock-linux/net2-rs-sgx#554583d15f3c9dff5d862a6ae64e227bb38fa729" -dependencies = [ - "cfg-if 0.1.10", - "sgx_libc", - "sgx_tstd", -] - -[[package]] -name = "no-std-net" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" - -[[package]] -name = "num" -version = "0.2.0" -source = "git+https://github.com/mesalock-linux/num-sgx#22645415542cc67551890dfdd34f4d5638b9ec78" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits 0.2.10", -] - -[[package]] -name = "num-bigint" -version = "0.2.5" -source = "git+https://github.com/mesalock-linux/num-bigint-sgx#76a5bed94dc31c32bd1670dbf72877abcf9bbc09" -dependencies = [ - "autocfg 1.1.0", - "num-integer", - "num-traits 0.2.10", - "sgx_tstd", -] - -[[package]] -name = "num-complex" -version = "0.2.3" -source = "git+https://github.com/mesalock-linux/num-complex-sgx#19700ad6de079ebc5560db472c282d1591e0d84f" -dependencies = [ - "autocfg 0.1.8", - "num-traits 0.2.10", - "sgx_tstd", -] - -[[package]] -name = "num-derive" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "num-integer" -version = "0.1.41" -source = "git+https://github.com/mesalock-linux/num-integer-sgx#404c50e5378ca635261688b080dee328ff42b6bd" -dependencies = [ - "autocfg 0.1.8", - "num-traits 0.2.10", - "sgx_tstd", -] - -[[package]] -name = "num-iter" -version = "0.1.39" -source = "git+https://github.com/mesalock-linux/num-iter-sgx#f19fc44fcad0b82a040e5a24c511e5049cc04b60" -dependencies = [ - "num-integer", - "num-traits 0.2.10", - "sgx_tstd", -] - -[[package]] -name = "num-rational" -version = "0.2.2" -source = "git+https://github.com/mesalock-linux/num-rational-sgx#be65f9ce439f3c9ec850d8041635ab6c3309b816" -dependencies = [ - "autocfg 0.1.8", - "num-bigint", - "num-integer", - "num-traits 0.2.10", - "sgx_tstd", -] - -[[package]] -name = "num-traits" -version = "0.2.10" -source = "git+https://github.com/mesalock-linux/num-traits-sgx#af046e0b15c594c960007418097dd4ff37ec3f7a" -dependencies = [ - "autocfg 0.1.8", - "sgx_tstd", -] - -[[package]] -name = "num-traits" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "ofb" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e609fc8b72da3dabd56427be9489d8a9f4bd2e4dc41660dd033c3c8e90b93c" -dependencies = [ - "cipher", -] - -[[package]] -name = "once_cell" -version = "1.4.0" -source = "git+https://github.com/mesalock-linux/once_cell-sgx?branch=master#cefcaa03fed4d85276b3235d875f1b45d399cc3c" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "once_cell" -version = "1.4.0" -source = "git+https://github.com/mesalock-linux/once_cell-sgx#cefcaa03fed4d85276b3235d875f1b45d399cc3c" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "pallet-balances" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "frame-support", - "frame-system", - "log 0.4.21", - "parity-scale-codec", - "scale-info", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "pallet-evm" -version = "6.0.0-dev" -source = "git+https://github.com/polkadot-evm/frontier?branch=bar/polkadot-v0.9.42#a5a5e1e6ec08cd542a6084c310863150fb8841b1" -dependencies = [ - "evm", - "fp-account", - "fp-evm", - "frame-support", - "frame-system", - "hex", - "impl-trait-for-tuples", - "log 0.4.21", - "parity-scale-codec", - "rlp", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "pallet-parentchain" -version = "0.1.0" -dependencies = [ - "frame-support", - "frame-system", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", -] - -[[package]] -name = "pallet-sudo" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "frame-support", - "frame-system", - "parity-scale-codec", - "scale-info", - "sp-io", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "pallet-timestamp" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "frame-support", - "frame-system", - "log 0.4.21", - "parity-scale-codec", - "scale-info", - "sp-inherents", - "sp-runtime", - "sp-std", - "sp-timestamp", -] - -[[package]] -name = "pallet-transaction-payment" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "frame-support", - "frame-system", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "parity-scale-codec" -version = "3.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" -dependencies = [ - "arrayvec 0.7.4", - "bitvec", - "byte-slice-cast", - "bytes 1.5.0", - "impl-trait-for-tuples", - "parity-scale-codec-derive", - "serde 1.0.204", -] - -[[package]] -name = "parity-scale-codec-derive" -version = "3.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312270ee71e1cd70289dacf597cab7b207aa107d2f28191c2ae45b2ece18a260" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - -[[package]] -name = "pem" -version = "0.8.2" -source = "git+https://github.com/mesalock-linux/pem-rs-sgx#fdfef4f24a9fb3fa72e8a71bb28bd8ff15feff2f" -dependencies = [ - "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx)", - "once_cell 1.4.0 (git+https://github.com/mesalock-linux/once_cell-sgx)", - "regex 1.3.1", - "sgx_tstd", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "git+https://github.com/mesalock-linux/rust-url-sgx?tag=sgx_1.1.3#23832f3191456c2d4a0faab10952e1747be58ca8" - -[[package]] -name = "percent-encoding" -version = "2.3.1" -source = "git+https://github.com/domenukk/rust-url?rev=316c868#316c8683206f3cb741163779bb30963fa05b3612" - -[[package]] -name = "pin-project-lite" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der 0.7.8", - "spki 0.7.3", -] - -[[package]] -name = "postcard" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25c0b0ae06fcffe600ad392aabfa535696c8973f2253d9ac83171924c58a858" -dependencies = [ - "postcard-cobs", - "serde 1.0.204", -] - -[[package]] -name = "postcard-cobs" -version = "0.1.5-pre" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c68cb38ed13fd7bc9dd5db8f165b7c8d9c1a315104083a2b10f11354c2af97f" - -[[package]] -name = "ppv-lite86" -version = "0.2.6" -source = "git+https://github.com/mesalock-linux/cryptocorrosion-sgx#32d7de50b5f03a10fe5a42167410be2dd3c2e389" - -[[package]] -name = "primitive-types" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" -dependencies = [ - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "scale-info", - "uint", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell 1.18.0", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "version_check", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro-nested" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" - -[[package]] -name = "proc-macro-warning" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e99670bafb56b9a106419397343bdbc8b8742c3cc449fec6345f86173f47cd4" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "syn 2.0.72", -] - -[[package]] -name = "proc-macro2" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "quick-error" -version = "1.2.2" -source = "git+https://github.com/mesalock-linux/quick-error-sgx#468bf2cce746f34dd3df8c1c5b4a5a6494914d36" - -[[package]] -name = "quick-protobuf" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e489d4a83c17ea69b0291630229b5d4c92a94a3bf0165f7f72f506e94cda8b4b" -dependencies = [ - "byteorder 1.4.3", -] - -[[package]] -name = "quote" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" - -[[package]] -name = "quote" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.7.3" -source = "git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3#83583f073de3b4f75c3c3ef5e174d484ed941f85" -dependencies = [ - "getrandom 0.1.14", - "rand_chacha", - "rand_core 0.5.1 (git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3)", - "sgx_tstd", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3#83583f073de3b4f75c3c3ef5e174d484ed941f85" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1 (git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3)", - "sgx_tstd", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "git+https://github.com/mesalock-linux/rand-sgx?tag=sgx_1.1.3#83583f073de3b4f75c3c3ef5e174d484ed941f85" -dependencies = [ - "getrandom 0.1.14", - "sgx_tstd", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" - -[[package]] -name = "rcgen" -version = "0.9.2" -source = "git+https://github.com/integritee-network/rcgen#1852c8dbeb74de36a422d218254b659497daf717" -dependencies = [ - "chrono 0.4.11", - "pem", - "ring 0.16.19", - "sgx_tstd", - "yasna", -] - -[[package]] -name = "ref-cast" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acde58d073e9c79da00f2b5b84eed919c8326832648a5b109b3fce1bb1175280" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7473c2cfcf90008193dd0e3e16599455cb601a9fce322b5bb55de799664925" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "syn 2.0.72", -] - -[[package]] -name = "regex" -version = "1.3.1" -source = "git+https://github.com/mesalock-linux/regex-sgx#76aef86f9836532d17764523d0fa23bb7d2e31cf" -dependencies = [ - "aho-corasick 0.7.10", - "memchr 2.2.1", - "regex-syntax 0.6.12", - "sgx_tstd", - "thread_local", -] - -[[package]] -name = "regex" -version = "1.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" -dependencies = [ - "aho-corasick 1.1.1", - "memchr 2.6.3", - "regex-automata", - "regex-syntax 0.7.5", -] - -[[package]] -name = "regex-automata" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" -dependencies = [ - "aho-corasick 1.1.1", - "memchr 2.6.3", - "regex-syntax 0.7.5", -] - -[[package]] -name = "regex-syntax" -version = "0.6.12" -source = "git+https://github.com/mesalock-linux/regex-sgx#76aef86f9836532d17764523d0fa23bb7d2e31cf" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "regex-syntax" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle 2.4.1", -] - -[[package]] -name = "ring" -version = "0.16.19" -source = "git+https://github.com/mesalock-linux/ring-sgx?tag=v0.16.5#844efe271ed78a399d803b2579f5f2424d543c9f" -dependencies = [ - "cc", - "sgx_tstd", - "spin", - "untrusted 0.7.1", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "git+https://github.com/betrusted-io/ring-xous?branch=0.16.20-cleanup#4296c2e7904898766cf7d8d589759a129794783b" -dependencies = [ - "cc", - "libc", - "log 0.4.21", - "once_cell 1.18.0", - "rkyv", - "spin", - "untrusted 0.7.1", - "winapi", - "xous", - "xous-api-names", - "xous-ipc", -] - -[[package]] -name = "rkyv" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70de01b38fe7baba4ecdd33b777096d2b326993d8ea99bc5b6ede691883d3010" -dependencies = [ - "memoffset", - "ptr_meta", - "rkyv_derive", -] - -[[package]] -name = "rkyv_derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a169f6bc5a81033e86ed39d0f4150e2608160b73d2b93c6e8e6a3efa873f14" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "rlp" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" -dependencies = [ - "bytes 1.5.0", - "rlp-derive", - "rustc-hex", -] - -[[package]] -name = "rlp-derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "rust-base58" -version = "0.0.4" -source = "git+https://github.com/mesalock-linux/rust-base58-sgx?rev=sgx_1.1.3#13fb3e0a543690e6e19332f37ba85fd74c56cb2f" -dependencies = [ - "num", - "sgx_tstd", -] - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustls" -version = "0.19.0" -source = "git+https://github.com/mesalock-linux/rustls?tag=sgx_1.1.3#95b5e79dc24b02f3ce424437eb9698509d0baf58" -dependencies = [ - "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx)", - "log 0.4.17", - "ring 0.16.19", - "sct", - "sgx_tstd", - "webpki", -] - -[[package]] -name = "rustls" -version = "0.19.0" -source = "git+https://github.com/mesalock-linux/rustls?rev=sgx_1.1.3#95b5e79dc24b02f3ce424437eb9698509d0baf58" -dependencies = [ - "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx)", - "log 0.4.17", - "ring 0.16.19", - "sct", - "sgx_tstd", - "webpki", -] - -[[package]] -name = "rustls-pki-types" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47003264dea418db67060fa420ad16d0d2f8f0a0360d825c00e177ac52cb5d8" - -[[package]] -name = "rustls-webpki" -version = "0.102.0-alpha.3" -source = "git+https://github.com/rustls/webpki?rev=da923ed#da923edaab56f599971e58773617fb574cd019dc" -dependencies = [ - "ring 0.16.20", - "rustls-pki-types", - "untrusted 0.9.0", -] - -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "scale-bits" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "036575c29af9b6e4866ffb7fa055dbf623fe7a9cc159b33786de6013a6969d89" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde 1.0.204", -] - -[[package]] -name = "scale-decode" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea509715113edab351e1f4d51fba6b186653259049a1155b52e2e994dd2f0e6d" -dependencies = [ - "parity-scale-codec", - "primitive-types", - "scale-bits", - "scale-decode-derive", - "scale-info", - "smallvec 1.11.1", -] - -[[package]] -name = "scale-decode-derive" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66c9d7a1341497e9d016722144310de3dc6c933909c0376017c88f65092fff37" -dependencies = [ - "darling", - "proc-macro-crate", - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "scale-encode" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6f51bc8cd927dab2f4567b1a8a8e9d7fd5d0866f2dbc7c84fc97cfa9383a26" -dependencies = [ - "parity-scale-codec", - "primitive-types", - "scale-bits", - "scale-encode-derive", - "scale-info", - "smallvec 1.11.1", -] - -[[package]] -name = "scale-encode-derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28be1877787156a2df01be3c029b92bdffa6b6a9748d4996e383fff218c88f3" -dependencies = [ - "darling", - "proc-macro-crate", - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "scale-info" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ef2175c2907e7c8bc0a9c3f86aeb5ec1f3b275300ad58a44d0c3ae379a5e52e" -dependencies = [ - "bitvec", - "cfg-if 1.0.0", - "derive_more", - "parity-scale-codec", - "scale-info-derive", - "serde 1.0.204", -] - -[[package]] -name = "scale-info-derive" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "schnorrkel" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021b403afe70d81eea68f6ea12f6b3c9588e5d536a94c3bf80f15e7faa267862" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "curve25519-dalek 2.1.3", - "merlin", - "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.8.2", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "sct" -version = "0.6.0" -source = "git+https://github.com/mesalock-linux/sct.rs?branch=mesalock_sgx#c4d859cca232e6c9d88ca12048df3bc26e1ed4ad" -dependencies = [ - "ring 0.16.19", - "sgx_tstd", - "untrusted 0.7.1", -] - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der 0.7.8", - "generic-array 0.14.7", - "pkcs8", - "subtle 2.4.1", - "zeroize", -] - -[[package]] -name = "secp" -version = "0.2.3" -source = "git+https://github.com/kziemianek/secp.git?branch=sgx#0479a3b12fc204015cdb63c138078fefe7e32341" -dependencies = [ - "base16ct", - "k256", - "once_cell 1.4.0 (git+https://github.com/mesalock-linux/once_cell-sgx?branch=master)", - "secp256k1 0.28.0", - "sgx_tstd", - "subtle 2.5.0", -] - -[[package]] -name = "secp256k1" -version = "0.24.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" -dependencies = [ - "secp256k1-sys 0.6.1", -] - -[[package]] -name = "secp256k1" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2acea373acb8c21ecb5a23741452acd2593ed44ee3d343e72baaa143bc89d0d5" -dependencies = [ - "bitcoin_hashes", - "secp256k1-sys 0.9.1", -] - -[[package]] -name = "secp256k1-sys" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" -dependencies = [ - "cc", -] - -[[package]] -name = "secp256k1-sys" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd97a086ec737e30053fd5c46f097465d25bb81dd3608825f65298c4c98be83" -dependencies = [ - "cc", -] - -[[package]] -name = "secrecy" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" -dependencies = [ - "zeroize", -] - -[[package]] -name = "semver" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" - -[[package]] -name = "serde" -version = "1.0.118" -source = "git+https://github.com/mesalock-linux/serde-sgx#db0226f1d5d70fca6b96af2c285851502204e21c" -dependencies = [ - "serde_derive 1.0.118", - "sgx_tstd", -] - -[[package]] -name = "serde" -version = "1.0.204" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" -dependencies = [ - "serde_derive 1.0.204", -] - -[[package]] -name = "serde-big-array" -version = "0.3.0" -source = "git+https://github.com/mesalock-linux/serde-big-array-sgx#94122c5167aee38b39b09a620a60db2c28cf7428" -dependencies = [ - "serde 1.0.118", - "serde_derive 1.0.118", -] - -[[package]] -name = "serde_derive" -version = "1.0.118" -source = "git+https://github.com/mesalock-linux/serde-sgx#db0226f1d5d70fca6b96af2c285851502204e21c" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "serde_derive" -version = "1.0.204" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "syn 2.0.72", -] - -[[package]] -name = "serde_json" -version = "1.0.60" -source = "git+https://github.com/mesalock-linux/serde-json-sgx?tag=sgx_1.1.3#380893814ad2a057758d825bab798aa117f7362a" -dependencies = [ - "indexmap 1.6.1", - "itoa 0.4.5", - "ryu", - "serde 1.0.118", - "sgx_tstd", -] - -[[package]] -name = "serde_json" -version = "1.0.60" -source = "git+https://github.com/mesalock-linux/serde-json-sgx#380893814ad2a057758d825bab798aa117f7362a" -dependencies = [ - "itoa 0.4.5", - "ryu", - "serde 1.0.118", - "sgx_tstd", -] - -[[package]] -name = "serde_json" -version = "1.0.120" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" -dependencies = [ - "itoa 1.0.9", - "ryu", - "serde 1.0.204", -] - -[[package]] -name = "serde_spanned" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" -dependencies = [ - "serde 1.0.204", -] - -[[package]] -name = "sgx_alloc" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#1b1d03376056321441ef99716aa0888bd5ef19f7" - -[[package]] -name = "sgx_backtrace_sys" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#1b1d03376056321441ef99716aa0888bd5ef19f7" -dependencies = [ - "cc", - "sgx_build_helper", - "sgx_libc", -] - -[[package]] -name = "sgx_build_helper" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#1b1d03376056321441ef99716aa0888bd5ef19f7" - -[[package]] -name = "sgx_crypto_helper" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" -dependencies = [ - "itertools 0.11.0", - "serde 1.0.118", - "serde-big-array", - "serde_derive 1.0.118", - "sgx_tcrypto", - "sgx_tstd", - "sgx_types", -] - -[[package]] -name = "sgx_demangle" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#1b1d03376056321441ef99716aa0888bd5ef19f7" - -[[package]] -name = "sgx_libc" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#1b1d03376056321441ef99716aa0888bd5ef19f7" -dependencies = [ - "sgx_types", -] - -[[package]] -name = "sgx_rand" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" -dependencies = [ - "sgx_trts", - "sgx_tstd", - "sgx_types", -] - -[[package]] -name = "sgx_serialize" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "sgx_serialize_derive" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" -dependencies = [ - "quote 0.3.15", - "sgx_serialize_derive_internals", - "syn 0.11.11", -] - -[[package]] -name = "sgx_serialize_derive_internals" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" -dependencies = [ - "syn 0.11.11", -] - -[[package]] -name = "sgx_tcrypto" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" -dependencies = [ - "sgx_types", -] - -[[package]] -name = "sgx_tprotected_fs" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#1b1d03376056321441ef99716aa0888bd5ef19f7" -dependencies = [ - "sgx_trts", - "sgx_types", -] - -[[package]] -name = "sgx_trts" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" -dependencies = [ - "sgx_libc", - "sgx_types", -] - -[[package]] -name = "sgx_tse" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" -dependencies = [ - "sgx_types", -] - -[[package]] -name = "sgx_tseal" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" -dependencies = [ - "sgx_tcrypto", - "sgx_trts", - "sgx_tse", - "sgx_types", -] - -[[package]] -name = "sgx_tstd" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" -dependencies = [ - "hashbrown_tstd", - "sgx_alloc", - "sgx_backtrace_sys", - "sgx_demangle", - "sgx_libc", - "sgx_tprotected_fs", - "sgx_trts", - "sgx_types", - "sgx_unwind", -] - -[[package]] -name = "sgx_tunittest" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "sgx_types" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" - -[[package]] -name = "sgx_unwind" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#1b1d03376056321441ef99716aa0888bd5ef19f7" -dependencies = [ - "sgx_build_helper", -] - -[[package]] -name = "sha-1" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "sha1" -version = "0.6.0" -source = "git+https://github.com/mesalock-linux/rust-sha1-sgx?tag=sgx_1.1.3#482a4d489e860d63a21662aaea988f600f8e20a4" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "sha2" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "sha2" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.7", -] - -[[package]] -name = "sha2_v08_wrapper" -version = "0.1.0" -source = "git+https://github.com/kailai-wang/sha2_v08#c41176becc675e84cd708e8b18ba2cd0c9cf8eb0" -dependencies = [ - "sha2 0.8.2", -] - -[[package]] -name = "sha3" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "keccak", - "opaque-debug 0.3.0", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest 0.10.7", - "keccak", -] - -[[package]] -name = "signature" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" -dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", -] - -[[package]] -name = "slab" -version = "0.4.2" -source = "git+https://github.com/mesalock-linux/slab-sgx#0b0e6ec2abd588afd2f40fd082bc473d100d0f40" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg 1.1.0", -] - -[[package]] -name = "smallvec" -version = "1.6.1" -source = "git+https://github.com/mesalock-linux/rust-smallvec-sgx#b5925f10aa5bc3370a0fb339140ee063f5a888dd" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "smallvec" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" - -[[package]] -name = "sp-api" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "log 0.4.21", - "parity-scale-codec", - "scale-info", - "sp-api-proc-macro", - "sp-core", - "sp-metadata-ir", - "sp-runtime", - "sp-std", - "sp-version", -] - -[[package]] -name = "sp-api-proc-macro" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "Inflector", - "blake2", - "expander", - "proc-macro-crate", - "proc-macro2", - "quote 1.0.36", - "syn 2.0.72", -] - -[[package]] -name = "sp-application-crypto" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-std", -] - -[[package]] -name = "sp-arithmetic" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "integer-sqrt", - "num-traits 0.2.16", - "parity-scale-codec", - "scale-info", - "sp-std", - "static_assertions", -] - -[[package]] -name = "sp-consensus-aura" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "scale-info", - "sp-api", - "sp-application-crypto", - "sp-consensus-slots", - "sp-inherents", - "sp-runtime", - "sp-std", - "sp-timestamp", -] - -[[package]] -name = "sp-consensus-grandpa" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "finality-grandpa", - "log 0.4.21", - "parity-scale-codec", - "scale-info", - "sp-api", - "sp-application-crypto", - "sp-core", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "sp-consensus-slots" -version = "0.10.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "scale-info", - "sp-std", - "sp-timestamp", -] - -[[package]] -name = "sp-core" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "array-bytes", - "bitflags", - "blake2", - "bounded-collections", - "ed25519-zebra", - "hash-db 0.16.0", - "hash256-std-hasher", - "libsecp256k1", - "log 0.4.21", - "merlin", - "parity-scale-codec", - "paste", - "primitive-types", - "scale-info", - "schnorrkel", - "secp256k1 0.24.3", - "secrecy", - "sp-core-hashing", - "sp-debug-derive", - "sp-runtime-interface", - "sp-std", - "sp-storage", - "ss58-registry", - "zeroize", -] - -[[package]] -name = "sp-core-hashing" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "blake2b_simd 1.0.2", - "byteorder 1.4.3", - "digest 0.10.7", - "sha2 0.10.7", - "sha3 0.10.8", - "sp-std", - "twox-hash", -] - -[[package]] -name = "sp-core-hashing-proc-macro" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "sp-core-hashing", - "syn 2.0.72", -] - -[[package]] -name = "sp-debug-derive" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "syn 2.0.72", -] - -[[package]] -name = "sp-externalities" -version = "0.13.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "environmental 1.1.4", - "parity-scale-codec", - "sp-std", - "sp-storage", -] - -[[package]] -name = "sp-inherents" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "impl-trait-for-tuples", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-std", -] - -[[package]] -name = "sp-io" -version = "7.0.0" -dependencies = [ - "itp-sgx-externalities", - "libsecp256k1", - "log 0.4.21", - "parity-scale-codec", - "sgx_tstd", - "sp-core", -] - -[[package]] -name = "sp-metadata-ir" -version = "0.1.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "frame-metadata", - "parity-scale-codec", - "scale-info", - "sp-std", -] - -[[package]] -name = "sp-runtime" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "either", - "hash256-std-hasher", - "impl-trait-for-tuples", - "log 0.4.21", - "parity-scale-codec", - "paste", - "scale-info", - "sp-application-crypto", - "sp-arithmetic", - "sp-core", - "sp-io", - "sp-std", - "sp-weights", -] - -[[package]] -name = "sp-runtime-interface" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "bytes 1.5.0", - "impl-trait-for-tuples", - "parity-scale-codec", - "primitive-types", - "sp-externalities", - "sp-runtime-interface-proc-macro", - "sp-std", - "sp-storage", - "sp-tracing", - "sp-wasm-interface", - "static_assertions", -] - -[[package]] -name = "sp-runtime-interface-proc-macro" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "Inflector", - "proc-macro-crate", - "proc-macro2", - "quote 1.0.36", - "syn 2.0.72", -] - -[[package]] -name = "sp-staking" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "sp-std" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" - -[[package]] -name = "sp-storage" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "ref-cast", - "sp-debug-derive", - "sp-std", -] - -[[package]] -name = "sp-timestamp" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "sp-inherents", - "sp-runtime", - "sp-std", -] - -[[package]] -name = "sp-tracing" -version = "6.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "sp-std", - "tracing", - "tracing-core", -] - -[[package]] -name = "sp-trie" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "hash-db 0.16.0", - "memory-db", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-std", - "trie-db", - "trie-root", -] - -[[package]] -name = "sp-version" -version = "5.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "scale-info", - "sp-core-hashing-proc-macro", - "sp-runtime", - "sp-std", - "sp-version-proc-macro", -] - -[[package]] -name = "sp-version-proc-macro" -version = "4.0.0-dev" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "proc-macro2", - "quote 1.0.36", - "syn 2.0.72", -] - -[[package]] -name = "sp-wasm-interface" -version = "7.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "impl-trait-for-tuples", - "parity-scale-codec", - "sp-std", -] - -[[package]] -name = "sp-weights" -version = "4.0.0" -source = "git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.42#ff24c60ac7d9f87727ecdd0ded9a80c56e4f4b65" -dependencies = [ - "parity-scale-codec", - "scale-info", - "smallvec 1.11.1", - "sp-arithmetic", - "sp-core", - "sp-debug-derive", - "sp-std", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spki" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" -dependencies = [ - "der 0.6.1", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der 0.7.8", -] - -[[package]] -name = "ss58-registry" -version = "1.43.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6915280e2d0db8911e5032a5c275571af6bdded2916abd691a659be25d3439" -dependencies = [ - "Inflector", - "proc-macro2", - "quote 1.0.36", - "serde 1.0.204", - "serde_json 1.0.120", - "unicode-xid 0.2.4", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "strum" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f" - -[[package]] -name = "strum_macros" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18" -dependencies = [ - "heck", - "proc-macro2", - "quote 1.0.36", - "rustversion", - "syn 2.0.72", -] - -[[package]] -name = "substrate-api-client" -version = "0.14.0" -source = "git+https://github.com/Kailai-Wang/substrate-api-client?branch=polkadot-v0.9.42-litentry#f867fea44a3de5352d419a605afdd7bf22859e78" -dependencies = [ - "ac-compose-macros", - "ac-node-api", - "ac-primitives", - "async-trait", - "derive_more", - "frame-metadata", - "hex", - "log 0.4.21", - "maybe-async", - "parity-scale-codec", - "serde 1.0.204", - "serde_json 1.0.120", - "sp-core", - "sp-runtime", - "sp-runtime-interface", -] - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "subtle" -version = "2.5.0" -source = "git+https://github.com/kziemianek/subtle-sgx.git?branch=2.5.0-update#57c424bdb6b98cbf9cfe19879748f20c3525c80e" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "syn" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" -dependencies = [ - "quote 0.3.15", - "synom", - "unicode-xid 0.0.4", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "unicode-ident", -] - -[[package]] -name = "synom" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" -dependencies = [ - "unicode-xid 0.0.4", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "termcolor" -version = "1.0.5" -source = "git+https://github.com/mesalock-linux/termcolor-sgx#fee5ac79b4a90197d646f3df5e1b45ac56be718b" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "thiserror" -version = "1.0.9" -source = "git+https://github.com/mesalock-linux/thiserror-sgx?tag=sgx_1.1.3#c2f806b88616e06aab0af770366a76885d974fdc" -dependencies = [ - "sgx_tstd", - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.9" -source = "git+https://github.com/mesalock-linux/thiserror-sgx?tag=sgx_1.1.3#c2f806b88616e06aab0af770366a76885d974fdc" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "syn 1.0.109", -] - -[[package]] -name = "thread_local" -version = "1.0.0" -source = "git+https://github.com/mesalock-linux/thread_local-rs-sgx#a8e6e6ce280c53358f7b9e6febe534cba9950547" -dependencies = [ - "lazy_static", - "sgx_tstd", -] - -[[package]] -name = "threadpool" -version = "1.8.0" -source = "git+https://github.com/mesalock-linux/rust-threadpool-sgx?tag=sgx_1.1.3#098d98a85b7e2b02e2bb451a3dec0b027017ff4c" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "toml" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c226a7bba6d859b63c92c4b4fe69c5b6b72d0cb897dbc8e6012298e6154cb56e" -dependencies = [ - "serde 1.0.204", - "serde_spanned", - "toml_datetime", - "toml_edit 0.20.0", -] - -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" -dependencies = [ - "serde 1.0.204", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.0.0", - "toml_datetime", - "winnow", -] - -[[package]] -name = "toml_edit" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ff63e60a958cefbb518ae1fd6566af80d9d4be430a33f3723dfc47d1d411d95" -dependencies = [ - "indexmap 2.0.0", - "serde 1.0.204", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if 1.0.0", - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" - -[[package]] -name = "trie-db" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "767abe6ffed88a1889671a102c2861ae742726f52e0a5a425b92c9fbfa7e9c85" -dependencies = [ - "hash-db 0.16.0", - "hashbrown 0.13.2", - "log 0.4.21", - "smallvec 1.11.1", -] - -[[package]] -name = "trie-root" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" -dependencies = [ - "hash-db 0.16.0", -] - -[[package]] -name = "triehash" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1631b201eb031b563d2e85ca18ec8092508e262a3196ce9bd10a67ec87b9f5c" -dependencies = [ - "hash-db 0.15.2", - "rlp", -] - -[[package]] -name = "tt-call" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f195fd851901624eee5a58c4bb2b4f06399148fcd0ed336e6f1cb60a9881df" - -[[package]] -name = "tungstenite" -version = "0.14.0" -source = "git+https://github.com/integritee-network/tungstenite-rs-sgx?branch=sgx-experimental#c87a2c08ea00897bb8b127ca0a5c30c3671492b0" -dependencies = [ - "base64 0.13.0 (git+https://github.com/mesalock-linux/rust-base64-sgx?tag=sgx_1.1.3)", - "byteorder 1.3.4", - "bytes 1.0.1", - "http", - "httparse", - "log 0.4.17", - "rand 0.7.3", - "rustls 0.19.0 (git+https://github.com/mesalock-linux/rustls?tag=sgx_1.1.3)", - "sgx_tstd", - "sha1", - "thiserror", - "url 2.1.1", - "utf-8", - "webpki", - "webpki-roots 0.21.0 (git+https://github.com/mesalock-linux/webpki-roots?tag=sgx_1.1.3)", -] - -[[package]] -name = "twox-hash" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" -dependencies = [ - "cfg-if 1.0.0", - "digest 0.10.7", - "static_assertions", -] - -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder 1.4.3", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.4" -source = "git+https://github.com/mesalock-linux/unicode-bidi-sgx#eb10728a635a046e75747849fbc680cbbb7832c7" -dependencies = [ - "matches", - "sgx_tstd", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-normalization" -version = "0.1.12" -source = "git+https://github.com/mesalock-linux/unicode-normalization-sgx#c1b030611969f87d75782c1df77975167cbbd509" -dependencies = [ - "smallvec 1.6.1", -] - -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-xid" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "unsigned-varint" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fdeedbf205afadfe39ae559b75c3240f24e257d0ca27e85f85cb82aa19ac35" - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.1.1" -source = "git+https://github.com/mesalock-linux/rust-url-sgx?tag=sgx_1.1.3#23832f3191456c2d4a0faab10952e1747be58ca8" -dependencies = [ - "idna 0.2.0", - "matches", - "percent-encoding 2.1.0", - "sgx_tstd", -] - -[[package]] -name = "url" -version = "2.5.0" -source = "git+https://github.com/domenukk/rust-url?rev=316c868#316c8683206f3cb741163779bb30963fa05b3612" -dependencies = [ - "form_urlencoded", - "idna 0.5.0", - "no-std-net", - "percent-encoding 2.3.1", -] - -[[package]] -name = "utf-8" -version = "0.7.4" -source = "git+https://github.com/integritee-network/rust-utf8-sgx?branch=sgx-experimental#b026700da83a2f00f0e9f36f813ef28e447a719e" -dependencies = [ - "sgx_tstd", -] - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "webpki" -version = "0.21.4" -source = "git+https://github.com/mesalock-linux/webpki?branch=mesalock_sgx#8dbe6fbeefadf05582ae47c7fa818b04db49c61e" -dependencies = [ - "ring 0.16.19", - "sgx_tstd", - "untrusted 0.7.1", -] - -[[package]] -name = "webpki-roots" -version = "0.21.0" -source = "git+https://github.com/mesalock-linux/webpki-roots?tag=sgx_1.1.3#6ff3be547ac13ccd46ae55605ad6506ce30688ef" -dependencies = [ - "sgx_tstd", - "webpki", -] - -[[package]] -name = "webpki-roots" -version = "0.21.0" -source = "git+https://github.com/mesalock-linux/webpki-roots?branch=mesalock_sgx#6ff3be547ac13ccd46ae55605ad6506ce30688ef" -dependencies = [ - "sgx_tstd", - "webpki", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "winnow" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" -dependencies = [ - "memchr 2.6.3", -] - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "x509-cert" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d224a125dec5adda27d0346b9cae9794830279c4f9c27e4ab0b6c408d54012" -dependencies = [ - "const-oid", - "der 0.6.1", - "flagset", - "spki 0.6.0", -] - -[[package]] -name = "xous" -version = "0.9.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb93122c9dab77f8bde501ff0677d3df7777982f9faa23603423aeb5eae4e55d" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "xous-api-log" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "941f7fd7c114e1e0c401bbbe8222e8adbb4bc8ebeb782f7812d8f1678a578934" -dependencies = [ - "log 0.4.21", - "num-derive", - "num-traits 0.2.16", - "xous", - "xous-ipc", -] - -[[package]] -name = "xous-api-names" -version = "0.9.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "471502d9379d198738ec55dec7cd688ce21e1cfe7d93af6dad25e38c1c404d1c" -dependencies = [ - "log 0.4.21", - "num-derive", - "num-traits 0.2.16", - "rkyv", - "xous", - "xous-api-log", - "xous-ipc", -] - -[[package]] -name = "xous-ipc" -version = "0.9.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f5081581314f115e2005403ee2cc0957d86cbfc49edf6e8b05d2fcf6c226c5d" -dependencies = [ - "bitflags", - "rkyv", - "xous", -] - -[[package]] -name = "yasna" -version = "0.3.1" -source = "git+https://github.com/mesalock-linux/yasna.rs-sgx?rev=sgx_1.1.3#a1f50714cd3eb29608ecf7888cacedc173edfdb2" -dependencies = [ - "bit-vec", - "chrono 0.4.11", - "num-bigint", - "sgx_tstd", -] - -[[package]] -name = "zeroize" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote 1.0.36", - "syn 2.0.72", -] - -[[patch.unused]] -name = "sgx_tcrypto_helper" -version = "1.1.6" -source = "git+https://github.com/apache/incubator-teaclave-sgx-sdk?branch=master#3c903bdac4e503dd27b9b1f761c4abfc55f2464c" diff --git a/tee-worker/bitacross/enclave-runtime/Cargo.toml b/tee-worker/bitacross/enclave-runtime/Cargo.toml deleted file mode 100644 index b74c56ca31..0000000000 --- a/tee-worker/bitacross/enclave-runtime/Cargo.toml +++ /dev/null @@ -1,186 +0,0 @@ -[package] -name = "enclave-runtime" -version = "0.0.1" -authors = ['Trust Computing GmbH ', 'Integritee AG '] -edition = "2021" - -[workspace] -resolver = "2" -members = [] - -[lib] -name = "enclave_runtime" -crate-type = ["staticlib"] - -[features] -default = [] -development = [ - "ita-stf/development", - "itp-settings/development", - "itp-attestation-handler/development", - "litentry-primitives/development", - "litentry-macros/development", - "bc-task-processor/development", -] -offchain-worker = [ - "itp-settings/offchain-worker", - "itp-top-pool-author/offchain-worker", -] -test = [ - "ita-stf/test", - "itc-parentchain/test", - "itp-attestation-handler/test", - "itp-extrinsics-factory/mocks", - "itp-sgx-crypto/test", - "itp-sgx-temp-dir", - "itp-stf-executor/test", - "itp-stf-executor/mocks", - "itp-stf-state-handler/test", - "itp-stf-state-observer/mocks", - "itp-storage/test", - "itp-test/sgx", - "itp-top-pool-author/test", - "itp-top-pool-author/mocks", - "bc-musig2-ceremony/sgx-test", -] -dcap = [] - -[target.'cfg(not(target_env = "sgx"))'.dependencies] -sgx_crypto_helper = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", default-features = false, features = ["mesalock_sgx"] } -sgx_rand = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } -sgx_serialize = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } -sgx_serialize_derive = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } -sgx_tcrypto = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } -sgx_trts = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } -sgx_tse = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } -sgx_tseal = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } -sgx_tstd = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git", features = ["untrusted_fs", "net", "backtrace"] } -sgx_tunittest = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } -sgx_types = { branch = "master", git = "https://github.com/apache/teaclave-sgx-sdk.git" } - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } -derive_more = { version = "0.99.5" } -futures_sgx = { package = "futures", git = "https://github.com/mesalock-linux/futures-rs-sgx" } -hex = { version = "0.4.3", default-features = false, features = ["alloc"] } -ipfs-unixfs = { default-features = false, git = "https://github.com/whalelephant/rust-ipfs", branch = "w-nstd" } -lazy_static = { version = "1.1.0", features = ["spin_no_std"] } -primitive-types = { version = "0.12.1", default-features = false, features = ["codec", "serde_no_std"] } -url = { git = "https://github.com/domenukk/rust-url", rev = "316c868", default-features = false, features = ["alloc", "no_std_net"] } - -# scs / integritee -jsonrpc-core = { default-features = false, git = "https://github.com/scs/jsonrpc", branch = "no_std_v18" } - -# mesalock -env_logger = { git = "https://github.com/integritee-network/env_logger-sgx" } -log = { git = "https://github.com/integritee-network/log-sgx" } -# Todo #1313: use the `once_cell` included in rusts core library once we use rust v1.70.0 -once_cell = { git = "https://github.com/mesalock-linux/once_cell-sgx" } -rustls = { rev = "sgx_1.1.3", features = ["dangerous_configuration"], git = "https://github.com/mesalock-linux/rustls" } -serde_json = { tag = "sgx_1.1.3", git = "https://github.com/mesalock-linux/serde-json-sgx" } -webpki = { git = "https://github.com/mesalock-linux/webpki", branch = "mesalock_sgx" } - -# for attestation -base58 = { rev = "sgx_1.1.3", package = "rust-base58", default-features = false, features = ["mesalock_sgx"], git = "https://github.com/mesalock-linux/rust-base58-sgx" } - -cid = { default-features = false, git = "https://github.com/whalelephant/rust-cid", branch = "nstd" } -multibase = { default-features = false, git = "https://github.com/whalelephant/rust-multibase", branch = "nstd" } - -# local deps -ita-parentchain-interface = { package = "bc-ita-parentchain-interface", path = "../app-libs/parentchain-interface", default-features = false, features = ["sgx"] } -ita-sgx-runtime = { package = "bc-ita-sgx-runtime", path = "../app-libs/sgx-runtime", default-features = false } -ita-stf = { package = "bc-ita-stf", path = "../app-libs/stf", default-features = false, features = ["sgx"] } -itc-direct-rpc-server = { package = "bc-itc-direct-rpc-server", path = "../core/direct-rpc-server", default-features = false, features = ["sgx"] } -itc-offchain-worker-executor = { package = "bc-itc-offchain-worker-executor", path = "../core/offchain-worker-executor", default-features = false, features = ["sgx"] } -itc-parentchain = { package = "bc-itc-parentchain", path = "../core/parentchain/parentchain-crate", default-features = false, features = ["sgx"] } -itc-parentchain-light-client = { path = "../../common/core/parentchain/light-client", default-features = false } -itc-parentchain-test = { path = "../../common/core/parentchain/test", default-features = false } -itc-tls-websocket-server = { path = "../../common/core/tls-websocket-server", default-features = false, features = ["sgx"] } -itp-attestation-handler = { path = "../../common/core-primitives/attestation-handler", default-features = false, features = ["sgx"] } -itp-component-container = { path = "../../common/core-primitives/component-container", default-features = false, features = ["sgx"] } -itp-extrinsics-factory = { path = "../../common/core-primitives/extrinsics-factory", default-features = false, features = ["sgx"] } -itp-import-queue = { path = "../../common/core-primitives/import-queue", default-features = false, features = ["sgx"] } -itp-node-api = { path = "../../common/core-primitives/node-api", default-features = false, features = ["sgx"] } -itp-node-api-metadata = { path = "../../common/core-primitives/node-api/metadata", default-features = false } -itp-node-api-metadata-provider = { path = "../../common/core-primitives/node-api/metadata-provider", default-features = false } -itp-nonce-cache = { path = "../../common/core-primitives/nonce-cache", default-features = false, features = ["sgx"] } -itp-ocall-api = { path = "../../common/core-primitives/ocall-api", default-features = false } -itp-primitives-cache = { path = "../../common/core-primitives/primitives-cache", default-features = false, features = ["sgx"] } -itp-rpc = { path = "../../common/core-primitives/rpc", default-features = false, features = ["sgx"] } -itp-settings = { path = "../../common/core-primitives/settings" } -itp-sgx-crypto = { path = "../../common/core-primitives/sgx/crypto", default-features = false, features = ["sgx"] } -itp-sgx-externalities = { path = "../../common/core-primitives/substrate-sgx/externalities", default-features = false, features = ["sgx"] } -itp-stf-executor = { package = "bc-itp-stf-executor", path = "../core-primitives/stf-executor", default-features = false, features = ["sgx"] } -itp-stf-interface = { path = "../../common/core-primitives/stf-interface", default-features = false } -itp-stf-primitives = { path = "../../common/core-primitives/stf-primitives", default-features = false } -itp-stf-state-handler = { path = "../../common/core-primitives/stf-state-handler", default-features = false, features = ["sgx"] } -itp-stf-state-observer = { path = "../../common/core-primitives/stf-state-observer", default-features = false, features = ["sgx"] } -itp-storage = { path = "../../common/core-primitives/storage", default-features = false, features = ["sgx"] } -itp-test = { path = "../../common/core-primitives/test", default-features = false, optional = true } -itp-top-pool = { package = "bc-itp-top-pool", path = "../core-primitives/top-pool", default-features = false, features = ["sgx"] } -itp-top-pool-author = { package = "bc-itp-top-pool-author", path = "../core-primitives/top-pool-author", default-features = false, features = ["sgx"] } -itp-types = { path = "../../common/core-primitives/types", default-features = false } -itp-utils = { path = "../../common/core-primitives/utils", default-features = false } - -# litentry -bc-enclave-registry = { path = "../bitacross/core/bc-enclave-registry", default-features = false, features = ["sgx"] } -bc-musig2-ceremony = { path = "../bitacross/core/bc-musig2-ceremony", default-features = false, features = ["sgx"] } -bc-relayer-registry = { path = "../bitacross/core/bc-relayer-registry", default-features = false, features = ["sgx"] } -bc-signer-registry = { path = "../bitacross/core/bc-signer-registry", default-features = false, features = ["sgx"] } -bc-task-sender = { path = "../bitacross/core/bc-task-sender", default-features = false, features = ["sgx"] } -lc-direct-call = { path = "../litentry/core/direct-call", default-features = false } -litentry-hex-utils = { path = "../../../common/utils/hex", default-features = false } -litentry-macros = { path = "../../../common/primitives/core/macros", default-features = false } -litentry-primitives = { path = "../../common/litentry/primitives", default-features = false, features = ["sgx"] } -litentry-proc-macros = { path = "../../../common/primitives/core/proc-macros", default-features = false } - -# bitacross -bc-task-processor = { path = "../bitacross/core/bc-task-processor", default-features = false, features = ["sgx"] } - -# substrate deps -frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false, features = ["full_crypto"] } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42", default-features = false } - -# test-deps -itp-sgx-temp-dir = { path = "../../common/core-primitives/sgx/temp-dir", default-features = false, optional = true } - -[patch.crates-io] -env_logger = { git = "https://github.com/integritee-network/env_logger-sgx" } -log = { git = "https://github.com/integritee-network/log-sgx" } -ring = { git = "https://github.com/betrusted-io/ring-xous", branch = "0.16.20-cleanup" } - -[patch."https://github.com/mesalock-linux/log-sgx"] -log = { git = "https://github.com/integritee-network/log-sgx" } - -[patch."https://github.com/paritytech/polkadot-sdk"] -frame-support = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-api = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-consensus-aura = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-io = { path = "../../common/core-primitives/substrate-sgx/sp-io" } -sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } -sp-std = { git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.42" } - -[patch."https://github.com/paritytech/frontier"] -pallet-evm = { git = "https://github.com/polkadot-evm/frontier", branch = "bar/polkadot-v0.9.42" } - -[patch."https://github.com/paritytech/substrate"] -sp-io = { path = "../../common/core-primitives/substrate-sgx/sp-io" } - -[patch."https://github.com/apache/teaclave-sgx-sdk.git"] -sgx_alloc = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } -sgx_crypto_helper = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } -sgx_libc = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } -sgx_rand = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } -sgx_serialize = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } -sgx_serialize_derive = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } -sgx_serialize_derive_internals = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } -sgx_tcrypto = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } -sgx_tcrypto_helper = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } -sgx_trts = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } -sgx_tse = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } -sgx_tseal = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } -sgx_tstd = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } -sgx_tunittest = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } -sgx_types = { version = "1.1.6", git = "https://github.com/apache/incubator-teaclave-sgx-sdk", branch = "master" } diff --git a/tee-worker/bitacross/enclave-runtime/Enclave.config.production.xml b/tee-worker/bitacross/enclave-runtime/Enclave.config.production.xml deleted file mode 100644 index 669cbe2087..0000000000 --- a/tee-worker/bitacross/enclave-runtime/Enclave.config.production.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - 0 - 0 - 0x40000 - 0x20000000 - 64 - 0 - 1 - 0 - 0xFFFFFFFF - diff --git a/tee-worker/bitacross/enclave-runtime/Enclave.config.xml b/tee-worker/bitacross/enclave-runtime/Enclave.config.xml deleted file mode 100644 index 747875739c..0000000000 --- a/tee-worker/bitacross/enclave-runtime/Enclave.config.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - 0 - 0 - 0x40000 - 0x20000000 - 64 - 0 - 0 - 0 - 0xFFFFFFFF - diff --git a/tee-worker/bitacross/enclave-runtime/Enclave.edl b/tee-worker/bitacross/enclave-runtime/Enclave.edl deleted file mode 100644 index 11a93e5f49..0000000000 --- a/tee-worker/bitacross/enclave-runtime/Enclave.edl +++ /dev/null @@ -1,253 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -enclave { - from "sgx_backtrace.edl" import *; - from "sgx_tstd.edl" import *; - from "sgx_stdio.edl" import *; - from "sgx_backtrace.edl" import *; - from "sgx_tstdc.edl" import *; - from "sgx_tprotected_fs.edl" import *; - from "sgx_fs.edl" import *; - from "sgx_net.edl" import *; - from "sgx_time.edl" import *; - from "sgx_env.edl" import *; - from "sgx_thread.edl" import *; - from "sgx_pipe.edl" import *; - from "sgx_file.edl" import *; - from "sgx_dcap_tvl.edl" import *; - - include "sgx_quote.h" - include "sgx_report.h" - include "sgx_ql_quote.h" - include "sgx_qve_header.h" - - trusted { - /* define ECALLs here. */ - public sgx_status_t init( - [in, size=mu_ra_addr_size] uint8_t* mu_ra_addr, uint32_t mu_ra_addr_size, - [in, size=untrusted_worker_addr_size] uint8_t* untrusted_worker_addr, uint32_t untrusted_worker_addr_size, - [in, size=encoded_base_dir_size] uint8_t* encoded_base_dir_str, uint32_t encoded_base_dir_size, - uint8_t ceremony_commands_thread_count, uint8_t ceremony_events_thread_count - ); - - public sgx_status_t publish_wallets(); - - public sgx_status_t finish_enclave_init(); - - public sgx_status_t init_wallets( - [in, size=encoded_base_dir_size] uint8_t* encoded_base_dir_str, uint32_t encoded_base_dir_size - ); - - public sgx_status_t init_direct_invocation_server( - [in, size=server_addr_size] uint8_t* server_addr, uint32_t server_addr_size - ); - - public sgx_status_t init_parentchain_components( - [in, size=params_size] uint8_t* params, size_t params_size, - [out, size=latest_header_size] uint8_t* latest_header, size_t latest_header_size - ); - - public sgx_status_t init_shard( - [in, size=shard_size] uint8_t* shard, uint32_t shard_size - ); - - public sgx_status_t init_shard_creation_parentchain_header( - [in, size=shard_size] uint8_t* shard, uint32_t shard_size, - [in, size=parentchain_id_size] uint8_t* parentchain_id, uint32_t parentchain_id_size, - [in, size=header_size] uint8_t* header, uint32_t header_size - ); - - public sgx_status_t get_shard_creation_info( - [in, size=shard_size] uint8_t* shard, uint32_t shard_size, - [out, size=creation_size] uint8_t* creation, uint32_t creation_size); - - public sgx_status_t sync_parentchain( - [in, size=blocks_size] uint8_t* blocks, size_t blocks_size, - [in, size=events_size] uint8_t* events, size_t events_size, - [in, size=events_proofs_size] uint8_t* events_proofs, size_t events_proofs_size, - [in, size=parentchain_id_size] uint8_t* parentchain_id, uint32_t parentchain_id_size, - int immediate_import - ); - - public sgx_status_t set_nonce( - [in] uint32_t* nonce, - [in, size=parentchain_id_size] uint8_t* parentchain_id, uint32_t parentchain_id_size - ); - - public sgx_status_t set_node_metadata( - [in, size=node_metadata_size] uint8_t* node_metadata, uint32_t node_metadata_size, - [in, size=parentchain_id_size] uint8_t* parentchain_id, uint32_t parentchain_id_size - ); - - public sgx_status_t get_rsa_encryption_pubkey( - [out, size=pubkey_size] uint8_t* pubkey, uint32_t pubkey_size); - - public sgx_status_t get_ecc_signing_pubkey( - [out, size=pubkey_size] uint8_t* pubkey, uint32_t pubkey_size); - - public sgx_status_t get_bitcoin_wallet_pair( - [out, size=pair_size] uint8_t* pair, uint32_t pair_size); - - public sgx_status_t get_ethereum_wallet_pair( - [out, size=pair_size] uint8_t* pair, uint32_t pair_size); - - public sgx_status_t get_ton_wallet_pair( - [out, size=pair_size] uint8_t* pair, uint32_t pair_size); - - public sgx_status_t get_mrenclave( - [out, size=mrenclave_size] uint8_t* mrenclave, uint32_t mrenclave_size); - - public sgx_status_t generate_ias_ra_extrinsic( - [in, size=w_url_size] uint8_t* w_url, uint32_t w_url_size, - [out, size=unchecked_extrinsic_max_size] uint8_t* unchecked_extrinsic, uint32_t unchecked_extrinsic_max_size, - [out] uint32_t* unchecked_extrinsic_size, - int skip_ra - ); - public sgx_status_t generate_dcap_ra_quote( - int skip_ra, - [in] const sgx_target_info_t* quoting_enclave_target_info, - uint32_t quote_size, - [out, size=dcap_quote_size] uint8_t* dcap_quote_p, uint32_t dcap_quote_size - ); - - public sgx_status_t generate_dcap_ra_extrinsic_from_quote( - [in, size=w_url_size] uint8_t* w_url, uint32_t w_url_size, - [in, size=quote_size] uint8_t* quote, uint32_t quote_size, - [out, size=unchecked_extrinsic_max_size] uint8_t* unchecked_extrinsic, uint32_t unchecked_extrinsic_max_size, - [out] uint32_t* unchecked_extrinsic_size - ); - - public sgx_status_t generate_dcap_ra_extrinsic( - [in, size=w_url_size] uint8_t* w_url, uint32_t w_url_size, - [out, size=unchecked_extrinsic_max_size] uint8_t* unchecked_extrinsic, uint32_t unchecked_extrinsic_max_size, - [out] uint32_t* unchecked_extrinsic_size, - int skip_ra, - [in] const sgx_target_info_t* quoting_enclave_target_info, - [in] uint32_t* quote_size - ); - - public sgx_status_t generate_register_quoting_enclave_extrinsic( - [in] const sgx_ql_qve_collateral_t *p_quote_collateral, - [out, size=unchecked_extrinsic_max_size] uint8_t* unchecked_extrinsic, uint32_t unchecked_extrinsic_max_size, - [out] uint32_t* unchecked_extrinsic_size - ); - - public sgx_status_t generate_register_tcb_info_extrinsic( - [in] const sgx_ql_qve_collateral_t *p_quote_collateral, - [out, size=unchecked_extrinsic_max_size] uint8_t* unchecked_extrinsic, uint32_t unchecked_extrinsic_max_size, - [out] uint32_t* unchecked_extrinsic_size - ); - - public sgx_status_t dump_ias_ra_cert_to_disk(); - - public sgx_status_t dump_dcap_ra_cert_to_disk([in] const sgx_target_info_t* quoting_enclave_target_info, uint32_t quote_size); - - public sgx_status_t dump_dcap_collateral_to_disk([in] const sgx_ql_qve_collateral_t *p_quote_collateral); - - public sgx_status_t run_state_provisioning_server( - int fd, - sgx_quote_sign_type_t quote_type, - [in] sgx_target_info_t* quoting_enclave_target_info, - [in] uint32_t* quote_size, - int skip_ra - ); - public sgx_status_t request_state_provisioning( - int fd, - sgx_quote_sign_type_t quote_type, - [in] sgx_target_info_t* quoting_enclave_target_info, - [in] uint32_t* quote_size, - [in, size=shard_size] uint8_t* shard, uint32_t shard_size, - int skip_ra - ); - - public size_t test_main_entrance(); - - public sgx_status_t migrate_shard( - [in, size=shard_size] uint8_t* new_shard, - uint32_t shard_size - ); - - public sgx_status_t ignore_parentchain_block_import_validation_until( - [in] uint32_t* until - ); - }; - - untrusted { - sgx_status_t ocall_sgx_init_quote( - [out] sgx_target_info_t *ret_ti, - [out] sgx_epid_group_id_t *ret_gid - ); - - sgx_status_t ocall_get_ias_socket([out] int *ret_fd); - - sgx_status_t ocall_get_quote( - [in, size = sigrl_len] uint8_t * p_sigrl, uint32_t sigrl_len, - [in] sgx_report_t *report, sgx_quote_sign_type_t quote_type, - [in] sgx_spid_t *p_spid, [in] sgx_quote_nonce_t *p_nonce, - [out] sgx_report_t *p_qe_report, - [out, size = maxlen] sgx_quote_t *p_quote, uint32_t maxlen, - [out] uint32_t* p_quote_len - ); - - sgx_status_t ocall_get_dcap_quote( - [in] sgx_report_t *report, - [out, size = quote_size] sgx_quote_t *p_quote, uint32_t quote_size - ); - - sgx_status_t ocall_get_qve_report_on_quote( - [in, size = quote_size] const uint8_t * quote, uint32_t quote_size, - time_t current_time, - [in] const sgx_ql_qve_collateral_t *p_quote_collateral, - [out] uint32_t *collateral_expiration_status, - [out] sgx_ql_qv_result_t *quote_verification_result, - [in, out] sgx_ql_qe_report_info_t *qve_report_info, - [out, size=supplemental_data_size] uint8_t *p_supplemental_data, - uint32_t supplemental_data_size - ); - - sgx_status_t ocall_get_update_info( - [in] sgx_platform_info_t * platformBlob, int32_t enclaveTrusted, - [out] sgx_update_info_bit_t * update_info - ); - - sgx_status_t ocall_read_ipfs( - [in, size = cid_size] uint8_t * cid, uint32_t cid_size - ); - - sgx_status_t ocall_write_ipfs( - [in, size = state_size] uint8_t * enc_state, uint32_t state_size, - [out, size = cid_size] uint8_t * cid, uint32_t cid_size - ); - - sgx_status_t ocall_worker_request( - [in, size = req_size] uint8_t * request, uint32_t req_size, - [in, size=parentchain_id_size] uint8_t* parentchain_id, uint32_t parentchain_id_size, - [out, size = resp_size] uint8_t * response, uint32_t resp_size - ); - - sgx_status_t ocall_update_metric( - [in, size = metric_size] uint8_t * metric, uint32_t metric_size - ); - - sgx_status_t ocall_send_to_parentchain( - [in, size = extrinsics_size] uint8_t * extrinsics, uint32_t extrinsics_size, - [in, size=parentchain_id_size] uint8_t* parentchain_id, uint32_t parentchain_id_size, - [in, size=watch_until_size] uint8_t* watch_until, uint32_t watch_until_size, - [out, size = resp_size] uint8_t * response, uint32_t resp_size - ); - }; -}; diff --git a/tee-worker/bitacross/enclave-runtime/Enclave.lds b/tee-worker/bitacross/enclave-runtime/Enclave.lds deleted file mode 100644 index e3d9d0ee0d..0000000000 --- a/tee-worker/bitacross/enclave-runtime/Enclave.lds +++ /dev/null @@ -1,9 +0,0 @@ -enclave.so -{ - global: - g_global_data_sim; - g_global_data; - enclave_entry; - local: - *; -}; diff --git a/tee-worker/bitacross/enclave-runtime/Enclave_private.pem b/tee-worker/bitacross/enclave-runtime/Enclave_private.pem deleted file mode 100644 index 529d07be35..0000000000 --- a/tee-worker/bitacross/enclave-runtime/Enclave_private.pem +++ /dev/null @@ -1,39 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- diff --git a/tee-worker/bitacross/enclave-runtime/Makefile b/tee-worker/bitacross/enclave-runtime/Makefile deleted file mode 100644 index 41286a26c3..0000000000 --- a/tee-worker/bitacross/enclave-runtime/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (C) 2017-2018 Baidu, Inc. All Rights Reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Baidu, Inc., nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -######## Worker Feature Settings ######## -# Set offchain-worker as default feature mode -WORKER_MODE ?= offchain-worker -WORKER_DEV ?= 0 -RA_METHOD ?= dcap - -Rust_Enclave_Name := libenclave.a -Rust_Enclave_Files := $(wildcard src/*.rs) $(wildcard ../stf/src/*.rs) -RUSTFLAGS :="-C target-feature=+avx2" - -ifeq ($(SGX_DEBUG), 1) - OUTPUT_PATH := debug - CARGO_TARGET := -else - OUTPUT_PATH := release - CARGO_TARGET := --release -endif - -ifeq ($(SGX_PRODUCTION), 1) - ENCLAVE_FEATURES = --features=$(WORKER_MODE),$(ADDITIONAL_FEATURES) -else - ENCLAVE_FEATURES = --features=test,development,$(WORKER_MODE),$(ADDITIONAL_FEATURES) -endif - -ifeq ($(WORKER_DEV), 1) - ADDITIONAL_FEATURES := $(ADDITIONAL_FEATURES),development -endif - -ifeq ($(RA_METHOD), dcap) - ADDITIONAL_FEATURES := $(ADDITIONAL_FEATURES),dcap -endif - -.PHONY: all - -all: $(Rust_Enclave_Name) - -$(Rust_Enclave_Name): $(Rust_Enclave_Files) - RUSTFLAGS=$(RUSTFLAGS) cargo build $(CARGO_TARGET) $(ENCLAVE_FEATURES) - cp ./target/$(OUTPUT_PATH)/libenclave_runtime.a ../lib/libenclave.a - diff --git a/tee-worker/bitacross/enclave-runtime/README.md b/tee-worker/bitacross/enclave-runtime/README.md deleted file mode 100644 index a4b88a52d1..0000000000 --- a/tee-worker/bitacross/enclave-runtime/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# sidechain dependency graph -cargo depgraph --features dcap,sidechain --include enclave-runtime,itp-types,ita-stf | dot -Tsvg > dependency-graph.svg diff --git a/tee-worker/bitacross/enclave-runtime/rust-toolchain.toml b/tee-worker/bitacross/enclave-runtime/rust-toolchain.toml deleted file mode 100644 index 23ed88e6c8..0000000000 --- a/tee-worker/bitacross/enclave-runtime/rust-toolchain.toml +++ /dev/null @@ -1,4 +0,0 @@ -[toolchain] -channel = "nightly-2022-10-22" -targets = ["wasm32-unknown-unknown"] -profile = "default" # include rustfmt, clippy diff --git a/tee-worker/bitacross/enclave-runtime/rustfmt.toml b/tee-worker/bitacross/enclave-runtime/rustfmt.toml deleted file mode 100644 index 104b9aa998..0000000000 --- a/tee-worker/bitacross/enclave-runtime/rustfmt.toml +++ /dev/null @@ -1,18 +0,0 @@ -# Basic -hard_tabs = true -max_width = 100 -use_small_heuristics = "Max" -# Imports -imports_granularity = "Crate" -reorder_imports = true -# Consistency -newline_style = "Unix" -# Misc -chain_width = 80 -spaces_around_ranges = false -match_arm_leading_pipes = "Preserve" -match_arm_blocks = false -match_block_trailing_comma = true -trailing_comma = "Vertical" -trailing_semicolon = false -use_field_init_shorthand = true \ No newline at end of file diff --git a/tee-worker/bitacross/enclave-runtime/src/attestation.rs b/tee-worker/bitacross/enclave-runtime/src/attestation.rs deleted file mode 100644 index 732f6d2851..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/attestation.rs +++ /dev/null @@ -1,583 +0,0 @@ -// Copyright 2022 Integritee AG and Supercomputing Systems AG -// Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in -// the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Baidu, Inc., nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::{ - initialization::global_components::{ - GLOBAL_ATTESTATION_HANDLER_COMPONENT, GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, - }, - utils::{ - get_extrinsic_factory_from_integritee_solo_or_parachain, - get_node_metadata_repository_from_integritee_solo_or_parachain, - }, - Error as EnclaveError, Result as EnclaveResult, -}; -use codec::{Decode, Encode}; -use itp_attestation_handler::{AttestationHandler, RemoteAttestationType, SgxQlQveCollateral}; -use itp_component_container::ComponentGetter; -use itp_extrinsics_factory::CreateExtrinsics; -use itp_node_api::metadata::{ - pallet_teebag::TeebagCallIndexes, - provider::{AccessNodeMetadata, Error as MetadataProviderError}, - Error as MetadataError, -}; -use itp_node_api_metadata::NodeMetadata; -use itp_settings::worker::MR_ENCLAVE_SIZE; -use itp_sgx_crypto::{ - ed25519_derivation::DeriveEd25519, key_repository::AccessKey, Error as SgxCryptoError, -}; -use itp_types::{AttestationType, DcapProvider, OpaqueCall, WorkerType}; -use itp_utils::write_slice_and_whitespace_pad; -use litentry_primitives::WorkerMode; -use log::*; -use sgx_types::*; -use sp_core::{ed25519::Public as Ed25519Public, Pair}; -use sp_runtime::OpaqueExtrinsic; -use std::{prelude::v1::*, slice, vec::Vec}; - -#[no_mangle] -pub unsafe extern "C" fn get_mrenclave(mrenclave: *mut u8, mrenclave_size: usize) -> sgx_status_t { - if mrenclave.is_null() || mrenclave_size < MR_ENCLAVE_SIZE { - return sgx_status_t::SGX_ERROR_INVALID_PARAMETER - } - let attestation_handler = match GLOBAL_ATTESTATION_HANDLER_COMPONENT.get() { - Ok(r) => r, - Err(e) => { - error!("Component get failure: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - match attestation_handler.get_mrenclave() { - Ok(mrenclave_value) => { - let mrenclave_slice = slice::from_raw_parts_mut(mrenclave, mrenclave_size); - if let Err(e) = - write_slice_and_whitespace_pad(mrenclave_slice, mrenclave_value.to_vec()) - { - error!("Failed to transfer mrenclave to o-call buffer: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - } - sgx_status_t::SGX_SUCCESS - }, - Err(e) => e.into(), - } -} - -// FIXME: add dcap suppoort for call site -pub fn create_ra_report_and_signature( - skip_ra: bool, - remote_attestation_type: RemoteAttestationType, - sign_type: sgx_quote_sign_type_t, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, -) -> EnclaveResult<(Vec, Vec)> { - let attestation_handler = match GLOBAL_ATTESTATION_HANDLER_COMPONENT.get() { - Ok(r) => r, - Err(e) => { - error!("Component get failure: {:?}", e); - return Err(e.into()) - }, - }; - - match remote_attestation_type { - RemoteAttestationType::Epid => { - match attestation_handler.create_epid_ra_report_and_signature(sign_type, skip_ra) { - Ok(epid) => Ok(epid), - Err(e) => { - error!("create_epid_ra_report_and_signature failure: {:?}", e); - Err(e.into()) - }, - } - }, - RemoteAttestationType::Dcap => { - match attestation_handler.generate_dcap_ra_cert( - quoting_enclave_target_info, - quote_size, - skip_ra, - ) { - Ok((key_der, cert_der, _qe_quote)) => Ok((key_der, cert_der)), - Err(e) => { - error!("generate_dcap_ra_cert failure: {:?}", e); - Err(e.into()) - }, - } - }, - } -} - -#[no_mangle] -pub unsafe extern "C" fn generate_ias_ra_extrinsic( - w_url: *const u8, - w_url_size: u32, - unchecked_extrinsic: *mut u8, - unchecked_extrinsic_max_size: u32, - unchecked_extrinsic_size: *mut u32, - skip_ra: c_int, -) -> sgx_status_t { - if w_url.is_null() || unchecked_extrinsic.is_null() { - return sgx_status_t::SGX_ERROR_INVALID_PARAMETER - } - let mut url_slice = slice::from_raw_parts(w_url, w_url_size as usize); - let url = match String::decode(&mut url_slice) { - // Litentry: the teebag extrinsic expects an URL with plain utf8 encoded Vec, not string scale-encoded - Ok(url) => url.as_bytes().to_vec(), - Err(_) => - return EnclaveError::Other("Could not decode url slice to a valid String".into()).into(), - }; - let extrinsic_slice = - slice::from_raw_parts_mut(unchecked_extrinsic, unchecked_extrinsic_max_size as usize); - - let extrinsic = match generate_ias_ra_extrinsic_internal(url, skip_ra == 1) { - Ok(xt) => xt, - Err(e) => return e.into(), - }; - - *unchecked_extrinsic_size = - match write_slice_and_whitespace_pad(extrinsic_slice, extrinsic.encode()) { - Ok(l) => l as u32, - Err(e) => return EnclaveError::BufferError(e).into(), - }; - sgx_status_t::SGX_SUCCESS -} - -#[no_mangle] -pub unsafe extern "C" fn generate_dcap_ra_extrinsic( - w_url: *const u8, - w_url_size: u32, - unchecked_extrinsic: *mut u8, - unchecked_extrinsic_max_size: u32, - unchecked_extrinsic_size: *mut u32, - skip_ra: c_int, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, -) -> sgx_status_t { - if w_url.is_null() || unchecked_extrinsic.is_null() { - return sgx_status_t::SGX_ERROR_INVALID_PARAMETER - } - let mut url_slice = slice::from_raw_parts(w_url, w_url_size as usize); - let url = match String::decode(&mut url_slice) { - // Litentry: the teebag extrinsic expects an URL with plain utf8 encoded Vec, not string scale-encoded - Ok(url) => url.as_bytes().to_vec(), - Err(_) => - return EnclaveError::Other("Could not decode url slice to a valid String".into()).into(), - }; - let extrinsic_slice = - slice::from_raw_parts_mut(unchecked_extrinsic, unchecked_extrinsic_max_size as usize); - - let extrinsic = match generate_dcap_ra_extrinsic_internal( - url, - skip_ra == 1, - quoting_enclave_target_info, - quote_size, - ) { - Ok(xt) => xt, - Err(e) => return e.into(), - }; - - *unchecked_extrinsic_size = - match write_slice_and_whitespace_pad(extrinsic_slice, extrinsic.encode()) { - Ok(l) => l as u32, - Err(e) => return EnclaveError::BufferError(e).into(), - }; - sgx_status_t::SGX_SUCCESS -} - -pub fn generate_dcap_ra_extrinsic_internal( - url: Vec, - skip_ra: bool, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, -) -> EnclaveResult { - let attestation_handler = GLOBAL_ATTESTATION_HANDLER_COMPONENT.get()?; - - if !skip_ra { - let (_priv_key_der, _cert_der, dcap_quote) = attestation_handler.generate_dcap_ra_cert( - quoting_enclave_target_info, - quote_size, - skip_ra, - )?; - - generate_dcap_ra_extrinsic_from_quote_internal(url, &dcap_quote) - } else { - generate_dcap_skip_ra_extrinsic_from_mr_enclave( - url, - &attestation_handler.get_mrenclave()?.encode(), - ) - } -} - -#[no_mangle] -pub unsafe extern "C" fn generate_dcap_ra_quote( - skip_ra: c_int, - quoting_enclave_target_info: &sgx_target_info_t, - quote_size: u32, - dcap_quote_p: *mut u8, - dcap_quote_size: u32, -) -> sgx_status_t { - if dcap_quote_p.is_null() { - return sgx_status_t::SGX_ERROR_INVALID_PARAMETER - } - let dcap_quote = match generate_dcap_ra_quote_internal( - skip_ra == 1, - quoting_enclave_target_info, - quote_size, - ) { - Ok(dcap_quote) => dcap_quote, - Err(e) => return e.into(), - }; - - let dcap_quote_slice = slice::from_raw_parts_mut(dcap_quote_p, dcap_quote_size as usize); - - if let Err(e) = write_slice_and_whitespace_pad(dcap_quote_slice, dcap_quote) { - return EnclaveError::BufferError(e).into() - }; - - sgx_status_t::SGX_SUCCESS -} - -pub fn generate_dcap_ra_quote_internal( - skip_ra: bool, - quoting_enclave_target_info: &sgx_target_info_t, - quote_size: u32, -) -> EnclaveResult> { - let attestation_handler = GLOBAL_ATTESTATION_HANDLER_COMPONENT.get()?; - - let (_priv_key_der, _cert_der, dcap_quote) = attestation_handler.generate_dcap_ra_cert( - Some(quoting_enclave_target_info), - Some("e_size), - skip_ra, - )?; - - Ok(dcap_quote) -} - -#[no_mangle] -pub unsafe extern "C" fn generate_dcap_ra_extrinsic_from_quote( - w_url: *const u8, - w_url_size: u32, - quote: *const u8, - quote_size: u32, - unchecked_extrinsic: *mut u8, - unchecked_extrinsic_max_size: u32, - unchecked_extrinsic_size: *mut u32, -) -> sgx_status_t { - if w_url.is_null() || unchecked_extrinsic.is_null() { - return sgx_status_t::SGX_ERROR_INVALID_PARAMETER - } - let mut url_slice = slice::from_raw_parts(w_url, w_url_size as usize); - let url = match String::decode(&mut url_slice) { - // Litentry: the teebag extrinsic expects an URL with plain utf8 encoded Vec, not string scale-encoded - Ok(url) => url.as_bytes().to_vec(), - Err(_) => - return EnclaveError::Other("Could not decode url slice to a valid String".into()).into(), - }; - - let extrinsic_slice = - slice::from_raw_parts_mut(unchecked_extrinsic, unchecked_extrinsic_max_size as usize); - - let quote_slice = slice::from_raw_parts(quote, quote_size as usize); - - let extrinsic = match generate_dcap_ra_extrinsic_from_quote_internal(url, quote_slice) { - Ok(xt) => xt, - Err(e) => return e.into(), - }; - - *unchecked_extrinsic_size = - match write_slice_and_whitespace_pad(extrinsic_slice, extrinsic.encode()) { - Ok(l) => l as u32, - Err(e) => return EnclaveError::BufferError(e).into(), - }; - sgx_status_t::SGX_SUCCESS -} - -pub fn generate_dcap_ra_extrinsic_from_quote_internal( - url: Vec, - quote: &[u8], -) -> EnclaveResult { - let node_metadata_repo = get_node_metadata_repository_from_integritee_solo_or_parachain()?; - trace!(" [Enclave] Compose register enclave getting callIDs:"); - - let call_ids = node_metadata_repo - .get_from_metadata(|m| m.register_enclave_call_indexes())? - .map_err(MetadataProviderError::MetadataError)?; - trace!(" [Enclave] Compose register enclave call DCAP IDs: {:?}", call_ids); - - let shielding_pubkey = get_shielding_pubkey()?; - let vc_pubkey = get_vc_pubkey()?; - let attestation_type = AttestationType::Dcap(DcapProvider::Intel); // skip_ra should be false here already - - let call = OpaqueCall::from_tuple(&( - call_ids, - WorkerType::BitAcross, - WorkerMode::OffChainWorker, - quote, - url, - shielding_pubkey, - vc_pubkey, - attestation_type, - )); - trace!(" [Enclave] Compose register enclave got extrinsic, returning"); - create_extrinsics(call) -} - -pub fn generate_dcap_skip_ra_extrinsic_from_mr_enclave( - url: Vec, - quote: &[u8], -) -> EnclaveResult { - let node_metadata_repo = get_node_metadata_repository_from_integritee_solo_or_parachain()?; - trace!(" [Enclave] Compose register enclave (skip-ra) getting callIDs:"); - - let call_ids = node_metadata_repo - .get_from_metadata(|m| m.register_enclave_call_indexes())? - .map_err(MetadataProviderError::MetadataError)?; - trace!(" [Enclave] Compose register enclave (skip-ra) call DCAP IDs: {:?}", call_ids); - - let shielding_pubkey = get_shielding_pubkey()?; - let vc_pubkey = get_vc_pubkey()?; - - let call = OpaqueCall::from_tuple(&( - call_ids, - WorkerType::BitAcross, - WorkerMode::OffChainWorker, - quote, - url, - shielding_pubkey, - vc_pubkey, - AttestationType::Ignore, - )); - info!(" [Enclave] Compose register enclave (skip-ra) got extrinsic, returning"); - create_extrinsics(call) -} - -fn generate_ias_ra_extrinsic_internal( - url: Vec, - skip_ra: bool, -) -> EnclaveResult { - let attestation_handler = GLOBAL_ATTESTATION_HANDLER_COMPONENT.get()?; - let cert_der = attestation_handler.generate_ias_ra_cert(skip_ra)?; - - generate_ias_ra_extrinsic_from_der_cert_internal(url, &cert_der, skip_ra) -} - -pub fn generate_ias_ra_extrinsic_from_der_cert_internal( - url: Vec, - cert_der: &[u8], - skip_ra: bool, -) -> EnclaveResult { - let node_metadata_repo = get_node_metadata_repository_from_integritee_solo_or_parachain()?; - - info!(" [Enclave] Compose register ias enclave (skip-ra) call"); - let call_ids = node_metadata_repo - .get_from_metadata(|m| m.register_enclave_call_indexes())? - .map_err(MetadataProviderError::MetadataError)?; - - let shielding_pubkey = get_shielding_pubkey()?; - let vc_pubkey = get_vc_pubkey()?; - let attestation_type = if skip_ra { AttestationType::Ignore } else { AttestationType::Ias }; - - let call = OpaqueCall::from_tuple(&( - call_ids, - WorkerType::BitAcross, - WorkerMode::OffChainWorker, - cert_der, - url, - shielding_pubkey, - vc_pubkey, - attestation_type, - )); - create_extrinsics(call) -} - -fn create_extrinsics(call: OpaqueCall) -> EnclaveResult { - let extrinsics_factory = get_extrinsic_factory_from_integritee_solo_or_parachain()?; - let extrinsics = extrinsics_factory.create_extrinsics(&[call], None)?; - - match extrinsics.get(0) { - Some(xt) => Ok(xt.clone()), - None => Err(EnclaveError::Other("Could not create extrinsic".into())), - } -} - -#[no_mangle] -pub unsafe extern "C" fn generate_register_quoting_enclave_extrinsic( - collateral: *const sgx_ql_qve_collateral_t, - unchecked_extrinsic: *mut u8, - unchecked_extrinsic_max_size: u32, - unchecked_extrinsic_size: *mut u32, -) -> sgx_status_t { - if unchecked_extrinsic.is_null() || collateral.is_null() { - return sgx_status_t::SGX_ERROR_INVALID_PARAMETER - } - let extrinsic_slice = - slice::from_raw_parts_mut(unchecked_extrinsic, unchecked_extrinsic_max_size as usize); - let collateral = SgxQlQveCollateral::from_c_type(&*collateral); - let collateral_data = match collateral.get_quoting_enclave_split() { - Some(d) => d, - None => return sgx_status_t::SGX_ERROR_INVALID_PARAMETER, - }; - - let call_index_getter = |m: &NodeMetadata| m.register_quoting_enclave_call_indexes(); - *unchecked_extrinsic_size = match generate_generic_register_collateral_extrinsic( - call_index_getter, - extrinsic_slice, - &collateral_data.0, - &collateral_data.1, - &collateral.qe_identity_issuer_chain, - ) { - Ok(l) => l as u32, - Err(e) => return e.into(), - }; - sgx_status_t::SGX_SUCCESS -} - -#[no_mangle] -pub unsafe extern "C" fn generate_register_tcb_info_extrinsic( - collateral: *const sgx_ql_qve_collateral_t, - unchecked_extrinsic: *mut u8, - unchecked_extrinsic_max_size: u32, - unchecked_extrinsic_size: *mut u32, -) -> sgx_status_t { - if unchecked_extrinsic.is_null() || collateral.is_null() { - return sgx_status_t::SGX_ERROR_INVALID_PARAMETER - } - let extrinsic_slice = - slice::from_raw_parts_mut(unchecked_extrinsic, unchecked_extrinsic_max_size as usize); - let collateral = SgxQlQveCollateral::from_c_type(&*collateral); - let collateral_data = match collateral.get_tcb_info_split() { - Some(d) => d, - None => return sgx_status_t::SGX_ERROR_INVALID_PARAMETER, - }; - - let call_index_getter = |m: &NodeMetadata| m.register_tcb_info_call_indexes(); - *unchecked_extrinsic_size = match generate_generic_register_collateral_extrinsic( - call_index_getter, - extrinsic_slice, - &collateral_data.0, - &collateral_data.1, - &collateral.tcb_info_issuer_chain, - ) { - Ok(l) => l as u32, - Err(e) => return e.into(), - }; - sgx_status_t::SGX_SUCCESS -} - -pub fn generate_generic_register_collateral_extrinsic( - getter: F, - extrinsic_slice: &mut [u8], - collateral_data: &str, - data_signature: &[u8], - issuer_chain: &[u8], -) -> EnclaveResult -where - F: Fn(&NodeMetadata) -> Result<[u8; 2], MetadataError>, -{ - let node_metadata_repo = get_node_metadata_repository_from_integritee_solo_or_parachain()?; - let call_ids = node_metadata_repo - .get_from_metadata(getter)? - .map_err(MetadataProviderError::MetadataError)?; - info!(" [Enclave] Compose register collateral call: {:?}", call_ids); - let call = OpaqueCall::from_tuple(&(call_ids, collateral_data, data_signature, issuer_chain)); - - let xt = create_extrinsics(call)?; - write_slice_and_whitespace_pad(extrinsic_slice, xt.encode()) - .map_err(|e| format!("{:?}", e).into()) -} - -#[no_mangle] -pub extern "C" fn dump_ias_ra_cert_to_disk() -> sgx_status_t { - let attestation_handler = match GLOBAL_ATTESTATION_HANDLER_COMPONENT.get() { - Ok(r) => r, - Err(e) => { - error!("Component get failure: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - match attestation_handler.dump_ias_ra_cert_to_disk() { - Ok(_) => sgx_status_t::SGX_SUCCESS, - Err(e) => e.into(), - } -} - -#[no_mangle] -pub unsafe extern "C" fn dump_dcap_ra_cert_to_disk( - quoting_enclave_target_info: &sgx_target_info_t, - quote_size: u32, -) -> sgx_status_t { - let attestation_handler = match GLOBAL_ATTESTATION_HANDLER_COMPONENT.get() { - Ok(r) => r, - Err(e) => { - error!("Component get failure: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - match attestation_handler.dump_dcap_ra_cert_to_disk(quoting_enclave_target_info, quote_size) { - Ok(_) => sgx_status_t::SGX_SUCCESS, - Err(e) => e.into(), - } -} - -#[no_mangle] -pub unsafe extern "C" fn dump_dcap_collateral_to_disk( - collateral: *const sgx_ql_qve_collateral_t, -) -> sgx_status_t { - let collateral = SgxQlQveCollateral::from_c_type(&*collateral); - collateral.dump_to_disk(); - sgx_status_t::SGX_SUCCESS -} - -fn get_shielding_pubkey() -> EnclaveResult>> { - let shielding_pubkey = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT - .get()? - .retrieve_key() - .and_then(|keypair| { - keypair - .export_pubkey() - .and_then(|pubkey| { - serde_json::to_vec(&pubkey).map_err(|e| SgxCryptoError::Serialization(e).into()) - }) - .map_err(|e| SgxCryptoError::Other(Box::new(e))) - }) - .ok(); - - Ok(shielding_pubkey) -} - -fn get_vc_pubkey() -> EnclaveResult> { - let vc_pubkey = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT - .get()? - .retrieve_key() - .and_then(|keypair| { - // vc signing pubkey - keypair.derive_ed25519().map(|keypair| keypair.public()) - }) - .ok(); - - debug!("[Enclave] VC pubkey: {:?}", vc_pubkey); - - Ok(vc_pubkey) -} diff --git a/tee-worker/bitacross/enclave-runtime/src/empty_impls.rs b/tee-worker/bitacross/enclave-runtime/src/empty_impls.rs deleted file mode 100644 index e011e4d19c..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/empty_impls.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -/// Empty tests entry for production mode. -#[cfg(not(feature = "test"))] -#[no_mangle] -#[allow(clippy::unreachable)] -pub extern "C" fn test_main_entrance() -> sgx_types::size_t { - unreachable!("Tests are not available when compiled in production mode.") -} diff --git a/tee-worker/bitacross/enclave-runtime/src/error.rs b/tee-worker/bitacross/enclave-runtime/src/error.rs deleted file mode 100644 index 48659a5672..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/error.rs +++ /dev/null @@ -1,87 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use derive_more::From; -use sgx_types::{sgx_quote3_error_t, sgx_status_t}; -use std::{boxed::Box, result::Result as StdResult, string::String}; - -pub type Result = StdResult; - -#[derive(Debug, From)] -pub enum Error { - TopPoolAuthor(itp_top_pool_author::error::Error), - Codec(codec::Error), - ComponentContainer(itp_component_container::error::Error), - Crypto(itp_sgx_crypto::Error), - ChainStorage(itp_ocall_api::Error), - ExtrinsicsFactory(itp_extrinsics_factory::error::Error), - IO(std::io::Error), - LightClient(itc_parentchain::light_client::error::Error), - NodeMetadataProvider(itp_node_api::metadata::provider::Error), - Sgx(sgx_status_t), - SgxQuote(sgx_quote3_error_t), - Stf(String), - StfStateHandler(itp_stf_state_handler::error::Error), - StfExecution(itp_stf_executor::error::Error), - ParentchainBlockImportDispatch(itc_parentchain::block_import_dispatcher::error::Error), - ExpectedTriggeredImportDispatcher, - CouldNotDispatchBlockImport, - NoShardAssigned, - NoLitentryParentchainAssigned, - NoTargetAParentchainAssigned, - NoTargetBParentchainAssigned, - ParentChainValidation(itp_storage::error::Error), - ParentChainSync, - PrimitivesAccess(itp_primitives_cache::error::Error), - MutexAccess, - Attestation(itp_attestation_handler::error::Error), - Metadata(itp_node_api_metadata::error::Error), - BufferError(itp_utils::buffer::BufferError), - Other(Box), -} - -impl From for sgx_status_t { - /// return sgx_status for top level enclave functions - fn from(error: Error) -> sgx_status_t { - match error { - Error::Sgx(status) => status, - _ => { - log::error!("Returning error {:?} as sgx unexpected.", error); - sgx_status_t::SGX_ERROR_UNEXPECTED - }, - } - } -} - -impl From for sgx_quote3_error_t { - /// return sgx_quote error - fn from(error: Error) -> sgx_quote3_error_t { - match error { - Error::SgxQuote(status) => status, - _ => { - log::error!("Returning error {:?} as sgx unexpected.", error); - sgx_quote3_error_t::SGX_QL_ERROR_UNEXPECTED - }, - } - } -} - -impl From for StdResult { - fn from(error: Error) -> StdResult { - Err(error) - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/initialization/global_components.rs b/tee-worker/bitacross/enclave-runtime/src/initialization/global_components.rs deleted file mode 100644 index 6e60d93af5..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/initialization/global_components.rs +++ /dev/null @@ -1,459 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Defines all concrete types and global components of the enclave. -//! -//! This allows the crates themselves to stay as generic as possible -//! and ensures that the global instances are initialized once. -use crate::{ - initialization::parentchain::{ - integritee_parachain::IntegriteeParachainHandler, - integritee_solochain::IntegriteeSolochainHandler, - target_a_parachain::TargetAParachainHandler, target_a_solochain::TargetASolochainHandler, - target_b_parachain::TargetBParachainHandler, target_b_solochain::TargetBSolochainHandler, - }, - ocall::OcallApi, - rpc::rpc_response_channel::RpcResponseChannel, - tls_ra::seal_handler::SealHandler, -}; -use bc_enclave_registry::EnclaveRegistry; -use bc_relayer_registry::RelayerRegistry; -use bc_signer_registry::SignerRegistry; -use ita_parentchain_interface::{integritee, target_a, target_b}; -use ita_sgx_runtime::Runtime; -use ita_stf::{Getter, State as StfState, Stf, TrustedCallSigned}; -use itc_direct_rpc_server::{ - rpc_connection_registry::ConnectionRegistry, rpc_responder::RpcResponder, - rpc_watch_extractor::RpcWatchExtractor, rpc_ws_handler::RpcWsHandler, -}; -use itc_parentchain::{ - block_import_dispatcher::{ - immediate_dispatcher::ImmediateDispatcher, triggered_dispatcher::TriggeredDispatcher, - BlockImportDispatcher, - }, - block_importer::ParentchainBlockImporter, - indirect_calls_executor::{filter_metadata::EventCreator, IndirectCallsExecutor}, - light_client::{ - concurrent_access::ValidatorAccessor, io::LightClientStateSealSync, - light_validation::LightValidation, light_validation_state::LightValidationState, - }, -}; -use itc_tls_websocket_server::{ - config_provider::FromFileConfigProvider, ws_server::TungsteniteWsServer, ConnectionToken, -}; -use itp_attestation_handler::IntelAttestationHandler; -use itp_component_container::ComponentContainer; -use itp_extrinsics_factory::ExtrinsicsFactory; -use itp_import_queue::ImportQueue; -use itp_node_api::{ - api_client::PairSignature, - metadata::{provider::NodeMetadataRepository, NodeMetadata}, -}; -use itp_nonce_cache::NonceCache; -use itp_sgx_crypto::{ - ecdsa::{Pair as EcdsaPair, Seal as EcdsaSeal}, - key_repository::KeyRepository, - schnorr::{Pair as SchnorrPair, Seal as SchnorrSeal}, - Aes, AesSeal, Ed25519Seal, Rsa3072Seal, -}; -use itp_stf_executor::{ - enclave_signer::StfEnclaveSigner, executor::StfExecutor, getter_executor::GetterExecutor, - state_getter::StfStateGetter, -}; -use itp_stf_primitives::types::{Hash, TrustedOperation}; -use itp_stf_state_handler::{ - file_io::sgx::SgxStateFileIo, state_initializer::StateInitializer, - state_snapshot_repository::StateSnapshotRepository, StateHandler, -}; -use itp_stf_state_observer::state_observer::StateObserver; -use itp_top_pool::basic_pool::BasicPool; -use itp_top_pool_author::{ - api::SidechainApi, - author::{Author, AuthorTopFilter}, -}; -use itp_types::{Block as ParentchainBlock, SignedBlock as SignedParentchainBlock}; -use lazy_static::lazy_static; -use sgx_crypto_helper::rsa3072::Rsa3072KeyPair; -use sgx_tstd::vec::Vec; -use sp_core::{ed25519, ed25519::Pair}; -use std::sync::Arc; - -pub type EnclaveParentchainSigner = - itp_node_api::api_client::StaticExtrinsicSigner; - -pub type EnclaveGetter = Getter; -pub type EnclaveTrustedCallSigned = TrustedCallSigned; -pub type EnclaveStf = Stf; -pub type EnclaveStateKeyRepository = KeyRepository; -pub type EnclaveShieldingKeyRepository = KeyRepository; -pub type EnclaveSigningKeyRepository = KeyRepository; -pub type EnclaveBitcoinKeyRepository = KeyRepository; -pub type EnclaveEthereumKeyRepository = KeyRepository; -pub type EnclaveTonKeyRepository = KeyRepository; -pub type EnclaveStateFileIo = SgxStateFileIo; -pub type EnclaveStateSnapshotRepository = StateSnapshotRepository; -pub type EnclaveStateObserver = StateObserver; -pub type EnclaveStateInitializer = - StateInitializer; -pub type EnclaveStateHandler = - StateHandler; -pub type EnclaveGetterExecutor = - GetterExecutor, Getter>; -pub type EnclaveOCallApi = OcallApi; -pub type EnclaveNodeMetadataRepository = NodeMetadataRepository; -pub type EnclaveStfExecutor = StfExecutor< - EnclaveOCallApi, - EnclaveStateHandler, - EnclaveNodeMetadataRepository, - EnclaveStf, - EnclaveTrustedCallSigned, - EnclaveGetter, ->; -pub type EnclaveStfEnclaveSigner = StfEnclaveSigner< - EnclaveOCallApi, - EnclaveStateObserver, - EnclaveShieldingKeyRepository, - EnclaveStf, - EnclaveTopPoolAuthor, - EnclaveTrustedCallSigned, - EnclaveGetter, ->; -pub type EnclaveAttestationHandler = - IntelAttestationHandler; - -pub type EnclaveRpcConnectionRegistry = ConnectionRegistry; -pub type EnclaveRpcWsHandler = - RpcWsHandler, EnclaveRpcConnectionRegistry, Hash>; -pub type EnclaveWebSocketServer = TungsteniteWsServer; -pub type EnclaveRpcResponder = RpcResponder; -pub type EnclaveSidechainApi = SidechainApi; - -// Parentchain types relevant for all parentchains -pub type EnclaveLightClientSeal = - LightClientStateSealSync>; -pub type EnclaveExtrinsicsFactory = - ExtrinsicsFactory; - -pub type EnclaveValidatorAccessor = ValidatorAccessor< - LightValidation, - ParentchainBlock, - EnclaveLightClientSeal, ->; - -pub type IntegriteeParentchainBlockImportQueue = ImportQueue; -pub type TargetAParentchainBlockImportQueue = ImportQueue; -pub type TargetBParentchainBlockImportQueue = ImportQueue; - -/// Import queue for the events -/// -/// Note: `Vec` is correct. It should not be `Vec` -pub type IntegriteeParentchainEventImportQueue = ImportQueue>; -pub type TargetAParentchainEventImportQueue = ImportQueue>; -pub type TargetBParentchainEventImportQueue = ImportQueue>; - -// Stuff for the integritee parentchain - -pub type IntegriteeParentchainIndirectCallsExecutor = IndirectCallsExecutor< - EnclaveShieldingKeyRepository, - EnclaveStfEnclaveSigner, - EnclaveTopPoolAuthor, - EnclaveNodeMetadataRepository, - EventCreator, - integritee::ParentchainEventHandler, - EnclaveTrustedCallSigned, - EnclaveGetter, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, ->; - -pub type IntegriteeParentchainBlockImporter = ParentchainBlockImporter< - ParentchainBlock, - EnclaveValidatorAccessor, - EnclaveStfExecutor, - EnclaveExtrinsicsFactory, - IntegriteeParentchainIndirectCallsExecutor, - EnclaveOCallApi, ->; - -pub type IntegriteeParentchainTriggeredBlockImportDispatcher = TriggeredDispatcher< - IntegriteeParentchainBlockImporter, - IntegriteeParentchainBlockImportQueue, - IntegriteeParentchainEventImportQueue, ->; - -pub type IntegriteeParentchainImmediateBlockImportDispatcher = - ImmediateDispatcher; - -pub type IntegriteeParentchainBlockImportDispatcher = BlockImportDispatcher< - IntegriteeParentchainTriggeredBlockImportDispatcher, - IntegriteeParentchainImmediateBlockImportDispatcher, ->; - -// Stuff for the Target A parentchain - -/// IndirectCalls executor instance of the Target A parentchain. -/// -/// **Note**: The filter here is purely used for demo purposes. -/// -/// Also note that the extrinsic parser must be changed if the signed extra contains the -/// `AssetTxPayment`. -pub type TargetAParentchainIndirectCallsExecutor = IndirectCallsExecutor< - EnclaveShieldingKeyRepository, - EnclaveStfEnclaveSigner, - EnclaveTopPoolAuthor, - EnclaveNodeMetadataRepository, - EventCreator, - target_a::ParentchainEventHandler, - EnclaveTrustedCallSigned, - EnclaveGetter, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, ->; - -pub type TargetAParentchainBlockImporter = ParentchainBlockImporter< - ParentchainBlock, - EnclaveValidatorAccessor, - EnclaveStfExecutor, - EnclaveExtrinsicsFactory, - TargetAParentchainIndirectCallsExecutor, - EnclaveOCallApi, ->; - -pub type TargetAParentchainTriggeredBlockImportDispatcher = TriggeredDispatcher< - TargetAParentchainBlockImporter, - TargetAParentchainBlockImportQueue, - TargetAParentchainEventImportQueue, ->; - -pub type TargetAParentchainImmediateBlockImportDispatcher = - ImmediateDispatcher; - -pub type TargetAParentchainBlockImportDispatcher = BlockImportDispatcher< - TargetAParentchainTriggeredBlockImportDispatcher, - TargetAParentchainImmediateBlockImportDispatcher, ->; - -// Stuff for the Target B parentchain - -/// IndirectCalls executor instance of the Target B parentchain. -/// -/// **Note**: The filter here is purely used for demo purposes. -/// -/// Also note that the extrinsic parser must be changed if the signed extra contains the -/// `AssetTxPayment`. -pub type TargetBParentchainIndirectCallsExecutor = IndirectCallsExecutor< - EnclaveShieldingKeyRepository, - EnclaveStfEnclaveSigner, - EnclaveTopPoolAuthor, - EnclaveNodeMetadataRepository, - EventCreator, - target_b::ParentchainEventHandler, - EnclaveTrustedCallSigned, - EnclaveGetter, - RelayerRegistry, - SignerRegistry, - EnclaveRegistry, ->; - -pub type TargetBParentchainBlockImporter = ParentchainBlockImporter< - ParentchainBlock, - EnclaveValidatorAccessor, - EnclaveStfExecutor, - EnclaveExtrinsicsFactory, - TargetBParentchainIndirectCallsExecutor, - EnclaveOCallApi, ->; - -pub type TargetBParentchainTriggeredBlockImportDispatcher = TriggeredDispatcher< - TargetBParentchainBlockImporter, - TargetBParentchainBlockImportQueue, - TargetBParentchainEventImportQueue, ->; - -pub type TargetBParentchainImmediateBlockImportDispatcher = - ImmediateDispatcher; - -pub type TargetBParentchainBlockImportDispatcher = BlockImportDispatcher< - TargetBParentchainTriggeredBlockImportDispatcher, - TargetBParentchainImmediateBlockImportDispatcher, ->; - -/// Sidechain types -pub type EnclaveTopPool = BasicPool< - EnclaveSidechainApi, - ParentchainBlock, - EnclaveRpcResponder, - TrustedOperation, ->; - -pub type EnclaveTopPoolAuthor = Author< - EnclaveTopPool, - AuthorTopFilter, - EnclaveStateHandler, - EnclaveShieldingKeyRepository, - EnclaveTrustedCallSigned, - EnclaveGetter, ->; -pub type EnclaveSealHandler = SealHandler< - EnclaveShieldingKeyRepository, - EnclaveStateKeyRepository, - EnclaveStateHandler, - EnclaveLightClientSeal, - SignerRegistry, - EnclaveRegistry, ->; -pub type EnclaveOffchainWorkerExecutor = itc_offchain_worker_executor::executor::Executor< - ParentchainBlock, - EnclaveTopPoolAuthor, - EnclaveStfExecutor, - EnclaveStateHandler, - EnclaveValidatorAccessor, - EnclaveExtrinsicsFactory, - EnclaveStf, - EnclaveTrustedCallSigned, - EnclaveGetter, ->; - -// Base component instances -//------------------------------------------------------------------------------------------------- - -/// State key repository -pub static GLOBAL_STATE_KEY_REPOSITORY_COMPONENT: ComponentContainer = - ComponentContainer::new("State key repository"); - -/// Shielding key repository -pub static GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT: ComponentContainer< - EnclaveShieldingKeyRepository, -> = ComponentContainer::new("Shielding key repository"); - -/// Signing key repository -pub static GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT: ComponentContainer< - EnclaveSigningKeyRepository, -> = ComponentContainer::new("Signing key repository"); - -/// Bitcoin key repository -pub static GLOBAL_BITCOIN_KEY_REPOSITORY_COMPONENT: ComponentContainer< - EnclaveBitcoinKeyRepository, -> = ComponentContainer::new("Bitcoin key repository"); - -/// Ethereum key repository -pub static GLOBAL_ETHEREUM_KEY_REPOSITORY_COMPONENT: ComponentContainer< - EnclaveEthereumKeyRepository, -> = ComponentContainer::new("Ethereum key repository"); - -/// Ton key repository -pub static GLOBAL_TON_KEY_REPOSITORY_COMPONENT: ComponentContainer = - ComponentContainer::new("Ton key repository"); - -/// Light client db seal for the Integritee parentchain -pub static GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL: ComponentContainer< - EnclaveLightClientSeal, -> = ComponentContainer::new("Integritee Parentchain EnclaveLightClientSealSync"); - -/// Light client db seal for the Target A parentchain. -pub static GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL: ComponentContainer< - EnclaveLightClientSeal, -> = ComponentContainer::new("Target A EnclaveLightClientSealSync"); - -/// Light client db seal for the Target A parentchain. -pub static GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL: ComponentContainer< - EnclaveLightClientSeal, -> = ComponentContainer::new("Target B EnclaveLightClientSealSync"); - -/// O-Call API -pub static GLOBAL_OCALL_API_COMPONENT: ComponentContainer = - ComponentContainer::new("O-call API"); - -/// Trusted Web-socket server -pub static GLOBAL_WEB_SOCKET_SERVER_COMPONENT: ComponentContainer = - ComponentContainer::new("Web-socket server"); - -/// State handler. -pub static GLOBAL_STATE_HANDLER_COMPONENT: ComponentContainer = - ComponentContainer::new("state handler"); - -/// State observer. -pub static GLOBAL_STATE_OBSERVER_COMPONENT: ComponentContainer = - ComponentContainer::new("state observer"); - -/// TOP pool author. -pub static GLOBAL_TOP_POOL_AUTHOR_COMPONENT: ComponentContainer = - ComponentContainer::new("top_pool_author"); - -/// attestation handler -pub static GLOBAL_ATTESTATION_HANDLER_COMPONENT: ComponentContainer = - ComponentContainer::new("Attestation handler"); - -// Parentchain component instances -//------------------------------------------------------------------------------------------------- - -lazy_static! { - /// Global nonce cache for the Integritee Parentchain. - pub static ref GLOBAL_INTEGRITEE_PARENTCHAIN_NONCE_CACHE: Arc = Default::default(); - - /// Global nonce cache for the Target A parentchain.. - pub static ref GLOBAL_TARGET_A_PARENTCHAIN_NONCE_CACHE: Arc = Default::default(); - - /// Global nonce cache for the Target B parentchain.. - pub static ref GLOBAL_TARGET_B_PARENTCHAIN_NONCE_CACHE: Arc = Default::default(); -} - -/// Solochain Handler. -pub static GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT: ComponentContainer< - IntegriteeSolochainHandler, -> = ComponentContainer::new("integritee solochain handler"); - -pub static GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT: ComponentContainer< - IntegriteeParachainHandler, -> = ComponentContainer::new("integritee parachain handler"); - -pub static GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT: ComponentContainer< - TargetASolochainHandler, -> = ComponentContainer::new("target A solochain handler"); - -pub static GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT: ComponentContainer< - TargetAParachainHandler, -> = ComponentContainer::new("target A parachain handler"); - -pub static GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT: ComponentContainer< - TargetBSolochainHandler, -> = ComponentContainer::new("target B solochain handler"); - -pub static GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT: ComponentContainer< - TargetBParachainHandler, -> = ComponentContainer::new("target B parachain handler"); - -// Sidechain component instances -//------------------------------------------------------------------------------------------------- - -/// Enclave RPC WS handler. -pub static GLOBAL_RPC_WS_HANDLER_COMPONENT: ComponentContainer = - ComponentContainer::new("rpc_ws_handler"); - -/// Relayer registry -pub static GLOBAL_RELAYER_REGISTRY: ComponentContainer = - ComponentContainer::new("relayer_registry"); - -/// Signer registry -pub static GLOBAL_SIGNER_REGISTRY: ComponentContainer = - ComponentContainer::new("signer_registry"); - -/// Enclave registry -pub static GLOBAL_ENCLAVE_REGISTRY: ComponentContainer = - ComponentContainer::new("enclave_registry"); diff --git a/tee-worker/bitacross/enclave-runtime/src/initialization/mod.rs b/tee-worker/bitacross/enclave-runtime/src/initialization/mod.rs deleted file mode 100644 index dacb97c261..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/initialization/mod.rs +++ /dev/null @@ -1,517 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#![allow(clippy::unwrap_used)] - -pub mod global_components; -pub mod parentchain; -use crate::{ - error::{Error, Result as EnclaveResult}, - get_node_metadata_repository_from_integritee_solo_or_parachain, - get_validator_accessor_from_integritee_solo_or_parachain, - initialization::global_components::{ - EnclaveGetterExecutor, EnclaveLightClientSeal, EnclaveRpcResponder, - EnclaveShieldingKeyRepository, EnclaveSidechainApi, EnclaveStateFileIo, - EnclaveStateHandler, EnclaveStateInitializer, EnclaveStateObserver, - EnclaveStateSnapshotRepository, EnclaveStfEnclaveSigner, EnclaveTopPool, - EnclaveTopPoolAuthor, GLOBAL_ATTESTATION_HANDLER_COMPONENT, - GLOBAL_BITCOIN_KEY_REPOSITORY_COMPONENT, GLOBAL_ETHEREUM_KEY_REPOSITORY_COMPONENT, - GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_OCALL_API_COMPONENT, - GLOBAL_RPC_WS_HANDLER_COMPONENT, GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, - GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, - GLOBAL_STATE_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_OBSERVER_COMPONENT, - GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL, - GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TON_KEY_REPOSITORY_COMPONENT, - GLOBAL_TOP_POOL_AUTHOR_COMPONENT, GLOBAL_WEB_SOCKET_SERVER_COMPONENT, - }, - ocall::OcallApi, - rpc::{rpc_response_channel::RpcResponseChannel, worker_api_direct::public_api_rpc_handler}, - utils::get_extrinsic_factory_from_integritee_solo_or_parachain, - Hash, -}; -use base58::ToBase58; -use bc_enclave_registry::EnclaveRegistryUpdater; -use bc_musig2_ceremony::{CeremonyCommandTmp, CeremonyId, CeremonyRegistry, MuSig2Ceremony}; -use bc_relayer_registry::{RelayerRegistry, RelayerRegistryUpdater}; -use bc_signer_registry::SignerRegistryUpdater; -use bc_task_processor::{run_bit_across_handler_runner, BitAcrossTaskContext}; -use codec::Encode; -use ita_stf::{Getter, TrustedCallSigned}; -use itc_direct_rpc_server::{ - create_determine_watch, rpc_connection_registry::ConnectionRegistry, - rpc_responder::RpcResponder, rpc_ws_handler::RpcWsHandler, -}; - -use itc_parentchain_light_client::{concurrent_access::ValidatorAccess, ExtrinsicSender}; -use itc_tls_websocket_server::{ - certificate_generation::ed25519_self_signed_certificate, - config_provider::FromFileConfigProvider, ws_server::TungsteniteWsServer, ConnectionToken, - WebSocketServer, -}; -use itp_attestation_handler::IntelAttestationHandler; -use itp_component_container::{ComponentGetter, ComponentInitializer}; -use itp_extrinsics_factory::CreateExtrinsics; -use itp_node_api_metadata::pallet_bitacross::BitAcrossCallIndexes; -use itp_node_api_metadata_provider::AccessNodeMetadata; -use itp_primitives_cache::GLOBAL_PRIMITIVES_CACHE; -use itp_settings::files::{ - LITENTRY_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, STATE_SNAPSHOTS_CACHE_SIZE, - TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, -}; -use itp_sgx_crypto::{ - ecdsa::create_ecdsa_repository, - get_aes_repository, get_ed25519_repository, get_rsa3072_repository, - key_repository::{AccessKey, KeyRepository}, - schnorr::{create_schnorr_repository, Pair as SchnorrPair, Seal}, -}; - -use crate::initialization::global_components::{ - GLOBAL_ENCLAVE_REGISTRY, GLOBAL_RELAYER_REGISTRY, GLOBAL_SIGNER_REGISTRY, -}; -use bc_enclave_registry::EnclaveRegistry; -use bc_signer_registry::SignerRegistry; -use itp_stf_state_handler::{ - file_io::StateDir, handle_state::HandleState, query_shard_state::QueryShardState, - state_snapshot_repository::VersionedStateAccess, - state_snapshot_repository_loader::StateSnapshotRepositoryLoader, StateHandler, -}; -use itp_top_pool::pool::Options as PoolOptions; -use itp_top_pool_author::author::AuthorTopFilter; -use itp_types::{parentchain::ParentchainId, OpaqueCall, ShardIdentifier}; -use litentry_macros::if_development_or; -use log::*; -use sp_core::{crypto::Pair, H256}; -use std::{ - collections::HashMap, - path::PathBuf, - string::{String, ToString}, - sync::Arc, -}; - -use std::sync::SgxRwLock as RwLock; - -pub(crate) fn init_enclave( - mu_ra_url: String, - untrusted_worker_url: String, - base_dir: PathBuf, - ceremony_commands_thread_count: u8, - ceremony_events_thread_count: u8, -) -> EnclaveResult<()> { - info!("Ceremony commands thread count: {}", ceremony_commands_thread_count); - info!("Ceremony events thread count: {}", ceremony_events_thread_count); - - let signing_key_repository = Arc::new(get_ed25519_repository(base_dir.clone(), None, None)?); - - GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.initialize(signing_key_repository.clone()); - let signer = signing_key_repository.retrieve_key()?; - info!("[Enclave initialized] Ed25519 prim raw : {:?}", signer.public().0); - - let bitcoin_key_repository = - Arc::new(create_schnorr_repository(base_dir.clone(), "bitcoin", None)?); - GLOBAL_BITCOIN_KEY_REPOSITORY_COMPONENT.initialize(bitcoin_key_repository.clone()); - let bitcoin_key = bitcoin_key_repository.retrieve_key()?; - info!("[Enclave initialized] Bitcoin public key raw : {:?}", bitcoin_key.public_bytes()); - - let ethereum_key_repository = - Arc::new(create_ecdsa_repository(base_dir.clone(), "ethereum", None)?); - GLOBAL_ETHEREUM_KEY_REPOSITORY_COMPONENT.initialize(ethereum_key_repository.clone()); - let ethereum_key = ethereum_key_repository.retrieve_key()?; - info!("[Enclave initialized] Ethereum public key raw : {:?}", ethereum_key.public_bytes()); - - let ton_key_repository = - Arc::new(get_ed25519_repository(base_dir.clone(), Some("ton".to_string()), None)?); - GLOBAL_TON_KEY_REPOSITORY_COMPONENT.initialize(ton_key_repository.clone()); - let ton_key = ton_key_repository.retrieve_key()?; - info!("[Enclave initialized] Ton public key raw : {:?}", ton_key.public().0); - - let shielding_key_repository = Arc::new(get_rsa3072_repository(base_dir.clone())?); - GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.initialize(shielding_key_repository.clone()); - - // Create the aes key that is used for state encryption such that a key is always present in tests. - // It will be overwritten anyway if mutual remote attestation is performed with the primary worker. - let state_key_repository = Arc::new(get_aes_repository(base_dir.clone())?); - GLOBAL_STATE_KEY_REPOSITORY_COMPONENT.initialize(state_key_repository.clone()); - - let integritee_light_client_seal = Arc::new(EnclaveLightClientSeal::new( - base_dir.join(LITENTRY_PARENTCHAIN_LIGHT_CLIENT_DB_PATH), - ParentchainId::Litentry, - )?); - GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL.initialize(integritee_light_client_seal); - - let target_a_light_client_seal = Arc::new(EnclaveLightClientSeal::new( - base_dir.join(TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH), - ParentchainId::TargetA, - )?); - GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL.initialize(target_a_light_client_seal); - - let target_b_light_client_seal = Arc::new(EnclaveLightClientSeal::new( - base_dir.join(TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH), - ParentchainId::TargetB, - )?); - GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL.initialize(target_b_light_client_seal); - - let state_file_io = - Arc::new(EnclaveStateFileIo::new(state_key_repository, StateDir::new(base_dir.clone()))); - let state_initializer = - Arc::new(EnclaveStateInitializer::new(shielding_key_repository.clone())); - let state_snapshot_repository_loader = StateSnapshotRepositoryLoader::< - EnclaveStateFileIo, - EnclaveStateInitializer, - >::new(state_file_io, state_initializer.clone()); - - let state_snapshot_repository = - state_snapshot_repository_loader.load_snapshot_repository(STATE_SNAPSHOTS_CACHE_SIZE)?; - let state_observer = initialize_state_observer(&state_snapshot_repository)?; - GLOBAL_STATE_OBSERVER_COMPONENT.initialize(state_observer.clone()); - - let state_handler = Arc::new(StateHandler::load_from_repository( - state_snapshot_repository, - state_observer.clone(), - state_initializer, - )?); - - GLOBAL_STATE_HANDLER_COMPONENT.initialize(state_handler.clone()); - - let ocall_api = Arc::new(OcallApi); - GLOBAL_OCALL_API_COMPONENT.initialize(ocall_api.clone()); - - // For debug purposes, list shards. no problem to panic if fails. - #[allow(clippy::unwrap_used)] - let shards = state_handler.list_shards().unwrap(); - debug!("found the following {} shards on disk:", shards.len()); - for s in shards { - debug!("{}", s.encode().to_base58()) - } - - itp_primitives_cache::set_primitives( - GLOBAL_PRIMITIVES_CACHE.as_ref(), - mu_ra_url, - untrusted_worker_url, - ) - .map_err(Error::PrimitivesAccess)?; - - let watch_extractor = Arc::new(create_determine_watch::()); - - let connection_registry = Arc::new(ConnectionRegistry::::new()); - - // We initialize components for the public RPC / direct invocation server here, so we can start the server - // before registering on the parentchain. If we started the RPC AFTER registering on the parentchain and - // initializing the light-client, there is a period of time where a peer might want to reach us, - // but the RPC server is not yet up and running, resulting in error messages or even in that - // validateer completely breaking (IO PipeError). - // Corresponding GH issues are #545 and #600. - - let response_channel = Arc::new(RpcResponseChannel::default()); - let rpc_responder = - Arc::new(EnclaveRpcResponder::new(connection_registry.clone(), response_channel)); - - let top_pool_author = create_top_pool_author( - rpc_responder.clone(), - state_handler, - shielding_key_repository.clone(), - ); - GLOBAL_TOP_POOL_AUTHOR_COMPONENT.initialize(top_pool_author.clone()); - - let getter_executor = Arc::new(EnclaveGetterExecutor::new(state_observer)); - - let ceremony_registry = Arc::new(RwLock::new(HashMap::< - CeremonyId, - (Arc>>>, u64), - >::new())); - - let ceremony_command_tmp = Arc::new(RwLock::new(CeremonyCommandTmp::new())); - - let attestation_handler = - Arc::new(IntelAttestationHandler::new(ocall_api.clone(), signing_key_repository.clone())); - GLOBAL_ATTESTATION_HANDLER_COMPONENT.initialize(attestation_handler); - - let relayer_registry = RelayerRegistry::new(base_dir.clone()); - relayer_registry.init().map_err(|e| Error::Other(e.into()))?; - GLOBAL_RELAYER_REGISTRY.initialize(relayer_registry.into()); - - let signer_registry = Arc::new(SignerRegistry::new(base_dir.clone())); - signer_registry.init().map_err(|e| Error::Other(e.into()))?; - GLOBAL_SIGNER_REGISTRY.initialize(signer_registry.clone()); - - let enclave_registry = Arc::new(EnclaveRegistry::new(base_dir)); - enclave_registry.init().map_err(|e| Error::Other(e.into()))?; - GLOBAL_ENCLAVE_REGISTRY.initialize(enclave_registry); - - let io_handler = public_api_rpc_handler( - top_pool_author, - getter_executor, - shielding_key_repository, - ocall_api, - signing_key_repository, - bitcoin_key_repository, - ethereum_key_repository, - ton_key_repository, - signer_registry, - ); - let rpc_handler = Arc::new(RpcWsHandler::new(io_handler, watch_extractor, connection_registry)); - GLOBAL_RPC_WS_HANDLER_COMPONENT.initialize(rpc_handler); - - std::thread::spawn(move || { - run_bit_across_handler( - ceremony_registry, - ceremony_command_tmp, - signer.public().0, - rpc_responder, - ceremony_commands_thread_count, - ceremony_events_thread_count, - ) - .unwrap() - }); - - Ok(()) -} - -pub(crate) fn finish_enclave_init() -> EnclaveResult<()> { - // TODO: it's not required after ScheduledEnclave is removed - // however, it's not bad to leave a placeholder as post-init hook - Ok(()) -} - -#[allow(unused_variables)] -pub(crate) fn init_wallets(base_dir: PathBuf) -> EnclaveResult<()> { - if_development_or!( - { - println!("Initializing wallets from BTC_KEY, ETH_KEY and TON_KEY env variables"); - let btc_key: Option<[u8; 32]> = read_key_from_env("BTC_KEY")?; - if btc_key.is_some() { - create_schnorr_repository(base_dir.clone(), "bitcoin", btc_key)?; - } - - let eth_key: Option<[u8; 32]> = read_key_from_env("ETH_KEY")?; - if eth_key.is_some() { - create_ecdsa_repository(base_dir.clone(), "ethereum", eth_key)?; - } - - let ton_key: Option<[u8; 32]> = read_key_from_env("TON_KEY")?; - if ton_key.is_some() { - get_ed25519_repository(base_dir, Some("ton".to_string()), ton_key)?; - } - }, - { - println!("Init wallets available in dev mode only!"); - } - ); - - Ok(()) -} - -#[cfg(feature = "development")] -fn read_key_from_env(env_variable: &str) -> EnclaveResult> { - use std::env; - if let Ok(value) = env::var(env_variable) { - let decoded = - hex::decode(value).map_err(|_| Error::Other("Could not decode key value".into()))?; - Ok(Some( - decoded - .try_into() - .map_err(|_| Error::Other("Provided key is not 32 bytes long".into()))?, - )) - } else { - Ok(None) - } -} - -pub(crate) fn publish_wallets() -> EnclaveResult<()> { - let metadata_repository = get_node_metadata_repository_from_integritee_solo_or_parachain()?; - let extrinsics_factory = get_extrinsic_factory_from_integritee_solo_or_parachain()?; - let validator_accessor = get_validator_accessor_from_integritee_solo_or_parachain()?; - - let bitcoin_key_repository = GLOBAL_BITCOIN_KEY_REPOSITORY_COMPONENT.get()?; - let bitcoin_key = bitcoin_key_repository.retrieve_key()?; - - let bitcoin_call = metadata_repository - .get_from_metadata(|m| m.btc_wallet_generated_indexes()) - .map_err(|e| Error::Other(e.into()))? - .map_err(|e| Error::Other(format!("{:?}", e).into()))?; - - let bitcoin_opaque_call = OpaqueCall::from_tuple(&(bitcoin_call, bitcoin_key.public_bytes())); - - let ethereum_key_repository = GLOBAL_ETHEREUM_KEY_REPOSITORY_COMPONENT.get()?; - let ethereum_key = ethereum_key_repository.retrieve_key()?; - - let ethereum_call = metadata_repository - .get_from_metadata(|m| m.eth_wallet_generated_indexes()) - .map_err(|e| Error::Other(e.into()))? - .map_err(|e| Error::Other(format!("{:?}", e).into()))?; - - let ethereum_opaque_call = - OpaqueCall::from_tuple(&(ethereum_call, ethereum_key.public_bytes())); - - let ton_key_repository = GLOBAL_TON_KEY_REPOSITORY_COMPONENT.get()?; - let ton_key = ton_key_repository.retrieve_key()?; - - let ton_call = metadata_repository - .get_from_metadata(|m| m.ton_wallet_generated_indexes()) - .map_err(|e| Error::Other(e.into()))? - .map_err(|e| Error::Other(format!("{:?}", e).into()))?; - - let ton_opaque_call = OpaqueCall::from_tuple(&(ton_call, ton_key.public().0)); - - let xts = extrinsics_factory - .create_extrinsics(&[bitcoin_opaque_call, ethereum_opaque_call, ton_opaque_call], None) - .map_err(|e| Error::Other(e.into()))?; - validator_accessor - .execute_mut_on_validator(|v| v.send_extrinsics(xts)) - .map_err(|e| Error::Other(e.into()))?; - - Ok(()) -} - -fn initialize_state_observer( - snapshot_repository: &EnclaveStateSnapshotRepository, -) -> EnclaveResult> { - let shards = snapshot_repository.list_shards()?; - let mut states_map = HashMap::< - ShardIdentifier, - ::StateType, - >::new(); - for shard in shards.into_iter() { - let state = snapshot_repository.load_latest(&shard)?; - states_map.insert(shard, state); - } - Ok(Arc::new(EnclaveStateObserver::from_map(states_map))) -} - -fn run_bit_across_handler( - ceremony_registry: Arc>>>, - musig2_ceremony_pending_commands: Arc>, - signing_key_pub: [u8; 32], - responder: Arc< - RpcResponder, H256, RpcResponseChannel>, - >, - ceremony_commands_thread_count: u8, - ceremony_events_thread_count: u8, -) -> Result<(), Error> { - let author_api = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; - let signing_key = GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.get()?; - let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; - let state_observer = GLOBAL_STATE_OBSERVER_COMPONENT.get()?; - let relayer_registry_lookup = GLOBAL_RELAYER_REGISTRY.get()?; - let enclave_registry_lookup = GLOBAL_ENCLAVE_REGISTRY.get()?; - let signer_registry_lookup = GLOBAL_SIGNER_REGISTRY.get()?; - - let shielding_key_repository = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get()?; - let ethereum_key_repository = GLOBAL_ETHEREUM_KEY_REPOSITORY_COMPONENT.get()?; - let bitcoin_key_repository = GLOBAL_BITCOIN_KEY_REPOSITORY_COMPONENT.get()?; - let ton_key_repository = GLOBAL_TON_KEY_REPOSITORY_COMPONENT.get()?; - - #[allow(clippy::unwrap_used)] - let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; - let stf_enclave_signer = Arc::new(EnclaveStfEnclaveSigner::new( - state_observer, - ocall_api.clone(), - shielding_key_repository.clone(), - author_api, - )); - - let task_context = BitAcrossTaskContext::new( - shielding_key_repository, - signing_key, - ethereum_key_repository, - bitcoin_key_repository, - ton_key_repository, - stf_enclave_signer, - state_handler, - ocall_api, - relayer_registry_lookup, - enclave_registry_lookup, - signer_registry_lookup, - signing_key_pub, - ceremony_registry, - musig2_ceremony_pending_commands, - responder, - ); - run_bit_across_handler_runner( - Arc::new(task_context), - ceremony_commands_thread_count, - ceremony_events_thread_count, - ); - Ok(()) -} - -pub(crate) fn init_direct_invocation_server(server_addr: String) -> EnclaveResult<()> { - info!("Initialize direct invocation server on {}", &server_addr); - let rpc_handler = GLOBAL_RPC_WS_HANDLER_COMPONENT.get()?; - let signer = GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.get()?.retrieve_key()?; - - let url = url::Url::parse(&server_addr).map_err(|e| Error::Other(format!("{}", e).into()))?; - let maybe_config_provider = if url.scheme() == "wss" { - let cert = ed25519_self_signed_certificate(signer, "Enclave") - .map_err(|e| Error::Other(e.into()))?; - - // Serialize certificate(s) and private key to PEM. - // PEM format is needed as a certificate chain can only be serialized into PEM. - let pem_serialized = cert.serialize_pem().map_err(|e| Error::Other(e.into()))?; - let private_key = cert.serialize_private_key_pem(); - - Some(Arc::new(FromFileConfigProvider::new(private_key, pem_serialized))) - } else { - return Err(Error::Other("Only accept wss scheme".into())) - }; - - let web_socket_server = Arc::new(TungsteniteWsServer::new( - url.authority().into(), - maybe_config_provider, - rpc_handler, - )); - - GLOBAL_WEB_SOCKET_SERVER_COMPONENT.initialize(web_socket_server.clone()); - - match web_socket_server.run() { - Ok(()) => {}, - Err(e) => { - error!("Web socket server encountered an unexpected error: {:?}", e) - }, - } - - Ok(()) -} - -pub(crate) fn init_shard(shard: ShardIdentifier) -> EnclaveResult<()> { - let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; - let _ = state_handler.initialize_shard(shard)?; - Ok(()) -} - -pub(crate) fn migrate_shard(new_shard: ShardIdentifier) -> EnclaveResult<()> { - let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; - let _ = state_handler.migrate_shard(new_shard)?; - Ok(()) -} - -/// Initialize the TOP pool author component. -pub fn create_top_pool_author( - rpc_responder: Arc, - state_handler: Arc, - shielding_key_repository: Arc, -) -> Arc { - let side_chain_api = Arc::new(EnclaveSidechainApi::new()); - let top_pool = - Arc::new(EnclaveTopPool::create(PoolOptions::default(), side_chain_api, rpc_responder)); - - Arc::new(EnclaveTopPoolAuthor::new( - top_pool, - AuthorTopFilter::::new(), - state_handler, - shielding_key_repository, - )) -} diff --git a/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/common.rs b/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/common.rs deleted file mode 100644 index 1d45f55dc7..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/common.rs +++ /dev/null @@ -1,291 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - error::Result, - initialization::{ - global_components::{ - EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveOffchainWorkerExecutor, - EnclaveParentchainSigner, EnclaveStfExecutor, EnclaveValidatorAccessor, - IntegriteeParentchainBlockImportDispatcher, IntegriteeParentchainBlockImporter, - IntegriteeParentchainImmediateBlockImportDispatcher, - IntegriteeParentchainIndirectCallsExecutor, TargetAParentchainBlockImportDispatcher, - TargetAParentchainBlockImporter, TargetAParentchainImmediateBlockImportDispatcher, - TargetAParentchainIndirectCallsExecutor, TargetBParentchainBlockImportDispatcher, - TargetBParentchainBlockImporter, TargetBParentchainImmediateBlockImportDispatcher, - TargetBParentchainIndirectCallsExecutor, GLOBAL_OCALL_API_COMPONENT, - GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, - GLOBAL_STATE_HANDLER_COMPONENT, GLOBAL_STATE_OBSERVER_COMPONENT, - GLOBAL_TOP_POOL_AUTHOR_COMPONENT, - }, - EnclaveStfEnclaveSigner, GLOBAL_ENCLAVE_REGISTRY, GLOBAL_RELAYER_REGISTRY, - GLOBAL_SIGNER_REGISTRY, - }, -}; -use ita_parentchain_interface::{ - integritee::ParentchainEventHandler as LitentryParentchainEventHandler, - target_a::ParentchainEventHandler as TargetAParentchainEventHandler, - target_b::ParentchainEventHandler as TargetBParentchainEventHandler, -}; -use itp_component_container::ComponentGetter; -use itp_nonce_cache::NonceCache; -use itp_sgx_crypto::key_repository::AccessKey; -use itp_stf_interface::ShardCreationInfo; -use itp_types::parentchain::ParentchainId; -use log::*; -use sp_core::H256; -use std::sync::Arc; - -pub(crate) fn create_integritee_parentchain_block_importer( - validator_access: Arc, - stf_executor: Arc, - extrinsics_factory: Arc, - node_metadata_repository: Arc, - shard_creation_info: ShardCreationInfo, -) -> Result { - let state_observer = GLOBAL_STATE_OBSERVER_COMPONENT.get()?; - let top_pool_author = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; - let shielding_key_repository = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get()?; - let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; - let relayer_registry = GLOBAL_RELAYER_REGISTRY.get()?; - let signer_registry = GLOBAL_SIGNER_REGISTRY.get()?; - let enclave_registry = GLOBAL_ENCLAVE_REGISTRY.get()?; - - let parentchain_event_handler = LitentryParentchainEventHandler {}; - - let stf_enclave_signer = Arc::new(EnclaveStfEnclaveSigner::new( - state_observer, - ocall_api.clone(), - shielding_key_repository.clone(), - top_pool_author.clone(), - )); - let indirect_calls_executor = Arc::new(IntegriteeParentchainIndirectCallsExecutor::new( - shielding_key_repository, - stf_enclave_signer, - top_pool_author, - node_metadata_repository, - ParentchainId::Litentry, - parentchain_event_handler, - relayer_registry, - signer_registry, - enclave_registry, - )); - Ok(IntegriteeParentchainBlockImporter::new( - validator_access, - stf_executor, - extrinsics_factory, - indirect_calls_executor, - ocall_api, - shard_creation_info, - ParentchainId::TargetB, - )) -} - -pub(crate) fn create_target_a_parentchain_block_importer( - validator_access: Arc, - stf_executor: Arc, - extrinsics_factory: Arc, - node_metadata_repository: Arc, - shard_creation_info: ShardCreationInfo, -) -> Result { - let state_observer = GLOBAL_STATE_OBSERVER_COMPONENT.get()?; - let top_pool_author = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; - let shielding_key_repository = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get()?; - let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; - let relayer_registry = GLOBAL_RELAYER_REGISTRY.get()?; - let signer_registry = GLOBAL_SIGNER_REGISTRY.get()?; - let enclave_registry = GLOBAL_ENCLAVE_REGISTRY.get()?; - - let parentchain_event_handler = TargetAParentchainEventHandler {}; - - let stf_enclave_signer = Arc::new(EnclaveStfEnclaveSigner::new( - state_observer, - ocall_api.clone(), - shielding_key_repository.clone(), - top_pool_author.clone(), - )); - let indirect_calls_executor = Arc::new(TargetAParentchainIndirectCallsExecutor::new( - shielding_key_repository, - stf_enclave_signer, - top_pool_author, - node_metadata_repository, - ParentchainId::TargetA, - parentchain_event_handler, - relayer_registry, - signer_registry, - enclave_registry, - )); - Ok(TargetAParentchainBlockImporter::new( - validator_access, - stf_executor, - extrinsics_factory, - indirect_calls_executor, - ocall_api, - shard_creation_info, - ParentchainId::Litentry, - )) -} - -pub(crate) fn create_target_b_parentchain_block_importer( - validator_access: Arc, - stf_executor: Arc, - extrinsics_factory: Arc, - node_metadata_repository: Arc, - shard_creation_info: ShardCreationInfo, -) -> Result { - let state_observer = GLOBAL_STATE_OBSERVER_COMPONENT.get()?; - let top_pool_author = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; - let shielding_key_repository = GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get()?; - let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; - let relayer_registry = GLOBAL_RELAYER_REGISTRY.get()?; - let signer_registry = GLOBAL_SIGNER_REGISTRY.get()?; - let enclave_registry = GLOBAL_ENCLAVE_REGISTRY.get()?; - - let parentchain_event_handler = TargetBParentchainEventHandler {}; - - let stf_enclave_signer = Arc::new(EnclaveStfEnclaveSigner::new( - state_observer, - ocall_api.clone(), - shielding_key_repository.clone(), - top_pool_author.clone(), - )); - let indirect_calls_executor = Arc::new(TargetBParentchainIndirectCallsExecutor::new( - shielding_key_repository, - stf_enclave_signer, - top_pool_author, - node_metadata_repository, - ParentchainId::TargetB, - parentchain_event_handler, - relayer_registry, - signer_registry, - enclave_registry, - )); - Ok(TargetBParentchainBlockImporter::new( - validator_access, - stf_executor, - extrinsics_factory, - indirect_calls_executor, - ocall_api, - shard_creation_info, - ParentchainId::TargetA, - )) -} - -pub(crate) fn create_extrinsics_factory( - genesis_hash: H256, - nonce_cache: Arc, - node_metadata_repository: Arc, -) -> Result> { - let signer = GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.get()?.retrieve_key()?; - - Ok(Arc::new(EnclaveExtrinsicsFactory::new( - genesis_hash, - EnclaveParentchainSigner::new(signer), - nonce_cache, - node_metadata_repository, - ))) -} - -pub(crate) fn create_integritee_offchain_immediate_import_dispatcher( - stf_executor: Arc, - block_importer: IntegriteeParentchainBlockImporter, - validator_access: Arc, - extrinsics_factory: Arc, -) -> Result> { - let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; - let top_pool_author = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; - - let offchain_worker_executor = Arc::new(EnclaveOffchainWorkerExecutor::new( - top_pool_author, - stf_executor, - state_handler, - validator_access, - extrinsics_factory, - )); - let immediate_dispatcher = IntegriteeParentchainImmediateBlockImportDispatcher::new( - block_importer, - ) - .with_observer(move || { - if let Err(e) = offchain_worker_executor.execute() { - error!("Failed to execute trusted calls: {:?}", e); - } - }); - - Ok(Arc::new(IntegriteeParentchainBlockImportDispatcher::new_immediate_dispatcher(Arc::new( - immediate_dispatcher, - )))) -} - -pub(crate) fn create_target_a_offchain_immediate_import_dispatcher( - stf_executor: Arc, - block_importer: TargetAParentchainBlockImporter, - validator_access: Arc, - extrinsics_factory: Arc, -) -> Result> { - let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; - let top_pool_author = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; - - let offchain_worker_executor = Arc::new(EnclaveOffchainWorkerExecutor::new( - top_pool_author, - stf_executor, - state_handler, - validator_access, - extrinsics_factory, - )); - let immediate_dispatcher = TargetAParentchainImmediateBlockImportDispatcher::new( - block_importer, - ) - .with_observer(move || { - if let Err(e) = offchain_worker_executor.execute() { - error!("Failed to execute trusted calls: {:?}", e); - } - }); - - Ok(Arc::new(TargetAParentchainBlockImportDispatcher::new_immediate_dispatcher(Arc::new( - immediate_dispatcher, - )))) -} - -pub(crate) fn create_target_b_offchain_immediate_import_dispatcher( - stf_executor: Arc, - block_importer: TargetBParentchainBlockImporter, - validator_access: Arc, - extrinsics_factory: Arc, -) -> Result> { - let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; - let top_pool_author = GLOBAL_TOP_POOL_AUTHOR_COMPONENT.get()?; - - let offchain_worker_executor = Arc::new(EnclaveOffchainWorkerExecutor::new( - top_pool_author, - stf_executor, - state_handler, - validator_access, - extrinsics_factory, - )); - let immediate_dispatcher = TargetBParentchainImmediateBlockImportDispatcher::new( - block_importer, - ) - .with_observer(move || { - if let Err(e) = offchain_worker_executor.execute() { - error!("Failed to execute trusted calls: {:?}", e); - } - }); - - Ok(Arc::new(TargetBParentchainBlockImportDispatcher::new_immediate_dispatcher(Arc::new( - immediate_dispatcher, - )))) -} diff --git a/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/integritee_parachain.rs b/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/integritee_parachain.rs deleted file mode 100644 index f0cc06a94b..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/integritee_parachain.rs +++ /dev/null @@ -1,113 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - error::Result, - initialization::{ - global_components::{ - EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveOCallApi, - EnclaveStfExecutor, EnclaveValidatorAccessor, - IntegriteeParentchainBlockImportDispatcher, - GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL, - GLOBAL_INTEGRITEE_PARENTCHAIN_NONCE_CACHE, GLOBAL_OCALL_API_COMPONENT, - GLOBAL_STATE_HANDLER_COMPONENT, - }, - parentchain::common::{ - create_extrinsics_factory, create_integritee_offchain_immediate_import_dispatcher, - create_integritee_parentchain_block_importer, - }, - }, -}; -use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, LightClientState}; -use itp_component_container::ComponentGetter; -use itp_stf_interface::ShardCreationInfo; -use itp_types::parentchain::ParentchainId; -pub use itp_types::parentchain::{ParachainBlock, ParachainHeader, ParachainParams}; -use std::{path::PathBuf, sync::Arc}; - -#[derive(Clone)] -pub struct IntegriteeParachainHandler { - pub genesis_header: ParachainHeader, - pub node_metadata_repository: Arc, - pub stf_executor: Arc, - pub validator_accessor: Arc, - pub extrinsics_factory: Arc, - pub import_dispatcher: Arc, -} - -impl IntegriteeParachainHandler { - pub fn init( - _base_path: PathBuf, - params: ParachainParams, - shard_creation_info: ShardCreationInfo, - ) -> Result { - let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; - let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; - let node_metadata_repository = Arc::new(EnclaveNodeMetadataRepository::default()); - - let genesis_header = params.genesis_header.clone(); - - let light_client_seal = GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL.get()?; - let validator = itc_parentchain::light_client::io::read_or_init_parachain_validator::< - ParachainBlock, - EnclaveOCallApi, - _, - >(params, ocall_api.clone(), &*light_client_seal, ParentchainId::Litentry)?; - let validator_accessor = - Arc::new(EnclaveValidatorAccessor::new(validator, light_client_seal)); - - let genesis_hash = validator_accessor.execute_on_validator(|v| v.genesis_hash())?; - - let extrinsics_factory = create_extrinsics_factory( - genesis_hash, - GLOBAL_INTEGRITEE_PARENTCHAIN_NONCE_CACHE.clone(), - node_metadata_repository.clone(), - )?; - - let stf_executor = Arc::new(EnclaveStfExecutor::new( - ocall_api, - state_handler, - node_metadata_repository.clone(), - )); - - let block_importer = create_integritee_parentchain_block_importer( - validator_accessor.clone(), - stf_executor.clone(), - extrinsics_factory.clone(), - node_metadata_repository.clone(), - shard_creation_info, - )?; - - let import_dispatcher = create_integritee_offchain_immediate_import_dispatcher( - stf_executor.clone(), - block_importer, - validator_accessor.clone(), - extrinsics_factory.clone(), - )?; - - let parachain_handler = Self { - genesis_header, - node_metadata_repository, - stf_executor, - validator_accessor, - extrinsics_factory, - import_dispatcher, - }; - - Ok(parachain_handler) - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/integritee_solochain.rs b/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/integritee_solochain.rs deleted file mode 100644 index ee5697967f..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/integritee_solochain.rs +++ /dev/null @@ -1,112 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - error::Result, - initialization::{ - global_components::{ - EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveOCallApi, - EnclaveStfExecutor, EnclaveValidatorAccessor, - IntegriteeParentchainBlockImportDispatcher, - GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL, - GLOBAL_INTEGRITEE_PARENTCHAIN_NONCE_CACHE, GLOBAL_OCALL_API_COMPONENT, - GLOBAL_STATE_HANDLER_COMPONENT, - }, - parentchain::common::{ - create_extrinsics_factory, create_integritee_offchain_immediate_import_dispatcher, - create_integritee_parentchain_block_importer, - }, - }, -}; -use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, LightClientState}; -use itp_component_container::ComponentGetter; -use itp_stf_interface::ShardCreationInfo; -use itp_types::parentchain::ParentchainId; -pub use itp_types::parentchain::{SolochainBlock, SolochainHeader, SolochainParams}; -use std::{path::PathBuf, sync::Arc}; - -pub struct IntegriteeSolochainHandler { - pub genesis_header: SolochainHeader, - pub node_metadata_repository: Arc, - pub stf_executor: Arc, - pub validator_accessor: Arc, - pub extrinsics_factory: Arc, - pub import_dispatcher: Arc, -} - -impl IntegriteeSolochainHandler { - pub fn init( - _base_path: PathBuf, - params: SolochainParams, - shard_creation_info: ShardCreationInfo, - ) -> Result { - let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; - let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; - let light_client_seal = GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL.get()?; - let node_metadata_repository = Arc::new(EnclaveNodeMetadataRepository::default()); - - let genesis_header = params.genesis_header.clone(); - - let validator = itc_parentchain::light_client::io::read_or_init_grandpa_validator::< - SolochainBlock, - EnclaveOCallApi, - _, - >(params, ocall_api.clone(), &*light_client_seal, ParentchainId::Litentry)?; - let validator_accessor = - Arc::new(EnclaveValidatorAccessor::new(validator, light_client_seal)); - - let genesis_hash = validator_accessor.execute_on_validator(|v| v.genesis_hash())?; - - let extrinsics_factory = create_extrinsics_factory( - genesis_hash, - GLOBAL_INTEGRITEE_PARENTCHAIN_NONCE_CACHE.clone(), - node_metadata_repository.clone(), - )?; - - let stf_executor = Arc::new(EnclaveStfExecutor::new( - ocall_api, - state_handler, - node_metadata_repository.clone(), - )); - - let block_importer = create_integritee_parentchain_block_importer( - validator_accessor.clone(), - stf_executor.clone(), - extrinsics_factory.clone(), - node_metadata_repository.clone(), - shard_creation_info, - )?; - - let import_dispatcher = create_integritee_offchain_immediate_import_dispatcher( - stf_executor.clone(), - block_importer, - validator_accessor.clone(), - extrinsics_factory.clone(), - )?; - - let solochain_handler = Self { - genesis_header, - node_metadata_repository, - stf_executor, - validator_accessor, - extrinsics_factory, - import_dispatcher, - }; - - Ok(solochain_handler) - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/mod.rs b/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/mod.rs deleted file mode 100644 index d658ec6c36..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/mod.rs +++ /dev/null @@ -1,136 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - error::Result, - initialization::{ - global_components::{ - GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT, - GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT, - GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT, - GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT, - GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT, - GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT, - }, - parentchain::{ - target_a_parachain::TargetAParachainHandler, - target_a_solochain::TargetASolochainHandler, - target_b_parachain::TargetBParachainHandler, - target_b_solochain::TargetBSolochainHandler, - }, - }, - shard_creation_info::get_shard_creation_info_internal, -}; -use codec::{Decode, Encode}; -use integritee_parachain::IntegriteeParachainHandler; -use integritee_solochain::IntegriteeSolochainHandler; -use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, LightClientState}; -use itp_component_container::ComponentInitializer; -use itp_types::parentchain::{ParentchainId, ParentchainInitParams}; -use log::*; -use std::{path::PathBuf, vec::Vec}; - -mod common; -pub mod integritee_parachain; -pub mod integritee_solochain; -pub mod target_a_parachain; -pub mod target_a_solochain; -pub mod target_b_parachain; -pub mod target_b_solochain; - -pub(crate) fn init_parentchain_components( - base_path: PathBuf, - encoded_params: Vec, -) -> Result> { - match ParentchainInitParams::decode(&mut encoded_params.as_slice())? { - ParentchainInitParams::Parachain { id, shard, params } => { - info!( - "[{:?}] initializing parachain parentchain components for shard: {:?}", - id, shard - ); - let shard_creation_info = get_shard_creation_info_internal(shard)?; - - // todo: query timestamp of creation header to give a creation reference to target_a/b as well in order to fast-sync - match id { - ParentchainId::Litentry => { - let handler = - IntegriteeParachainHandler::init(base_path, params, shard_creation_info)?; - let header = handler - .validator_accessor - .execute_on_validator(|v| v.latest_finalized_header())?; - GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT.initialize(handler.into()); - Ok(header.encode()) - }, - ParentchainId::TargetA => { - let handler = - TargetAParachainHandler::init(base_path, params, shard_creation_info)?; - let header = handler - .validator_accessor - .execute_on_validator(|v| v.latest_finalized_header())?; - GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT.initialize(handler.into()); - Ok(header.encode()) - }, - ParentchainId::TargetB => { - let handler = - TargetBParachainHandler::init(base_path, params, shard_creation_info)?; - let header = handler - .validator_accessor - .execute_on_validator(|v| v.latest_finalized_header())?; - GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT.initialize(handler.into()); - Ok(header.encode()) - }, - } - }, - ParentchainInitParams::Solochain { id, shard, params } => { - info!( - "[{:?}] initializing solochain parentchain components for shard: {:?}", - id, shard - ); - let shard_creation_info = get_shard_creation_info_internal(shard)?; - // todo: query timestamp of creation header to give a creation reference to target_a/b as well in order to fast-sync - match id { - ParentchainId::Litentry => { - let handler = - IntegriteeSolochainHandler::init(base_path, params, shard_creation_info)?; - let header = handler - .validator_accessor - .execute_on_validator(|v| v.latest_finalized_header())?; - GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT.initialize(handler.into()); - Ok(header.encode()) - }, - ParentchainId::TargetA => { - let handler = - TargetASolochainHandler::init(base_path, params, shard_creation_info)?; - let header = handler - .validator_accessor - .execute_on_validator(|v| v.latest_finalized_header())?; - GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT.initialize(handler.into()); - Ok(header.encode()) - }, - ParentchainId::TargetB => { - let handler = - TargetBSolochainHandler::init(base_path, params, shard_creation_info)?; - let header = handler - .validator_accessor - .execute_on_validator(|v| v.latest_finalized_header())?; - GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT.initialize(handler.into()); - Ok(header.encode()) - }, - } - }, - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/target_a_parachain.rs b/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/target_a_parachain.rs deleted file mode 100644 index 32de87cfba..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/target_a_parachain.rs +++ /dev/null @@ -1,117 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Naive implementation of adding a second parachain handler to the setup. -//! -//! Ideally, most of the redundant code can be abstracted away, but it turns out -//! that this is quite tedious, so for now this is a copy-past of the [IntegriteeParachainHandler]: -//! * https://github.com/integritee-network/worker/issues/1417 - -use crate::{ - error::Result, - initialization::{ - global_components::{ - EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveOCallApi, - EnclaveStfExecutor, EnclaveValidatorAccessor, TargetAParentchainBlockImportDispatcher, - GLOBAL_OCALL_API_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, - GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TARGET_A_PARENTCHAIN_NONCE_CACHE, - }, - parentchain::common::{ - create_extrinsics_factory, create_target_a_offchain_immediate_import_dispatcher, - create_target_a_parentchain_block_importer, - }, - }, -}; -use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, LightClientState}; -use itp_component_container::ComponentGetter; -use itp_stf_interface::ShardCreationInfo; -use itp_types::parentchain::ParentchainId; -pub use itp_types::parentchain::{ParachainBlock, ParachainHeader, ParachainParams}; -use std::{path::PathBuf, sync::Arc}; - -#[derive(Clone)] -pub struct TargetAParachainHandler { - pub genesis_header: ParachainHeader, - pub node_metadata_repository: Arc, - pub stf_executor: Arc, - pub validator_accessor: Arc, - pub extrinsics_factory: Arc, - pub import_dispatcher: Arc, -} - -impl TargetAParachainHandler { - pub fn init( - _base_path: PathBuf, - params: ParachainParams, - shard_creation_info: ShardCreationInfo, - ) -> Result { - let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; - let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; - let node_metadata_repository = Arc::new(EnclaveNodeMetadataRepository::default()); - - let genesis_header = params.genesis_header.clone(); - - let light_client_seal = GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL.get()?; - let validator = itc_parentchain::light_client::io::read_or_init_parachain_validator::< - ParachainBlock, - EnclaveOCallApi, - _, - >(params, ocall_api.clone(), &*light_client_seal, ParentchainId::TargetA)?; - let validator_accessor = - Arc::new(EnclaveValidatorAccessor::new(validator, light_client_seal)); - - let genesis_hash = validator_accessor.execute_on_validator(|v| v.genesis_hash())?; - - let extrinsics_factory = create_extrinsics_factory( - genesis_hash, - GLOBAL_TARGET_A_PARENTCHAIN_NONCE_CACHE.clone(), - node_metadata_repository.clone(), - )?; - - let stf_executor = Arc::new(EnclaveStfExecutor::new( - ocall_api, - state_handler, - node_metadata_repository.clone(), - )); - - let block_importer = create_target_a_parentchain_block_importer( - validator_accessor.clone(), - stf_executor.clone(), - extrinsics_factory.clone(), - node_metadata_repository.clone(), - shard_creation_info, - )?; - - let import_dispatcher = create_target_a_offchain_immediate_import_dispatcher( - stf_executor.clone(), - block_importer, - validator_accessor.clone(), - extrinsics_factory.clone(), - )?; - - let parachain_handler = Self { - genesis_header, - node_metadata_repository, - stf_executor, - validator_accessor, - extrinsics_factory, - import_dispatcher, - }; - - Ok(parachain_handler) - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/target_a_solochain.rs b/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/target_a_solochain.rs deleted file mode 100644 index bd76a450f6..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/target_a_solochain.rs +++ /dev/null @@ -1,110 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - error::Result, - initialization::{ - global_components::{ - EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveOCallApi, - EnclaveStfExecutor, EnclaveValidatorAccessor, TargetAParentchainBlockImportDispatcher, - GLOBAL_OCALL_API_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, - GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TARGET_A_PARENTCHAIN_NONCE_CACHE, - }, - parentchain::common::{ - create_extrinsics_factory, create_target_a_offchain_immediate_import_dispatcher, - create_target_a_parentchain_block_importer, - }, - }, -}; -use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, LightClientState}; -use itp_component_container::ComponentGetter; -use itp_stf_interface::ShardCreationInfo; -use itp_types::parentchain::ParentchainId; -pub use itp_types::parentchain::{SolochainBlock, SolochainHeader, SolochainParams}; -use std::{path::PathBuf, sync::Arc}; - -pub struct TargetASolochainHandler { - pub genesis_header: SolochainHeader, - pub node_metadata_repository: Arc, - pub stf_executor: Arc, - pub validator_accessor: Arc, - pub extrinsics_factory: Arc, - pub import_dispatcher: Arc, -} - -impl TargetASolochainHandler { - pub fn init( - _base_path: PathBuf, - params: SolochainParams, - shard_creation_info: ShardCreationInfo, - ) -> Result { - let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; - let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; - let light_client_seal = GLOBAL_TARGET_A_PARENTCHAIN_LIGHT_CLIENT_SEAL.get()?; - let node_metadata_repository = Arc::new(EnclaveNodeMetadataRepository::default()); - - let genesis_header = params.genesis_header.clone(); - - let validator = itc_parentchain::light_client::io::read_or_init_grandpa_validator::< - SolochainBlock, - EnclaveOCallApi, - _, - >(params, ocall_api.clone(), &*light_client_seal, ParentchainId::TargetA)?; - let validator_accessor = - Arc::new(EnclaveValidatorAccessor::new(validator, light_client_seal)); - - let genesis_hash = validator_accessor.execute_on_validator(|v| v.genesis_hash())?; - - let extrinsics_factory = create_extrinsics_factory( - genesis_hash, - GLOBAL_TARGET_A_PARENTCHAIN_NONCE_CACHE.clone(), - node_metadata_repository.clone(), - )?; - - let stf_executor = Arc::new(EnclaveStfExecutor::new( - ocall_api, - state_handler, - node_metadata_repository.clone(), - )); - - let block_importer = create_target_a_parentchain_block_importer( - validator_accessor.clone(), - stf_executor.clone(), - extrinsics_factory.clone(), - node_metadata_repository.clone(), - shard_creation_info, - )?; - - let import_dispatcher = create_target_a_offchain_immediate_import_dispatcher( - stf_executor.clone(), - block_importer, - validator_accessor.clone(), - extrinsics_factory.clone(), - )?; - - let solochain_handler = Self { - genesis_header, - node_metadata_repository, - stf_executor, - validator_accessor, - extrinsics_factory, - import_dispatcher, - }; - - Ok(solochain_handler) - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/target_b_parachain.rs b/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/target_b_parachain.rs deleted file mode 100644 index 221a37b0c0..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/target_b_parachain.rs +++ /dev/null @@ -1,117 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Naive implementation of adding a second parachain handler to the setup. -//! -//! Ideally, most of the redundant code can be abstracted away, but it turns out -//! that this is quite tedious, so for now this is a copy-past of the [IntegriteeParachainHandler]: -//! * https://github.com/integritee-network/worker/issues/1417 - -use crate::{ - error::Result, - initialization::{ - global_components::{ - EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveOCallApi, - EnclaveStfExecutor, EnclaveValidatorAccessor, TargetBParentchainBlockImportDispatcher, - GLOBAL_OCALL_API_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, - GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TARGET_B_PARENTCHAIN_NONCE_CACHE, - }, - parentchain::common::{ - create_extrinsics_factory, create_target_b_offchain_immediate_import_dispatcher, - create_target_b_parentchain_block_importer, - }, - }, -}; -use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, LightClientState}; -use itp_component_container::ComponentGetter; -use itp_stf_interface::ShardCreationInfo; -use itp_types::parentchain::ParentchainId; -pub use itp_types::parentchain::{ParachainBlock, ParachainHeader, ParachainParams}; -use std::{path::PathBuf, sync::Arc}; - -#[derive(Clone)] -pub struct TargetBParachainHandler { - pub genesis_header: ParachainHeader, - pub node_metadata_repository: Arc, - pub stf_executor: Arc, - pub validator_accessor: Arc, - pub extrinsics_factory: Arc, - pub import_dispatcher: Arc, -} - -impl TargetBParachainHandler { - pub fn init( - _base_path: PathBuf, - params: ParachainParams, - shard_creation_info: ShardCreationInfo, - ) -> Result { - let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; - let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; - let node_metadata_repository = Arc::new(EnclaveNodeMetadataRepository::default()); - - let genesis_header = params.genesis_header.clone(); - - let light_client_seal = GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL.get()?; - let validator = itc_parentchain::light_client::io::read_or_init_parachain_validator::< - ParachainBlock, - EnclaveOCallApi, - _, - >(params, ocall_api.clone(), &*light_client_seal, ParentchainId::TargetB)?; - let validator_accessor = - Arc::new(EnclaveValidatorAccessor::new(validator, light_client_seal)); - - let genesis_hash = validator_accessor.execute_on_validator(|v| v.genesis_hash())?; - - let extrinsics_factory = create_extrinsics_factory( - genesis_hash, - GLOBAL_TARGET_B_PARENTCHAIN_NONCE_CACHE.clone(), - node_metadata_repository.clone(), - )?; - - let stf_executor = Arc::new(EnclaveStfExecutor::new( - ocall_api, - state_handler, - node_metadata_repository.clone(), - )); - - let block_importer = create_target_b_parentchain_block_importer( - validator_accessor.clone(), - stf_executor.clone(), - extrinsics_factory.clone(), - node_metadata_repository.clone(), - shard_creation_info, - )?; - - let import_dispatcher = create_target_b_offchain_immediate_import_dispatcher( - stf_executor.clone(), - block_importer, - validator_accessor.clone(), - extrinsics_factory.clone(), - )?; - - let parachain_handler = Self { - genesis_header, - node_metadata_repository, - stf_executor, - validator_accessor, - extrinsics_factory, - import_dispatcher, - }; - - Ok(parachain_handler) - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/target_b_solochain.rs b/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/target_b_solochain.rs deleted file mode 100644 index 0953d15779..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/initialization/parentchain/target_b_solochain.rs +++ /dev/null @@ -1,110 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - error::Result, - initialization::{ - global_components::{ - EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveOCallApi, - EnclaveStfExecutor, EnclaveValidatorAccessor, TargetBParentchainBlockImportDispatcher, - GLOBAL_OCALL_API_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, - GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL, GLOBAL_TARGET_B_PARENTCHAIN_NONCE_CACHE, - }, - parentchain::common::{ - create_extrinsics_factory, create_target_b_offchain_immediate_import_dispatcher, - create_target_b_parentchain_block_importer, - }, - }, -}; -use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, LightClientState}; -use itp_component_container::ComponentGetter; -use itp_stf_interface::ShardCreationInfo; -use itp_types::parentchain::ParentchainId; -pub use itp_types::parentchain::{SolochainBlock, SolochainHeader, SolochainParams}; -use std::{path::PathBuf, sync::Arc}; - -pub struct TargetBSolochainHandler { - pub genesis_header: SolochainHeader, - pub node_metadata_repository: Arc, - pub stf_executor: Arc, - pub validator_accessor: Arc, - pub extrinsics_factory: Arc, - pub import_dispatcher: Arc, -} - -impl TargetBSolochainHandler { - pub fn init( - _base_path: PathBuf, - params: SolochainParams, - shard_creation_info: ShardCreationInfo, - ) -> Result { - let ocall_api = GLOBAL_OCALL_API_COMPONENT.get()?; - let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; - let light_client_seal = GLOBAL_TARGET_B_PARENTCHAIN_LIGHT_CLIENT_SEAL.get()?; - let node_metadata_repository = Arc::new(EnclaveNodeMetadataRepository::default()); - - let genesis_header = params.genesis_header.clone(); - - let validator = itc_parentchain::light_client::io::read_or_init_grandpa_validator::< - SolochainBlock, - EnclaveOCallApi, - _, - >(params, ocall_api.clone(), &*light_client_seal, ParentchainId::TargetB)?; - let validator_accessor = - Arc::new(EnclaveValidatorAccessor::new(validator, light_client_seal)); - - let genesis_hash = validator_accessor.execute_on_validator(|v| v.genesis_hash())?; - - let extrinsics_factory = create_extrinsics_factory( - genesis_hash, - GLOBAL_TARGET_B_PARENTCHAIN_NONCE_CACHE.clone(), - node_metadata_repository.clone(), - )?; - - let stf_executor = Arc::new(EnclaveStfExecutor::new( - ocall_api, - state_handler, - node_metadata_repository.clone(), - )); - - let block_importer = create_target_b_parentchain_block_importer( - validator_accessor.clone(), - stf_executor.clone(), - extrinsics_factory.clone(), - node_metadata_repository.clone(), - shard_creation_info, - )?; - - let import_dispatcher = create_target_b_offchain_immediate_import_dispatcher( - stf_executor.clone(), - block_importer, - validator_accessor.clone(), - extrinsics_factory.clone(), - )?; - - let solochain_handler = Self { - genesis_header, - node_metadata_repository, - stf_executor, - validator_accessor, - extrinsics_factory, - import_dispatcher, - }; - - Ok(solochain_handler) - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/ipfs.rs b/tee-worker/bitacross/enclave-runtime/src/ipfs.rs deleted file mode 100644 index c376456455..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/ipfs.rs +++ /dev/null @@ -1,105 +0,0 @@ -use cid::{Cid, Result as CidResult}; -use ipfs_unixfs::file::adder::FileAdder; -use log::*; -use multibase::Base; -use std::{convert::TryFrom, vec::Vec}; - -pub struct IpfsContent { - pub cid: CidResult, - pub file_content: Vec, - pub stats: Stats, -} -#[derive(Debug, PartialEq)] -pub enum IpfsError { - InputCidInvalid, - FinalCidMissing, - Verification, -} - -impl IpfsContent { - pub fn new(_cid: &str, _content: Vec) -> IpfsContent { - IpfsContent { cid: Cid::try_from(_cid), file_content: _content, stats: Stats::default() } - } - - pub fn verify(&mut self) -> Result<(), IpfsError> { - let mut adder: FileAdder = FileAdder::default(); - let mut total: usize = 0; - while total < self.file_content.len() { - #[allow(clippy::string_slice)] - let bytes = &self.file_content.get(total..).ok_or(IpfsError::Verification)?; - let (blocks, consumed) = adder.push(bytes); - total = total.saturating_add(consumed); - self.stats.process(blocks); - } - let blocks = adder.finish(); - self.stats.process(blocks); - - if let Some(last_cid) = self.stats.last.as_ref() { - let cid_str = Base::Base58Btc.encode(last_cid.hash().as_bytes()); - info!( - "new cid: {} generated from {} blocks, total of {} bytes", - cid_str, self.stats.blocks, self.stats.block_bytes - ); - match self.cid.as_ref() { - Ok(initial_cid) => - if last_cid.hash().eq(&initial_cid.hash()) { - Ok(()) - } else { - Err(IpfsError::Verification) - }, - Err(_) => Err(IpfsError::InputCidInvalid), - } - } else { - Err(IpfsError::FinalCidMissing) - } - } -} -#[derive(Default)] -pub struct Stats { - pub blocks: usize, - pub block_bytes: u64, - pub last: Option, -} - -impl Stats { - fn process)>>(&mut self, new_blocks: I) { - for (cid, block) in new_blocks { - self.last = Some(cid); - self.blocks = self.blocks.saturating_add(1); - self.block_bytes = self.block_bytes.saturating_add(block.len() as u64); - } - } -} - -#[allow(unused)] -pub fn test_creates_ipfs_content_struct_works() { - let cid = "QmSaFjwJ2QtS3rZDKzC98XEzv2bqT4TfpWLCpphPPwyQTr"; - let content: Vec = vec![20; 512 * 1024]; - let ipfs_content = IpfsContent::new(cid, content.clone()); - - #[allow(clippy::unwrap_used)] - let cid_str = Base::Base58Btc.encode(ipfs_content.cid.as_ref().unwrap().hash().as_bytes()); - assert_eq!(cid_str, cid); - assert_eq!(ipfs_content.file_content, content); -} - -#[allow(unused)] -pub fn test_verification_ok_for_correct_content() { - let cid = "QmSaFjwJ2QtS3rZDKzC98XEzv2bqT4TfpWLCpphPPwyQTr"; - let content: Vec = vec![20; 512 * 1024]; - let mut ipfs_content = IpfsContent::new(cid, content); - let verification = ipfs_content.verify(); - assert!(verification.is_ok()); -} - -#[allow(unused)] -pub fn test_verification_fails_for_incorrect_content() { - let cid = "QmSaFjwJ2QtS3rZDKzC98XEzv2bqT4TfpWLCpphPPwyQTr"; - let content: Vec = vec![10; 512 * 1024]; - let mut ipfs_content = IpfsContent::new(cid, content); - let verification = ipfs_content.verify(); - #[allow(clippy::unwrap_used)] - { - assert_eq!(verification.unwrap_err(), IpfsError::Verification); - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/lib.rs b/tee-worker/bitacross/enclave-runtime/src/lib.rs deleted file mode 100644 index e5593baaa9..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/lib.rs +++ /dev/null @@ -1,772 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -#![feature(structural_match)] -#![feature(rustc_attrs)] -#![feature(core_intrinsics)] -#![feature(derive_eq)] -#![feature(trait_alias)] -#![crate_name = "enclave_runtime"] -#![crate_type = "staticlib"] -#![cfg_attr(not(target_env = "sgx"), no_std)] -#![cfg_attr(target_env = "sgx", feature(rustc_private))] -#![allow(clippy::missing_safety_doc)] -#![allow(clippy::unreachable)] -#![warn( - clippy::unwrap_used, - /* comment out for the moment. There are some upstream code `unimplemented` */ - // clippy::unimplemented, - // clippy::panic_in_result_fn, - clippy::string_slice, - clippy::panic, - clippy::indexing_slicing, - clippy::expect_used, - clippy::arithmetic_side_effects -)] - -#[cfg(not(target_env = "sgx"))] -#[macro_use] -extern crate sgx_tstd as std; - -use crate::{ - error::{Error, Result}, - initialization::global_components::{ - GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT, GLOBAL_INTEGRITEE_PARENTCHAIN_NONCE_CACHE, - GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT, GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, - GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, - GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT, GLOBAL_TARGET_A_PARENTCHAIN_NONCE_CACHE, - GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT, GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT, - GLOBAL_TARGET_B_PARENTCHAIN_NONCE_CACHE, GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT, - }, - utils::{ - get_node_metadata_repository_from_integritee_solo_or_parachain, - get_node_metadata_repository_from_target_a_solo_or_parachain, - get_node_metadata_repository_from_target_b_solo_or_parachain, - get_validator_accessor_from_integritee_solo_or_parachain, DecodeRaw, - }, -}; -use codec::Decode; -use core::ffi::c_int; -#[cfg(feature = "development")] -use initialization::global_components::{ - GLOBAL_BITCOIN_KEY_REPOSITORY_COMPONENT, GLOBAL_ETHEREUM_KEY_REPOSITORY_COMPONENT, - GLOBAL_TON_KEY_REPOSITORY_COMPONENT, -}; -use itc_parentchain::{ - block_import_dispatcher::DispatchBlockImport, - light_client::{concurrent_access::ValidatorAccess, Validator}, -}; -use itp_component_container::ComponentGetter; -use itp_node_api::metadata::NodeMetadata; -use itp_nonce_cache::{MutateNonce, Nonce}; -#[cfg(feature = "development")] -use itp_sgx_crypto::key_repository::AccessKey; -use itp_sgx_crypto::key_repository::AccessPubkey; -use itp_storage::{StorageProof, StorageProofChecker}; -use itp_types::{parentchain::ParentchainId, ShardIdentifier, SignedBlock}; -use itp_utils::write_slice_and_whitespace_pad; -use litentry_macros::if_development_or; -use log::*; -use once_cell::sync::OnceCell; -use sgx_types::sgx_status_t; -use sp_runtime::traits::BlakeTwo256; -use std::{ - path::PathBuf, - slice, - string::{String, ToString}, - vec::Vec, -}; - -mod attestation; -mod empty_impls; -mod initialization; -mod ipfs; -mod ocall; -mod shard_config; -mod shard_creation_info; -mod stf_task_handler; -mod utils; - -pub mod error; -pub mod rpc; -mod sync; -#[cfg(feature = "test")] -pub mod test; -mod tls_ra; - -pub type Hash = sp_core::H256; -pub type AuthorityPair = sp_core::ed25519::Pair; - -static BASE_PATH: OnceCell = OnceCell::new(); - -fn get_base_path() -> Result { - let base_path = BASE_PATH.get().ok_or_else(|| { - Error::Other("BASE_PATH not initialized. Broken enclave init flow!".to_string().into()) - })?; - - Ok(base_path.clone()) -} - -/// Initialize the enclave. -#[no_mangle] -pub unsafe extern "C" fn init( - mu_ra_addr: *const u8, - mu_ra_addr_size: u32, - untrusted_worker_addr: *const u8, - untrusted_worker_addr_size: u32, - encoded_base_dir_str: *const u8, - encoded_base_dir_size: u32, - ceremony_commands_thread_count: u8, - ceremony_events_thread_count: u8, -) -> sgx_status_t { - // Initialize the logging environment in the enclave. - if_development_or!( - env_logger::builder() - .format_timestamp(Some(env_logger::TimestampPrecision::Micros)) - .init(), - { - let module_names = litentry_proc_macros::local_modules!(); - println!( - "Initializing logger to filter only following local modules: {:?}", - module_names - ); - let mut builder = env_logger::Builder::new(); - builder.format_timestamp(Some(env_logger::TimestampPrecision::Micros)); - builder.filter(None, LevelFilter::Off); - module_names.into_iter().for_each(|module| { - builder.filter(Some(module), LevelFilter::Info); - }); - builder.init(); - } - ); - - #[cfg(feature = "dcap")] - info!(" DCAP is enabled within enclave"); - #[cfg(not(feature = "dcap"))] - info!(" DCAP is disabled within enclave"); - - let mu_ra_url = - match String::decode(&mut slice::from_raw_parts(mu_ra_addr, mu_ra_addr_size as usize)) - .map_err(Error::Codec) - { - Ok(addr) => addr, - Err(e) => return e.into(), - }; - - let untrusted_worker_url = match String::decode(&mut slice::from_raw_parts( - untrusted_worker_addr, - untrusted_worker_addr_size as usize, - )) - .map_err(Error::Codec) - { - Ok(addr) => addr, - Err(e) => return e.into(), - }; - - let base_dir = match String::decode(&mut slice::from_raw_parts( - encoded_base_dir_str, - encoded_base_dir_size as usize, - )) - .map_err(Error::Codec) - { - Ok(b) => b, - Err(e) => return e.into(), - }; - - info!("Setting base_dir to {}", base_dir); - let path = PathBuf::from(base_dir); - // Litentry: the default value here is only for clippy checking - BASE_PATH.set(path.clone()).unwrap_or(()); - - match initialization::init_enclave( - mu_ra_url, - untrusted_worker_url, - path, - ceremony_commands_thread_count, - ceremony_events_thread_count, - ) { - Err(e) => e.into(), - Ok(()) => sgx_status_t::SGX_SUCCESS, - } -} - -#[no_mangle] -pub unsafe extern "C" fn get_rsa_encryption_pubkey( - pubkey: *mut u8, - pubkey_size: u32, -) -> sgx_status_t { - let shielding_key_repository = match GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let rsa_pubkey = match shielding_key_repository.retrieve_pubkey() { - Ok(key) => key, - Err(e) => return e.into(), - }; - - let rsa_pubkey_json = match serde_json::to_string(&rsa_pubkey) { - Ok(k) => k, - Err(x) => { - println!("[Enclave] can't serialize rsa_pubkey {:?} {}", rsa_pubkey, x); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let pubkey_slice = slice::from_raw_parts_mut(pubkey, pubkey_size as usize); - - if let Err(e) = - write_slice_and_whitespace_pad(pubkey_slice, rsa_pubkey_json.as_bytes().to_vec()) - { - return Error::BufferError(e).into() - }; - - sgx_status_t::SGX_SUCCESS -} - -#[no_mangle] -pub unsafe extern "C" fn get_ecc_signing_pubkey(pubkey: *mut u8, pubkey_size: u32) -> sgx_status_t { - let signing_key_repository = match GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let signer_public = match signing_key_repository.retrieve_pubkey() { - Ok(s) => s, - Err(e) => return e.into(), - }; - - debug!("Restored ECC pubkey: {:?}", signer_public); - - let pubkey_slice = slice::from_raw_parts_mut(pubkey, pubkey_size as usize); - pubkey_slice.clone_from_slice(&signer_public); - - sgx_status_t::SGX_SUCCESS -} - -#[no_mangle] -#[cfg_attr(not(feature = "development"), allow(unused_variables))] -pub unsafe extern "C" fn get_bitcoin_wallet_pair(pair: *mut u8, pair_size: u32) -> sgx_status_t { - if_development_or!( - { - let bitcoin_key_repository = match GLOBAL_BITCOIN_KEY_REPOSITORY_COMPONENT.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let keypair = match bitcoin_key_repository.retrieve_key() { - Ok(p) => p, - Err(e) => return e.into(), - }; - - let privkey_slice = slice::from_raw_parts_mut(pair, pair_size as usize); - privkey_slice.clone_from_slice(&keypair.private_bytes()); - - sgx_status_t::SGX_SUCCESS - }, - { - error!("Bitcoin wallet can only be retrieved in non-prod"); - sgx_status_t::SGX_ERROR_UNEXPECTED - } - ) -} - -#[no_mangle] -#[cfg_attr(not(feature = "development"), allow(unused_variables))] -pub unsafe extern "C" fn get_ethereum_wallet_pair(pair: *mut u8, pair_size: u32) -> sgx_status_t { - if_development_or!( - { - let ethereum_key_repository = match GLOBAL_ETHEREUM_KEY_REPOSITORY_COMPONENT.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let keypair = match ethereum_key_repository.retrieve_key() { - Ok(p) => p, - Err(e) => return e.into(), - }; - - let privkey_slice = slice::from_raw_parts_mut(pair, pair_size as usize); - privkey_slice.clone_from_slice(&keypair.private_bytes()); - - sgx_status_t::SGX_SUCCESS - }, - { - error!("Ethereum wallet can only be retrieved in non-prod"); - sgx_status_t::SGX_ERROR_UNEXPECTED - } - ) -} - -#[no_mangle] -#[cfg_attr(not(feature = "development"), allow(unused_variables))] -pub unsafe extern "C" fn get_ton_wallet_pair(pair: *mut u8, pair_size: u32) -> sgx_status_t { - if_development_or!( - { - let ton_key_repository = match GLOBAL_TON_KEY_REPOSITORY_COMPONENT.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let keypair = match ton_key_repository.retrieve_key() { - Ok(p) => p, - Err(e) => return e.into(), - }; - - let privkey_slice = slice::from_raw_parts_mut(pair, pair_size as usize); - privkey_slice.clone_from_slice(&keypair.seed()); - - sgx_status_t::SGX_SUCCESS - }, - { - error!("Ton wallet can only be retrieved in non-prod"); - sgx_status_t::SGX_ERROR_UNEXPECTED - } - ) -} - -#[no_mangle] -pub unsafe extern "C" fn set_nonce( - nonce: *const u32, - parentchain_id: *const u8, - parentchain_id_size: u32, -) -> sgx_status_t { - let id = match ParentchainId::decode_raw(parentchain_id, parentchain_id_size as usize) { - Err(e) => { - error!("Failed to decode parentchain_id: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - Ok(m) => m, - }; - - info!("Setting the nonce of the enclave to: {} for parentchain: {:?}", *nonce, id); - - let nonce_lock = match id { - ParentchainId::Litentry => GLOBAL_INTEGRITEE_PARENTCHAIN_NONCE_CACHE.load_for_mutation(), - ParentchainId::TargetA => GLOBAL_TARGET_A_PARENTCHAIN_NONCE_CACHE.load_for_mutation(), - ParentchainId::TargetB => GLOBAL_TARGET_B_PARENTCHAIN_NONCE_CACHE.load_for_mutation(), - }; - - match nonce_lock { - Ok(mut nonce_guard) => *nonce_guard = Nonce(*nonce), - Err(e) => { - error!("Failed to set {:?} parentchain nonce in enclave: {:?}", id, e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - sgx_status_t::SGX_SUCCESS -} - -#[no_mangle] -pub unsafe extern "C" fn set_node_metadata( - node_metadata: *const u8, - node_metadata_size: u32, - parentchain_id: *const u8, - parentchain_id_size: u32, -) -> sgx_status_t { - let id = match ParentchainId::decode_raw(parentchain_id, parentchain_id_size as usize) { - Err(e) => { - error!("Failed to decode parentchain_id: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - Ok(m) => m, - }; - - let metadata = match NodeMetadata::decode_raw(node_metadata, node_metadata_size as usize) { - Err(e) => { - error!("Failed to decode node metadata: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - Ok(m) => m, - }; - - info!("Setting node meta data for parentchain: {:?}", id); - - let node_metadata_repository = match id { - ParentchainId::Litentry => get_node_metadata_repository_from_integritee_solo_or_parachain(), - ParentchainId::TargetA => get_node_metadata_repository_from_target_a_solo_or_parachain(), - ParentchainId::TargetB => get_node_metadata_repository_from_target_b_solo_or_parachain(), - }; - - match node_metadata_repository { - Ok(repo) => repo.set_metadata(metadata), - Err(e) => { - error!("Could not get {:?} parentchain component: {:?}", id, e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - trace!("Successfully set the node meta data"); - - sgx_status_t::SGX_SUCCESS -} - -#[no_mangle] -pub unsafe extern "C" fn publish_wallets() -> sgx_status_t { - if let Err(e) = initialization::publish_wallets() { - error!("Failed to publish generated wallets: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - } - - sgx_status_t::SGX_SUCCESS -} - -#[no_mangle] -pub unsafe extern "C" fn finish_enclave_init() -> sgx_status_t { - if let Err(e) = initialization::finish_enclave_init() { - error!("Failed to finish enclave init: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - } - - sgx_status_t::SGX_SUCCESS -} - -#[no_mangle] -pub unsafe extern "C" fn init_wallets( - encoded_base_dir_str: *const u8, - encoded_base_dir_size: u32, -) -> sgx_status_t { - let base_dir = match String::decode(&mut slice::from_raw_parts( - encoded_base_dir_str, - encoded_base_dir_size as usize, - )) - .map_err(Error::Codec) - { - Ok(b) => b, - Err(e) => return e.into(), - }; - - info!("Setting base_dir to {}", base_dir); - - let path = PathBuf::from(base_dir); - // Litentry: the default value here is only for clippy checking - BASE_PATH.set(path.clone()).unwrap_or(()); - - if let Err(e) = initialization::init_wallets(path) { - error!("Failed to init wallets: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - } - - sgx_status_t::SGX_SUCCESS -} - -/// Call this once at worker startup to initialize the TOP pool and direct invocation RPC server. -/// -/// This function will run the RPC server on the same thread as it is called and will loop there. -/// That means that this function will not return as long as the RPC server is running. The calling -/// code should therefore spawn a new thread when calling this function. -#[no_mangle] -pub unsafe extern "C" fn init_direct_invocation_server( - server_addr: *const u8, - server_addr_size: usize, -) -> sgx_status_t { - let mut server_addr_encoded = slice::from_raw_parts(server_addr, server_addr_size); - - let server_addr = match String::decode(&mut server_addr_encoded) { - Ok(s) => s, - Err(e) => { - error!("Decoding RPC server address failed. Error: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - if let Err(e) = initialization::init_direct_invocation_server(server_addr) { - error!("Failed to initialize direct invocation server: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - } - - sgx_status_t::SGX_SUCCESS -} - -#[no_mangle] -pub unsafe extern "C" fn init_parentchain_components( - params: *const u8, - params_size: usize, - latest_header: *mut u8, - latest_header_size: usize, -) -> sgx_status_t { - let encoded_params = slice::from_raw_parts(params, params_size); - let latest_header_slice = slice::from_raw_parts_mut(latest_header, latest_header_size); - - match init_parentchain_params_internal(encoded_params.to_vec(), latest_header_slice) { - Ok(()) => sgx_status_t::SGX_SUCCESS, - Err(e) => e.into(), - } -} - -/// Initializes the parentchain components and writes the latest header into the `latest_header` slice. -fn init_parentchain_params_internal(params: Vec, latest_header: &mut [u8]) -> Result<()> { - use initialization::parentchain::init_parentchain_components; - - let encoded_latest_header = init_parentchain_components(get_base_path()?, params)?; - - write_slice_and_whitespace_pad(latest_header, encoded_latest_header)?; - - Ok(()) -} - -#[no_mangle] -pub unsafe extern "C" fn init_shard(shard: *const u8, shard_size: u32) -> sgx_status_t { - let shard_identifier = - ShardIdentifier::from_slice(slice::from_raw_parts(shard, shard_size as usize)); - - if let Err(e) = initialization::init_shard(shard_identifier) { - error!("Failed to initialize shard ({:?}): {:?}", shard_identifier, e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - } - - sgx_status_t::SGX_SUCCESS -} - -#[no_mangle] -pub unsafe extern "C" fn migrate_shard(new_shard: *const u8, shard_size: u32) -> sgx_status_t { - let shard_identifier = - ShardIdentifier::from_slice(slice::from_raw_parts(new_shard, shard_size as usize)); - - if let Err(e) = initialization::migrate_shard(shard_identifier) { - error!("Failed to migrate shard ({:?}): {:?}", shard_identifier, e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - } - - sgx_status_t::SGX_SUCCESS -} - -#[no_mangle] -pub unsafe extern "C" fn sync_parentchain( - blocks_to_sync: *const u8, - blocks_to_sync_size: usize, - events_to_sync: *const u8, - events_to_sync_size: usize, - events_proofs_to_sync: *const u8, - events_proofs_to_sync_size: usize, - parentchain_id: *const u8, - parentchain_id_size: u32, - immediate_import: c_int, -) -> sgx_status_t { - if let Err(e) = sync_parentchain_internal( - blocks_to_sync, - blocks_to_sync_size, - events_to_sync, - events_to_sync_size, - events_proofs_to_sync, - events_proofs_to_sync_size, - parentchain_id, - parentchain_id_size, - immediate_import == 1, - ) { - error!("Error synching parentchain: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - } - - sgx_status_t::SGX_SUCCESS -} - -#[allow(clippy::too_many_arguments)] -unsafe fn sync_parentchain_internal( - blocks_to_sync: *const u8, - blocks_to_sync_size: usize, - events_to_sync: *const u8, - events_to_sync_size: usize, - events_proofs_to_sync: *const u8, - events_proofs_to_sync_size: usize, - parentchain_id: *const u8, - parentchain_id_size: u32, - immediate_import: bool, -) -> Result<()> { - let blocks_to_sync = Vec::::decode_raw(blocks_to_sync, blocks_to_sync_size)?; - let events_to_sync = Vec::>::decode_raw(events_to_sync, events_to_sync_size)?; - let events_proofs_to_sync = - Vec::::decode_raw(events_proofs_to_sync, events_proofs_to_sync_size)?; - let parentchain_id = ParentchainId::decode_raw(parentchain_id, parentchain_id_size as usize)?; - - if !events_proofs_to_sync.is_empty() { - let blocks_to_sync_merkle_roots: Vec = - blocks_to_sync.iter().map(|block| block.block.header.state_root).collect(); - // fixme: vulnerability! https://github.com/integritee-network/worker/issues/1518 - // until fixed properly, we deactivate the panic upon error altogether in the scope of #1547 - if let Err(e) = validate_events(&events_proofs_to_sync, &blocks_to_sync_merkle_roots) { - warn!("ignoring event validation error {:?}", e); - // return e.into() - } - } - dispatch_parentchain_blocks_for_import( - blocks_to_sync, - events_to_sync, - &parentchain_id, - immediate_import, - ) -} - -#[no_mangle] -pub unsafe extern "C" fn ignore_parentchain_block_import_validation_until( - until: *const u32, -) -> sgx_status_t { - let va = match get_validator_accessor_from_integritee_solo_or_parachain() { - Ok(r) => r, - Err(e) => { - error!("Can't get validator accessor: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let _ = va.execute_mut_on_validator(|v| v.set_ignore_validation_until(*until)); - - sgx_status_t::SGX_SUCCESS -} - -/// Dispatch the parentchain blocks for import. -/// Depending on the worker mode, a different dispatcher is used: -/// -/// * An immediate dispatcher will immediately import any parentchain blocks and execute -/// the corresponding extrinsics (offchain-worker executor). -/// * The sidechain uses a triggered dispatcher, where the import of a parentchain block is -/// synchronized and triggered by the sidechain block production cycle. -/// -fn dispatch_parentchain_blocks_for_import( - blocks_to_sync: Vec, - events_to_sync: Vec>, - id: &ParentchainId, - immediate_import: bool, -) -> Result<()> { - trace!( - "[{:?}] Dispatching Import of {} blocks and {} events", - id, - blocks_to_sync.len(), - events_to_sync.len() - ); - match id { - ParentchainId::Litentry => { - if let Ok(handler) = GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT.get() { - handler.import_dispatcher.dispatch_import( - blocks_to_sync, - events_to_sync, - immediate_import, - )?; - } else if let Ok(handler) = GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT.get() { - handler.import_dispatcher.dispatch_import( - blocks_to_sync, - events_to_sync, - immediate_import, - )?; - } else { - return Err(Error::NoLitentryParentchainAssigned) - }; - }, - ParentchainId::TargetA => { - if let Ok(handler) = GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT.get() { - handler.import_dispatcher.dispatch_import( - blocks_to_sync, - events_to_sync, - immediate_import, - )?; - } else if let Ok(handler) = GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT.get() { - handler.import_dispatcher.dispatch_import( - blocks_to_sync, - events_to_sync, - immediate_import, - )?; - } else { - return Err(Error::NoTargetAParentchainAssigned) - }; - }, - ParentchainId::TargetB => { - if let Ok(handler) = GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT.get() { - handler.import_dispatcher.dispatch_import( - blocks_to_sync, - events_to_sync, - immediate_import, - )?; - } else if let Ok(handler) = GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT.get() { - handler.import_dispatcher.dispatch_import( - blocks_to_sync, - events_to_sync, - immediate_import, - )?; - } else { - return Err(Error::NoTargetBParentchainAssigned) - }; - }, - } - - Ok(()) -} - -/// Validates the events coming from the parentchain -fn validate_events( - events_proofs: &Vec, - blocks_merkle_roots: &Vec, -) -> Result<()> { - debug!( - "Validating events, events_proofs_length: {:?}, blocks_merkle_roots_lengths: {:?}", - events_proofs.len(), - blocks_merkle_roots.len() - ); - - if events_proofs.len() != blocks_merkle_roots.len() { - return Err(Error::ParentChainSync) - } - - let events_key = itp_storage::storage_value_key("System", "Events"); - - let validated_events: Result>> = events_proofs - .iter() - .zip(blocks_merkle_roots.iter()) - .map(|(proof, root)| { - StorageProofChecker::::check_proof( - *root, - events_key.as_slice(), - proof.clone(), - ) - .ok() - .flatten() - .ok_or_else(|| Error::ParentChainValidation(itp_storage::Error::WrongValue)) - }) - .collect(); - - let _ = validated_events?; - - Ok(()) -} - -// This is required, because `ring` / `ring-xous` would not compile without it non-release (debug) mode. -// See #1200 for more details. -#[cfg(debug_assertions)] -#[no_mangle] -pub extern "C" fn __assert_fail( - __assertion: *const u8, - __file: *const u8, - __line: u32, - __function: *const u8, -) -> ! { - use core::intrinsics::abort; - abort() -} diff --git a/tee-worker/bitacross/enclave-runtime/src/ocall/attestation_ocall.rs b/tee-worker/bitacross/enclave-runtime/src/ocall/attestation_ocall.rs deleted file mode 100644 index 3a3abbae9e..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/ocall/attestation_ocall.rs +++ /dev/null @@ -1,275 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::ocall::{ffi, OcallApi}; -use frame_support::ensure; -use itp_ocall_api::EnclaveAttestationOCallApi; -use lazy_static::lazy_static; -use log::*; -use sgx_tse::rsgx_create_report; -use sgx_types::*; -use std::{ptr, sync::Arc, vec::Vec}; - -use std::sync::SgxRwLock as RwLock; - -const RET_QUOTE_BUF_LEN: usize = 2048; - -lazy_static! { - /// Global cache of MRENCLAVE - /// will never change at runtime but must be initialized at runtime - static ref MY_MRENCLAVE: RwLock> = RwLock::new(Default::default()); -} - -#[derive(Default, Copy, Clone, Debug)] -pub struct MrEnclave { - pub maybe_mrenclave: Option, -} - -impl MrEnclave { - pub fn current() -> SgxResult> { - Ok(MY_MRENCLAVE - .read() - .map_err(|e| { - error!("fetching current value of MR_ENCLAVE lazy static failed: {:?}", e); - sgx_status_t::SGX_ERROR_UNEXPECTED - })? - .clone()) - } - pub fn make_current(self) -> SgxResult<()> { - *MY_MRENCLAVE.write().map_err(|e| { - error!("writing current value of MR_ENCLAVE lazy static failed: {:?}", e); - sgx_status_t::SGX_ERROR_UNEXPECTED - })? = Arc::new(self); - Ok(()) - } -} - -impl EnclaveAttestationOCallApi for OcallApi { - fn sgx_init_quote(&self) -> SgxResult<(sgx_target_info_t, sgx_epid_group_id_t)> { - let mut ti: sgx_target_info_t = sgx_target_info_t::default(); - let mut eg: sgx_epid_group_id_t = sgx_epid_group_id_t::default(); - let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; - - let res = unsafe { - ffi::ocall_sgx_init_quote( - &mut rt as *mut sgx_status_t, - &mut ti as *mut sgx_target_info_t, - &mut eg as *mut sgx_epid_group_id_t, - ) - }; - - ensure!(res == sgx_status_t::SGX_SUCCESS, res); - ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); - - Ok((ti, eg)) - } - - fn get_ias_socket(&self) -> SgxResult { - let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; - let mut ias_sock: i32 = 0; - - let res = unsafe { - ffi::ocall_get_ias_socket(&mut rt as *mut sgx_status_t, &mut ias_sock as *mut i32) - }; - - ensure!(res == sgx_status_t::SGX_SUCCESS, res); - ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); - - Ok(ias_sock) - } - - fn get_quote( - &self, - sig_rl: Vec, - report: sgx_report_t, - sign_type: sgx_quote_sign_type_t, - spid: sgx_spid_t, - quote_nonce: sgx_quote_nonce_t, - ) -> SgxResult<(sgx_report_t, Vec)> { - let mut qe_report = sgx_report_t::default(); - let mut return_quote_buf = [0u8; RET_QUOTE_BUF_LEN]; - let mut quote_len: u32 = 0; - - let (p_sigrl, sigrl_len) = if sig_rl.is_empty() { - (ptr::null(), 0) - } else { - (sig_rl.as_ptr(), sig_rl.len() as u32) - }; - let p_report = &report as *const sgx_report_t; - let quote_type = sign_type; - - let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; - let p_spid = &spid as *const sgx_spid_t; - let p_nonce = "e_nonce as *const sgx_quote_nonce_t; - let p_qe_report = &mut qe_report as *mut sgx_report_t; - let p_quote = return_quote_buf.as_mut_ptr(); - let maxlen = RET_QUOTE_BUF_LEN as u32; - let p_quote_len = &mut quote_len as *mut u32; - - let result = unsafe { - ffi::ocall_get_quote( - &mut rt as *mut sgx_status_t, - p_sigrl, - sigrl_len, - p_report, - quote_type, - p_spid, - p_nonce, - p_qe_report, - p_quote, - maxlen, - p_quote_len, - ) - }; - - ensure!(result == sgx_status_t::SGX_SUCCESS, result); - ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); - - #[allow(clippy::indexing_slicing)] - let quote_vec: Vec = Vec::from(&return_quote_buf[..quote_len as usize]); - - Ok((qe_report, quote_vec)) - } - - fn get_dcap_quote(&self, report: sgx_report_t, quote_size: u32) -> SgxResult> { - let mut return_quote_buf = vec![0u8; quote_size as usize]; - let p_quote = return_quote_buf.as_mut_ptr(); - let p_report = &report as *const sgx_report_t; - let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; - - let result = unsafe { - ffi::ocall_get_dcap_quote(&mut rt as *mut sgx_status_t, p_report, p_quote, quote_size) - }; - ensure!(result == sgx_status_t::SGX_SUCCESS, result); - ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); - #[allow(clippy::indexing_slicing)] - let quote_vec: Vec = Vec::from(&return_quote_buf[..quote_size as usize]); - Ok(quote_vec) - } - - fn get_qve_report_on_quote( - &self, - quote: Vec, - current_time: i64, - quote_collateral: sgx_ql_qve_collateral_t, - qve_report_info: sgx_ql_qe_report_info_t, - supplemental_data_size: u32, - ) -> SgxResult<(u32, sgx_ql_qv_result_t, sgx_ql_qe_report_info_t, Vec)> { - let mut supplemental_data = vec![0u8; supplemental_data_size as usize]; - let mut qve_report_info_return_value: sgx_ql_qe_report_info_t = qve_report_info; - let mut quote_verification_result = sgx_ql_qv_result_t::SGX_QL_QV_RESULT_UNSPECIFIED; - let mut collateral_expiration_status = 1u32; - let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; - - let result = unsafe { - ffi::ocall_get_qve_report_on_quote( - &mut rt as *mut sgx_status_t, - quote.as_ptr(), - quote.len() as u32, - current_time, - "e_collateral as *const sgx_ql_qve_collateral_t, - &mut collateral_expiration_status as *mut u32, - &mut quote_verification_result as *mut sgx_ql_qv_result_t, - &mut qve_report_info_return_value as *mut sgx_ql_qe_report_info_t, - supplemental_data.as_mut_ptr(), - supplemental_data_size, - ) - }; - ensure!(result == sgx_status_t::SGX_SUCCESS, result); - ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); - - Ok(( - collateral_expiration_status, - quote_verification_result, - qve_report_info_return_value, - supplemental_data.to_vec(), - )) - } - - fn get_update_info( - &self, - platform_info: sgx_platform_info_t, - enclave_trusted: i32, - ) -> SgxResult { - let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; - let mut update_info = sgx_update_info_bit_t::default(); - - let result = unsafe { - ffi::ocall_get_update_info( - &mut rt as *mut sgx_status_t, - &platform_info as *const sgx_platform_info_t, - enclave_trusted, - &mut update_info as *mut sgx_update_info_bit_t, - ) - }; - - // debug logging - if rt != sgx_status_t::SGX_SUCCESS { - warn!("ocall_get_update_info unsuccessful. rt={:?}", rt); - // Curly braces to copy `unaligned_references` of packed fields into properly aligned temporary: - // https://github.com/rust-lang/rust/issues/82523 - debug!("update_info.pswUpdate: {}", { update_info.pswUpdate }); - debug!("update_info.csmeFwUpdate: {}", { update_info.csmeFwUpdate }); - debug!("update_info.ucodeUpdate: {}", { update_info.ucodeUpdate }); - } - - ensure!(result == sgx_status_t::SGX_SUCCESS, result); - ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); - - Ok(update_info) - } - - fn get_mrenclave_of_self(&self) -> SgxResult { - if let Some(mrenclave) = MrEnclave::current()?.maybe_mrenclave { - trace!("found cached MRENCLAVE"); - return Ok(mrenclave) - }; - debug!("initializing MY_MRENCLAVE cache"); - let mrenclave_value = self.get_report_of_self()?.mr_enclave; - MrEnclave { maybe_mrenclave: Some(mrenclave_value) }.make_current()?; - Ok(mrenclave_value) - } -} - -trait GetSgxReport { - fn get_report_of_self(&self) -> SgxResult; -} - -impl GetSgxReport for T { - fn get_report_of_self(&self) -> SgxResult { - // (1) get ti + eg - let init_quote_result = self.sgx_init_quote()?; - - let target_info = init_quote_result.0; - let report_data: sgx_report_data_t = sgx_report_data_t::default(); - - let rep = match rsgx_create_report(&target_info, &report_data) { - Ok(r) => { - debug!( - " [Enclave] Report creation successful. mr_signer.m = {:?}", - r.body.mr_signer.m - ); - r - }, - Err(e) => { - error!(" [Enclave] Report creation failed. {:?}", e); - return Err(e) - }, - }; - Ok(rep.body) - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/ocall/ffi.rs b/tee-worker/bitacross/enclave-runtime/src/ocall/ffi.rs deleted file mode 100644 index b2b163f827..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/ocall/ffi.rs +++ /dev/null @@ -1,111 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use sgx_types::*; - -extern "C" { - pub fn ocall_sgx_init_quote( - ret_val: *mut sgx_status_t, - ret_ti: *mut sgx_target_info_t, - ret_gid: *mut sgx_epid_group_id_t, - ) -> sgx_status_t; - - pub fn ocall_get_ias_socket(ret_val: *mut sgx_status_t, ret_fd: *mut i32) -> sgx_status_t; - - pub fn ocall_get_quote( - ret_val: *mut sgx_status_t, - p_sigrl: *const u8, - sigrl_len: u32, - p_report: *const sgx_report_t, - quote_type: sgx_quote_sign_type_t, - p_spid: *const sgx_spid_t, - p_nonce: *const sgx_quote_nonce_t, - p_qe_report: *mut sgx_report_t, - p_quote: *mut u8, - maxlen: u32, - p_quote_len: *mut u32, - ) -> sgx_status_t; - - pub fn ocall_get_dcap_quote( - ret_val: *mut sgx_status_t, - p_report: *const sgx_report_t, - p_quote: *mut u8, - quote_size: u32, - ) -> sgx_status_t; - - pub fn ocall_get_qve_report_on_quote( - ret_val: *mut sgx_status_t, - p_quote: *const u8, - quote_len: u32, - current_time: i64, - p_quote_collateral: *const sgx_ql_qve_collateral_t, - p_collateral_expiration_status: *mut u32, - p_quote_verification_result: *mut sgx_ql_qv_result_t, - p_qve_report_info: *mut sgx_ql_qe_report_info_t, - p_supplemental_data: *mut u8, - supplemental_data_size: u32, - ) -> sgx_status_t; - - pub fn ocall_get_update_info( - ret_val: *mut sgx_status_t, - platform_blob: *const sgx_platform_info_t, - enclave_trusted: i32, - update_info: *mut sgx_update_info_bit_t, - ) -> sgx_status_t; - - pub fn ocall_worker_request( - ret_val: *mut sgx_status_t, - request: *const u8, - req_size: u32, - parentchain_id: *const u8, - parentchain_id_size: u32, - response: *mut u8, - resp_size: u32, - ) -> sgx_status_t; - - pub fn ocall_update_metric( - ret_val: *mut sgx_status_t, - metric_ptr: *const u8, - metric_size: u32, - ) -> sgx_status_t; - - pub fn ocall_send_to_parentchain( - ret_val: *mut sgx_status_t, - extrinsics: *const u8, - extrinsics_size: u32, - parentchain_id: *const u8, - parentchain_id_size: u32, - watch_until: *const u8, - watch_until_size: u32, - response: *mut u8, - response_size: u32, - ) -> sgx_status_t; - - pub fn ocall_read_ipfs( - ret_val: *mut sgx_status_t, - cid: *const u8, - cid_size: u32, - ) -> sgx_status_t; - - pub fn ocall_write_ipfs( - ret_val: *mut sgx_status_t, - enc_state: *const u8, - enc_state_size: u32, - cid: *mut u8, - cid_size: u32, - ) -> sgx_status_t; -} diff --git a/tee-worker/bitacross/enclave-runtime/src/ocall/ipfs_ocall.rs b/tee-worker/bitacross/enclave-runtime/src/ocall/ipfs_ocall.rs deleted file mode 100644 index d1a5530856..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/ocall/ipfs_ocall.rs +++ /dev/null @@ -1,57 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::ocall::{ffi, OcallApi}; -use frame_support::ensure; -use itp_ocall_api::{EnclaveIpfsOCallApi, IpfsCid}; -use sgx_types::{sgx_status_t, SgxResult}; - -impl EnclaveIpfsOCallApi for OcallApi { - fn write_ipfs(&self, encoded_state: &[u8]) -> SgxResult { - let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; - let mut cid_buf = IpfsCid([0u8; 46]); - - let res = unsafe { - ffi::ocall_write_ipfs( - &mut rt as *mut sgx_status_t, - encoded_state.as_ptr(), - encoded_state.len() as u32, - cid_buf.0.as_mut_ptr(), - cid_buf.0.len() as u32, - ) - }; - - ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); - ensure!(res == sgx_status_t::SGX_SUCCESS, res); - - Ok(cid_buf) - } - - fn read_ipfs(&self, cid: &IpfsCid) -> SgxResult<()> { - let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; - - let res = unsafe { - ffi::ocall_read_ipfs(&mut rt as *mut sgx_status_t, cid.0.as_ptr(), cid.0.len() as u32) - }; - - ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); - ensure!(res == sgx_status_t::SGX_SUCCESS, res); - - Ok(()) - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/ocall/metrics_ocall.rs b/tee-worker/bitacross/enclave-runtime/src/ocall/metrics_ocall.rs deleted file mode 100644 index 0d12dfd7d6..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/ocall/metrics_ocall.rs +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::ocall::{ffi, OcallApi}; -use codec::Encode; -use frame_support::ensure; -use itp_ocall_api::EnclaveMetricsOCallApi; -use sgx_types::{sgx_status_t, SgxResult}; - -impl EnclaveMetricsOCallApi for OcallApi { - fn update_metric(&self, metric: Metric) -> SgxResult<()> { - let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; - let metric_encoded = metric.encode(); - - let res = unsafe { - ffi::ocall_update_metric( - &mut rt as *mut sgx_status_t, - metric_encoded.as_ptr(), - metric_encoded.len() as u32, - ) - }; - - ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); - ensure!(res == sgx_status_t::SGX_SUCCESS, res); - - Ok(()) - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/ocall/mod.rs b/tee-worker/bitacross/enclave-runtime/src/ocall/mod.rs deleted file mode 100644 index f85d82d8b3..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/ocall/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -mod attestation_ocall; -mod ffi; -mod ipfs_ocall; -mod metrics_ocall; -mod on_chain_ocall; - -#[derive(Clone, Debug, Default)] -pub struct OcallApi; diff --git a/tee-worker/bitacross/enclave-runtime/src/ocall/on_chain_ocall.rs b/tee-worker/bitacross/enclave-runtime/src/ocall/on_chain_ocall.rs deleted file mode 100644 index 60ee217585..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/ocall/on_chain_ocall.rs +++ /dev/null @@ -1,226 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::ocall::{ffi, OcallApi}; -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::ensure; -use itp_node_api::api_client::{ExtrinsicReport, XtStatus}; -use itp_ocall_api::{EnclaveOnChainOCallApi, Error, Result}; -use itp_storage::{verify_storage_entries, Error as StorageError}; -use itp_types::{ - parentchain::{AccountId, Index as ParentchainIndex, ParentchainId}, - storage::StorageEntryVerified, - WorkerRequest, WorkerResponse, H256, -}; -use log::*; -use sgx_types::*; -use sp_runtime::{traits::Header, OpaqueExtrinsic}; -use std::{mem::size_of, vec::Vec}; - -impl EnclaveOnChainOCallApi for OcallApi { - fn send_to_parentchain( - &self, - extrinsics: Vec, - parentchain_id: &ParentchainId, - watch_until: Option, - ) -> SgxResult>> { - let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; - let extrinsics_encoded = extrinsics.encode(); - let parentchain_id_encoded = parentchain_id.encode(); - let watch_until_encoded = watch_until.encode(); - let response_size = match watch_until { - Some(_) => extrinsics - .len() - .checked_mul(ExtrinsicReport::::max_encoded_len()) - .ok_or(sgx_status_t::SGX_ERROR_UNEXPECTED)? - .checked_add(size_of::>()) - .ok_or(sgx_status_t::SGX_ERROR_UNEXPECTED)?, - None => size_of::>(), - }; - let mut response: Vec = vec![0; response_size]; - - let res = unsafe { - ffi::ocall_send_to_parentchain( - &mut rt as *mut sgx_status_t, - extrinsics_encoded.as_ptr(), - extrinsics_encoded.len() as u32, - parentchain_id_encoded.as_ptr(), - parentchain_id_encoded.len() as u32, - watch_until_encoded.as_ptr(), - watch_until_encoded.len() as u32, - response.as_mut_ptr(), - response_size as u32, - ) - }; - - ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); - ensure!(res == sgx_status_t::SGX_SUCCESS, res); - - let decoded_response: Vec> = Decode::decode(&mut response.as_slice()) - .map_err(|e| { - error!("Failed to decode ExtrinsicReport: {}", e); - sgx_status_t::SGX_ERROR_UNEXPECTED - })?; - - Ok(decoded_response) - } - - fn worker_request( - &self, - req: Vec, - parentchain_id: &ParentchainId, - ) -> SgxResult>> { - let mut rt: sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; - // Litentry: since #1221 we need 28139 bytes - let mut resp: Vec = vec![0; 4196 * 16]; - let request_encoded = req.encode(); - let parentchain_id_encoded = parentchain_id.encode(); - - let res = unsafe { - ffi::ocall_worker_request( - &mut rt as *mut sgx_status_t, - request_encoded.as_ptr(), - request_encoded.len() as u32, - parentchain_id_encoded.as_ptr(), - parentchain_id_encoded.len() as u32, - resp.as_mut_ptr(), - resp.len() as u32, - ) - }; - - ensure!(rt == sgx_status_t::SGX_SUCCESS, rt); - ensure!(res == sgx_status_t::SGX_SUCCESS, res); - - let decoded_response: Vec> = Decode::decode(&mut resp.as_slice()) - .map_err(|e| { - error!("Failed to decode WorkerResponse: {}", e); - sgx_status_t::SGX_ERROR_UNEXPECTED - })?; - - Ok(decoded_response) - } - - fn get_storage_verified, V: Decode>( - &self, - storage_hash: Vec, - header: &H, - parentchain_id: &ParentchainId, - ) -> Result> { - // the code below seems like an overkill, but it is surprisingly difficult to - // get an owned value from a `Vec` without cloning. - Ok(self - .get_multiple_storages_verified(vec![storage_hash], header, parentchain_id)? - .into_iter() - .next() - .ok_or(StorageError::StorageValueUnavailable)?) - } - - fn get_multiple_storages_verified, V: Decode>( - &self, - storage_hashes: Vec>, - header: &H, - parentchain_id: &ParentchainId, - ) -> Result>> { - let requests = storage_hashes - .into_iter() - .map(|key| WorkerRequest::ChainStorage(key, Some(header.hash()))) - .collect(); - - let storage_entries = self - .worker_request::>(requests, parentchain_id) - .map(|storages| verify_storage_entries(storages, header))??; - - Ok(storage_entries) - } - - fn get_storage_keys>( - &self, - key_prefix: Vec, - header: Option<&H>, - ) -> Result>> { - let header_hash = header.map(|h| h.hash()); - let requests = vec![WorkerRequest::ChainStorageKeys(key_prefix, header_hash)]; - - let responses: Vec>> = self - .worker_request::>(requests, &ParentchainId::Litentry)? - .iter() - .filter_map(|r| match r { - WorkerResponse::ChainStorageKeys(k) => Some(k.clone()), - _ => None, - }) - .collect(); - - // we should only have one response as we only sent one request - let first_response = responses.get(0).ok_or(StorageError::WrongValue)?; - Ok(first_response.clone()) - } - - fn get_storage_keys_paged>( - &self, - key_prefix: Vec, - count: u32, - start_key: Option>, - header: Option<&H>, - ) -> Result>> { - let header_hash = header.map(|h| h.hash()); - let requests = - vec![WorkerRequest::ChainStorageKeysPaged(key_prefix, count, start_key, header_hash)]; - - let responses: Vec>> = self - .worker_request::>(requests, &ParentchainId::Litentry)? - .iter() - .filter_map(|r| match r { - WorkerResponse::ChainStorageKeys(k) => Some(k.clone()), - _ => None, - }) - .collect(); - - // we should only have one response as we only sent one request - let first_response = responses.get(0).ok_or(StorageError::WrongValue)?; - Ok(first_response.clone()) - } - - fn get_header>(&self) -> Result { - let request = vec![WorkerRequest::ChainHeader(None)]; - let responses: Vec = self - .worker_request::>(request, &ParentchainId::Litentry)? - .iter() - .filter_map(|r| match r { - WorkerResponse::ChainHeader(Some(h)) => - Some(Decode::decode(&mut h.as_slice()).ok()?), - _ => None, - }) - .collect(); - - responses.first().cloned().ok_or(Error::ChainCallFailed) - } - - fn get_account_nonce(&self, account_id: AccountId) -> Result { - let request = vec![WorkerRequest::ChainAccountNonce(account_id.encode())]; - let responses: Vec = self - .worker_request::>(request, &ParentchainId::Litentry)? - .iter() - .filter_map(|r| match r { - WorkerResponse::ChainAccountNonce(Some(index)) => Some(*index), - _ => None, - }) - .collect(); - - responses.first().cloned().ok_or(Error::ChainCallFailed) - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/rpc/mod.rs b/tee-worker/bitacross/enclave-runtime/src/rpc/mod.rs deleted file mode 100644 index 5b359ab270..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/rpc/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pub mod rpc_response_channel; -pub mod worker_api_direct; diff --git a/tee-worker/bitacross/enclave-runtime/src/rpc/rpc_response_channel.rs b/tee-worker/bitacross/enclave-runtime/src/rpc/rpc_response_channel.rs deleted file mode 100644 index 7a84fde928..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/rpc/rpc_response_channel.rs +++ /dev/null @@ -1,40 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::initialization::global_components::GLOBAL_WEB_SOCKET_SERVER_COMPONENT; -use itc_direct_rpc_server::{response_channel::ResponseChannel, DirectRpcError}; -use itc_tls_websocket_server::{ConnectionToken, WebSocketResponder}; -use itp_component_container::ComponentGetter; -use std::string::String; - -/// RPC response channel. -/// -/// Uses the web-socket server to send an RPC response/update. -/// In case no server is available or running, the response will be discarded. -#[derive(Default)] -pub struct RpcResponseChannel; - -impl ResponseChannel for RpcResponseChannel { - type Error = DirectRpcError; - - fn respond(&self, token: ConnectionToken, message: String) -> Result<(), Self::Error> { - let web_socket_server = GLOBAL_WEB_SOCKET_SERVER_COMPONENT - .get() - .map_err(|e| DirectRpcError::Other(e.into()))?; - web_socket_server.send_message(token, message).map_err(|e| e.into()) - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/rpc/worker_api_direct.rs b/tee-worker/bitacross/enclave-runtime/src/rpc/worker_api_direct.rs deleted file mode 100644 index e89f840a32..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/rpc/worker_api_direct.rs +++ /dev/null @@ -1,603 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - attestation::{ - generate_dcap_ra_extrinsic_from_quote_internal, - generate_ias_ra_extrinsic_from_der_cert_internal, - }, - initialization::global_components::{ - EnclaveBitcoinKeyRepository, EnclaveEthereumKeyRepository, EnclaveSigningKeyRepository, - EnclaveTonKeyRepository, - }, - std::string::ToString, - utils::get_validator_accessor_from_integritee_solo_or_parachain, -}; -use bc_musig2_ceremony::{generate_aggregated_public_key, PublicKey}; -use bc_signer_registry::SignerRegistryLookup; -use bc_task_sender::{BitAcrossProcessingResult, BitAcrossRequest, BitAcrossRequestSender}; -use codec::Encode; -use core::result::Result; -use futures_sgx::channel::oneshot; -use ita_sgx_runtime::Runtime; -use ita_stf::{Getter, TrustedCallSigned}; -use itc_parentchain::light_client::{concurrent_access::ValidatorAccess, ExtrinsicSender}; -use itp_ocall_api::EnclaveAttestationOCallApi; -use itp_primitives_cache::{GetPrimitives, GLOBAL_PRIMITIVES_CACHE}; -use itp_rpc::RpcReturnValue; -use itp_sgx_crypto::{ - ed25519_derivation::DeriveEd25519, - key_repository::{AccessKey, AccessPubkey}, - ShieldingCryptoDecrypt, ShieldingCryptoEncrypt, -}; -use itp_stf_executor::getter_executor::ExecuteGetter; -use itp_stf_primitives::types::KeyPair; -use itp_top_pool_author::traits::AuthorApi; -use itp_types::{DirectRequestStatus, RsaRequest, ShardIdentifier, H256}; -use itp_utils::{FromHexPrefixed, ToHexPrefixed}; -use jsonrpc_core::{serde_json::json, IoHandler, Params, Value}; -use lc_direct_call::DirectCall; -use litentry_primitives::{Identity, PlainRequest}; -use log::{debug, error}; -use sgx_crypto_helper::rsa3072::Rsa3072PubKey; -use sp_core::crypto::Pair; -use sp_runtime::OpaqueExtrinsic; -use std::{borrow::ToOwned, boxed::Box, format, str, string::String, sync::Arc, vec::Vec}; - -fn compute_hex_encoded_return_error(error_msg: &str) -> String { - RpcReturnValue::from_error_message(error_msg).to_hex() -} - -fn get_all_rpc_methods_string(io_handler: &IoHandler) -> String { - let method_string = io_handler - .iter() - .map(|rp_tuple| rp_tuple.0.to_owned()) - .collect::>() - .join(", "); - - format!("methods: [{}]", method_string) -} - -#[allow(clippy::too_many_arguments)] -pub fn public_api_rpc_handler( - top_pool_author: Arc, - getter_executor: Arc, - shielding_key: Arc, - ocall_api: Arc, - signing_key_repository: Arc, - bitcoin_key_repository: Arc, - ethereum_key_repository: Arc, - ton_key_repository: Arc, - signer_lookup: Arc, -) -> IoHandler -where - Author: AuthorApi + Send + Sync + 'static, - GetterExecutor: ExecuteGetter + Send + Sync + 'static, - AccessShieldingKey: AccessPubkey + AccessKey + Send + Sync + 'static, - ::KeyType: - ShieldingCryptoDecrypt + ShieldingCryptoEncrypt + DeriveEd25519 + Send + Sync + 'static, - OcallApi: EnclaveAttestationOCallApi + Send + Sync + 'static, - SR: SignerRegistryLookup + Send + Sync + 'static, -{ - let mut io = IoHandler::new(); - - let signer_lookup_cloned = signer_lookup.clone(); - let shielding_key_cloned = shielding_key.clone(); - let signing_key_repository_cloned = signing_key_repository.clone(); - let ocall_api_cloned = ocall_api.clone(); - io.add_sync_method("author_getShieldingKey", move |_: Params| { - debug!("worker_api_direct rpc was called: author_getShieldingKey"); - let rsa_pubkey = match shielding_key_cloned.retrieve_pubkey() { - Ok(key) => key, - Err(status) => { - let error_msg: String = format!("Could not get rsa pubkey due to: {}", status); - return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) - }, - }; - - let rsa_pubkey_json = match serde_json::to_string(&rsa_pubkey) { - Ok(k) => k, - Err(x) => { - let error_msg: String = - format!("[Enclave] can't serialize rsa_pubkey {:?} {}", rsa_pubkey, x); - return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) - }, - }; - let json_value = - RpcReturnValue::new(rsa_pubkey_json.encode(), false, DirectRequestStatus::Ok); - Ok(json!(json_value.to_hex())) - }); - - // author_getEnclaveSignerAccount - let rsa_pubkey_name: &str = "author_getEnclaveSignerAccount"; - io.add_sync_method(rsa_pubkey_name, move |_: Params| { - let enclave_signer_public_key = match shielding_key - .retrieve_key() - .and_then(|keypair| keypair.derive_ed25519().map(|keypair| keypair.public().to_hex())) - { - Err(e) => { - let error_msg: String = format!("{:?}", e); - return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) - }, - Ok(public_key) => public_key, - }; - debug!("[Enclave] enclave_signer_public_key: {:?}", enclave_signer_public_key); - - let json_value = RpcReturnValue { - do_watch: false, - value: enclave_signer_public_key.encode(), - status: DirectRequestStatus::Ok, - }; - - Ok(json!(json_value.to_hex())) - }); - - // Submit BitAcross Request - io.add_method("bitacross_submitRequest", move |params: Params| { - debug!("worker_api_direct rpc was called: bitacross_submitRequest"); - async move { - let json_value = match bitacross_task_create_inner(params).await { - Ok(value) => value.to_hex(), - Err(error) => RpcReturnValue { - value: error, - do_watch: false, - status: DirectRequestStatus::Error, - } - .to_hex(), - }; - Ok(json!(json_value)) - } - }); - - // btc sign task data share - io.add_sync_method("bitacross_btcDataShare", move |params: Params| { - debug!("worker_api_direct rpc was called: bitacross_btcDataShare"); - let json_value = match bitacross_data_share_inner(params) { - Ok(value) => value.to_hex(), - Err(error) => - RpcReturnValue { value: error, do_watch: false, status: DirectRequestStatus::Error } - .to_hex(), - }; - Ok(json!(json_value)) - }); - - io.add_method("bitacross_checkSignBitcoin", move |_params: Params| { - debug!("worker_api_direct rpc was called: bitacross_checkSignBitcoin"); - let request = prepare_check_sign_bitcoin_request( - signing_key_repository_cloned.as_ref(), - ocall_api_cloned.as_ref(), - ); - async move { - if let Ok(request) = request { - let params = Params::Array(vec![jsonrpc_core::Value::String(request.to_hex())]); - let json_value = match bitacross_task_create_inner(params).await { - Ok(value) => value.to_hex(), - Err(error) => RpcReturnValue { - value: error, - do_watch: false, - status: DirectRequestStatus::Error, - } - .to_hex(), - }; - Ok(json!(json_value)) - } else { - Ok(json!(RpcReturnValue { - value: vec![], - do_watch: false, - status: DirectRequestStatus::Error, - } - .to_hex())) - } - } - }); - - io.add_sync_method("bitacross_aggregatedPublicKey", move |_: Params| { - debug!("worker_api_direct rpc was called: bitacross_aggregatedPublicKey"); - if let Ok(keys) = signer_lookup - .get_all() - .iter() - .map(|(_, pub_key)| PublicKey::from_sec1_bytes(pub_key)) - .collect() - { - let key_bytes = generate_aggregated_public_key(keys).to_sec1_bytes().to_vec(); - let json_value = RpcReturnValue::new(key_bytes, false, DirectRequestStatus::Ok); - Ok(json!(json_value.to_hex())) - } else { - Ok(json!(compute_hex_encoded_return_error("Could not produce aggregate key"))) - } - }); - - io.add_sync_method("bitacross_getPublicKeys", move |_: Params| { - debug!("worker_api_direct rpc was called: bitacross_getPublicKeys"); - - let signer = match signing_key_repository.retrieve_key() { - Ok(pair) => pair.public().0.to_hex(), - Err(_e) => compute_hex_encoded_return_error("Can not obtain signer key"), - }; - - let bitcoin_key = match bitcoin_key_repository.retrieve_key() { - Ok(pair) => pair.public_bytes().to_hex(), - Err(_e) => compute_hex_encoded_return_error("Can not obtain bitcoin key"), - }; - - let ethereum_key = match ethereum_key_repository.retrieve_key() { - Ok(pair) => pair.public_bytes().to_hex(), - Err(_e) => compute_hex_encoded_return_error("Can not obtain ethereum key"), - }; - - let ton_key = match ton_key_repository.retrieve_key() { - Ok(pair) => pair.public().0.to_hex(), - Err(_e) => compute_hex_encoded_return_error("Can not obtain ton key"), - }; - - Ok(json!({ - "signer": signer, - "bitcoin_key": bitcoin_key, - "ethereum_key": ethereum_key, - "ton_key": ton_key - })) - }); - - io.add_sync_method("bitacross_getSealedSigners", move |_: Params| { - debug!("worker_api_direct rpc was called: bitacross_getSealedSigners"); - - let keys: Vec = signer_lookup_cloned - .get_all() - .iter() - .map(|(signer, pub_key)| { - json!({ - "signer": signer.as_ref().to_vec(), - "key": pub_key.to_vec() - }) - }) - .collect(); - Ok(json!(keys)) - }); - - io.add_sync_method("author_getShard", move |_: Params| { - debug!("worker_api_direct rpc was called: author_getShard"); - let shard = top_pool_author.list_handled_shards().first().copied().unwrap_or_default(); - let json_value = RpcReturnValue::new(shard.encode(), false, DirectRequestStatus::Ok); - Ok(json!(json_value.to_hex())) - }); - - io.add_sync_method("author_getMuRaUrl", move |_: Params| { - debug!("worker_api_direct rpc was called: author_getMuRaUrl"); - let url = match GLOBAL_PRIMITIVES_CACHE.get_mu_ra_url() { - Ok(url) => url, - Err(status) => { - let error_msg: String = format!("Could not get mu ra url due to: {}", status); - return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) - }, - }; - - let json_value = RpcReturnValue::new(url.encode(), false, DirectRequestStatus::Ok); - Ok(json!(json_value.to_hex())) - }); - - io.add_sync_method("author_getUntrustedUrl", move |_: Params| { - debug!("worker_api_direct rpc was called: author_getUntrustedUrl"); - let url = match GLOBAL_PRIMITIVES_CACHE.get_untrusted_worker_url() { - Ok(url) => url, - Err(status) => { - let error_msg: String = format!("Could not get untrusted url due to: {}", status); - return Ok(json!(compute_hex_encoded_return_error(error_msg.as_str()))) - }, - }; - - let json_value = RpcReturnValue::new(url.encode(), false, DirectRequestStatus::Ok); - Ok(json!(json_value.to_hex())) - }); - - io.add_sync_method("chain_subscribeAllHeads", |_: Params| { - debug!("worker_api_direct rpc was called: chain_subscribeAllHeads"); - let parsed = "world"; - Ok(Value::String(format!("hello, {}", parsed))) - }); - - io.add_sync_method("state_getMetadata", |_: Params| { - debug!("worker_api_direct rpc was called: tate_getMetadata"); - let metadata = Runtime::metadata(); - let json_value = RpcReturnValue::new(metadata.into(), false, DirectRequestStatus::Ok); - Ok(json!(json_value.to_hex())) - }); - - io.add_sync_method("state_getRuntimeVersion", |_: Params| { - debug!("worker_api_direct rpc was called: state_getRuntimeVersion"); - let parsed = "world"; - Ok(Value::String(format!("hello, {}", parsed))) - }); - - io.add_sync_method("state_executeGetter", move |params: Params| { - debug!("worker_api_direct rpc was called: state_executeGetter"); - let json_value = match execute_getter_inner(getter_executor.as_ref(), params) { - Ok(state_getter_value) => RpcReturnValue { - do_watch: false, - value: state_getter_value.encode(), - status: DirectRequestStatus::Ok, - } - .to_hex(), - Err(error) => compute_hex_encoded_return_error(error.as_str()), - }; - Ok(json!(json_value)) - }); - - io.add_sync_method("attesteer_forwardDcapQuote", move |params: Params| { - debug!("worker_api_direct rpc was called: attesteer_forwardDcapQuote"); - let json_value = match forward_dcap_quote_inner(params) { - Ok(val) => RpcReturnValue { - do_watch: false, - value: val.encode(), - status: DirectRequestStatus::Ok, - } - .to_hex(), - Err(error) => compute_hex_encoded_return_error(error.as_str()), - }; - - Ok(json!(json_value)) - }); - - io.add_sync_method("attesteer_forwardIasAttestationReport", move |params: Params| { - debug!("worker_api_direct rpc was called: attesteer_forwardIasAttestationReport"); - let json_value = match attesteer_forward_ias_attestation_report_inner(params) { - Ok(val) => RpcReturnValue { - do_watch: false, - value: val.encode(), - status: DirectRequestStatus::Ok, - } - .to_hex(), - Err(error) => compute_hex_encoded_return_error(error.as_str()), - }; - Ok(json!(json_value)) - }); - - // state_getMrenclave - io.add_sync_method("state_getMrenclave", move |_: Params| { - let json_value = match ocall_api.get_mrenclave_of_self() { - Ok(m) => RpcReturnValue { - do_watch: false, - value: m.m.encode(), - status: DirectRequestStatus::Ok, - } - .to_hex(), - Err(e) => { - let error_msg: String = format!("Could not get current mrenclave due to: {}", e); - compute_hex_encoded_return_error(error_msg.as_str()) - }, - }; - Ok(json!(json_value)) - }); - - // system_health - io.add_sync_method("system_health", |_: Params| { - debug!("worker_api_direct rpc was called: system_health"); - let parsed = "world"; - Ok(Value::String(format!("hello, {}", parsed))) - }); - - io.add_sync_method("system_name", |_: Params| { - debug!("worker_api_direct rpc was called: system_name"); - let parsed = "world"; - Ok(Value::String(format!("hello, {}", parsed))) - }); - - io.add_sync_method("system_version", |_: Params| { - debug!("worker_api_direct rpc was called: system_version"); - let parsed = "world"; - Ok(Value::String(format!("hello, {}", parsed))) - }); - - let rpc_methods_string = get_all_rpc_methods_string(&io); - io.add_sync_method("rpc_methods", move |_: Params| { - debug!("worker_api_direct rpc was called: rpc_methods"); - Ok(Value::String(rpc_methods_string.to_owned())) - }); - - io -} - -fn prepare_check_sign_bitcoin_request( - signing_key_repository: &EnclaveSigningKeyRepository, - ocall_api: &OcallApi, -) -> Result -where - OcallApi: EnclaveAttestationOCallApi + Send + Sync + 'static, -{ - let signer = signing_key_repository.retrieve_key().map_err(|_| ())?; - let identity = Identity::Substrate(signer.public().into()); - let mrenclave = ocall_api.get_mrenclave_of_self().map_err(|_| ())?.m; - let call = DirectCall::CheckSignBitcoin(identity).sign( - &KeyPair::Ed25519(Box::new(signer)), - &mrenclave, - &mrenclave.into(), - ); - Ok(PlainRequest { shard: mrenclave.into(), payload: call.encode() }) -} - -// Litentry: TODO - we still use `RsaRequest` for trusted getter, as the result -// in unencrypted, see P-183 -fn execute_getter_inner( - getter_executor: &GE, - params: Params, -) -> Result>, String> { - let hex_encoded_params = params.parse::>().map_err(|e| format!("{:?}", e))?; - - let param = &hex_encoded_params.get(0).ok_or("Could not get first param")?; - let request = RsaRequest::from_hex(param).map_err(|e| format!("{:?}", e))?; - - let shard: ShardIdentifier = request.shard; - let encoded_trusted_getter: Vec = request.payload.to_vec(); - - let getter_result = getter_executor - .execute_getter(&shard, encoded_trusted_getter) - .map_err(|e| format!("{:?}", e))?; - - Ok(getter_result) -} - -fn forward_dcap_quote_inner(params: Params) -> Result { - let hex_encoded_params = params.parse::>().map_err(|e| format!("{:?}", e))?; - - if hex_encoded_params.len() != 1 { - return Err(format!( - "Wrong number of arguments for IAS attestation report forwarding: {}, expected: {}", - hex_encoded_params.len(), - 1 - )) - } - - let param = &hex_encoded_params.get(0).ok_or("Could not get first param")?; - let encoded_quote_to_forward: Vec = - litentry_hex_utils::decode_hex(param).map_err(|e| format!("{:?}", e))?; - - let url = String::new(); - let ext = generate_dcap_ra_extrinsic_from_quote_internal( - url.as_bytes().to_vec(), - &encoded_quote_to_forward, - ) - .map_err(|e| format!("{:?}", e))?; - - let validator_access = get_validator_accessor_from_integritee_solo_or_parachain() - .map_err(|e| format!("{:?}", e))?; - validator_access - .execute_mut_on_validator(|v| v.send_extrinsics(vec![ext.clone()])) - .map_err(|e| format!("{:?}", e))?; - - Ok(ext) -} - -fn attesteer_forward_ias_attestation_report_inner( - params: Params, -) -> Result { - let hex_encoded_params = params.parse::>().map_err(|e| format!("{:?}", e))?; - - if hex_encoded_params.len() != 1 { - return Err(format!( - "Wrong number of arguments for IAS attestation report forwarding: {}, expected: {}", - hex_encoded_params.len(), - 1 - )) - } - - let param = &hex_encoded_params.get(0).ok_or("Could not get first param")?; - let ias_attestation_report = - litentry_hex_utils::decode_hex(param).map_err(|e| format!("{:?}", e))?; - - let url = String::new(); - let ext = generate_ias_ra_extrinsic_from_der_cert_internal( - url.as_bytes().to_vec(), - &ias_attestation_report, - false, - ) - .map_err(|e| format!("{:?}", e))?; - - let validator_access = get_validator_accessor_from_integritee_solo_or_parachain() - .map_err(|e| format!("{:?}", e))?; - validator_access - .execute_mut_on_validator(|v| v.send_extrinsics(vec![ext.clone()])) - .map_err(|e| format!("{:?}", e))?; - - Ok(ext) -} - -pub enum BitacrossRequestError { - DirectCallError(Vec), - Other(Vec), -} - -async fn bitacross_task_create_inner(params: Params) -> Result> { - let request = get_request_from_params(params)?; - - let bit_across_request_sender = BitAcrossRequestSender::new(); - let (sender, receiver) = oneshot::channel::>>(); - - bit_across_request_sender.send(BitAcrossRequest::Request(request, sender))?; - - // we only expect one response, hence no loop - match receiver.await { - Ok(Ok(response)) => match response { - BitAcrossProcessingResult::Ok(response_payload) => { - println!("BitAcrossProcessingResult::Ok"); - - Ok(RpcReturnValue { - do_watch: false, - value: response_payload, - status: DirectRequestStatus::Ok, - }) - }, - BitAcrossProcessingResult::Submitted(hash) => { - println!("BitAcrossProcessingResult::Submitted"); - Ok(RpcReturnValue { - do_watch: true, - value: vec![], - status: DirectRequestStatus::Processing(hash.into()), - }) - }, - }, - Ok(Err(e)) => { - error!("Error while processing request: {:?}", e); - - Err(e) - }, - Err(_) => { - println!("Got Err"); - // This case will only happen if the sender has been dropped - Err(vec![]) - }, - } -} - -fn bitacross_data_share_inner(params: Params) -> Result> { - let request = get_request_from_params(params)?; - let bit_across_request_sender = BitAcrossRequestSender::new(); - bit_across_request_sender.send(BitAcrossRequest::ShareCeremonyData(request))?; - Ok(RpcReturnValue { do_watch: false, value: vec![], status: DirectRequestStatus::Ok }) -} - -// we expect our `params` to be "by-position array" -// see https://www.jsonrpc.org/specification#parameter_structures -fn get_request_from_params(params: Params) -> Result { - let s_vec = params.parse::>().map_err(|e| format!("{}", e))?; - - let s = s_vec.get(0).ok_or_else(|| "Empty params".to_string())?; - debug!("Request payload: {}", s); - - let request = PlainRequest::from_hex(s) - .map_err(|e| format!("PlainRequest construction error: {:?}", e))?; - Ok(request) -} - -#[cfg(feature = "test")] -pub mod tests { - use super::*; - use std::string::ToString; - - pub fn test_given_io_handler_methods_then_retrieve_all_names_as_string() { - let mut io = IoHandler::new(); - let method_names: [&str; 4] = ["method1", "another_method", "fancy_thing", "solve_all"]; - - for method_name in method_names.iter() { - io.add_sync_method(method_name, |_: Params| Ok(Value::String("".to_string()))); - } - - let method_string = get_all_rpc_methods_string(&io); - - for method_name in method_names.iter() { - assert!(method_string.contains(method_name)); - } - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/shard_config.rs b/tee-worker/bitacross/enclave-runtime/src/shard_config.rs deleted file mode 100644 index 126e5c2503..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/shard_config.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* - Copyright 2021 Integritee AG - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -use crate::error::Result as EnclaveResult; -use itp_types::ShardIdentifier; -use log::*; - -pub(crate) fn init_shard_config(_shard: ShardIdentifier) -> EnclaveResult<()> { - warn!("TODO(Litentry P-627): init_shard_config"); - Ok(()) -} diff --git a/tee-worker/bitacross/enclave-runtime/src/shard_creation_info.rs b/tee-worker/bitacross/enclave-runtime/src/shard_creation_info.rs deleted file mode 100644 index 4eef7be830..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/shard_creation_info.rs +++ /dev/null @@ -1,144 +0,0 @@ -/* - Copyright 2021 Integritee AG - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -use crate::{ - error::{Error, Result as EnclaveResult}, - initialization::global_components::{EnclaveStf, GLOBAL_STATE_HANDLER_COMPONENT}, - shard_config, - std::string::ToString, - utils::DecodeRaw, -}; -use codec::{Decode, Encode}; -use itp_component_container::ComponentGetter; - -use itp_stf_interface::{ - parentchain_pallet::ParentchainPalletInstancesInterface, ShardCreationInfo, ShardCreationQuery, -}; -use itp_stf_state_handler::{handle_state::HandleState, query_shard_state::QueryShardState}; -use itp_types::{ - parentchain::{Header, ParentchainId}, - ShardIdentifier, -}; -use itp_utils::write_slice_and_whitespace_pad; -use log::*; -use sgx_types::sgx_status_t; -use std::slice; - -#[no_mangle] -pub unsafe extern "C" fn init_shard_creation_parentchain_header( - shard: *const u8, - shard_size: u32, - parentchain_id: *const u8, - parentchain_id_size: u32, - header: *const u8, - header_size: u32, -) -> sgx_status_t { - let shard_identifier = - ShardIdentifier::from_slice(slice::from_raw_parts(shard, shard_size as usize)); - let header = match Header::decode(&mut slice::from_raw_parts(header, header_size as usize)) { - Ok(hdr) => hdr, - Err(e) => { - error!("Could not decode header: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - let parentchain_id = - match ParentchainId::decode_raw(parentchain_id, parentchain_id_size as usize) { - Ok(id) => id, - Err(e) => { - error!("Could not decode parentchain id: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - if let Err(e) = - init_shard_creation_parentchain_header_internal(shard_identifier, parentchain_id, header) - { - error!( - "Failed to initialize first relevant parentchain header [{:?}]: {:?}", - parentchain_id, e - ); - return sgx_status_t::SGX_ERROR_UNEXPECTED - } - sgx_status_t::SGX_SUCCESS -} - -fn init_shard_creation_parentchain_header_internal( - shard: ShardIdentifier, - parentchain_id: ParentchainId, - header: Header, -) -> EnclaveResult<()> { - if let Some(creation_block) = - get_shard_creation_info_internal(shard)?.for_parentchain(parentchain_id) - { - error!("first relevant parentchain header has been previously initialized to {:?}. cannot change: {:?}", creation_block.number, parentchain_id); - return Err(Error::Other( - "first relevant parentchain header has been previously initialized. cannot change" - .into(), - )) - } - debug!("initializing shard creation header: {:?}", parentchain_id); - - let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; - if !state_handler - .shard_exists(&shard) - .map_err(|_| Error::Other("get shard_exists failed".into()))? - { - return Err(Error::Other("shard not initialized".into())) - }; - - let (state_lock, mut state) = state_handler.load_for_mutation(&shard)?; - EnclaveStf::set_creation_block(&mut state, header, parentchain_id) - .map_err(|e| Error::Stf(e.to_string()))?; - state_handler.write_after_mutation(state, state_lock, &shard)?; - - shard_config::init_shard_config(shard)?; - Ok(()) -} - -/// reads the shard vault account id form state if it has been initialized previously -pub(crate) fn get_shard_creation_info_internal( - shard: ShardIdentifier, -) -> EnclaveResult { - let state_handler = GLOBAL_STATE_HANDLER_COMPONENT.get()?; - let (_state_lock, mut state) = state_handler.load_for_mutation(&shard)?; - Ok(EnclaveStf::get_shard_creation_info(&mut state)) -} - -/// reads the shard vault account id form state if it has been initialized previously -#[no_mangle] -pub unsafe extern "C" fn get_shard_creation_info( - shard: *const u8, - shard_size: u32, - creation: *mut u8, - creation_size: u32, -) -> sgx_status_t { - let shard = ShardIdentifier::from_slice(slice::from_raw_parts(shard, shard_size as usize)); - - let shard_creation_info = match get_shard_creation_info_internal(shard) { - Ok(creation) => creation, - Err(e) => { - warn!("Failed to fetch creation header: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - trace!("fetched shard creation header from state: {:?}", shard_creation_info); - - let creation_slice = slice::from_raw_parts_mut(creation, creation_size as usize); - if let Err(e) = write_slice_and_whitespace_pad(creation_slice, shard_creation_info.encode()) { - return Error::BufferError(e).into() - }; - sgx_status_t::SGX_SUCCESS -} diff --git a/tee-worker/bitacross/enclave-runtime/src/stf_task_handler.rs b/tee-worker/bitacross/enclave-runtime/src/stf_task_handler.rs deleted file mode 100644 index 8b13789179..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/stf_task_handler.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tee-worker/bitacross/enclave-runtime/src/sync.rs b/tee-worker/bitacross/enclave-runtime/src/sync.rs deleted file mode 100644 index a348134d6f..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/sync.rs +++ /dev/null @@ -1,104 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -//! Primitives to handle multithreaded state access in the enclave. -//! -//! Note: In general the design should try to minimize usage of these, as potential deadlocks can -//! occur. Documentation of the `SgxRwLock` says that panics __might__ occur when trying to acquire -//! a lock multiple times in the same thread. However, tests have shown that it also might result in -//! a deadlock. -//! -//! @clangenb: Does currently not see any way to entirely get rid of these synchronization -//! primitives because we can only start new threads from the untrusted side. `parking_lot` would be -//! an alternative to consider for the primitives. It has several performance and ergonomic benefits -//! over the `std` lib's. One of the benefits would be compile-time deadlock detection (experimental). -//! Unfortunately, it would need to be ported to SGX. -//! -//! `https://amanieu.github.io/parking_lot/parking_lot/index.html` - -use crate::error::{Error, Result as EnclaveResult}; -use lazy_static::lazy_static; -use std::sync::{SgxRwLock, SgxRwLockReadGuard, SgxRwLockWriteGuard}; - -lazy_static! { - pub static ref SIDECHAIN_DB_LOCK: SgxRwLock<()> = Default::default(); -} - -pub struct EnclaveLock; - -impl SidechainRwLock for EnclaveLock { - fn read_sidechain_db() -> EnclaveResult> { - SIDECHAIN_DB_LOCK.read().map_err(|e| Error::Other(e.into())) - } - - fn write_sidechain_db() -> EnclaveResult> { - SIDECHAIN_DB_LOCK.write().map_err(|e| Error::Other(e.into())) - } -} - -pub trait SidechainRwLock { - fn read_sidechain_db() -> EnclaveResult>; - fn write_sidechain_db() -> EnclaveResult>; -} - -// simple type defs to prevent too long names -type AggregatedReadGuards<'a> = SgxRwLockReadGuard<'a, ()>; -type AggregatedWriteGuards<'a> = SgxRwLockWriteGuard<'a, ()>; - -/// Useful, if all state must be accessed. Reduces the number of lines. -pub trait EnclaveStateRWLock: SidechainRwLock { - /// return read locks of all enclave states - fn read_all() -> EnclaveResult>; - - /// return write locks of all enclave states - fn write_all() -> EnclaveResult>; -} - -impl EnclaveStateRWLock for T { - fn read_all() -> EnclaveResult> { - Self::read_sidechain_db() - } - - fn write_all() -> EnclaveResult> { - Self::write_sidechain_db() - } -} - -#[cfg(feature = "test")] -pub mod tests { - use super::*; - pub fn sidechain_rw_lock_works() { - drop(EnclaveLock::read_sidechain_db().unwrap()); - drop(EnclaveLock::write_sidechain_db().unwrap()); - - let x1 = EnclaveLock::read_sidechain_db().unwrap(); - let x2 = EnclaveLock::read_sidechain_db().unwrap(); - - drop((x1, x2)); - drop(EnclaveLock::write_sidechain_db().unwrap()) - } - - pub fn enclave_rw_lock_works() { - drop(EnclaveLock::read_all().unwrap()); - drop(EnclaveLock::write_all().unwrap()); - - let x1 = EnclaveLock::read_all().unwrap(); - let x2 = EnclaveLock::read_all().unwrap(); - - drop((x1, x2)); - drop(EnclaveLock::write_all().unwrap()) - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/test/Counter.sol b/tee-worker/bitacross/enclave-runtime/src/test/Counter.sol deleted file mode 100644 index ce3cce3259..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/test/Counter.sol +++ /dev/null @@ -1,31 +0,0 @@ -pragma solidity >=0.8.0; - -contract Counter { - uint256 private value; - address private last_caller; - - constructor() { - value = 1; - last_caller = msg.sender; - } - - fallback() external payable { value = 5; } - - function inc() public { - value += 1; - last_caller = msg.sender; - } - - function add(uint delta) public { - value += delta; - last_caller = msg.sender; - } - - function get_value() view public returns (uint) { - return value; - } - - function get_last_caller() view public returns (address) { - return last_caller; - } -} \ No newline at end of file diff --git a/tee-worker/bitacross/enclave-runtime/src/test/cert_tests.rs b/tee-worker/bitacross/enclave-runtime/src/test/cert_tests.rs deleted file mode 100644 index ad3b78df76..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/test/cert_tests.rs +++ /dev/null @@ -1,72 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -use crate::test::mocks::attestation_ocall_mock::AttestationOCallMock; -use hex::FromHexError; -use itp_attestation_handler::cert::{verify_attn_report, verify_mra_cert}; -use sgx_types::{sgx_measurement_t, sgx_status_t, SGX_HASH_SIZE}; -use std::vec::Vec; - -// Test data and tests are mostly copied from: -// https://github.com/integritee-network/pallet-teerex/blob/master/ias-verify/ - -const TEST4_CERT: &[u8] = include_bytes!("fixtures/ra_dump_cert_TEST4.der"); - -const TEST4_MRENCLAVE: &str = "7a3454ec8f42e265cb5be7dfd111e1d95ac6076ed82a0948b2e2a45cf17b62a0"; - -#[allow(clippy::octal_escapes)] -const CERT_WRONG_PLATFORM_BLOB: &[u8] = b"0\x82\x0c\x8c0\x82\x0c2\xa0\x03\x02\x01\x02\x02\x01\x010\n\x06\x08*\x86H\xce=\x04\x03\x020\x121\x100\x0e\x06\x03U\x04\x03\x0c\x07MesaTEE0\x1e\x17\r190617124609Z\x17\r190915124609Z0\x121\x100\x0e\x06\x03U\x04\x03\x0c\x07MesaTEE0Y0\x13\x06\x07*\x86H\xce=\x02\x01\x06\x08*\x86H\xce=\x03\x01\x07\x03B\0\x04RT\x16\x16 \xef_\xd8\xe7\xc3\xb7\x03\x1d\xd6:\x1fF\xe3\xf2b!\xa9/\x8b\xd4\x82\x8f\xd1\xff[\x9c\x97\xbc\xf27\xb8,L\x8a\x01\xb0r;;\xa9\x83\xdc\x86\x9f\x1d%y\xf4;I\xe4Y\xc80'$K[\xd6\xa3\x82\x0bw0\x82\x0bs0\x82\x0bo\x06\t`\x86H\x01\x86\xf8B\x01\r\x04\x82\x0b`{\"id\":\"117077750682263877593646412006783680848\",\"timestamp\":\"2019-06-17T12:46:04.002066\",\"version\":3,\"isvEnclaveQuoteStatus\":\"GROUP_OUT_OF_DATE\",\"platformInfoBlob\":\"1602006504000900000909020401800000000000000000000008000009000000020000000000000B401A355B313FC939B4F48A54349C914A32A3AE2C4871BFABF22E960C55635869FC66293A3D9B2D58ED96CA620B65D669A444C80291314EF691E896F664317CF80C\",\"isvEnclaveQuoteBody\":\"AgAAAEALAAAIAAcAAAAAAOE6wgoHKsZsnVWSrsWX9kky0kWt9K4xcan0fQ996Ct+CAj//wGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAHAAAAAAAAAFJJYIbPVot9NzRCjW2z9+k+9K8BsHQKzVMEHOR14hNbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD1xnnferKFHD2uvYqTXdDA8iZ22kCD5xw7h38CMfOngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSVBYWIO9f2OfDtwMd1jofRuPyYiGpL4vUgo/R/1ucl7zyN7gsTIoBsHI7O6mD3IafHSV59DtJ5FnIMCckS1vW\"}|EbPFH/ThUaS/dMZoDKC5EgmdUXUORFtQzF49Umi1P55oeESreJaUvmA0sg/ATSTn5t2e+e6ZoBQIUbLHjcWLMLzK4pJJUeHhok7EfVgoQ378i+eGR9v7ICNDGX7a1rroOe0s1OKxwo/0hid2KWvtAUBvf1BDkqlHy025IOiXWhXFLkb/qQwUZDWzrV4dooMfX5hfqJPi1q9s18SsdLPmhrGBheh9keazeCR9hiLhRO9TbnVgR9zJk43SPXW+pHkbNigW+2STpVAi5ugWaSwBOdK11ZjaEU1paVIpxQnlW1D6dj1Zc3LibMH+ly9ZGrbYtuJks4eRnjPhroPXxlJWpQ==|MIIEoTCCAwmgAwIBAgIJANEHdl0yo7CWMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQDDCdJbnRlbCBTR1ggQXR0ZXN0YXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwHhcNMTYxMTIyMDkzNjU4WhcNMjYxMTIwMDkzNjU4WjB7MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExFDASBgNVBAcMC1NhbnRhIENsYXJhMRowGAYDVQQKDBFJbnRlbCBDb3Jwb3JhdGlvbjEtMCsGA1UEAwwkSW50ZWwgU0dYIEF0dGVzdGF0aW9uIFJlcG9ydCBTaWduaW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqXot4OZuphR8nudFrAFiaGxxkgma/Es/BA+tbeCTUR106AL1ENcWA4FX3K+E9BBL0/7X5rj5nIgX/R/1ubhkKWw9gfqPG3KeAtIdcv/uTO1yXv50vqaPvE1CRChvzdS/ZEBqQ5oVvLTPZ3VEicQjlytKgN9cLnxbwtuvLUK7eyRPfJW/ksddOzP8VBBniolYnRCD2jrMRZ8nBM2ZWYwnXnwYeOAHV+W9tOhAImwRwKF/95yAsVwd21ryHMJBcGH70qLagZ7Ttyt++qO/6+KAXJuKwZqjRlEtSEz8gZQeFfVYgcwSfo96oSMAzVr7V0L6HSDLRnpb6xxmbPdqNol4tQIDAQABo4GkMIGhMB8GA1UdIwQYMBaAFHhDe3amfrzQr35CN+s1fDuHAVE8MA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMGAGA1UdHwRZMFcwVaBToFGGT2h0dHA6Ly90cnVzdGVkc2VydmljZXMuaW50ZWwuY29tL2NvbnRlbnQvQ1JML1NHWC9BdHRlc3RhdGlvblJlcG9ydFNpZ25pbmdDQS5jcmwwDQYJKoZIhvcNAQELBQADggGBAGcIthtcK9IVRz4rRq+ZKE+7k50/OxUsmW8aavOzKb0iCx07YQ9rzi5nU73tME2yGRLzhSViFs/LpFa9lpQL6JL1aQwmDR74TxYGBAIi5f4I5TJoCCEqRHz91kpG6Uvyn2tLmnIdJbPE4vYvWLrtXXfFBSSPD4Afn7+3/XUggAlc7oCTizOfbbtOFlYA4g5KcYgS1J2ZAeMQqbUdZseZCcaZZZn65tdqee8UXZlDvx0+NdO0LR+5pFy+juM0wWbu59MvzcmTXbjsi7HY6zd53Yq5K244fwFHRQ8eOB0IWB+4PfM7FeAApZvlfqlKOlLcZL2uyVmzRkyR5yW72uo9mehX44CiPJ2fse9Y6eQtcfEhMPkmHXI01sN+KwPbpA39+xOsStjhP9N1Y1a2tQAVo+yVgLgV2Hws73Fc0o3wC78qPEA+v2aRs/Be3ZFDgDyghc/1fgU+7C+P6kbqd4poyb6IW8KCJbxfMJvkordNOgOUUxndPHEi/tb/U7uLjLOgPA==0\n\x06\x08*\x86H\xce=\x04\x03\x02\x03H\00E\x02!\0\xae6\x06\t@Sy\x8f\x8ec\x9d\xdci^Ex*\x92}\xdcG\x15A\x97\xd7\xd7\xd1\xccx\xe0\x1e\x08\x02 \x15Q\xa0BT\xde'~\xec\xbd\x027\xd3\xd8\x83\xf7\xe6Z\xc5H\xb4D\xf7\xe2\r\xa7\xe4^f\x10\x85p"; - -pub fn test_verify_mra_cert_should_work() { - let mr_enclave = get_mr_enclave_from_hex_string(TEST4_MRENCLAVE).unwrap(); - let attestation_ocall = - AttestationOCallMock::create_with_mr_enclave(sgx_measurement_t { m: mr_enclave }); - let result = verify_mra_cert(TEST4_CERT, false, false, &attestation_ocall); - - assert!(result.is_ok()); -} - -pub fn test_verify_wrong_cert_is_err() { - let mr_enclave = get_mr_enclave_from_hex_string(TEST4_MRENCLAVE).unwrap(); - let attestation_ocall = - AttestationOCallMock::create_with_mr_enclave(sgx_measurement_t { m: mr_enclave }); - let result = verify_mra_cert(CERT_WRONG_PLATFORM_BLOB, false, false, &attestation_ocall); - - assert!(result.is_err()); - assert_eq!(result.unwrap_err(), sgx_status_t::SGX_ERROR_UNEXPECTED); -} - -pub fn test_given_wrong_platform_info_when_verifying_attestation_report_then_return_error() { - let attestation_ocall = AttestationOCallMock::new(); - let result = verify_attn_report(CERT_WRONG_PLATFORM_BLOB, Vec::new(), &attestation_ocall); - - assert!(result.is_err()); - assert_eq!(result.unwrap_err(), sgx_status_t::SGX_ERROR_UNEXPECTED); -} - -fn get_mr_enclave_from_hex_string(input_str: &str) -> Result<[u8; SGX_HASH_SIZE], FromHexError> { - let decoded_str = hex::decode(input_str)?; - - if decoded_str.len() != SGX_HASH_SIZE { - return Err(FromHexError::InvalidStringLength) - } - - let mut mr_enclave = [0u8; SGX_HASH_SIZE]; - mr_enclave.clone_from_slice(decoded_str.as_slice()); - - Ok(mr_enclave) -} diff --git a/tee-worker/bitacross/enclave-runtime/src/test/direct_rpc_tests.rs b/tee-worker/bitacross/enclave-runtime/src/test/direct_rpc_tests.rs deleted file mode 100644 index 122da0484b..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/test/direct_rpc_tests.rs +++ /dev/null @@ -1,176 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - initialization::global_components::{ - GLOBAL_BITCOIN_KEY_REPOSITORY_COMPONENT, GLOBAL_ETHEREUM_KEY_REPOSITORY_COMPONENT, - GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, GLOBAL_TON_KEY_REPOSITORY_COMPONENT, - }, - rpc::worker_api_direct::public_api_rpc_handler, - test::{ - fixtures::components::create_ocall_api, - mocks::types::{TestOCallApi, TestSigner}, - }, - Hash, -}; -use bc_signer_registry::{PubKey, SignerRegistryLookup}; -use codec::{Decode, Encode}; -use ita_stf::{Getter, PublicGetter}; -use itc_direct_rpc_server::{ - create_determine_watch, rpc_connection_registry::ConnectionRegistry, - rpc_ws_handler::RpcWsHandler, -}; -use itc_parentchain_test::ParentchainHeaderBuilder; -use itc_tls_websocket_server::{ConnectionToken, WebSocketMessageHandler}; -use itp_component_container::ComponentGetter; -use itp_rpc::{Id, RpcRequest, RpcReturnValue}; -use itp_sgx_crypto::get_rsa3072_repository; -use itp_sgx_temp_dir::TempDir; -use itp_stf_executor::{getter_executor::GetterExecutor, mocks::GetStateMock}; -use itp_stf_state_observer::mock::ObserveStateMock; -use itp_top_pool_author::mocks::AuthorApiMock; -use itp_types::{DirectRequestStatus, RsaRequest, ShardIdentifier}; -use itp_utils::{FromHexPrefixed, ToHexPrefixed}; -use litentry_primitives::{Address32, Identity}; -use sp_core::Pair; -use std::{string::ToString, sync::Arc, vec::Vec}; - -struct SignerRegistryMock {} - -impl SignerRegistryLookup for SignerRegistryMock { - fn contains_key(&self, _account: &Address32) -> bool { - true - } - fn get_all(&self) -> Vec<(Address32, PubKey)> { - vec![] - } -} - -pub fn state_get_mrenclave_works() { - type TestState = u64; - - let temp_dir = TempDir::with_prefix("get_state_request_works").unwrap(); - - let connection_registry = Arc::new(ConnectionRegistry::::new()); - let watch_extractor = Arc::new(create_determine_watch::()); - let rsa_repository = get_rsa3072_repository(temp_dir.path().to_path_buf()).unwrap(); - - let mr_enclave = [1; 32]; - - let ocall_api = TestOCallApi::default().with_mr_enclave(mr_enclave.clone()); - - let state: TestState = 78234u64; - let state_observer = Arc::new(ObserveStateMock::::new(state)); - let getter_executor = - Arc::new(GetterExecutor::<_, GetStateMock, Getter>::new(state_observer)); - let top_pool_author = Arc::new(AuthorApiMock::default()); - let signer_lookup = Arc::new(SignerRegistryMock {}); - - let io_handler = public_api_rpc_handler( - top_pool_author, - getter_executor, - Arc::new(rsa_repository), - ocall_api.into(), - GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.get().unwrap(), - GLOBAL_BITCOIN_KEY_REPOSITORY_COMPONENT.get().unwrap(), - GLOBAL_ETHEREUM_KEY_REPOSITORY_COMPONENT.get().unwrap(), - GLOBAL_TON_KEY_REPOSITORY_COMPONENT.get().unwrap(), - signer_lookup, - ); - let rpc_handler = Arc::new(RpcWsHandler::new(io_handler, watch_extractor, connection_registry)); - - let request_string = RpcRequest::compose_jsonrpc_call( - Id::Text("1".to_string()), - "state_getMrenclave".to_string(), - vec![], - ) - .unwrap(); - - let response_string = - rpc_handler.handle_message(ConnectionToken(1), request_string).unwrap().unwrap(); - - assert!(!response_string.is_empty()); - - const EXPECTED_HEX_RETURN_VALUE: &str = - "0x8001010101010101010101010101010101010101010101010101010101010101010000"; - assert!(response_string.contains(EXPECTED_HEX_RETURN_VALUE)); - let rpc_return_value = RpcReturnValue::from_hex(EXPECTED_HEX_RETURN_VALUE).unwrap(); - assert_eq!(rpc_return_value.status, DirectRequestStatus::Ok); - let decoded_value: [u8; 32] = Decode::decode(&mut rpc_return_value.value.as_slice()).unwrap(); - assert_eq!(decoded_value, mr_enclave); -} - -pub fn get_state_request_works() { - type TestState = u64; - - let temp_dir = TempDir::with_prefix("get_state_request_works").unwrap(); - - let connection_registry = Arc::new(ConnectionRegistry::::new()); - let watch_extractor = Arc::new(create_determine_watch::()); - let rsa_repository = get_rsa3072_repository(temp_dir.path().to_path_buf()).unwrap(); - - let signer = TestSigner::from_seed(b"42315678901234567890123456789012"); - let header = ParentchainHeaderBuilder::default().build(); - - let ocall_api = create_ocall_api(&header, &signer); - - let state: TestState = 78234u64; - let state_observer = Arc::new(ObserveStateMock::::new(state)); - let getter_executor = - Arc::new(GetterExecutor::<_, GetStateMock, Getter>::new(state_observer)); - let top_pool_author = Arc::new(AuthorApiMock::default()); - let signer_lookup = Arc::new(SignerRegistryMock {}); - - let io_handler = public_api_rpc_handler( - top_pool_author, - getter_executor, - Arc::new(rsa_repository), - ocall_api, - GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.get().unwrap(), - GLOBAL_BITCOIN_KEY_REPOSITORY_COMPONENT.get().unwrap(), - GLOBAL_ETHEREUM_KEY_REPOSITORY_COMPONENT.get().unwrap(), - GLOBAL_TON_KEY_REPOSITORY_COMPONENT.get().unwrap(), - signer_lookup, - ); - let rpc_handler = Arc::new(RpcWsHandler::new(io_handler, watch_extractor, connection_registry)); - - let getter = - Getter::public(PublicGetter::nonce(Identity::Substrate(Address32::from([0u8; 32])))); - - let request = RsaRequest::new(ShardIdentifier::default(), getter.encode()); - - let request_string = RpcRequest::compose_jsonrpc_call( - Id::Text("1".to_string()), - "state_executeGetter".to_string(), - vec![request.to_hex()], - ) - .unwrap(); - - let response_string = - rpc_handler.handle_message(ConnectionToken(1), request_string).unwrap().unwrap(); - - assert!(!response_string.is_empty()); - - const EXPECTED_HEX_RETURN_VALUE: &str = "0x2801209a310100000000000000"; - assert!(response_string.contains(EXPECTED_HEX_RETURN_VALUE)); - let rpc_return_value = RpcReturnValue::from_hex(EXPECTED_HEX_RETURN_VALUE).unwrap(); - assert_eq!(rpc_return_value.status, DirectRequestStatus::Ok); - let decoded_value: Option> = - Option::decode(&mut rpc_return_value.value.as_slice()).unwrap(); - assert_eq!(decoded_value, Some(state.encode())); -} diff --git a/tee-worker/bitacross/enclave-runtime/src/test/enclave_signer_tests.rs b/tee-worker/bitacross/enclave-runtime/src/test/enclave_signer_tests.rs deleted file mode 100644 index b76af97d4a..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/test/enclave_signer_tests.rs +++ /dev/null @@ -1,172 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -use codec::Encode; -use ita_sgx_runtime::Runtime; -use ita_stf::{Getter, Stf, TrustedCall, TrustedCallSigned}; -use itp_node_api::metadata::{metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository}; -use itp_ocall_api::EnclaveAttestationOCallApi; -use itp_sgx_crypto::{ - ed25519_derivation::DeriveEd25519, key_repository::AccessKey, mocks::KeyRepositoryMock, -}; -use itp_sgx_externalities::SgxExternalities; -use itp_stf_executor::{enclave_signer::StfEnclaveSigner, traits::StfEnclaveSigning}; -use itp_stf_interface::{ - mocks::GetterExecutorMock, system_pallet::SystemPalletAccountInterface, InitState, - StateCallInterface, -}; -use itp_stf_primitives::{ - traits::TrustedCallVerification, - types::{AccountId, ShardIdentifier, TrustedOperation}, -}; -use itp_stf_state_observer::mock::ObserveStateMock; -use itp_test::mock::onchain_mock::OnchainMock; -use itp_top_pool_author::{mocks::AuthorApiMock, traits::AuthorApi}; -use itp_types::{parentchain::ParentchainId, RsaRequest}; -use litentry_primitives::Identity; -use sgx_crypto_helper::{rsa3072::Rsa3072KeyPair, RsaKeyPair}; -use sp_core::Pair; -use std::{sync::Arc, vec::Vec}; - -type ShieldingKeyRepositoryMock = KeyRepositoryMock; -type TestStf = Stf; - -pub fn derive_key_is_deterministic() { - let rsa_key = Rsa3072KeyPair::new().unwrap(); - - let first_ed_key = rsa_key.derive_ed25519().unwrap(); - let second_ed_key = rsa_key.derive_ed25519().unwrap(); - assert_eq!(first_ed_key.public(), second_ed_key.public()); -} - -pub fn enclave_signer_signatures_are_valid() { - let top_pool_author = Arc::new(AuthorApiMock::default()); - let ocall_api = Arc::new(OnchainMock::default()); - let shielding_key_repo = Arc::new(ShieldingKeyRepositoryMock::default()); - let enclave_account: AccountId = shielding_key_repo - .retrieve_key() - .unwrap() - .derive_ed25519() - .unwrap() - .public() - .into(); - - let state_observer: Arc> = - Arc::new(ObserveStateMock::new(TestStf::init_state(enclave_account.clone()))); - let shard = ShardIdentifier::default(); - let mr_enclave = ocall_api.get_mrenclave_of_self().unwrap(); - let enclave_signer = StfEnclaveSigner::<_, _, _, TestStf, _, TrustedCallSigned, Getter>::new( - state_observer, - ocall_api, - shielding_key_repo, - top_pool_author, - ); - let trusted_call = TrustedCall::balance_shield( - Identity::Substrate(enclave_account.into()), - AccountId::new([3u8; 32]), - 200u128, - ParentchainId::Litentry, - ); - - let trusted_call_signed = enclave_signer.sign_call_with_self(&trusted_call, &shard).unwrap(); - assert!(trusted_call_signed.verify_signature(&mr_enclave.m, &shard)); -} - -pub fn nonce_is_computed_correctly() { - let top_pool_author = Arc::new(AuthorApiMock::default()); - let ocall_api = Arc::new(OnchainMock::default()); - let shielding_key_repo = Arc::new(ShieldingKeyRepositoryMock::default()); - let enclave_account: AccountId = shielding_key_repo - .retrieve_key() - .unwrap() - .derive_ed25519() - .unwrap() - .public() - .into(); - let mut state = TestStf::init_state(enclave_account.clone()); - // only used to create the enclave signer, the state is **not** synchronised - let state_observer: Arc> = - Arc::new(ObserveStateMock::new(state.clone())); - let shard = ShardIdentifier::default(); - let enclave_signer = StfEnclaveSigner::<_, _, _, TestStf, _, TrustedCallSigned, Getter>::new( - state_observer, - ocall_api, - shielding_key_repo, - top_pool_author.clone(), - ); - assert_eq!(enclave_account, enclave_signer.get_enclave_account().unwrap()); - - // create the first trusted_call and submit it - let trusted_call_1 = TrustedCall::balance_shield( - Identity::Substrate(enclave_account.clone().into()), - AccountId::new([1u8; 32]), - 100u128, - ParentchainId::Litentry, - ); - let trusted_call_1_signed = - enclave_signer.sign_call_with_self(&trusted_call_1, &shard).unwrap(); - top_pool_author.submit_top(RsaRequest::new( - shard, - TrustedOperation::::indirect_call(trusted_call_1_signed.clone()) - .encode(), - )); - assert_eq!(1, top_pool_author.get_pending_trusted_calls_for(shard, &enclave_account).len()); - // create the second trusted_call and submit it - let trusted_call_2 = TrustedCall::balance_shield( - Identity::Substrate(enclave_account.clone().into()), - AccountId::new([2u8; 32]), - 200u128, - ParentchainId::Litentry, - ); - let trusted_call_2_signed = - enclave_signer.sign_call_with_self(&trusted_call_2, &shard).unwrap(); - top_pool_author.submit_top(RsaRequest::new( - shard, - TrustedOperation::::indirect_call(trusted_call_2_signed.clone()) - .encode(), - )); - assert_eq!(2, top_pool_author.get_pending_trusted_calls_for(shard, &enclave_account).len()); - // there should be no pending trusted calls for non-enclave-account - assert_eq!( - 0, - top_pool_author - .get_pending_trusted_calls_for(shard, &AccountId::new([1u8; 32])) - .len() - ); - - assert_eq!(0, TestStf::get_account_nonce(&mut state, &enclave_account)); - let repo = Arc::new(NodeMetadataRepository::new(NodeMetadataMock::new())); - assert!(TestStf::execute_call( - &mut state, - &shard, - trusted_call_1_signed, - Default::default(), - &mut Vec::new(), - repo.clone(), - ) - .is_ok()); - - assert!(TestStf::execute_call( - &mut state, - &shard, - trusted_call_2_signed, - Default::default(), - &mut Vec::new(), - repo, - ) - .is_ok()); - assert_eq!(2, TestStf::get_account_nonce(&mut state, &enclave_account)); -} diff --git a/tee-worker/bitacross/enclave-runtime/src/test/fixtures/components.rs b/tee-worker/bitacross/enclave-runtime/src/test/fixtures/components.rs deleted file mode 100644 index 34f3606010..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/test/fixtures/components.rs +++ /dev/null @@ -1,63 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::test::mocks::types::{TestOCallApi, TestRpcResponder, TestSigner, TestTopPool}; -use codec::Encode; -use ita_stf::{Getter, TrustedCall, TrustedCallSigned}; -use itp_ocall_api::EnclaveAttestationOCallApi; -use itp_sgx_crypto::ShieldingCryptoEncrypt; -use itp_stf_primitives::{ - traits::TrustedCallSigning, - types::{KeyPair, TrustedOperation}, -}; -use itp_top_pool::pool::Options as PoolOptions; -use itp_top_pool_author::api::SidechainApi; -use itp_types::{Block as ParentchainBlock, ShardIdentifier}; -use sp_core::{ed25519, Pair, H256}; -use sp_runtime::traits::Header as HeaderTrait; -use std::{boxed::Box, sync::Arc, vec::Vec}; -pub(crate) fn create_top_pool() -> Arc { - let rpc_responder = Arc::new(TestRpcResponder::new()); - let sidechain_api = Arc::new(SidechainApi::::new()); - Arc::new(TestTopPool::create(PoolOptions::default(), sidechain_api, rpc_responder)) -} - -pub(crate) fn create_ocall_api>( - header: &Header, - signer: &TestSigner, -) -> Arc { - Arc::new(TestOCallApi::default().add_validateer_set(header, Some(vec![signer.public().into()]))) -} - -pub(crate) fn encrypt_trusted_operation( - shielding_key: &ShieldingKey, - trusted_operation: &TrustedOperation, -) -> Vec { - let encoded_operation = trusted_operation.encode(); - shielding_key.encrypt(encoded_operation.as_slice()).unwrap() -} - -pub(crate) fn sign_trusted_call( - trusted_call: &TrustedCall, - attestation_api: &AttestationApi, - shard_id: &ShardIdentifier, - from: ed25519::Pair, -) -> TrustedCallSigned { - let mr_enclave = attestation_api.get_mrenclave_of_self().unwrap(); - trusted_call.sign(&KeyPair::Ed25519(Box::new(from)), 0, &mr_enclave.m, shard_id) -} diff --git a/tee-worker/bitacross/enclave-runtime/src/test/fixtures/initialize_test_state.rs b/tee-worker/bitacross/enclave-runtime/src/test/fixtures/initialize_test_state.rs deleted file mode 100644 index 41cba705b5..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/test/fixtures/initialize_test_state.rs +++ /dev/null @@ -1,43 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use super::test_setup::TestStf; -use ita_stf::State; -use itp_sgx_externalities::{SgxExternalities, SgxExternalitiesTrait}; -use itp_stf_interface::InitState; -use itp_stf_primitives::types::AccountId; -use itp_stf_state_handler::handle_state::HandleState; -use itp_types::ShardIdentifier; - -/// Returns an empty `State` with the corresponding `ShardIdentifier`. -pub fn init_state>( - state_handler: &S, - enclave_account: AccountId, -) -> (State, ShardIdentifier) { - let shard = ShardIdentifier::default(); - - let _hash = state_handler.initialize_shard(shard).unwrap(); - let (lock, _) = state_handler.load_for_mutation(&shard).unwrap(); - let mut state = TestStf::init_state(enclave_account); - - state.prune_state_diff(); - - state_handler.write_after_mutation(state.clone(), lock, &shard).unwrap(); - - (state, shard) -} diff --git a/tee-worker/bitacross/enclave-runtime/src/test/fixtures/mod.rs b/tee-worker/bitacross/enclave-runtime/src/test/fixtures/mod.rs deleted file mode 100644 index bc01106db1..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/test/fixtures/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pub mod components; -pub mod initialize_test_state; -pub mod test_setup; diff --git a/tee-worker/bitacross/enclave-runtime/src/test/fixtures/ra_dump_cert_TEST4.der b/tee-worker/bitacross/enclave-runtime/src/test/fixtures/ra_dump_cert_TEST4.der deleted file mode 100644 index 2e775236d6c14d5dc93b400a44d4d0a2ab195243..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3234 zcmd5O%uFVkC6i3X zigJZgsz^Z*L0qs8E+`Zfd~6c`*RHr%uiSh0m&F;DhKR;Gx@Y?|swl(fKByujjk405fp^wL34e zT)y;rx+|CNyu#ga^(W8$4Sf1rf1tC69(nGyKmPVc@bD|IUPqKyBEEXLdZE1Q=Rf-F z*Z%eDS8s}+e*x0ZH&0#{e)rh>|MbO|zw+n5)Ia<25AJ*M);%Y1>z<~6cGuqZiD#8d z&%VZ8y6@_(d*V-?Et0bvXBfudB#F~Bj?x%TVib+hC?F^RNRnVE6bBSW)6Ci9XZ2z| z-`C-KbM6H&@B|>9z$gbrZ=lo-jC>L&ScU|>VKd+D7gc$71AqK%v2VmOF2ZJR9jbah zsl)oP?*s6rA*r%!Lq|1@j~k*Wh(hnWDZ)Cfc5AgvE3T-b-V!DIT!13M`Cu-j2*h4T z0JNXd7n=VT+{N^^1n1sw85|Ty$YHD?5duTvB!LnlEfS<4@*KuPoZ?9ar5TbXP*kD- z$Fc+?auSD%Jr0K82wFfc!4~&bdEE;o|@%5i262@ z{~ws~PDigK%Jq}X$EmzwC&XknPm$1z#tS^k{Ed;VM%Bi3mhS-V*{7ZvCiZZOZEz8x z=ByH`=w!sJAu>4R(Vh%r3Dfx#?xtM=qf!scjFQlev>Ak~G#D$mVlbJO#tJ9z6@0PY zX=922+K3r+ei>}gvgtdc^E@#gt?}NpKtQ}8W=qs?_7dA^pg48A=tzxAer(j5n*>DX zv2F?``%{eh3%zFZeL%@Ru24ghSn69B_a_4Cbc<8VYz$v2{ML0!HgndI$SsBZBxs@@ z;9ib*h>UNmad^}=O{dk0h0wf(7e+$LTeAxhLF4J#5mqozDo)r6C4!{UF`2a^`*30~ zq1AX9*e1DKKq@bHv=<#}lER$9&>H$xx}J8jUKqJG@=U>>;@MIQq-r(LGPCfkn>U}) zRaLAUo`>x^gRRQJOdUc)RFVR870pj|2xNY;l_zS13zo?7tqXO9IPxbrF{2i02#|%`(fkLEsx;=G&VHBQkgGwGlo9s$^^e zjNC-)IJ1D*DtT<^&g`f=YNrdUld*J(>A7xsp=;mJEjjuvHOOK+^xTuE>2P&XL%%_M zAMYp$aB>@Wtn;Xm_n7h?_kQLNbKS058D8KW37qsFyh4xb99y2qMH4N>TIcr{F=5-T zCQ}jw(>8NKGE+qf^!sp1xlIAhb{JmSm}BK4K&2A<9>yY0GBmb}ZwgIjq$rZjggxqMhH6pxK7w zMnrj=&zn;+K?0H6T4d!lnltXnE-qqgS#jEFHZNeyoo)wA$>~@yowcQ3wzIhj6>l(QYcq%Hy0zPpgs@}>_g-UoXq;?~svzofdRvgI2zjNea;|gC`B^4vYt3{ngp!gAb39z9yOXuU zN!}Rj(Ns7n&=VOQ5*M~YD{@HNA5g2xtpN3NJ}I`&!ITkUe=+JZys%SRJJ6-L^+L|6 zBw2Q_93%>)w2lRFdCn7W6=PnPtczvf>*xG9=h9(@v9*R7%|*YIR%4+6 zI|xSNfL@URF^}$kJ!QhMF;AliEchd!XBN9VEy&WP@md$LPPVmEKJgaP9?>>Z$QBz* zqlOw9TJ2h}X~H?q`XuUtObL~Ah853?TU$A?=xQTVZqt=mt*uM7zYNAA(af5uwyS+V zmAJ`dED$g)=>d-TZkE9!rYqiAoXj+eYEvp^kjThUJj*8N5DVa3x3*D#mG9>PABRB@ zloVMf+xeVv{h$zZ2`Wz1=ArrbRj}bJ@+yTuG#?&g>Cd8)dB1)YyBYqeiQ+?R1 zs$F83nd!R6G8rr40;wa!r4QOtGqW+gdGoD54_D+nfN=Ry1^Zlp3{oPgfo~OO&`bR$WpnCby>n~0I-aNc`@s7{^_4og1k<9}?{V@B|ue9f% O`rhE9ALHNm<$nV!>ga#~ diff --git a/tee-worker/bitacross/enclave-runtime/src/test/fixtures/test_ra_signer_attn_MRSIGNER1_MRENCLAVE1.bin b/tee-worker/bitacross/enclave-runtime/src/test/fixtures/test_ra_signer_attn_MRSIGNER1_MRENCLAVE1.bin deleted file mode 100644 index d7149d37d53e9f10d74d72c85b4cf516f854c851..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64 zcmV-G0KfluX&Ryyn6JS^EMXg!)k6C~g7p19TtPrYRjrx&>avnmM;e(u@}l?JBjEwh W0K$Yu-;g|KOVI$sR&!?0#cUb+P#>-U diff --git a/tee-worker/bitacross/enclave-runtime/src/test/fixtures/test_setup.rs b/tee-worker/bitacross/enclave-runtime/src/test/fixtures/test_setup.rs deleted file mode 100644 index b9a357eab5..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/test/fixtures/test_setup.rs +++ /dev/null @@ -1,116 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -use crate::{ - ocall::OcallApi, - test::{ - fixtures::initialize_test_state::init_state, mocks::rpc_responder_mock::RpcResponderMock, - }, -}; -use ita_sgx_runtime::Runtime; -use ita_stf::{Getter, State, Stf, TrustedCallSigned}; -use itp_node_api::metadata::{metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository}; -use itp_ocall_api::EnclaveAttestationOCallApi; -use itp_sgx_crypto::{ed25519_derivation::DeriveEd25519, mocks::KeyRepositoryMock}; -use itp_sgx_externalities::SgxExternalities; -use itp_stf_executor::executor::StfExecutor; -use itp_stf_primitives::types::{ShardIdentifier, TrustedOperation}; -use itp_test::mock::{ - handle_state_mock::HandleStateMock, shielding_crypto_mock::ShieldingCryptoMock, -}; -use itp_top_pool::{basic_pool::BasicPool, pool::ExtrinsicHash}; -use itp_top_pool_author::{api::SidechainApi, author::Author, top_filter::AllowAllTopsFilter}; -use itp_types::{Block, MrEnclave}; -use sp_core::{crypto::Pair, ed25519 as spEd25519}; -use std::sync::Arc; -pub type TestRpcResponder = RpcResponderMock>>; -pub type TestTopPool = BasicPool< - SidechainApi, - Block, - TestRpcResponder, - TrustedOperation, ->; -pub type TestShieldingKeyRepo = KeyRepositoryMock; -pub type TestTopPoolAuthor = Author< - TestTopPool, - AllowAllTopsFilter, - HandleStateMock, - TestShieldingKeyRepo, - TrustedCallSigned, - Getter, ->; -pub type TestStf = Stf; - -pub type TestStfExecutor = StfExecutor< - OcallApi, - HandleStateMock, - NodeMetadataRepository, - TestStf, - TrustedCallSigned, - Getter, ->; - -/// Returns all the things that are commonly used in tests and runs -/// `ensure_no_empty_shard_directory_exists` -pub fn test_setup() -> ( - Arc, - State, - ShardIdentifier, - MrEnclave, - ShieldingCryptoMock, - Arc, - Arc, -) { - let shielding_key = ShieldingCryptoMock::default(); - let shielding_key_repo = Arc::new(KeyRepositoryMock::new(shielding_key.clone())); - - let state_handler = Arc::new(HandleStateMock::default()); - let (state, shard) = - init_state(state_handler.as_ref(), enclave_call_signer(&shielding_key).public().into()); - let top_pool = test_top_pool(); - let mrenclave = OcallApi.get_mrenclave_of_self().unwrap().m; - - let node_metadata_repo = Arc::new(NodeMetadataRepository::new(NodeMetadataMock::new())); - let stf_executor = Arc::new(TestStfExecutor::new( - Arc::new(OcallApi), - state_handler.clone(), - node_metadata_repo, - )); - - ( - Arc::new(TestTopPoolAuthor::new( - Arc::new(top_pool), - AllowAllTopsFilter::::new(), - state_handler.clone(), - shielding_key_repo, - )), - state, - shard, - mrenclave, - shielding_key, - state_handler, - stf_executor, - ) -} - -pub fn test_top_pool() -> TestTopPool { - let chain_api = Arc::new(SidechainApi::::new()); - BasicPool::create(Default::default(), chain_api, Arc::new(TestRpcResponder::new())) -} - -pub fn enclave_call_signer(key_source: &Source) -> spEd25519::Pair { - key_source.derive_ed25519().unwrap() -} diff --git a/tee-worker/bitacross/enclave-runtime/src/test/ipfs_tests.rs b/tee-worker/bitacross/enclave-runtime/src/test/ipfs_tests.rs deleted file mode 100644 index f1f94d3696..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/test/ipfs_tests.rs +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ipfs::IpfsContent, ocall::OcallApi}; -use itp_ocall_api::EnclaveIpfsOCallApi; -use log::*; -use std::{fs::File, io::Read, vec::Vec}; - -#[allow(unused)] -fn test_ocall_read_write_ipfs() { - info!("testing IPFS read/write. Hopefully ipfs daemon is running..."); - let enc_state: Vec = vec![20; 4 * 512 * 1024]; - - let cid = OcallApi.write_ipfs(enc_state.as_slice()).unwrap(); - - OcallApi.read_ipfs(&cid).unwrap(); - - let cid_str = std::str::from_utf8(&cid.0).unwrap(); - let mut f = File::open(cid_str).unwrap(); - let mut content_buf = Vec::new(); - f.read_to_end(&mut content_buf).unwrap(); - info!("reading file {:?} of size {} bytes", f, &content_buf.len()); - - let mut ipfs_content = IpfsContent::new(cid_str, content_buf); - let verification = ipfs_content.verify(); - assert!(verification.is_ok()); -} diff --git a/tee-worker/bitacross/enclave-runtime/src/test/mocks/attestation_ocall_mock.rs b/tee-worker/bitacross/enclave-runtime/src/test/mocks/attestation_ocall_mock.rs deleted file mode 100644 index a480890761..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/test/mocks/attestation_ocall_mock.rs +++ /dev/null @@ -1,101 +0,0 @@ -/* - CCopyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use itp_ocall_api::EnclaveAttestationOCallApi; -use sgx_types::*; -use std::{ - fmt::{Debug, Formatter, Result as FormatResult}, - vec::Vec, -}; - -#[derive(Clone)] -pub struct AttestationOCallMock { - mr_enclave: sgx_measurement_t, -} - -impl AttestationOCallMock { - pub fn new() -> Self { - Default::default() - } - - pub fn create_with_mr_enclave(mr_enclave: sgx_measurement_t) -> Self { - AttestationOCallMock { mr_enclave } - } -} - -impl EnclaveAttestationOCallApi for AttestationOCallMock { - fn sgx_init_quote(&self) -> SgxResult<(sgx_target_info_t, sgx_epid_group_id_t)> { - unreachable!() - } - - fn get_ias_socket(&self) -> SgxResult { - unreachable!() - } - - fn get_quote( - &self, - _sig_rl: Vec, - _report: sgx_report_t, - _sign_type: sgx_quote_sign_type_t, - _spid: sgx_spid_t, - _quote_nonce: sgx_quote_nonce_t, - ) -> SgxResult<(sgx_report_t, Vec)> { - unreachable!() - } - - fn get_dcap_quote(&self, _report: sgx_report_t, _quote_size: u32) -> SgxResult> { - unreachable!() - } - - fn get_qve_report_on_quote( - &self, - _quote: Vec, - _current_time: i64, - _quote_collateral: sgx_ql_qve_collateral_t, - _qve_report_info: sgx_ql_qe_report_info_t, - _supplemental_data_size: u32, - ) -> SgxResult<(u32, sgx_ql_qv_result_t, sgx_ql_qe_report_info_t, Vec)> { - unreachable!() - } - - fn get_update_info( - &self, - _platform_info: sgx_platform_info_t, - _enclave_trusted: i32, - ) -> SgxResult { - Ok(sgx_update_info_bit_t { csmeFwUpdate: 0, pswUpdate: 0, ucodeUpdate: 0 }) - } - - fn get_mrenclave_of_self(&self) -> SgxResult { - Ok(self.mr_enclave) - } -} - -impl Default for AttestationOCallMock { - fn default() -> Self { - AttestationOCallMock { mr_enclave: sgx_measurement_t { m: [1; SGX_HASH_SIZE] } } - } -} - -impl Debug for AttestationOCallMock { - fn fmt(&self, f: &mut Formatter<'_>) -> FormatResult { - f.debug_struct("AttestationOCallMock") - .field("mr_enclave", &self.mr_enclave.m) - .finish() - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/test/mocks/mod.rs b/tee-worker/bitacross/enclave-runtime/src/test/mocks/mod.rs deleted file mode 100644 index a6079c29eb..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/test/mocks/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pub mod attestation_ocall_mock; -pub mod rpc_responder_mock; -pub mod types; diff --git a/tee-worker/bitacross/enclave-runtime/src/test/mocks/rpc_responder_mock.rs b/tee-worker/bitacross/enclave-runtime/src/test/mocks/rpc_responder_mock.rs deleted file mode 100644 index 4fd85d68fd..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/test/mocks/rpc_responder_mock.rs +++ /dev/null @@ -1,84 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use itc_direct_rpc_server::{DirectRpcResult, RpcHash, SendRpcResponse}; -use itp_types::{DirectRequestStatus, TrustedOperationStatus}; -use std::{marker::PhantomData, vec::Vec}; - -pub struct RpcResponderMock { - _hash: PhantomData, -} - -impl RpcResponderMock { - pub fn new() -> Self { - RpcResponderMock { _hash: PhantomData } - } -} -impl Default for RpcResponderMock { - fn default() -> Self { - Self::new() - } -} - -impl SendRpcResponse for RpcResponderMock -where - Hash: RpcHash, -{ - type Hash = Hash; - - fn update_status_event( - &self, - _hash: Self::Hash, - _status_update: TrustedOperationStatus, - ) -> DirectRpcResult<()> { - Ok(()) - } - - fn send_state(&self, _hash: Self::Hash, _state_encoded: Vec) -> DirectRpcResult<()> { - Ok(()) - } - - fn send_state_with_status( - &self, - _hash: Self::Hash, - _state_encoded: Vec, - _status: DirectRequestStatus, - ) -> DirectRpcResult<()> { - Ok(()) - } - - fn update_force_wait(&self, _hash: Self::Hash, _force_wait: bool) -> DirectRpcResult<()> { - Ok(()) - } - - fn update_connection_state( - &self, - _hash: Self::Hash, - _encoded_value: Vec, - _force_wait: bool, - ) -> DirectRpcResult<()> { - Ok(()) - } - - fn swap_hash(&self, _old_hash: Self::Hash, _new_hash: Self::Hash) -> DirectRpcResult<()> { - Ok(()) - } - - fn is_force_wait(&self, _hash: Self::Hash) -> bool { - false - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/test/mocks/types.rs b/tee-worker/bitacross/enclave-runtime/src/test/mocks/types.rs deleted file mode 100644 index 1ffe90a2fd..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/test/mocks/types.rs +++ /dev/null @@ -1,84 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Type definitions for testing. Includes various mocks. - -use crate::test::mocks::rpc_responder_mock::RpcResponderMock; -use ita_sgx_runtime::Runtime; -use ita_stf::{Getter, Stf, TrustedCallSigned}; -use itc_parentchain::block_import_dispatcher::trigger_parentchain_block_import_mock::TriggerParentchainBlockImportMock; -use itp_node_api::metadata::{metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository}; -use itp_sgx_crypto::{mocks::KeyRepositoryMock, Aes}; -use itp_sgx_externalities::SgxExternalities; -use itp_stf_executor::executor::StfExecutor; -use itp_stf_primitives::types::TrustedOperation; -use itp_test::mock::{handle_state_mock::HandleStateMock, onchain_mock::OnchainMock}; -use itp_top_pool::basic_pool::BasicPool; -use itp_top_pool_author::{api::SidechainApi, author::Author, top_filter::AllowAllTopsFilter}; -use itp_types::{Block as ParentchainBlock, SignedBlock as SignedParentchainBlock}; -use primitive_types::H256; -use sgx_crypto_helper::rsa3072::Rsa3072KeyPair; -use sp_core::ed25519 as spEd25519; - -pub type TestSigner = spEd25519::Pair; -pub type TestShieldingKey = Rsa3072KeyPair; -pub type TestStateKey = Aes; - -pub type TestGetter = Getter; -pub type TestCall = TrustedCallSigned; -pub type TestStf = Stf; - -pub type TestShieldingKeyRepo = KeyRepositoryMock; - -pub type TestStateKeyRepo = KeyRepositoryMock; - -pub type TestStateHandler = HandleStateMock; - -pub type TestOCallApi = OnchainMock; - -pub type TestParentchainBlockImportTrigger = - TriggerParentchainBlockImportMock; - -pub type TestNodeMetadataRepository = NodeMetadataRepository; - -pub type TestStfExecutor = StfExecutor< - TestOCallApi, - TestStateHandler, - TestNodeMetadataRepository, - TestStf, - TrustedCallSigned, - Getter, ->; - -pub type TestRpcResponder = RpcResponderMock; - -pub type TestTopPool = BasicPool< - SidechainApi, - ParentchainBlock, - TestRpcResponder, - TrustedOperation, ->; - -pub type TestTopPoolAuthor = Author< - TestTopPool, - AllowAllTopsFilter, - TestStateHandler, - TestShieldingKeyRepo, - TrustedCallSigned, - Getter, ->; diff --git a/tee-worker/bitacross/enclave-runtime/src/test/mod.rs b/tee-worker/bitacross/enclave-runtime/src/test/mod.rs deleted file mode 100644 index 8bdbe375c9..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/test/mod.rs +++ /dev/null @@ -1,27 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pub mod cert_tests; -pub mod direct_rpc_tests; -pub mod enclave_signer_tests; -pub mod fixtures; -pub mod ipfs_tests; -pub mod mocks; -mod state_getter_tests; -pub mod tests_main; -pub mod top_pool_tests; diff --git a/tee-worker/bitacross/enclave-runtime/src/test/state_getter_tests.rs b/tee-worker/bitacross/enclave-runtime/src/test/state_getter_tests.rs deleted file mode 100644 index f902061e9e..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/test/state_getter_tests.rs +++ /dev/null @@ -1,53 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use codec::Decode; -use ita_sgx_runtime::Runtime; -use ita_stf::{ - test_genesis::{endowed_account, test_genesis_setup, ENDOWED_ACC_FUNDS}, - Balance, Getter, Stf, TrustedCallSigned, TrustedGetter, -}; -use itp_sgx_externalities::SgxExternalities; -use itp_stf_executor::state_getter::{GetState, StfStateGetter}; -use litentry_primitives::Identity; -use sp_core::Pair; - -type TestState = SgxExternalities; -type TestStf = Stf; -type TestStfStateGetter = StfStateGetter; - -pub fn state_getter_works() { - let sender = endowed_account(); - let signed_getter = TrustedGetter::free_balance(Identity::Substrate(sender.public().into())) - .sign(&sender.into()); - let mut state = test_state(); - - let encoded_balance = TestStfStateGetter::get_state(signed_getter.into(), &mut state) - .unwrap() - .unwrap(); - - let balance = Balance::decode(&mut encoded_balance.as_slice()).unwrap(); - - assert_eq!(balance, ENDOWED_ACC_FUNDS); -} - -fn test_state() -> TestState { - let mut state = TestState::default(); - test_genesis_setup(&mut state); - state -} diff --git a/tee-worker/bitacross/enclave-runtime/src/test/tests_main.rs b/tee-worker/bitacross/enclave-runtime/src/test/tests_main.rs deleted file mode 100644 index 0e44a1937b..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/test/tests_main.rs +++ /dev/null @@ -1,625 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -use crate::{ - rpc, - sync::tests::{enclave_rw_lock_works, sidechain_rw_lock_works}, - test::{ - cert_tests::*, - direct_rpc_tests, enclave_signer_tests, - fixtures::test_setup::{ - enclave_call_signer, test_setup, TestStf, TestStfExecutor, TestTopPoolAuthor, - }, - state_getter_tests, top_pool_tests, - }, - tls_ra, -}; -use codec::Decode; -use ita_sgx_runtime::ParentchainLitentry; -use ita_stf::{ - helpers::set_block_number, - stf_sgx_tests, - test_genesis::{endowed_account as funded_pair, unendowed_account}, - Getter, State, TrustedCall, TrustedCallSigned, TrustedGetter, -}; -use itp_node_api::metadata::{metadata_mocks::NodeMetadataMock, provider::NodeMetadataRepository}; -use itp_sgx_crypto::{Aes, StateCrypto}; -use itp_sgx_externalities::{SgxExternalitiesDiffType, SgxExternalitiesTrait}; -use itp_stf_executor::{ - executor_tests as stf_executor_tests, traits::StateUpdateProposer, BatchExecutionResult, -}; -use itp_stf_interface::{ - parentchain_pallet::ParentchainPalletInstancesInterface, - system_pallet::{SystemPalletAccountInterface, SystemPalletEventInterface}, - StateCallInterface, -}; -use itp_stf_primitives::{ - traits::TrustedCallSigning, - types::{ShardIdentifier, StatePayload, TrustedOperation}, -}; -use itp_stf_state_handler::handle_state::HandleState; -use itp_test::mock::handle_state_mock; -use itp_top_pool_author::{test_utils::submit_operation_to_top_pool, traits::AuthorApi}; -use itp_types::{parentchain::ParentchainId, AccountId, Header}; -use litentry_primitives::Identity; -use sgx_tunittest::*; -use sgx_types::size_t; -use sp_core::{crypto::Pair, ed25519 as spEd25519, H256}; -use sp_runtime::traits::Header as HeaderT; -use std::{string::String, sync::Arc, time::Duration, vec::Vec}; -#[no_mangle] -pub extern "C" fn test_main_entrance() -> size_t { - rsgx_unit_tests!( - itp_attestation_handler::attestation_handler::tests::decode_spid_works, - stf_sgx_tests::enclave_account_initialization_works, - stf_sgx_tests::shield_funds_increments_signer_account_nonce, - stf_sgx_tests::test_root_account_exists_after_initialization, - itp_stf_state_handler::test::sgx_tests::test_write_and_load_state_works, - itp_stf_state_handler::test::sgx_tests::test_sgx_state_decode_encode_works, - itp_stf_state_handler::test::sgx_tests::test_encrypt_decrypt_state_type_works, - itp_stf_state_handler::test::sgx_tests::test_write_access_locks_read_until_finished, - itp_stf_state_handler::test::sgx_tests::test_ensure_subsequent_state_loads_have_same_hash, - itp_stf_state_handler::test::sgx_tests::test_state_handler_file_backend_is_initialized, - itp_stf_state_handler::test::sgx_tests::test_multiple_state_updates_create_snapshots_up_to_cache_size, - itp_stf_state_handler::test::sgx_tests::test_state_files_from_handler_can_be_loaded_again, - itp_stf_state_handler::test::sgx_tests::test_file_io_get_state_hash_works, - itp_stf_state_handler::test::sgx_tests::test_list_state_ids_ignores_files_not_matching_the_pattern, - itp_stf_state_handler::test::sgx_tests::test_in_memory_state_initializes_from_shard_directory, - itp_sgx_crypto::tests::aes_sealing_works, - itp_sgx_crypto::tests::using_get_aes_repository_twice_initializes_key_only_once, - itp_sgx_crypto::tests::ed25529_sealing_works, - itp_sgx_crypto::tests::using_get_ed25519_repository_twice_initializes_key_only_once, - itp_sgx_crypto::tests::rsa3072_sealing_works, - itp_sgx_crypto::tests::using_get_rsa3072_repository_twice_initializes_key_only_once, - itp_sgx_crypto::tests::ecdsa_creating_repository_with_same_path_and_prefix_results_in_same_key, - itp_sgx_crypto::tests::ecdsa_creating_repository_with_same_path_and_prefix_but_new_key_results_in_new_key, - itp_sgx_crypto::tests::ecdsa_seal_init_should_create_new_key_if_not_present, - itp_sgx_crypto::tests::ecdsa_seal_init_should_seal_provided_key, - itp_sgx_crypto::tests::ecdsa_seal_init_should_not_change_key_if_exists_and_not_provided, - itp_sgx_crypto::tests::ecdsa_seal_init_with_key_should_change_current_key, - itp_sgx_crypto::tests::ecdsa_sign_should_produce_valid_signature, - itp_sgx_crypto::tests::schnorr_creating_repository_with_same_path_and_prefix_results_in_same_key, - itp_sgx_crypto::tests::schnorr_creating_repository_with_same_path_and_prefix_but_new_key_results_in_new_key, - itp_sgx_crypto::tests::schnorr_seal_init_should_create_new_key_if_not_present, - itp_sgx_crypto::tests::schnorr_seal_init_should_seal_provided_key, - itp_sgx_crypto::tests::schnorr_seal_init_should_not_change_key_if_exists_and_not_provided, - itp_sgx_crypto::tests::schnorr_seal_init_with_key_should_change_key_current_key, - test_submit_trusted_call_to_top_pool, - test_submit_trusted_getter_to_top_pool, - test_differentiate_getter_and_call_works, - test_executing_call_updates_account_nonce, - test_call_set_update_parentchain_block, - test_invalid_nonce_call_is_not_executed, - test_signature_must_match_public_sender_in_call, - test_non_root_shielding_call_is_not_executed, - test_shielding_call_with_enclave_self_is_executed, - test_retrieve_events, - test_retrieve_event_count, - test_reset_events, - rpc::worker_api_direct::tests::test_given_io_handler_methods_then_retrieve_all_names_as_string, - handle_state_mock::tests::initialized_shards_list_is_empty, - handle_state_mock::tests::shard_exists_after_inserting, - handle_state_mock::tests::from_shard_works, - handle_state_mock::tests::initialize_creates_default_state, - handle_state_mock::tests::load_mutate_and_write_works, - handle_state_mock::tests::ensure_subsequent_state_loads_have_same_hash, - handle_state_mock::tests::ensure_encode_and_encrypt_does_not_affect_state_hash, - handle_state_mock::tests::migrate_shard_works, - // mra cert tests - test_verify_mra_cert_should_work, - test_verify_wrong_cert_is_err, - test_given_wrong_platform_info_when_verifying_attestation_report_then_return_error, - // sync tests - sidechain_rw_lock_works, - enclave_rw_lock_works, - // unit tests of stf_executor - stf_executor_tests::propose_state_update_always_executes_preprocessing_step, - stf_executor_tests::propose_state_update_executes_no_trusted_calls_given_no_time, - stf_executor_tests::propose_state_update_executes_only_one_trusted_call_given_not_enough_time, - stf_executor_tests::propose_state_update_executes_all_calls_given_enough_time, - enclave_signer_tests::enclave_signer_signatures_are_valid, - enclave_signer_tests::derive_key_is_deterministic, - enclave_signer_tests::nonce_is_computed_correctly, - state_getter_tests::state_getter_works, - // sidechain integration tests - top_pool_tests::process_indirect_call_in_top_pool, - // TODO: Litentry disables it for now (P-494) - // top_pool_tests::submit_shielding_call_to_top_pool, - // tls_ra unit tests - tls_ra::seal_handler::test::seal_shielding_key_works, - tls_ra::seal_handler::test::seal_shielding_key_fails_for_invalid_key, - tls_ra::seal_handler::test::unseal_seal_shielding_key_works, - tls_ra::seal_handler::test::seal_state_key_works, - tls_ra::seal_handler::test::seal_state_key_fails_for_invalid_key, - tls_ra::seal_handler::test::unseal_seal_state_key_works, - tls_ra::seal_handler::test::seal_state_works, - tls_ra::seal_handler::test::seal_state_fails_for_invalid_state, - tls_ra::seal_handler::test::unseal_seal_state_works, - tls_ra::tests::test_state_and_key_provisioning, - tls_ra::tests::test_tls_ra_server_client_networking, - // RPC tests - direct_rpc_tests::get_state_request_works, - direct_rpc_tests::state_get_mrenclave_works, - - // light-client-test - itc_parentchain::light_client::io::sgx_tests::init_parachain_light_client_works, - itc_parentchain::light_client::io::sgx_tests::sealing_creates_backup, - - // test musig ceremony - bc_musig2_ceremony::sgx_tests::test_full_flow_with_3_ceremonies, - - // these unit test (?) need an ipfs node running.. - // ipfs::test_creates_ipfs_content_struct_works, - // ipfs::test_verification_ok_for_correct_content, - // ipfs::test_verification_fails_for_incorrect_content, - // test_ocall_read_write_ipfs, - ) -} - -fn test_submit_trusted_call_to_top_pool() { - // given - let (top_pool_author, _, shard, mrenclave, shielding_key, ..) = test_setup(); - - let sender = funded_pair(); - - let signed_call = TrustedCall::balance_set_balance( - Identity::Substrate(sender.public().into()), - sender.public().into(), - 42, - 42, - ) - .sign(&sender.into(), 0, &mrenclave, &shard); - let trusted_operation = direct_top(signed_call); - - // when - submit_operation_to_top_pool( - top_pool_author.as_ref(), - &trusted_operation, - &shielding_key, - shard, - ) - .unwrap(); - - let calls = top_pool_author.get_pending_trusted_calls(shard); - - // then - assert_eq!(calls[0], trusted_operation); -} - -// The TOP pool can hold any TrustedOperation, which at the moment also includes Getters. -// However, in reality we don't submit getters to the TOP pool anymore, they are executed immediately. -// The filter set in the TOP pool author prevents getters from being submitted. -// In this test however, we set the filter to `AllowAllTops`, so getters can be submitted. -// We want to keep this back door open, in case we would want to submit getter into the TOP pool again in the future. -fn test_submit_trusted_getter_to_top_pool() { - // given - let (top_pool_author, _, shard, _, shielding_key, ..) = test_setup(); - - let sender = funded_pair(); - - let signed_getter = TrustedGetter::free_balance(Identity::Substrate(sender.public().into())) - .sign(&sender.into()); - - // when - submit_operation_to_top_pool( - top_pool_author.as_ref(), - &TrustedOperation::::get(Getter::trusted(signed_getter.clone())), - &shielding_key, - shard, - ) - .unwrap(); - - let getters = top_pool_author.get_pending_getters(shard); - - // then - assert_eq!( - getters[0], - TrustedOperation::::get(Getter::trusted(signed_getter)) - ); -} - -fn test_differentiate_getter_and_call_works() { - // given - let (top_pool_author, _, shard, mrenclave, shielding_key, ..) = test_setup(); - - // create accounts - let sender = funded_pair(); - - let signed_getter = TrustedGetter::free_balance(Identity::Substrate(sender.public().into())) - .sign(&sender.into()); - - let signed_call = TrustedCall::balance_set_balance( - Identity::Substrate(sender.public().into()), - sender.public().into(), - 42, - 42, - ) - .sign(&sender.into(), 0, &mrenclave, &shard); - let trusted_operation = direct_top(signed_call); - - // when - submit_operation_to_top_pool( - top_pool_author.as_ref(), - &TrustedOperation::::get(Getter::trusted(signed_getter.clone())), - &shielding_key, - shard, - ) - .unwrap(); - submit_operation_to_top_pool( - top_pool_author.as_ref(), - &trusted_operation, - &shielding_key, - shard, - ) - .unwrap(); - - let calls = top_pool_author.get_pending_trusted_calls(shard); - let getters = top_pool_author.get_pending_getters(shard); - - // then - assert_eq!(calls[0], trusted_operation); - assert_eq!( - getters[0], - TrustedOperation::::get(Getter::trusted(signed_getter)) - ); -} - -fn test_executing_call_updates_account_nonce() { - // given - let (top_pool_author, _, shard, mrenclave, shielding_key, _, stf_executor) = test_setup(); - - let sender = funded_pair(); - let receiver = unfunded_public(); - - let trusted_operation = TrustedCall::balance_transfer( - Identity::Substrate(sender.public().into()), - receiver.into(), - 1000, - ) - .sign(&sender.into(), 0, &mrenclave, &shard) - .into_trusted_operation(false); - - submit_operation_to_top_pool( - top_pool_author.as_ref(), - &trusted_operation, - &shielding_key, - shard, - ) - .unwrap(); - - // when - let mut execution_result = - execute_trusted_calls(&shard, stf_executor.as_ref(), &top_pool_author); - - let nonce = TestStf::get_account_nonce( - &mut execution_result.state_after_execution, - &sender.public().into(), - ); - assert_eq!(nonce, 1); -} - -fn test_call_set_update_parentchain_block() { - let (_, _, shard, _, _, state_handler, _) = test_setup(); - let (mut state, _) = state_handler.load_cloned(&shard).unwrap(); - - let block_number = 3; - let parent_hash = H256::from([1; 32]); - - let header: Header = HeaderT::new( - block_number, - Default::default(), - Default::default(), - parent_hash, - Default::default(), - ); - - TestStf::update_parentchain_litentry_block(&mut state, header.clone()).unwrap(); - - assert_eq!(header.hash(), state.execute_with(ParentchainLitentry::block_hash)); - assert_eq!(parent_hash, state.execute_with(ParentchainLitentry::parent_hash)); - assert_eq!(block_number, state.execute_with(ParentchainLitentry::block_number)); -} - -fn test_signature_must_match_public_sender_in_call() { - // given - let (top_pool_author, _, shard, mrenclave, shielding_key, _, stf_executor) = test_setup(); - - // create accounts - let sender = funded_pair(); - let receiver = unfunded_public(); - - let trusted_operation = TrustedCall::balance_transfer( - Identity::Substrate(receiver.into()), - sender.public().into(), - 1000, - ) - .sign(&sender.into(), 10, &mrenclave, &shard) - .into_trusted_operation(true); - - submit_operation_to_top_pool( - top_pool_author.as_ref(), - &trusted_operation, - &shielding_key, - shard, - ) - .unwrap(); - - let executed_batch = execute_trusted_calls(&shard, stf_executor.as_ref(), &top_pool_author); - - // the top pool doesn't verify signatures, the call will only fail upon execution - assert!(!executed_batch.executed_operations[0].is_success()); -} - -fn test_invalid_nonce_call_is_not_executed() { - // given - let (top_pool_author, _, shard, mrenclave, shielding_key, _, stf_executor) = test_setup(); - - // create accounts - let sender = funded_pair(); - let receiver = unfunded_public(); - - let trusted_operation = TrustedCall::balance_transfer( - Identity::Substrate(sender.public().into()), - receiver.into(), - 1000, - ) - .sign(&sender.into(), 10, &mrenclave, &shard) - .into_trusted_operation(true); - - submit_operation_to_top_pool( - top_pool_author.as_ref(), - &trusted_operation, - &shielding_key, - shard, - ) - .unwrap(); - - let executed_batch = execute_trusted_calls(&shard, stf_executor.as_ref(), &top_pool_author); - - // due to #1488, even invalid nonces will enter the pool ready state, so we can only verify that the call will fail - assert!(!executed_batch.executed_operations[0].is_success()); -} - -fn test_non_root_shielding_call_is_not_executed() { - // given - let (top_pool_author, _state, shard, mrenclave, shielding_key, _, stf_executor) = test_setup(); - - let sender = funded_pair(); - let sender_acc: AccountId = sender.public().into(); - - let signed_call = TrustedCall::balance_shield( - Identity::Substrate(sender_acc.clone().into()), - sender_acc, - 1000, - ParentchainId::Litentry, - ) - .sign(&sender.into(), 0, &mrenclave, &shard); - - submit_operation_to_top_pool( - top_pool_author.as_ref(), - &direct_top(signed_call), - &shielding_key, - shard, - ) - .unwrap(); - - // when - let executed_batch = execute_trusted_calls(&shard, stf_executor.as_ref(), &top_pool_author); - - // then - assert!(!executed_batch.executed_operations[0].is_success()); -} - -fn test_shielding_call_with_enclave_self_is_executed() { - let (top_pool_author, _state, shard, mrenclave, shielding_key, _, stf_executor) = test_setup(); - - let sender = funded_pair(); - let sender_account: AccountId = sender.public().into(); - let enclave_call_signer = enclave_call_signer(&shielding_key); - - let signed_call = TrustedCall::balance_shield( - Identity::Substrate(enclave_call_signer.public().into()), - sender_account, - 1000, - ParentchainId::Litentry, - ) - .sign(&enclave_call_signer.into(), 0, &mrenclave, &shard); - let trusted_operation = - TrustedOperation::::indirect_call(signed_call); - - submit_operation_to_top_pool( - top_pool_author.as_ref(), - &trusted_operation, - &shielding_key, - shard, - ) - .unwrap(); - - // when - let executed_batch = - execute_trusted_calls(&shard, stf_executor.as_ref(), top_pool_author.as_ref()); - - // then - assert_eq!(1, executed_batch.executed_operations.len()); - assert!(executed_batch.executed_operations[0].is_success()); -} - -pub fn test_retrieve_events() { - // given - let (_, mut state, shard, mrenclave, ..) = test_setup(); - let mut opaque_vec = Vec::new(); - let sender = funded_pair(); - let receiver = unendowed_account(); - let transfer_value: u128 = 1_000; - // Events will only get executed after genesis. - state.execute_with(|| set_block_number(100)); - - // Execute a transfer extrinsic to generate events via the Balance pallet. - let trusted_call = TrustedCall::balance_transfer( - Identity::Substrate(sender.public().into()), - receiver.public().into(), - transfer_value, - ) - .sign(&sender.into(), 0, &mrenclave, &shard); - let repo = Arc::new(NodeMetadataRepository::::default()); - let shard = ShardIdentifier::default(); - TestStf::execute_call( - &mut state, - &shard, - trusted_call, - Default::default(), - &mut opaque_vec, - repo, - ) - .unwrap(); - - assert_eq!(TestStf::get_events(&mut state).len(), 4); -} - -pub fn test_retrieve_event_count() { - let (_, mut state, shard, mrenclave, ..) = test_setup(); - let mut opaque_vec = Vec::new(); - let sender = funded_pair(); - let receiver = unendowed_account(); - let transfer_value: u128 = 1_000; - // Events will only get executed after genesis. - state.execute_with(|| set_block_number(100)); - - // Execute a transfer extrinsic to generate events via the Balance pallet. - let trusted_call = TrustedCall::balance_transfer( - Identity::Substrate(sender.public().into()), - receiver.public().into(), - transfer_value, - ) - .sign(&sender.into(), 0, &mrenclave, &shard); - - // when - let repo = Arc::new(NodeMetadataRepository::::default()); - let shard = ShardIdentifier::default(); - TestStf::execute_call( - &mut state, - &shard, - trusted_call, - Default::default(), - &mut opaque_vec, - repo, - ) - .unwrap(); - - let event_count = TestStf::get_event_count(&mut state); - assert_eq!(event_count, 4); -} - -pub fn test_reset_events() { - let (_, mut state, shard, mrenclave, ..) = test_setup(); - let mut opaque_vec = Vec::new(); - let sender = funded_pair(); - let receiver = unendowed_account(); - let transfer_value: u128 = 1_000; - // Events will only get executed after genesis. - state.execute_with(|| set_block_number(100)); - // Execute a transfer extrinsic to generate events via the Balance pallet. - let trusted_call = TrustedCall::balance_transfer( - Identity::Substrate(sender.public().into()), - receiver.public().into(), - transfer_value, - ) - .sign(&sender.into(), 0, &mrenclave, &shard); - let repo = Arc::new(NodeMetadataRepository::::default()); - let shard = ShardIdentifier::default(); - TestStf::execute_call( - &mut state, - &shard, - trusted_call, - Default::default(), - &mut opaque_vec, - repo, - ) - .unwrap(); - let receiver_acc_info = TestStf::get_account_data(&mut state, &receiver.public().into()); - assert_eq!(receiver_acc_info.free, transfer_value); - // Ensure that there really have been events generated. - assert_eq!(TestStf::get_events(&mut state).len(), 4); - - // Remove the events. - TestStf::reset_events(&mut state); - - // Ensure that the events storage has been cleared. - assert_eq!(TestStf::get_events(&mut state).len(), 0); -} - -fn execute_trusted_calls( - shard: &ShardIdentifier, - stf_executor: &TestStfExecutor, - top_pool_author: &TestTopPoolAuthor, -) -> BatchExecutionResult { - let top_pool_calls = top_pool_author.get_pending_trusted_calls(*shard); - stf_executor - .propose_state_update( - &top_pool_calls, - &latest_parentchain_header(), - shard, - Duration::from_millis(600), - |s| s, - ) - .unwrap() -} - -// helper functions -/// Decrypt `encrypted` and decode it into `StatePayload` -pub fn encrypted_state_diff_from_encrypted( - encrypted: &[u8], -) -> StatePayload { - let mut encrypted_payload: Vec = encrypted.to_vec(); - let state_key = state_key(); - state_key.decrypt(&mut encrypted_payload).unwrap(); - StatePayload::decode(&mut encrypted_payload.as_slice()).unwrap() -} - -pub fn state_key() -> Aes { - Aes::default() -} - -/// Some random account that has no funds in the `Stf`'s `test_genesis` config. -pub fn unfunded_public() -> spEd25519::Public { - spEd25519::Public::from_raw(*b"asdfasdfadsfasdfasfasdadfadfasdf") -} - -pub fn test_account() -> spEd25519::Pair { - spEd25519::Pair::from_seed(b"42315678901234567890123456789012") -} - -/// transforms `call` into `TrustedOperation::direct(call)` -pub fn direct_top(call: TrustedCallSigned) -> TrustedOperation { - call.into_trusted_operation(true) -} - -/// Just some random onchain header -pub fn latest_parentchain_header() -> Header { - Header::new(1, Default::default(), Default::default(), [69; 32].into(), Default::default()) -} - -/// Reads the value at `key_hash` from `state_diff` and decodes it into `D` -pub fn get_from_state_diff(state_diff: &SgxExternalitiesDiffType, key_hash: &[u8]) -> D { - // fixme: what's up here with the wrapping?? - state_diff - .get(key_hash) - .unwrap() - .as_ref() - .map(|d| Decode::decode(&mut d.as_slice())) - .unwrap() - .unwrap() -} diff --git a/tee-worker/bitacross/enclave-runtime/src/test/top_pool_tests.rs b/tee-worker/bitacross/enclave-runtime/src/test/top_pool_tests.rs deleted file mode 100644 index c34e3209ac..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/test/top_pool_tests.rs +++ /dev/null @@ -1,210 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::test::{ - fixtures::{ - components::{ - create_ocall_api, create_top_pool, encrypt_trusted_operation, sign_trusted_call, - }, - initialize_test_state::init_state, - }, - mocks::types::{ - TestShieldingKey, TestShieldingKeyRepo, TestSigner, TestStateHandler, TestTopPoolAuthor, - }, -}; -use ita_stf::{ - test_genesis::{endowed_account, unendowed_account}, - Getter, TrustedCall, TrustedCallSigned, -}; -use itc_parentchain_test::ParentchainHeaderBuilder; -use itp_ocall_api::EnclaveAttestationOCallApi; -use itp_sgx_crypto::ShieldingCryptoEncrypt; -use itp_stf_primitives::types::TrustedOperation; -use itp_top_pool_author::{top_filter::AllowAllTopsFilter, traits::AuthorApi}; -use itp_types::{RsaRequest, ShardIdentifier}; -use jsonrpc_core::futures::executor; -use litentry_primitives::Identity; -use log::*; -use sgx_crypto_helper::RsaKeyPair; -use sp_core::Pair; -use std::{sync::Arc, vec::Vec}; - -pub fn process_indirect_call_in_top_pool() { - let _ = env_logger::builder().is_test(true).try_init(); - info!("Setting up test."); - - let signer = TestSigner::from_seed(b"42315678901234567890123456789012"); - let shielding_key = TestShieldingKey::new().unwrap(); - let shielding_key_repo = Arc::new(TestShieldingKeyRepo::new(shielding_key)); - let header = ParentchainHeaderBuilder::default().build(); - - let ocall_api = create_ocall_api(&header, &signer); - - let state_handler = Arc::new(TestStateHandler::default()); - let (_, shard_id) = init_state(state_handler.as_ref(), signer.public().into()); - - let top_pool = create_top_pool(); - - let top_pool_author = Arc::new(TestTopPoolAuthor::new( - top_pool, - AllowAllTopsFilter::::new(), - state_handler, - shielding_key_repo, - )); - - let encrypted_indirect_call = - encrypted_indirect_call(ocall_api.as_ref(), &shard_id, &shielding_key); - - executor::block_on( - top_pool_author.submit_top(RsaRequest::new(shard_id, encrypted_indirect_call)), - ) - .unwrap(); - - assert_eq!(1, top_pool_author.get_pending_trusted_calls(shard_id).len()); -} - -/* -// TODO: use our trusted call for testing - see P-494 - -pub fn submit_shielding_call_to_top_pool() { - let _ = env_logger::builder().is_test(true).try_init(); - - let signer = TestSigner::from_seed(b"42315678901234567890123456789012"); - let shielding_key = TestShieldingKey::new().unwrap(); - let shielding_key_repo = Arc::new(TestShieldingKeyRepo::new(shielding_key)); - let header = ParentchainHeaderBuilder::default().build(); - - let ocall_api = create_ocall_api(&header, &signer); - let mr_enclave = ocall_api.get_mrenclave_of_self().unwrap(); - - let state_handler = Arc::new(TestStateHandler::default()); - let (state, shard_id) = init_state(state_handler.as_ref(), signer.public().into()); - let state_observer = Arc::new(ObserveStateMock::new(state)); - - let top_pool = create_top_pool(); - let (sender, _receiver) = std::sync::mpsc::sync_channel(1000); - - let top_pool_author = Arc::new(TestTopPoolAuthor::new( - top_pool, - AllowAllTopsFilter::::new(), - DirectCallsOnlyFilter::::new(), - state_handler, - shielding_key_repo.clone(), - Arc::new(sender), - )); - - let enclave_signer = - Arc::new(StfEnclaveSigner::<_, _, _, TestStf, _, TrustedCallSigned, Getter>::new( - state_observer, - ocall_api, - shielding_key_repo.clone(), - top_pool_author.clone(), - )); - let node_meta_data_repository = Arc::new(NodeMetadataRepository::default()); - node_meta_data_repository.set_metadata(NodeMetadataMock::new()); - let indirect_calls_executor = IndirectCallsExecutor::< - _, - _, - _, - _, - integritee::ExtrinsicFilter, - TestEventCreator, - integritee::ParentchainEventHandler, - TrustedCallSigned, - Getter, - >::new( - shielding_key_repo, - enclave_signer, - top_pool_author.clone(), - node_meta_data_repository, - ParentchainId::Litentry, - ); - - let block_with_shielding_call = create_shielding_call_extrinsic(shard_id, &shielding_key); - - let _ = indirect_calls_executor - .execute_indirect_calls_in_extrinsics(&block_with_shielding_call, &Vec::new()) - .unwrap(); - - assert_eq!(1, top_pool_author.get_pending_trusted_calls(shard_id).len()); - let trusted_operation = - top_pool_author.get_pending_trusted_calls(shard_id).first().cloned().unwrap(); - let trusted_call = trusted_operation.to_call().unwrap(); - assert!(trusted_call.verify_signature(&mr_enclave.m, &shard_id)); -} - -*/ - -fn encrypted_indirect_call< - AttestationApi: EnclaveAttestationOCallApi, - ShieldingKey: ShieldingCryptoEncrypt, ->( - attestation_api: &AttestationApi, - shard_id: &ShardIdentifier, - shielding_key: &ShieldingKey, -) -> Vec { - let sender = endowed_account(); - let receiver = unendowed_account(); - - let call = TrustedCall::balance_transfer( - Identity::Substrate(sender.public().into()), - receiver.public().into(), - 10000u128, - ); - let call_signed = sign_trusted_call(&call, attestation_api, shard_id, sender); - let trusted_operation = - TrustedOperation::::indirect_call(call_signed); - encrypt_trusted_operation(shielding_key, &trusted_operation) -} - -/* -fn create_opaque_call_extrinsic( - _shard: ShardIdentifier, - _shielding_key: &ShieldingKey, -) -> Block { - let test_signer = ed25519::Pair::from_seed(b"33345678901234567890123456789012"); - let signature = test_signer.sign(&[0u8]); - - let default_extra_for_test = ParentchainExtrinsicParams::new( - 0, - 0, - 0, - H256::default(), - ParentchainAdditionalParams::default(), - ); - - let dummy_node_metadata = NodeMetadataMock::new(); - - let call_index = dummy_node_metadata.post_opaque_task_call_indexes().unwrap(); - let opaque_extrinsic = OpaqueExtrinsic::from_bytes( - ParentchainUncheckedExtrinsic::::new_signed( - (call_index, RsaRequest::default()), - Address::Address32([1u8; 32]), - MultiSignature::Ed25519(signature), - default_extra_for_test.signed_extra(), - ) - .encode() - .as_slice(), - ) - .unwrap(); - - ParentchainBlockBuilder::default() - .with_extrinsics(vec![opaque_extrinsic]) - .build() -} -*/ diff --git a/tee-worker/bitacross/enclave-runtime/src/tls_ra/README.md b/tee-worker/bitacross/enclave-runtime/src/tls_ra/README.md deleted file mode 100644 index 3f4effa148..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/tls_ra/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# provisioning - -each worker runs a provisioning server for other workers of the same MRENCLAVE and shard to get recent stf state and secrets from. - -Light client storage can also be provisioned to avoid re-synching the entire parentchains with each worker - -enclave instances are short-lived on both sides, just for a single request. - -```mermaid -sequenceDiagram -participant untrusted_server -participant enclave_server -participant enclave_client -participant untrusted_client -enclave_server ->> enclave_server: generate shielding & state encryption key -enclave_server ->> enclave_server: init_shard & sync parentchains -untrusted_client ->> untrusted_server: connect TCP -untrusted_client ->> enclave_client: request_state_provisioning -activate enclave_client -untrusted_server ->> enclave_server: run_state_provisioning_server -activate enclave_server -enclave_server ->> enclave_server: load state and secrets -enclave_client ->> enclave_server: open TLS session (including MU RA) -enclave_client ->> enclave_server: request_state_provisioning(shard, account) -enclave_server ->> enclave_client: write_provisioning_payloads -enclave_server ->> enclave_server: add client as vault proxy for shard -enclave_client ->> enclave_client: seal state and secrets to disk -enclave_client -->> untrusted_client: _ -deactivate enclave_client -enclave_server -->> untrusted_server: _ -deactivate enclave_server -untrusted_client --> untrusted_server: disconnect TCP -``` diff --git a/tee-worker/bitacross/enclave-runtime/src/tls_ra/authentication.rs b/tee-worker/bitacross/enclave-runtime/src/tls_ra/authentication.rs deleted file mode 100644 index a3c14528de..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/tls_ra/authentication.rs +++ /dev/null @@ -1,158 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Remote attestation certificate authentication of server and client -use itp_attestation_handler::cert; -use itp_ocall_api::EnclaveAttestationOCallApi; -use log::*; -use sgx_types::*; -use webpki::DNSName; - -pub struct ClientAuth
    { - outdated_ok: bool, - skip_ra: bool, - attestation_ocall: A, -} - -impl ClientAuth { - pub fn new(outdated_ok: bool, skip_ra: bool, attestation_ocall: A) -> Self { - ClientAuth { outdated_ok, skip_ra, attestation_ocall } - } -} - -impl rustls::ClientCertVerifier for ClientAuth -where - A: EnclaveAttestationOCallApi, -{ - fn client_auth_root_subjects( - &self, - _sni: Option<&DNSName>, - ) -> Option { - Some(rustls::DistinguishedNames::new()) - } - - fn verify_client_cert( - &self, - certs: &[rustls::Certificate], - _sni: Option<&DNSName>, - ) -> Result { - debug!("client cert: {:?}", certs); - let issuer = - certs.get(0).ok_or(rustls::TLSError::NoCertificatesPresented).and_then(|cert| { - cert::parse_cert_issuer(&cert.0) - .map_err(|_| rustls::TLSError::NoCertificatesPresented) - })?; - info!("client signer (issuer) is: 0x{}", hex::encode(issuer)); - - // This call will automatically verify cert is properly signed - if self.skip_ra { - warn!("Skip verifying ra-report"); - return Ok(rustls::ClientCertVerified::assertion()) - } - - if certs.is_empty() { - return Err(rustls::TLSError::NoCertificatesPresented) - } - - #[cfg(feature = "dcap")] - let is_dcap = true; - #[cfg(not(feature = "dcap"))] - let is_dcap = false; - match certs.first() { - Some(cert) => { - match cert::verify_mra_cert(&cert.0, true, is_dcap, &self.attestation_ocall) { - Ok(()) => Ok(rustls::ClientCertVerified::assertion()), - Err(sgx_status_t::SGX_ERROR_UPDATE_NEEDED) => - if self.outdated_ok { - warn!("outdated_ok is set, overriding outdated error"); - Ok(rustls::ClientCertVerified::assertion()) - } else { - Err(rustls::TLSError::WebPKIError(webpki::Error::ExtensionValueInvalid)) - }, - Err(_) => - Err(rustls::TLSError::WebPKIError(webpki::Error::ExtensionValueInvalid)), - } - }, - None => Err(rustls::TLSError::WebPKIError(webpki::Error::ExtensionValueInvalid)), - } - } -} - -pub struct ServerAuth { - outdated_ok: bool, - skip_ra: bool, - attestation_ocall: A, -} - -impl ServerAuth { - pub fn new(outdated_ok: bool, skip_ra: bool, attestation_ocall: A) -> Self { - ServerAuth { outdated_ok, skip_ra, attestation_ocall } - } -} - -impl rustls::ServerCertVerifier for ServerAuth -where - A: EnclaveAttestationOCallApi, -{ - fn verify_server_cert( - &self, - _roots: &rustls::RootCertStore, - certs: &[rustls::Certificate], - _hostname: webpki::DNSNameRef, - _ocsp: &[u8], - ) -> Result { - debug!("server cert: {:?}", certs); - let issuer = - certs.get(0).ok_or(rustls::TLSError::NoCertificatesPresented).and_then(|cert| { - cert::parse_cert_issuer(&cert.0) - .map_err(|_| rustls::TLSError::NoCertificatesPresented) - })?; - info!("server signer (issuer) is: 0x{}", hex::encode(issuer)); - - if self.skip_ra { - warn!("Skip verifying ra-report"); - return Ok(rustls::ServerCertVerified::assertion()) - } - - if certs.is_empty() { - return Err(rustls::TLSError::NoCertificatesPresented) - } - - #[cfg(feature = "dcap")] - let is_dcap = true; - #[cfg(not(feature = "dcap"))] - let is_dcap = false; - // This call will automatically verify cert is properly signed - match certs.first() { - Some(cert) => { - match cert::verify_mra_cert(&cert.0, true, is_dcap, &self.attestation_ocall) { - Ok(()) => Ok(rustls::ServerCertVerified::assertion()), - Err(sgx_status_t::SGX_ERROR_UPDATE_NEEDED) => - if self.outdated_ok { - warn!("outdated_ok is set, overriding outdated error"); - Ok(rustls::ServerCertVerified::assertion()) - } else { - Err(rustls::TLSError::WebPKIError(webpki::Error::ExtensionValueInvalid)) - }, - Err(_) => - Err(rustls::TLSError::WebPKIError(webpki::Error::ExtensionValueInvalid)), - } - }, - None => Err(rustls::TLSError::WebPKIError(webpki::Error::ExtensionValueInvalid)), - } - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/tls_ra/mocks.rs b/tee-worker/bitacross/enclave-runtime/src/tls_ra/mocks.rs deleted file mode 100644 index 0d5dea4b4c..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/tls_ra/mocks.rs +++ /dev/null @@ -1,103 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use super::seal_handler::{SealStateAndKeys, UnsealStateAndKeys}; -use crate::error::Result as EnclaveResult; -use itp_types::ShardIdentifier; -use std::{ - sync::{Arc, SgxRwLock as RwLock}, - vec::Vec, -}; - -#[derive(Clone)] -pub struct SealHandlerMock { - pub shielding_key: Arc>>, - pub state_key: Arc>>, - pub state: Arc>>, - pub light_client_state: Arc>>, -} - -impl SealHandlerMock { - pub fn new( - shielding_key: Arc>>, - state_key: Arc>>, - state: Arc>>, - light_client_state: Arc>>, - ) -> Self { - Self { shielding_key, state_key, state, light_client_state } - } -} - -impl SealStateAndKeys for SealHandlerMock { - fn seal_shielding_key(&self, bytes: &[u8]) -> EnclaveResult<()> { - *self.shielding_key.write().unwrap() = bytes.to_vec(); - Ok(()) - } - - fn seal_state_key(&self, bytes: &[u8]) -> EnclaveResult<()> { - *self.state_key.write().unwrap() = bytes.to_vec(); - Ok(()) - } - - fn seal_state(&self, bytes: &[u8], _shard: &ShardIdentifier) -> EnclaveResult<()> { - *self.state.write().unwrap() = bytes.to_vec(); - Ok(()) - } - - fn seal_new_empty_state(&self, _shard: &ShardIdentifier) -> EnclaveResult<()> { - Ok(()) - } - - fn seal_light_client_state(&self, bytes: &[u8]) -> EnclaveResult<()> { - *self.light_client_state.write().unwrap() = bytes.to_vec(); - Ok(()) - } - - fn seal_signers(&self, _bytes: &[u8]) -> EnclaveResult<()> { - Ok(()) - } - - fn seal_enclaves(&self, _bytes: &[u8]) -> EnclaveResult<()> { - Ok(()) - } -} - -impl UnsealStateAndKeys for SealHandlerMock { - fn unseal_shielding_key(&self) -> EnclaveResult> { - Ok(self.shielding_key.read().unwrap().clone()) - } - - fn unseal_state_key(&self) -> EnclaveResult> { - Ok(self.state_key.read().unwrap().clone()) - } - - fn unseal_state(&self, _shard: &ShardIdentifier) -> EnclaveResult> { - Ok(self.state.read().unwrap().clone()) - } - - fn unseal_light_client_state(&self) -> EnclaveResult> { - Ok(self.light_client_state.read().unwrap().clone()) - } - - fn unseal_signers(&self) -> EnclaveResult> { - Ok(vec![]) - } - - fn unseal_enclaves(&self) -> EnclaveResult> { - Ok(vec![]) - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/tls_ra/mod.rs b/tee-worker/bitacross/enclave-runtime/src/tls_ra/mod.rs deleted file mode 100644 index 3e07960dbe..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/tls_ra/mod.rs +++ /dev/null @@ -1,85 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Contains all logic of the state provisioning mechanism -//! including the remote attestation and tls / tcp connection part. - -use codec::{Decode, Encode, MaxEncodedLen}; -use itp_types::{AccountId, ShardIdentifier}; - -mod authentication; -pub mod seal_handler; -mod tls_ra_client; -mod tls_ra_server; - -#[cfg(feature = "test")] -pub mod tests; - -#[cfg(feature = "test")] -pub mod mocks; - -/// Header of an accompanied payload. Indicates the -/// length an the type (opcode) of the following payload. -#[derive(Clone, Debug, Decode, Encode, MaxEncodedLen)] -pub struct TcpHeader { - pub opcode: Opcode, - pub payload_length: u64, -} - -impl TcpHeader { - fn new(opcode: Opcode, payload_length: u64) -> Self { - Self { opcode, payload_length } - } -} - -/// Indicates the payload content type. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Decode, Encode, MaxEncodedLen)] -pub enum Opcode { - ShieldingKey, - StateKey, - State, - LightClient, - Signers, - Enclaves, -} - -impl From for Opcode { - fn from(item: u8) -> Self { - match item { - 0 => Opcode::ShieldingKey, - 1 => Opcode::StateKey, - 2 => Opcode::State, - 3 => Opcode::LightClient, - 4 => Opcode::Signers, - 5 => Opcode::Enclaves, - _ => unimplemented!("Unsupported/unknown Opcode for MU-RA exchange"), - } - } -} - -impl Opcode { - pub fn to_bytes(self) -> [u8; 1] { - (self as u8).to_be_bytes() - } -} - -/// The data structure to be sent by the client to request provisioning -#[derive(Clone, Debug, Eq, PartialEq, Decode, Encode, MaxEncodedLen)] -pub struct ClientProvisioningRequest { - pub shard: ShardIdentifier, - pub account: AccountId, -} diff --git a/tee-worker/bitacross/enclave-runtime/src/tls_ra/seal_handler.rs b/tee-worker/bitacross/enclave-runtime/src/tls_ra/seal_handler.rs deleted file mode 100644 index fe0e9947b8..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/tls_ra/seal_handler.rs +++ /dev/null @@ -1,382 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Abstraction of the reading (unseal) and storing (seal) part of the -//! shielding key, state key and state. - -use crate::error::{Error as EnclaveError, Result as EnclaveResult}; -use bc_enclave_registry::{EnclaveRegistryMap, EnclaveRegistrySealer}; -use bc_signer_registry::{SignerRegistryMap, SignerRegistrySealer}; -use codec::{Decode, Encode}; -use ita_stf::{State as StfState, StateType as StfStateType}; -use itc_parentchain::light_client::LightClientSealing; -use itp_sgx_crypto::{ - key_repository::{AccessKey, MutateKey}, - Aes, -}; -use itp_sgx_externalities::SgxExternalitiesTrait; -use itp_stf_state_handler::handle_state::HandleState; -use itp_types::ShardIdentifier; -use log::*; -use sgx_crypto_helper::rsa3072::Rsa3072KeyPair; -use std::{sync::Arc, vec::Vec}; - -/// Handles the sealing and unsealing of the shielding key, state key and the state. -#[derive(Default)] -pub struct SealHandler< - ShieldingKeyRepository, - StateKeyRepository, - StateHandler, - LightClientSeal, - SignersSeal, - EnclavesSeal, -> { - state_handler: Arc, - state_key_repository: Arc, - shielding_key_repository: Arc, - light_client_seal: Arc, - signers_seal: Arc, - enclaves_seal: Arc, -} - -impl< - ShieldingKeyRepository, - StateKeyRepository, - StateHandler, - LightClientSeal, - SignersSeal, - EnclavesSeal, - > - SealHandler< - ShieldingKeyRepository, - StateKeyRepository, - StateHandler, - LightClientSeal, - SignersSeal, - EnclavesSeal, - > -{ - pub fn new( - state_handler: Arc, - state_key_repository: Arc, - shielding_key_repository: Arc, - light_client_seal: Arc, - signers_seal: Arc, - enclaves_seal: Arc, - ) -> Self { - Self { - state_handler, - state_key_repository, - shielding_key_repository, - light_client_seal, - signers_seal, - enclaves_seal, - } - } -} - -pub trait SealStateAndKeys { - fn seal_shielding_key(&self, bytes: &[u8]) -> EnclaveResult<()>; - fn seal_state_key(&self, bytes: &[u8]) -> EnclaveResult<()>; - fn seal_state(&self, bytes: &[u8], shard: &ShardIdentifier) -> EnclaveResult<()>; - fn seal_new_empty_state(&self, shard: &ShardIdentifier) -> EnclaveResult<()>; - fn seal_light_client_state(&self, bytes: &[u8]) -> EnclaveResult<()>; - fn seal_signers(&self, bytes: &[u8]) -> EnclaveResult<()>; - fn seal_enclaves(&self, bytes: &[u8]) -> EnclaveResult<()>; -} - -pub trait UnsealStateAndKeys { - fn unseal_shielding_key(&self) -> EnclaveResult>; - fn unseal_state_key(&self) -> EnclaveResult>; - fn unseal_state(&self, shard: &ShardIdentifier) -> EnclaveResult>; - fn unseal_light_client_state(&self) -> EnclaveResult>; - fn unseal_signers(&self) -> EnclaveResult>; - fn unseal_enclaves(&self) -> EnclaveResult>; -} - -impl< - ShieldingKeyRepository, - StateKeyRepository, - StateHandler, - LightClientSeal, - SignersSeal, - EnclavesSeal, - > SealStateAndKeys - for SealHandler< - ShieldingKeyRepository, - StateKeyRepository, - StateHandler, - LightClientSeal, - SignersSeal, - EnclavesSeal, - > where - ShieldingKeyRepository: AccessKey + MutateKey, - StateKeyRepository: AccessKey + MutateKey, - StateHandler: HandleState, - LightClientSeal: LightClientSealing, - LightClientSeal::LightClientState: Decode, - SignersSeal: SignerRegistrySealer, - EnclavesSeal: EnclaveRegistrySealer, -{ - fn seal_shielding_key(&self, bytes: &[u8]) -> EnclaveResult<()> { - let key: Rsa3072KeyPair = serde_json::from_slice(bytes).map_err(|e| { - error!(" [Enclave] Received Invalid RSA key"); - EnclaveError::Other(e.into()) - })?; - self.shielding_key_repository.update_key(key)?; - info!("Successfully stored a new shielding key"); - Ok(()) - } - - fn seal_state_key(&self, mut bytes: &[u8]) -> EnclaveResult<()> { - let aes = Aes::decode(&mut bytes)?; - self.state_key_repository.update_key(aes)?; - info!("Successfully stored a new state key"); - Ok(()) - } - - fn seal_state(&self, mut bytes: &[u8], shard: &ShardIdentifier) -> EnclaveResult<()> { - let state = StfStateType::decode(&mut bytes)?; - let state_with_empty_diff = StfState::new(state); - - self.state_handler.reset(state_with_empty_diff, shard)?; - info!("Successfully updated shard {:?} with provisioned state", shard); - Ok(()) - } - - fn seal_light_client_state(&self, mut bytes: &[u8]) -> EnclaveResult<()> { - let state = ::LightClientState::decode(&mut bytes)?; - self.light_client_seal.seal(&state)?; - info!("Successfully sealed light client state"); - Ok(()) - } - - fn seal_signers(&self, mut bytes: &[u8]) -> EnclaveResult<()> { - let state = SignerRegistryMap::decode(&mut bytes)?; - self.signers_seal.seal(state).map_err(|e| { - error!(" [Enclave] Could not seal signers"); - EnclaveError::Other(e.into()) - })?; - info!("Successfully sealed signers state"); - Ok(()) - } - - fn seal_enclaves(&self, mut bytes: &[u8]) -> EnclaveResult<()> { - let state = EnclaveRegistryMap::decode(&mut bytes)?; - self.enclaves_seal.seal(state).map_err(|e| { - error!(" [Enclave] Could not seal enclaves"); - EnclaveError::Other(e.into()) - })?; - info!("Successfully sealed enclaves state"); - Ok(()) - } - - /// Seal an empty, newly initialized state. - /// - /// Requires the shielding key to be sealed and updated before calling this. - /// - /// Call this function in case we don't provision the state itself, only the shielding key. - /// Since the enclave signing account is derived from the shielding key, we need to - /// newly initialize the state with the updated shielding key. - fn seal_new_empty_state(&self, shard: &ShardIdentifier) -> EnclaveResult<()> { - self.state_handler.initialize_shard(*shard)?; - info!("Successfully reset state with new enclave account, for shard {:?}", shard); - Ok(()) - } -} - -impl< - ShieldingKeyRepository, - StateKeyRepository, - StateHandler, - LightClientSeal, - SignerSeal, - EnclavesSeal, - > UnsealStateAndKeys - for SealHandler< - ShieldingKeyRepository, - StateKeyRepository, - StateHandler, - LightClientSeal, - SignerSeal, - EnclavesSeal, - > where - ShieldingKeyRepository: AccessKey + MutateKey, - StateKeyRepository: AccessKey + MutateKey, - StateHandler: HandleState, - LightClientSeal: LightClientSealing, - LightClientSeal::LightClientState: Encode, - SignerSeal: SignerRegistrySealer, - EnclavesSeal: EnclaveRegistrySealer, -{ - fn unseal_shielding_key(&self) -> EnclaveResult> { - let shielding_key = self - .shielding_key_repository - .retrieve_key() - .map_err(|e| EnclaveError::Other(format!("{:?}", e).into()))?; - serde_json::to_vec(&shielding_key).map_err(|e| EnclaveError::Other(e.into())) - } - - fn unseal_state_key(&self) -> EnclaveResult> { - self.state_key_repository - .retrieve_key() - .map(|k| k.encode()) - .map_err(|e| EnclaveError::Other(format!("{:?}", e).into())) - } - - fn unseal_state(&self, shard: &ShardIdentifier) -> EnclaveResult> { - Ok(self.state_handler.execute_on_current(shard, |state, _| state.state.encode())?) - } - - fn unseal_light_client_state(&self) -> EnclaveResult> { - Ok(self.light_client_seal.unseal()?.encode()) - } - - fn unseal_signers(&self) -> EnclaveResult> { - Ok(self - .signers_seal - .unseal() - .map_err(|e| { - error!(" [Enclave] Could not unseal signers"); - EnclaveError::Other(e.into()) - })? - .encode()) - } - - fn unseal_enclaves(&self) -> EnclaveResult> { - Ok(self - .enclaves_seal - .unseal() - .map_err(|e| { - error!(" [Enclave] Could not unseal enclaves"); - EnclaveError::Other(e.into()) - })? - .encode()) - } -} - -#[cfg(feature = "test")] -pub mod test { - use super::*; - use bc_enclave_registry::EnclaveRegistry; - use bc_signer_registry::SignerRegistry; - use itc_parentchain::light_client::mocks::validator_mock_seal::LightValidationStateSealMock; - use itp_sgx_crypto::mocks::KeyRepositoryMock; - use itp_test::mock::handle_state_mock::HandleStateMock; - - type StateKeyRepositoryMock = KeyRepositoryMock; - type ShieldingKeyRepositoryMock = KeyRepositoryMock; - - type SealHandlerMock = SealHandler< - ShieldingKeyRepositoryMock, - StateKeyRepositoryMock, - HandleStateMock, - LightValidationStateSealMock, - SignerRegistry, - EnclaveRegistry, - >; - - pub fn seal_shielding_key_works() { - let seal_handler = SealHandlerMock::default(); - let key_pair_in_bytes = serde_json::to_vec(&Rsa3072KeyPair::default()).unwrap(); - - let result = seal_handler.seal_shielding_key(&key_pair_in_bytes); - - assert!(result.is_ok()); - } - - pub fn seal_shielding_key_fails_for_invalid_key() { - let seal_handler = SealHandlerMock::default(); - - let result = seal_handler.seal_shielding_key(&[1, 2, 3]); - - assert!(result.is_err()); - } - - pub fn unseal_seal_shielding_key_works() { - let seal_handler = SealHandlerMock::default(); - - let key_pair_in_bytes = seal_handler.unseal_shielding_key().unwrap(); - - let result = seal_handler.seal_shielding_key(&key_pair_in_bytes); - - assert!(result.is_ok()); - } - - pub fn seal_state_key_works() { - let seal_handler = SealHandlerMock::default(); - let key_pair_in_bytes = Aes::default().encode(); - - let result = seal_handler.seal_state_key(&key_pair_in_bytes); - - assert!(result.is_ok()); - } - - pub fn seal_state_key_fails_for_invalid_key() { - let seal_handler = SealHandlerMock::default(); - - let result = seal_handler.seal_state_key(&[1, 2, 3]); - - assert!(result.is_err()); - } - - pub fn unseal_seal_state_key_works() { - let seal_handler = SealHandlerMock::default(); - let key_pair_in_bytes = seal_handler.unseal_state_key().unwrap(); - - let result = seal_handler.seal_state_key(&key_pair_in_bytes); - - assert!(result.is_ok()); - } - - pub fn seal_state_works() { - let seal_handler = SealHandlerMock::default(); - let state = ::StateT::default(); - let shard = ShardIdentifier::default(); - let _init_hash = seal_handler.state_handler.initialize_shard(shard).unwrap(); - - let result = seal_handler.seal_state(&state.encode(), &shard); - - assert!(result.is_ok()); - } - - pub fn seal_state_fails_for_invalid_state() { - let seal_handler = SealHandlerMock::default(); - let shard = ShardIdentifier::default(); - - let result = seal_handler.seal_state(&[1, 0, 3], &shard); - - assert!(result.is_err()); - } - - pub fn unseal_seal_state_works() { - let seal_handler = SealHandlerMock::default(); - let shard = ShardIdentifier::default(); - seal_handler.state_handler.initialize_shard(shard).unwrap(); - // Fill our mock state: - let (lock, mut state) = seal_handler.state_handler.load_for_mutation(&shard).unwrap(); - let (key, value) = ("my_key", "my_value"); - state.insert(key.encode(), value.encode()); - seal_handler.state_handler.write_after_mutation(state, lock, &shard).unwrap(); - - let state_in_bytes = seal_handler.unseal_state(&shard).unwrap(); - - let result = seal_handler.seal_state(&state_in_bytes, &shard); - - assert!(result.is_ok()); - } -} diff --git a/tee-worker/bitacross/enclave-runtime/src/tls_ra/tests.rs b/tee-worker/bitacross/enclave-runtime/src/tls_ra/tests.rs deleted file mode 100644 index a96ec8cb11..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/tls_ra/tests.rs +++ /dev/null @@ -1,201 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Tests of tls-ra client / server communication. - -use super::{ - mocks::SealHandlerMock, tls_ra_client::request_state_provisioning_internal, - tls_ra_server::run_state_provisioning_server_internal, -}; -use crate::{ - initialization::global_components::EnclaveStf, - tls_ra::seal_handler::{SealHandler, SealStateAndKeys, UnsealStateAndKeys}, -}; -use bc_enclave_registry::EnclaveRegistry; -use bc_signer_registry::SignerRegistry; -use ita_stf::State; -use itc_parentchain::light_client::mocks::validator_mock_seal::LightValidationStateSealMock; -use itp_sgx_crypto::{mocks::KeyRepositoryMock, Aes}; -use itp_stf_interface::InitState; -use itp_stf_primitives::types::AccountId; -use itp_stf_state_handler::handle_state::HandleState; -use itp_test::mock::handle_state_mock::HandleStateMock; -use itp_types::ShardIdentifier; -use sgx_crypto_helper::{rsa3072::Rsa3072KeyPair, RsaKeyPair}; -use sgx_types::{sgx_quote_sign_type_t, sgx_target_info_t}; -use std::{ - net::{TcpListener, TcpStream}, - os::unix::io::AsRawFd, - string::String, - sync::{Arc, SgxRwLock as RwLock}, - thread, - time::Duration, - vec::Vec, -}; - -static SIGN_TYPE: sgx_quote_sign_type_t = sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE; -static SKIP_RA: i32 = 1; -static QUOTE_SIZE: u32 = 0; - -fn run_state_provisioning_server(seal_handler: impl UnsealStateAndKeys, port: u16) { - let listener = TcpListener::bind(server_addr(port)).unwrap(); - - let (socket, _addr) = listener.accept().unwrap(); - let sgx_target_info: sgx_target_info_t = sgx_target_info_t::default(); - run_state_provisioning_server_internal::<_>( - socket.as_raw_fd(), - SIGN_TYPE, - Some(&sgx_target_info), - Some("E_SIZE), - SKIP_RA, - seal_handler, - ) - .unwrap(); -} - -fn server_addr(port: u16) -> String { - format!("127.0.0.1:{}", port) -} - -pub fn test_tls_ra_server_client_networking() { - let shard = ShardIdentifier::default(); - let client_account = AccountId::from([42; 32]); - let shielding_key_encoded = vec![1, 2, 3]; - let state_key_encoded = vec![5, 2, 3, 7]; - let state_encoded = Vec::from([1u8; 26000]); // Have a decently sized state, so read() must be called multiple times. - let light_client_state_encoded = Vec::from([1u8; 10000]); // Have a decently sized state, so read() must be called multiple times. - - let server_seal_handler = SealHandlerMock::new( - Arc::new(RwLock::new(shielding_key_encoded.clone())), - Arc::new(RwLock::new(state_key_encoded.clone())), - Arc::new(RwLock::new(state_encoded.clone())), - Arc::new(RwLock::new(light_client_state_encoded.clone())), - ); - let initial_client_state = vec![0, 0, 1]; - let initial_client_state_key = vec![0, 0, 2]; - let initial_client_light_client_state = vec![0, 0, 3]; - let client_shielding_key = Arc::new(RwLock::new(Vec::new())); - let client_state_key = Arc::new(RwLock::new(initial_client_state_key.clone())); - let client_state = Arc::new(RwLock::new(initial_client_state.clone())); - let client_light_client_state = Arc::new(RwLock::new(initial_client_light_client_state)); - - let client_seal_handler = SealHandlerMock::new( - client_shielding_key.clone(), - client_state_key.clone(), - client_state.clone(), - client_light_client_state.clone(), - ); - - let port: u16 = 3149; - - // Start server. - let server_thread_handle = thread::spawn(move || { - run_state_provisioning_server(server_seal_handler, port); - }); - thread::sleep(Duration::from_secs(1)); - - // Start client. - let socket = TcpStream::connect(server_addr(port)).unwrap(); - let sgx_target_info: sgx_target_info_t = sgx_target_info_t::default(); - let result = request_state_provisioning_internal( - socket.as_raw_fd(), - SIGN_TYPE, - Some(&sgx_target_info), - Some("E_SIZE), - shard, - SKIP_RA, - client_seal_handler, - client_account, - ); - - // Ensure server thread has finished. - server_thread_handle.join().unwrap(); - - assert!(result.is_ok()); - assert_eq!(*client_shielding_key.read().unwrap(), shielding_key_encoded); - assert_eq!(*client_light_client_state.read().unwrap(), light_client_state_encoded); - - // Sidechain or OffchainWorker - assert_eq!(*client_state.read().unwrap(), state_encoded); - assert_eq!(*client_state_key.read().unwrap(), state_key_encoded); -} - -// Test state and key provisioning with 'real' data structures. -pub fn test_state_and_key_provisioning() { - let client_account = AccountId::from([42; 32]); - let state_key = Aes::new([3u8; 16], [0u8; 16]); - let shielding_key = Rsa3072KeyPair::new().unwrap(); - let initialized_state = EnclaveStf::init_state(AccountId::new([1u8; 32])); - let shard = ShardIdentifier::from([1u8; 32]); - - let server_seal_handler = - create_seal_handler(state_key, shielding_key, initialized_state, &shard); - let client_seal_handler = - create_seal_handler(Aes::default(), Rsa3072KeyPair::default(), State::default(), &shard); - - let port: u16 = 3150; - - // Start server. - let server_thread_handle = thread::spawn(move || { - run_state_provisioning_server(server_seal_handler, port); - }); - thread::sleep(Duration::from_secs(1)); - - // Start client. - let socket = TcpStream::connect(server_addr(port)).unwrap(); - let sgx_target_info: sgx_target_info_t = sgx_target_info_t::default(); - let result = request_state_provisioning_internal( - socket.as_raw_fd(), - SIGN_TYPE, - Some(&sgx_target_info), - Some("E_SIZE), - shard, - SKIP_RA, - client_seal_handler, - client_account, - ); - - // Ensure server thread has finished. - server_thread_handle.join().unwrap(); - - assert!(result.is_ok()); -} - -fn create_seal_handler( - state_key: Aes, - shielding_key: Rsa3072KeyPair, - state: State, - shard: &ShardIdentifier, -) -> impl UnsealStateAndKeys + SealStateAndKeys { - let state_key_repository = Arc::new(KeyRepositoryMock::::new(state_key)); - let shielding_key_repository = - Arc::new(KeyRepositoryMock::::new(shielding_key)); - let state_handler = Arc::new(HandleStateMock::default()); - state_handler.reset(state, shard).unwrap(); - let seal = Arc::new(LightValidationStateSealMock::new()); - let signer_sealer: Arc = Default::default(); - let enclave_sealer: Arc = Default::default(); - - SealHandler::new( - state_handler, - state_key_repository, - shielding_key_repository, - seal, - signer_sealer, - enclave_sealer, - ) -} diff --git a/tee-worker/bitacross/enclave-runtime/src/tls_ra/tls_ra_client.rs b/tee-worker/bitacross/enclave-runtime/src/tls_ra/tls_ra_client.rs deleted file mode 100644 index 0d6aac53c9..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/tls_ra/tls_ra_client.rs +++ /dev/null @@ -1,357 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Implementation of the client part of the state provisioning. - -use super::{authentication::ServerAuth, Opcode, TcpHeader}; -use crate::{ - attestation::create_ra_report_and_signature, - error::{Error as EnclaveError, Result as EnclaveResult}, - initialization::global_components::{ - EnclaveSealHandler, GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL, - GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, GLOBAL_SIGNER_REGISTRY, - GLOBAL_STATE_KEY_REPOSITORY_COMPONENT, - }, - ocall::OcallApi, - shard_config::init_shard_config, - tls_ra::{seal_handler::SealStateAndKeys, ClientProvisioningRequest}, - GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT, GLOBAL_STATE_HANDLER_COMPONENT, -}; -use codec::Encode; - -use itp_attestation_handler::{RemoteAttestationType, DEV_HOSTNAME}; -use itp_component_container::ComponentGetter; - -use itp_ocall_api::EnclaveAttestationOCallApi; -use itp_sgx_crypto::key_repository::AccessPubkey; -use itp_types::{AccountId, ShardIdentifier}; - -use crate::initialization::global_components::GLOBAL_ENCLAVE_REGISTRY; -use log::*; -use rustls::{ClientConfig, ClientSession, Stream}; -use sgx_types::*; -use std::{ - backtrace::{self, PrintFormat}, - convert::TryInto, - io::{Read, Write}, - net::TcpStream, - slice, - sync::Arc, - vec::Vec, -}; -/// Client part of the TCP-level connection and the underlying TLS-level session. -/// -/// Includes a seal handler, which handles the storage part of the received data. -struct TlsClient<'a, StateAndKeySealer> -where - StateAndKeySealer: SealStateAndKeys, -{ - tls_stream: Stream<'a, ClientSession, TcpStream>, - seal_handler: StateAndKeySealer, - shard: ShardIdentifier, -} - -impl<'a, StateAndKeySealer> TlsClient<'a, StateAndKeySealer> -where - StateAndKeySealer: SealStateAndKeys, -{ - fn new( - tls_stream: Stream<'a, ClientSession, TcpStream>, - seal_handler: StateAndKeySealer, - shard: ShardIdentifier, - ) -> TlsClient { - TlsClient { tls_stream, seal_handler, shard } - } - - /// Read all data sent by the server of the specific shard. - /// - /// We trust here that the server sends us the correct data, as - /// we do not have any way to test it. - fn obtain_provisioning_for_shard(&mut self, account: AccountId) -> EnclaveResult<()> { - debug!( - "obtain_provisioning_for_shard called, about to call self.send_provisioning_request()." - ); - self.send_provisioning_request(account)?; - debug!("self.send_provisioning_request() succeeded."); - self.read_and_seal_all() - } - - /// Send the shard of the state we want to receive to the provisioning server. - fn send_provisioning_request(&mut self, account: AccountId) -> EnclaveResult<()> { - debug!("self.send_provisioning_request() called."); - self.tls_stream - .write_all(&ClientProvisioningRequest { shard: self.shard, account }.encode())?; - debug!("write_all succeeded."); - Ok(()) - } - - /// Read and seal all relevant data sent by the server. - fn read_and_seal_all(&mut self) -> EnclaveResult<()> { - let mut received_payloads: Vec = Vec::new(); - - loop { - let maybe_opcode = self.read_and_seal()?; - match maybe_opcode { - None => break, - Some(o) => { - received_payloads.push(o); - }, - } - } - info!("Successfully read and sealed all data sent by the state provisioning server."); - - // In case we receive a shielding key, but no state, we need to reset our state - // to update the enclave account. - if received_payloads.contains(&Opcode::ShieldingKey) - && !received_payloads.contains(&Opcode::State) - { - self.seal_handler.seal_new_empty_state(&self.shard)?; - } - - Ok(()) - } - - /// Read a server header / payload pair and directly seal the received data. - fn read_and_seal(&mut self) -> EnclaveResult> { - let mut start_byte = [0u8; 1]; - let read_size = self.tls_stream.read(&mut start_byte)?; - // If we're reading but there's no data: EOF. - if read_size == 0 { - return Ok(None) - } - let header = self.read_header(start_byte[0])?; - let bytes = self.read_until(header.payload_length as usize)?; - match header.opcode { - Opcode::ShieldingKey => self.seal_handler.seal_shielding_key(&bytes)?, - Opcode::StateKey => self.seal_handler.seal_state_key(&bytes)?, - Opcode::State => self.seal_handler.seal_state(&bytes, &self.shard)?, - Opcode::LightClient => self.seal_handler.seal_light_client_state(&bytes)?, - Opcode::Signers => self.seal_handler.seal_signers(&bytes)?, - Opcode::Enclaves => self.seal_handler.seal_enclaves(&bytes)?, - }; - Ok(Some(header.opcode)) - } - - /// Reads the payload header, indicating the sent payload length and type. - fn read_header(&mut self, start_byte: u8) -> EnclaveResult { - debug!("Read first byte: {:?}", start_byte); - // The first sent byte indicates the payload type. - let opcode: Opcode = start_byte - .try_into() - .map_err(|_| EnclaveError::Other("Could not convert opcode".into()))?; - debug!("Read header opcode: {:?}", opcode); - // The following bytes contain the payload length, which is a u64. - let mut payload_length_buffer = [0u8; std::mem::size_of::()]; - self.tls_stream.read_exact(&mut payload_length_buffer)?; - let payload_length = u64::from_be_bytes(payload_length_buffer); - debug!("Payload length of {:?}: {}", opcode, payload_length); - - Ok(TcpHeader::new(opcode, payload_length)) - } - - /// Read all bytes into a buffer of given length. - fn read_until(&mut self, length: usize) -> EnclaveResult> { - let mut bytes = vec![0u8; length]; - self.tls_stream.read_exact(&mut bytes)?; - Ok(bytes) - } -} - -#[no_mangle] -pub unsafe extern "C" fn request_state_provisioning( - socket_fd: c_int, - sign_type: sgx_quote_sign_type_t, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, - shard: *const u8, - shard_size: u32, - skip_ra: c_int, -) -> sgx_status_t { - let _ = backtrace::enable_backtrace("enclave.signed.so", PrintFormat::Short); - let shard = ShardIdentifier::from_slice(slice::from_raw_parts(shard, shard_size as usize)); - - let state_handler = match GLOBAL_STATE_HANDLER_COMPONENT.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let state_key_repository = match GLOBAL_STATE_KEY_REPOSITORY_COMPONENT.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let shielding_key_repository = match GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let light_client_seal = match GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let signer_registry = match GLOBAL_SIGNER_REGISTRY.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - let enclave_registry = match GLOBAL_ENCLAVE_REGISTRY.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let seal_handler = EnclaveSealHandler::new( - state_handler, - state_key_repository, - shielding_key_repository, - light_client_seal, - signer_registry, - enclave_registry, - ); - - let signing_key_repository = match GLOBAL_SIGNING_KEY_REPOSITORY_COMPONENT.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let client_account = match signing_key_repository.retrieve_pubkey() { - Ok(s) => AccountId::from(s), - Err(e) => return e.into(), - }; - - if let Err(e) = request_state_provisioning_internal( - socket_fd, - sign_type, - quoting_enclave_target_info, - quote_size, - shard, - skip_ra, - seal_handler, - client_account, - ) { - error!("Failed to sync state due to: {:?}", e); - return e.into() - }; - - // fixme: this needs only be called in sidechain mode. no harm though - if let Err(e) = init_shard_config(shard) { - error!("touch shard error: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - } - sgx_status_t::SGX_SUCCESS -} - -/// Internal [`request_state_provisioning`] function to be able to use the handy `?` operator. -// allowing clippy rant because this fn will be refactored with MU RA deprecation -#[allow(clippy::too_many_arguments)] -pub(crate) fn request_state_provisioning_internal( - socket_fd: c_int, - sign_type: sgx_quote_sign_type_t, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, - shard: ShardIdentifier, - skip_ra: c_int, - seal_handler: StateAndKeySealer, - client_account: AccountId, -) -> EnclaveResult<()> { - debug!("Client config generate..."); - let client_config = tls_client_config( - sign_type, - quoting_enclave_target_info, - quote_size, - OcallApi, - skip_ra == 1, - )?; - debug!("Client config retrieved"); - let (mut client_session, mut tcp_stream) = tls_client_session_stream(socket_fd, client_config)?; - debug!("Client sesssion established."); - - let mut client = TlsClient::new( - rustls::Stream::new(&mut client_session, &mut tcp_stream), - seal_handler, - shard, - ); - - info!("Requesting keys and state from mu-ra server of fellow validateer"); - client.obtain_provisioning_for_shard(client_account) -} - -fn tls_client_config( - sign_type: sgx_quote_sign_type_t, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, - ocall_api: A, - skip_ra: bool, -) -> EnclaveResult { - #[cfg(not(feature = "dcap"))] - let attestation_type = RemoteAttestationType::Epid; - #[cfg(feature = "dcap")] - let attestation_type = RemoteAttestationType::Dcap; - - // report will be signed with client enclave ed25519 signing key - let (key_der, cert_der) = create_ra_report_and_signature( - skip_ra, - attestation_type, - sign_type, - quoting_enclave_target_info, - quote_size, - )?; - debug!("got key_der and cert_der"); - - let mut cfg = rustls::ClientConfig::new(); - let certs = vec![rustls::Certificate(cert_der)]; - let privkey = rustls::PrivateKey(key_der); - #[allow(clippy::unwrap_used)] - cfg.set_single_client_cert(certs, privkey).unwrap(); - // ServerAuth will perform MU RA as part of authentication process - cfg.dangerous() - .set_certificate_verifier(Arc::new(ServerAuth::new(true, skip_ra, ocall_api))); - cfg.versions.clear(); - cfg.versions.push(rustls::ProtocolVersion::TLSv1_2); - Ok(cfg) -} - -fn tls_client_session_stream( - socket_fd: i32, - client_config: ClientConfig, -) -> EnclaveResult<(ClientSession, TcpStream)> { - let dns_name = webpki::DNSNameRef::try_from_ascii_str(DEV_HOSTNAME) - .map_err(|e| EnclaveError::Other(e.into()))?; - let sess = rustls::ClientSession::new(&Arc::new(client_config), dns_name); - let conn = TcpStream::new(socket_fd)?; - Ok((sess, conn)) -} diff --git a/tee-worker/bitacross/enclave-runtime/src/tls_ra/tls_ra_server.rs b/tee-worker/bitacross/enclave-runtime/src/tls_ra/tls_ra_server.rs deleted file mode 100644 index a047b821f1..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/tls_ra/tls_ra_server.rs +++ /dev/null @@ -1,340 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Implementation of the server part of the state provisioning. - -use super::{authentication::ClientAuth, ClientProvisioningRequest, Opcode, TcpHeader}; -use crate::{ - attestation::create_ra_report_and_signature, - error::{Error as EnclaveError, Result as EnclaveResult}, - initialization::global_components::{ - EnclaveSealHandler, GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL, - GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT, GLOBAL_SIGNER_REGISTRY, - GLOBAL_STATE_KEY_REPOSITORY_COMPONENT, - }, - ocall::OcallApi, - tls_ra::seal_handler::UnsealStateAndKeys, - GLOBAL_STATE_HANDLER_COMPONENT, -}; - -use crate::initialization::global_components::GLOBAL_ENCLAVE_REGISTRY; -use codec::Decode; -use itp_attestation_handler::RemoteAttestationType; -use itp_component_container::ComponentGetter; -use itp_ocall_api::EnclaveAttestationOCallApi; -use itp_types::ShardIdentifier; -use litentry_primitives::WorkerMode; -use log::*; -use rustls::{ServerConfig, ServerSession, StreamOwned}; -use sgx_types::*; -use std::{ - backtrace::{self, PrintFormat}, - io::{Read, Write}, - net::TcpStream, - sync::Arc, -}; - -#[allow(dead_code)] -#[derive(Clone, Eq, PartialEq, Debug)] -enum ProvisioningPayload { - Everything, - ShieldingKeyAndLightClient, -} - -impl From for ProvisioningPayload { - fn from(m: WorkerMode) -> Self { - match m { - WorkerMode::OffChainWorker => ProvisioningPayload::Everything, - WorkerMode::Sidechain => ProvisioningPayload::Everything, - } - } -} - -/// Server part of the TCP-level connection and the underlying TLS-level session. -/// -/// Includes a seal handler, which handles the reading part of the data to be sent. -struct TlsServer { - tls_stream: StreamOwned, - seal_handler: StateAndKeyUnsealer, - provisioning_payload: ProvisioningPayload, -} - -impl TlsServer -where - StateAndKeyUnsealer: UnsealStateAndKeys, -{ - fn new( - tls_stream: StreamOwned, - seal_handler: StateAndKeyUnsealer, - provisioning_payload: ProvisioningPayload, - ) -> Self { - Self { tls_stream, seal_handler, provisioning_payload } - } - - /// Sends all relevant data of the specific shard to the client. - fn handle_shard_request_from_client(&mut self) -> EnclaveResult<()> { - println!( - " [Enclave] (MU-RA-Server) handle_shard_request_from_client, calling read_shard()" - ); - let request = self.await_shard_request_from_client()?; - println!(" [Enclave] (MU-RA-Server) handle_shard_request_from_client, await_shard_request_from_client() OK"); - println!(" [Enclave] (MU-RA-Server) handle_shard_request_from_client, write_all()"); - self.write_provisioning_payloads(&request.shard) - } - - /// Read the shard of the state the client wants to receive. - fn await_shard_request_from_client(&mut self) -> EnclaveResult { - let mut request = [0u8; std::mem::size_of::()]; - println!( - " [Enclave] (MU-RA-Server) await_shard_request_from_client, calling read_exact()" - ); - self.tls_stream.read_exact(&mut request)?; - ClientProvisioningRequest::decode(&mut request.as_slice()) - .map_err(|_| EnclaveError::Other("matching byte size can't fail to decode".into())) - } - - /// Sends all relevant data to the client. - fn write_provisioning_payloads(&mut self, shard: &ShardIdentifier) -> EnclaveResult<()> { - debug!("Provisioning is set to: {:?}", self.provisioning_payload); - match self.provisioning_payload { - ProvisioningPayload::Everything => { - self.write_shielding_key()?; - self.write_signers()?; - self.write_enclaves()?; - self.write_state_key()?; - self.write_state(shard)?; - self.write_light_client_state()?; - }, - ProvisioningPayload::ShieldingKeyAndLightClient => { - self.write_shielding_key()?; - self.write_light_client_state()?; - }, - } - - debug!("Successfully provisioned all payloads to peer"); - Ok(()) - } - - fn write_shielding_key(&mut self) -> EnclaveResult<()> { - let shielding_key = self.seal_handler.unseal_shielding_key()?; - self.write(Opcode::ShieldingKey, &shielding_key)?; - Ok(()) - } - - fn write_signers(&mut self) -> EnclaveResult<()> { - let signers = self.seal_handler.unseal_signers()?; - self.write(Opcode::Signers, &signers)?; - Ok(()) - } - - fn write_enclaves(&mut self) -> EnclaveResult<()> { - let enclaves = self.seal_handler.unseal_enclaves()?; - self.write(Opcode::Enclaves, &enclaves)?; - Ok(()) - } - - fn write_state_key(&mut self) -> EnclaveResult<()> { - let state_key = self.seal_handler.unseal_state_key()?; - self.write(Opcode::StateKey, &state_key)?; - Ok(()) - } - - fn write_state(&mut self, shard: &ShardIdentifier) -> EnclaveResult<()> { - let state = self.seal_handler.unseal_state(shard)?; - self.write(Opcode::State, &state)?; - Ok(()) - } - - fn write_light_client_state(&mut self) -> EnclaveResult<()> { - let state = self.seal_handler.unseal_light_client_state()?; - self.write(Opcode::LightClient, &state)?; - Ok(()) - } - - /// Sends the header followed by the payload. - fn write(&mut self, opcode: Opcode, bytes: &[u8]) -> EnclaveResult<()> { - let payload_length = bytes.len() as u64; - self.write_header(TcpHeader::new(opcode, payload_length))?; - debug!("Write payload - opcode: {:?}, payload_length: {}", opcode, payload_length); - self.tls_stream.write_all(bytes)?; - Ok(()) - } - - /// Sends the header which includes the payload length and the Opcode indicating the payload type. - fn write_header(&mut self, tcp_header: TcpHeader) -> EnclaveResult<()> { - self.tls_stream.write_all(&tcp_header.opcode.to_bytes())?; - self.tls_stream.write_all(&tcp_header.payload_length.to_be_bytes())?; - debug!( - "Write header - opcode: {:?}, payload length: {}", - tcp_header.opcode, tcp_header.payload_length - ); - Ok(()) - } -} - -#[no_mangle] -pub unsafe extern "C" fn run_state_provisioning_server( - socket_fd: c_int, - sign_type: sgx_quote_sign_type_t, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, - skip_ra: c_int, -) -> sgx_status_t { - let _ = backtrace::enable_backtrace("enclave.signed.so", PrintFormat::Short); - - let state_handler = match GLOBAL_STATE_HANDLER_COMPONENT.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let state_key_repository = match GLOBAL_STATE_KEY_REPOSITORY_COMPONENT.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let shielding_key_repository = match GLOBAL_SHIELDING_KEY_REPOSITORY_COMPONENT.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let light_client_seal = match GLOBAL_INTEGRITEE_PARENTCHAIN_LIGHT_CLIENT_SEAL.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let signer_registry = match GLOBAL_SIGNER_REGISTRY.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - let enclave_registry = match GLOBAL_ENCLAVE_REGISTRY.get() { - Ok(s) => s, - Err(e) => { - error!("{:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - }, - }; - - let seal_handler = EnclaveSealHandler::new( - state_handler, - state_key_repository, - shielding_key_repository, - light_client_seal, - signer_registry, - enclave_registry, - ); - - if let Err(e) = run_state_provisioning_server_internal::<_>( - socket_fd, - sign_type, - quoting_enclave_target_info, - quote_size, - skip_ra, - seal_handler, - ) { - error!("Failed to provision state due to: {:?}", e); - return e.into() - }; - - sgx_status_t::SGX_SUCCESS -} - -/// Internal [`run_state_provisioning_server`] function to be able to use the handy `?` operator. -pub(crate) fn run_state_provisioning_server_internal( - socket_fd: c_int, - sign_type: sgx_quote_sign_type_t, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, - skip_ra: c_int, - seal_handler: StateAndKeyUnsealer, -) -> EnclaveResult<()> { - let server_config = tls_server_config( - sign_type, - quoting_enclave_target_info, - quote_size, - OcallApi, - skip_ra == 1, - )?; - let (server_session, tcp_stream) = tls_server_session_stream(socket_fd, server_config)?; - - let provisioning = ProvisioningPayload::Everything; - - let mut server = - TlsServer::new(StreamOwned::new(server_session, tcp_stream), seal_handler, provisioning); - - // todo: verify client signer belongs to a registered enclave on integritee network with a - // matching or whitelisted MRENCLAVE as replacement for MU RA #1385 - - println!(" [Enclave] (MU-RA-Server) MU-RA successful sending keys"); - println!( - " [Enclave] (MU-RA-Server) MU-RA successful, calling handle_shard_request_from_client()" - ); - server.handle_shard_request_from_client() -} - -fn tls_server_session_stream( - socket_fd: i32, - server_config: ServerConfig, -) -> EnclaveResult<(ServerSession, TcpStream)> { - let sess = ServerSession::new(&Arc::new(server_config)); - let conn = TcpStream::new(socket_fd).map_err(|e| EnclaveError::Other(e.into()))?; - Ok((sess, conn)) -} - -fn tls_server_config( - sign_type: sgx_quote_sign_type_t, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, - ocall_api: A, - skip_ra: bool, -) -> EnclaveResult { - #[cfg(not(feature = "dcap"))] - let attestation_type = RemoteAttestationType::Epid; - #[cfg(feature = "dcap")] - let attestation_type = RemoteAttestationType::Dcap; - - // report will be signed with server enclave ed25519 signing key - let (key_der, cert_der) = create_ra_report_and_signature( - skip_ra, - attestation_type, - sign_type, - quoting_enclave_target_info, - quote_size, - )?; - - // ClientAuth will perform MU RA as part of authentication process - let mut cfg = rustls::ServerConfig::new(Arc::new(ClientAuth::new(true, skip_ra, ocall_api))); - let certs = vec![rustls::Certificate(cert_der)]; - let privkey = rustls::PrivateKey(key_der); - cfg.set_single_cert_with_ocsp_and_sct(certs, privkey, vec![], vec![]) - .map_err(|e| EnclaveError::Other(e.into()))?; - Ok(cfg) -} diff --git a/tee-worker/bitacross/enclave-runtime/src/utils.rs b/tee-worker/bitacross/enclave-runtime/src/utils.rs deleted file mode 100644 index 03fb776de0..0000000000 --- a/tee-worker/bitacross/enclave-runtime/src/utils.rs +++ /dev/null @@ -1,126 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -use crate::{ - error::{Error, Result}, - initialization::global_components::{ - EnclaveExtrinsicsFactory, EnclaveNodeMetadataRepository, EnclaveValidatorAccessor, - GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT, - GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT, GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT, - GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT, GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT, - GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT, - }, -}; -use codec::{Decode, Input}; -use itp_component_container::ComponentGetter; -use std::{result::Result as StdResult, slice, sync::Arc}; - -/// Helper trait to transform the sgx-ffi pointers to any type that implements -/// `parity-scale-codec::Decode` -pub unsafe trait DecodeRaw { - /// the type to decode into - type Decoded: Decode; - - unsafe fn decode_raw<'a, T>( - data: *const T, - len: usize, - ) -> StdResult - where - T: 'a, - &'a [T]: Input; -} - -unsafe impl DecodeRaw for D { - type Decoded = D; - - unsafe fn decode_raw<'a, T>( - data: *const T, - len: usize, - ) -> StdResult - where - T: 'a, - &'a [T]: Input, - { - let mut s = slice::from_raw_parts(data, len); - - Decode::decode(&mut s) - } -} - -pub(crate) fn get_validator_accessor_from_integritee_solo_or_parachain( -) -> Result> { - let validator_accessor = - if let Ok(solochain_handler) = GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT.get() { - solochain_handler.validator_accessor.clone() - } else if let Ok(parachain_handler) = GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT.get() { - parachain_handler.validator_accessor.clone() - } else { - return Err(Error::NoLitentryParentchainAssigned) - }; - Ok(validator_accessor) -} - -pub(crate) fn get_node_metadata_repository_from_integritee_solo_or_parachain( -) -> Result> { - let metadata_repository = - if let Ok(solochain_handler) = GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT.get() { - solochain_handler.node_metadata_repository.clone() - } else if let Ok(parachain_handler) = GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT.get() { - parachain_handler.node_metadata_repository.clone() - } else { - return Err(Error::NoLitentryParentchainAssigned) - }; - Ok(metadata_repository) -} - -pub(crate) fn get_node_metadata_repository_from_target_a_solo_or_parachain( -) -> Result> { - let metadata_repository = - if let Ok(solochain_handler) = GLOBAL_TARGET_A_SOLOCHAIN_HANDLER_COMPONENT.get() { - solochain_handler.node_metadata_repository.clone() - } else if let Ok(parachain_handler) = GLOBAL_TARGET_A_PARACHAIN_HANDLER_COMPONENT.get() { - parachain_handler.node_metadata_repository.clone() - } else { - return Err(Error::NoTargetAParentchainAssigned) - }; - Ok(metadata_repository) -} - -pub(crate) fn get_node_metadata_repository_from_target_b_solo_or_parachain( -) -> Result> { - let metadata_repository = - if let Ok(solochain_handler) = GLOBAL_TARGET_B_SOLOCHAIN_HANDLER_COMPONENT.get() { - solochain_handler.node_metadata_repository.clone() - } else if let Ok(parachain_handler) = GLOBAL_TARGET_B_PARACHAIN_HANDLER_COMPONENT.get() { - parachain_handler.node_metadata_repository.clone() - } else { - return Err(Error::NoTargetBParentchainAssigned) - }; - Ok(metadata_repository) -} - -pub(crate) fn get_extrinsic_factory_from_integritee_solo_or_parachain( -) -> Result> { - let extrinsics_factory = - if let Ok(solochain_handler) = GLOBAL_INTEGRITEE_SOLOCHAIN_HANDLER_COMPONENT.get() { - solochain_handler.extrinsics_factory.clone() - } else if let Ok(parachain_handler) = GLOBAL_INTEGRITEE_PARACHAIN_HANDLER_COMPONENT.get() { - parachain_handler.extrinsics_factory.clone() - } else { - return Err(Error::NoLitentryParentchainAssigned) - }; - Ok(extrinsics_factory) -} diff --git a/tee-worker/bitacross/enclave-runtime/src/vc_issuance_task.rs b/tee-worker/bitacross/enclave-runtime/src/vc_issuance_task.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tee-worker/bitacross/enclave-runtime/x86_64-unknown-linux-sgx.json b/tee-worker/bitacross/enclave-runtime/x86_64-unknown-linux-sgx.json deleted file mode 100644 index 10d37a7490..0000000000 --- a/tee-worker/bitacross/enclave-runtime/x86_64-unknown-linux-sgx.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "arch": "x86_64", - "cpu": "x86-64", - "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", - "dynamic-linking": true, - "env": "sgx", - "exe-allocation-crate": "alloc_system", - "executables": true, - "has-elf-tls": true, - "has-rpath": true, - "linker-flavor": "gcc", - "linker-is-gnu": true, - "llvm-target": "x86_64-unknown-linux-gnu", - "max-atomic-width": 64, - "os": "linux", - "position-independent-executables": true, - "pre-link-args": { - "gcc": [ - "-Wl,--as-needed", - "-Wl,-z,noexecstack", - "-m64" - ] - }, - "relro-level": "full", - "stack-probes": true, - "target-c-int-width": "32", - "target-endian": "little", - "target-family": "unix", - "target-pointer-width": "64", - "vendor": "mesalock" -} diff --git a/tee-worker/bitacross/entrypoint.sh b/tee-worker/bitacross/entrypoint.sh deleted file mode 100755 index ac71403ba5..0000000000 --- a/tee-worker/bitacross/entrypoint.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -log_file='./worker.log' - -touch ${log_file} - -check_env(){ - if [ -z $DATA_DIR ];then - echo "ENV DATA_DIR not set!" - exit 1 - fi -} - -copy_files(){ - for file in key.txt key_production.txt mrenclave.txt spid.txt spid_production.txt; do - wkdir_file="${DATA_DIR}/${file}" - if [ -s ${wkdir_file} ];then - echo "Working file ${wkdir_file} exist, not copy" - else - echo "Copy working file ${file} to ${DATA_DIR}" - cp /origin/${file} ${DATA_DIR}/ - fi - done - # Must copy,Ensure the consistency of binary files. - cp /origin/enclave.signed.so ${DATA_DIR}/ - -} - -runtime(){ - /usr/local/bin/bitacross-worker --version - echo "Worker subcommand is: $@" - /usr/local/bin/bitacross-worker $@ -} - -check_env -copy_files -runtime $@ >> ${log_file} 2>&1 \ No newline at end of file diff --git a/tee-worker/bitacross/example/README.md b/tee-worker/bitacross/example/README.md deleted file mode 100644 index 573f48d9e2..0000000000 --- a/tee-worker/bitacross/example/README.md +++ /dev/null @@ -1,9 +0,0 @@ -Example golang code for connecting to bitacross worker and requesting ethereum signature through trusted direct rpc. - -It connects to worker's trusted direct rpc endpoint exposed at `wss://localhost:2000` and requests `SignEthereum` direct call using JSON-RPC 2.0 protocol. -Direct call is signed by ethereum keypair. - -### Running -Specify worker's trusted rpc port as first argument - -`go run example --port 2000` \ No newline at end of file diff --git a/tee-worker/bitacross/example/client/definitions.json b/tee-worker/bitacross/example/client/definitions.json deleted file mode 100644 index 4824d890fa..0000000000 --- a/tee-worker/bitacross/example/client/definitions.json +++ /dev/null @@ -1,256 +0,0 @@ -{ - "RpcReturnValue": { - "type": "struct", - "type_mapping": [ - [ - "value", - "Vec" - ], - [ - "do_watch", - "bool" - ], - [ - "status", - "DirectRequestStatus" - ] - ] - }, - "DirectRequestStatus": { - "type": "enum", - "type_mapping": [ - [ - "Ok", - "()" - ], - [ - "TrustedOperationStatus", - "(TrustedOperationStatus, H256)" - ], - [ - "Error", - "()" - ], - [ - "Processing", - "H256" - ] - ] - }, - "TrustedOperationStatus": { - "type": "enum", - "type_mapping": [ - [ - "Submitted", - "()" - ], - [ - "Future", - "()" - ], - [ - "Ready", - "()" - ], - [ - "Broadcast", - "()" - ], - [ - "InSidechainBlock", - "H256" - ], - [ - "Retracted", - "()" - ], - [ - "FinalityTimeout", - "()" - ], - [ - "Finalized", - "()" - ], - [ - "Usurped", - "()" - ], - [ - "Dropped", - "()" - ], - [ - "Invalid", - "()" - ], - [ - "TopExecuted", - "(Vec, bool)" - ], - [ - "SuccessorExecuted", - "()" - ] - ] - }, - "DirectCallSigned": { - "type": "struct", - "type_mapping": [ - [ - "call", - "DirectCall" - ], - [ - "signature", - "LitentryMultiSignature" - ] - ] - }, - "SignEthereumPayload": "(bool, bool, bool)", - "SignBitcoinPayload": { - "type": "enum", - "type_mapping": [ - [ - "Derived", - "Vec" - ], - [ - "TaprootUnspendable", - "Vec" - ], - [ - "TaprootSpendable", - "(Vec, [u8; 32])" - ], - [ - "WithTweaks", - "(Vec, Vec<([u8; 32], bool)>)" - ] - ] - }, - "DirectCall": { - "type": "enum", - "type_mapping": [ - [ - "SignBitcoin", - "(LitentryIdentity, SignBitcoinPayload)" - ], - [ - "SignEthereum", - "(LitentryIdentity, PrehashedEthereumMessage)" - ], - [ - "SignTon", - "(LitentryIdentity, Vec)" - ] - ] - }, - "SignBitcoinError": { - "type": "enum", - "type_mapping": [ - ["InvalidSigner", "()"], - ["CeremonyError", "()"] - ] - }, - "SignEthereumError": { - "type": "enum", - "type_mapping": [ - ["InvalidSigner", "()"], - ["SigningError", "()"] - ] - }, - "SignTonError": { - "type": "enum", - "type_mapping": [ - ["InvalidSigner", "()"], - ["SigningError", "()"] - ] - }, - "PrehashedEthereumMessage": "[u8; 32]", - "PlainRequest": { - "type": "struct", - "type_mapping": [ - [ - "shard", - "ShardIdentifier" - ], - [ - "payload", - "Vec" - ] - ] - }, - "ShardIdentifier": "[u8; 32]", - "Address32": "[u8;32]", - "Address20": "[u8;20]", - "Address33": "[u8;33]", - "IdentityString": "Vec", - "LitentryIdentity": { - "type": "enum", - "type_mapping": [ - [ - "Twitter", - "IdentityString" - ], - [ - "Discord", - "IdentityString" - ], - [ - "Github", - "IdentityString" - ], - [ - "Substrate", - "[u8; 32]" - ], - [ - "Evm", - "[u8; 20]" - ], - [ - "Bitcoin", - "[u8; 33]" - ] - ] - }, - "LitentryMultiSignature": { - "type": "enum", - "type_mapping": [ - [ - "Ed25519", - "Ed25519Signature" - ], - [ - "Sr25519", - "Sr25519Signature" - ], - [ - "Ecdsa", - "EcdsaSignature" - ], - [ - "Ethereum", - "EthereumSignature" - ], - [ - "EthereumPrettified", - "EthereumSignature" - ], - [ - "Bitcoin", - "BitcoinSignature" - ], - [ - "BitcoinPrettified", - "BitcoinSignature" - ] - ] - }, - "Ed25519Signature": "([u8; 64])", - "Sr25519Signature": "([u8; 64])", - "EcdsaSignature": "([u8; 65])", - "EthereumSignature": "([u8; 65])", - "BitcoinSignature": "([u8; 65])" -} \ No newline at end of file diff --git a/tee-worker/bitacross/example/client/example.go b/tee-worker/bitacross/example/client/example.go deleted file mode 100644 index 1b11f01f54..0000000000 --- a/tee-worker/bitacross/example/client/example.go +++ /dev/null @@ -1,405 +0,0 @@ -package main - -import ( - "encoding/json" - "flag" - "fmt" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/crypto/blake2b" - "github.com/ethereum/go-ethereum/crypto/secp256k1" - "github.com/itering/scale.go/source" - "github.com/itering/scale.go/types/scaleBytes" - "io/ioutil" -) -import "github.com/gorilla/websocket" -import "crypto/tls" -import "github.com/itering/scale.go/types" -import "github.com/itering/scale.go/utiles" - -type response struct { - Jsonrpc string `json:"jsonrpc"` - Result string `json:"result"` - Id int `json:"id"` -} - -type request struct { - Jsonrpc string `json:"jsonrpc"` - Method string `json:"method"` - Params []string `json:"params"` - Id int `json:"id"` -} - -type rpcResult struct { - Value string `json:"value"` - Do_watch bool `json:"do_watch"` - Status map[string]interface{} `json:"status"` -} - -type Rsa3072PubKey struct { - N [384]byte `json:"n"` - E [4]byte `json:"e"` -} - -func main() { - portPtr := flag.String("port", "2000", "worker's port number") - flag.Parse() - - fmt.Println("port:", *portPtr) - - registerCustomTypes() - c := create_conn(*portPtr) - - //** request shielding key - requestAuthorGetShieldingKey(*c) - res := read_response(*c) - - //** request aggregated public key - requestAggregatedPublicKey(*c) - res = read_response(*c) - - aggregatedPubKeyResult, _ := decodeRpcReturnValue(res.Result) - fmt.Println("Aggregated public key:") - fmt.Println(utiles.HexToBytes(aggregatedPubKeyResult)) - - //** request mrenclave - requestStateGetMrenclave(*c) - res = read_response(*c) - // shard is also mrenclave - getStateMrEnclaveResult, _ := decodeRpcReturnValue(res.Result) - //at this point we got all stuff from worker - shielding key, mrenclave and shard (shard == mrenclave) - - //** WARNING: use this key only for environment without real value - //public 0xffefbfc831e25a4dc6ece5c3600db669132a06ff8db152e3d7a1bbc0a3d425e596e708015b72266e0c6b7975662c794db43846c312ab58a678d9440a42cceba9 - //address 0x144Fa896B5FAbcA9D352483f0741776d1F836094 - key, _ := crypto.HexToECDSA("453134b1fda19819772d2fe7de3c2a8670f930e3187f2a81a509a52500e3a281") - ethAddress := crypto.PubkeyToAddress(key.PublicKey).Bytes() - - fmt.Println("Eth address") - fmt.Println(crypto.PubkeyToAddress(key.PublicKey)) - - //** prepare identity (signer) - identity := map[string]interface{}{ - "Evm": hexutil.Encode(ethAddress), - } - - //** prepare SignEthereum direct call - prehashedEthereumMessage := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 64} - - //** prepare signed direct call - directCall := prepareSignEthereumDirectCall(identity, prehashedEthereumMessage) - encodedDirectCall := types.Encode("DirectCall", directCall) - - fmt.Println(encodedDirectCall) - - encodedMrEnclave := types.Encode("[u8; 32]", getStateMrEnclaveResult) - - //prepare payload to sign - payloadToSign := blake2b.Sum256(prepareDirectCallSignaturePayload(encodedDirectCall, encodedMrEnclave)) - - payloadHash := crypto.Keccak256(payloadToSign[:]) - sig, _ := secp256k1.Sign(payloadHash, math.PaddedBigBytes(key.D, key.Params().BitSize/8)) - - directCallSigned := prepareSignedDirectCall(directCall, sig) - fmt.Println("Direct call signed: ") - fmt.Println(directCallSigned) - encodedDirectCallSigned := types.Encode("DirectCallSigned", directCallSigned) - fmt.Println("Encoded Direct call signed: ") - fmt.Println(encodedDirectCallSigned) - - //** create PlainRequest - plainRequest := map[string]interface{}{ - "shard": getStateMrEnclaveResult, - "payload": hexutil.Encode(utiles.HexToBytes(encodedDirectCallSigned)), - } - - fmt.Println(plainRequest) - - encodedPlainRequest := types.Encode("PlainRequest", plainRequest) - fmt.Println("Encoded plain request:") - fmt.Println(encodedPlainRequest) - - // ** create rpc request with hex encoded scale encoded request - signRequest := request{ - Jsonrpc: "2.0", - Method: "bitacross_submitRequest", - Params: []string{encodedPlainRequest}, - Id: 1, - } - serializedRequest, srErr := json.Marshal(signRequest) - - if srErr != nil { - fmt.Println("Problem while serializing the request") - fmt.Println(srErr) - } - - sendRequest(*c, serializedRequest) - - // ** decode response and parse shielding key, status 0 means success - signResp := read_response(*c) - signResult, signStatus := decodeRpcReturnValue(signResp.Result) - - fmt.Println("Result") - fmt.Println(signResult) - - if _, ok := signStatus["Error"]; ok { - fmt.Println(signResult) - } else { - signature := signResult - fmt.Println("Got signature:") - fmt.Println(signature) - } -} - -func prepareDirectCallSignaturePayload(directCallScaleEncoded string, mrEnclaveScaleEncoded string) []byte { - enclaveAppended := append(utiles.HexToBytes(directCallScaleEncoded), utiles.HexToBytes(mrEnclaveScaleEncoded)...) - shardAppended := append(enclaveAppended, utiles.HexToBytes(mrEnclaveScaleEncoded)...) - return shardAppended -} - -func prepareSignedDirectCall(directCall map[string]interface{}, signature []byte) map[string]interface{} { - return map[string]interface{}{ - "call": directCall, - "signature": map[string]interface{}{ - "Ethereum": map[string]interface{}{ - "col1": hexutil.Encode(signature), - }, - }, - } -} - -func prepareSignEthereumDirectCall(identity map[string]interface{}, prehashedEthereumMessage []byte) map[string]interface{} { - signEthereumDirectCall := map[string]interface{}{ - "col1": identity, - "col2": utiles.BytesToHex(prehashedEthereumMessage), - } - - return map[string]interface{}{ - "SignEthereum": signEthereumDirectCall, - } - -} - -func prepareSignTonDirectCall(identity map[string]interface{}, payload []byte) map[string]interface{} { - signTonDirectCall := map[string]interface{}{ - "col1": identity, - "col2": utiles.BytesToHex(payload), - } - - return map[string]interface{}{ - "SignTon": signTonDirectCall, - } - -} - -func prepareSignBitcoinTaprootSpendableDirectCall(identity map[string]interface{}, bitcoinPayload []byte, merkleRootHash [32]byte) map[string]interface{} { - payload := map[string]interface{}{ - "TaprootSpendable": map[string]interface{}{ - "col1": string(bitcoinPayload), - "col2": utiles.BytesToHex(merkleRootHash[:]), - }, - } - - signBitcoinDirectCall := map[string]interface{}{ - "col1": identity, - "col2": payload, - } - - return map[string]interface{}{ - "SignBitcoin": signBitcoinDirectCall, - } -} - -func prepareSignBitcoinWithTweakDirectCall(identity map[string]interface{}, bitcoinPayload []byte, tweakBytes [32]byte, tweakIsXOnly bool) map[string]interface{} { - tweaks := []map[string]interface{}{ - map[string]interface{}{ - "col1": utiles.BytesToHex(tweakBytes[:]), - "col2": tweakIsXOnly, - }, - } - - payload := map[string]interface{}{ - "WithTweaks": map[string]interface{}{ - "col1": string(bitcoinPayload), - "col2": tweaks, - }, - } - - directCall := map[string]interface{}{ - "col1": identity, - "col2": payload, - } - - return map[string]interface{}{ - "SignBitcoin": directCall, - } -} - -func prepareSignBitcoinTaprootUnspendableDirectCall(identity map[string]interface{}, bitcoinPayload []byte) map[string]interface{} { - payload := map[string]interface{}{ - "TaprootUnspendable": string(bitcoinPayload), - } - - signBitcoinDirectCall := map[string]interface{}{ - "col1": identity, - "col2": payload, - } - - return map[string]interface{}{ - "SignBitcoin": signBitcoinDirectCall, - } - -} - -func prepareSignBitcoinDerivedDirectCall(identity map[string]interface{}, bitcoinPayload []byte) map[string]interface{} { - payload := map[string]interface{}{ - "Derived": string(bitcoinPayload), - } - - signBitcoinDirectCall := map[string]interface{}{ - "col1": identity, - "col2": payload, - } - - return map[string]interface{}{ - "SignBitcoin": signBitcoinDirectCall, - } - -} - -func parseShieldingKey(hexEncodedShieldingKey string) Rsa3072PubKey { - var pubKey Rsa3072PubKey - keyBytes := utiles.HexToBytes(hexEncodedShieldingKey) - //we need to strip first two bytes - I don't know why - err := json.Unmarshal(keyBytes[2:len(keyBytes)], &pubKey) - if err != nil { - fmt.Println("error unmarshaling") - fmt.Println(err) - } - return pubKey -} - -func requestAuthorGetShieldingKey(c websocket.Conn) { - err := c.WriteMessage(websocket.TextMessage, []byte("{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"author_getShieldingKey\",\"params\":[]}")) - if err != nil { - fmt.Println("Error sending message") - fmt.Println(err) - } -} - -func requestAggregatedPublicKey(c websocket.Conn) { - err := c.WriteMessage(websocket.TextMessage, []byte("{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"bitacross_aggregatedPublicKey\",\"params\":[]}")) - if err != nil { - fmt.Println("Error sending message") - fmt.Println(err) - } -} - -func sendRequest(c websocket.Conn, request []byte) { - err := c.WriteMessage(websocket.TextMessage, request) - if err != nil { - fmt.Println("Error sending message") - fmt.Println(err) - } -} - -func requestStateGetMrenclave(c websocket.Conn) { - err := c.WriteMessage(websocket.TextMessage, []byte("{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"state_getMrenclave\",\"params\":[]}")) - if err != nil { - fmt.Println("Error sending message") - fmt.Println(err) - } -} - -func decodeRpcReturnValue(hexEncoded string) (string, map[string]interface{}) { - bytes := scaleBytes.ScaleBytes{Data: utiles.HexToBytes(hexEncoded)} - m := types.ScaleDecoder{} - m.Init(bytes, nil) - var rpcResult rpcResult - err := utiles.UnmarshalAny(m.ProcessAndUpdateData("RpcReturnValue").(interface{}), &rpcResult) - - if err != nil { - fmt.Println("Unmarshall error!") - fmt.Println(err) - } - return rpcResult.Value, rpcResult.Status -} - -func decodeSignBitcoinError(encoded []byte) map[string]interface{} { - bytes := scaleBytes.ScaleBytes{Data: encoded} - m := types.ScaleDecoder{} - m.Init(bytes, &types.ScaleDecoderOption{ - SubType: "string,string", - }) - var output map[string]interface{} - err := utiles.UnmarshalAny(m.ProcessAndUpdateData("SignBitcoinError").(interface{}), &output) - - if err != nil { - fmt.Println("Unmarshall error!") - fmt.Println(err) - } - return output -} - -func decodeSignEthereumError(encoded []byte) map[string]interface{} { - bytes := scaleBytes.ScaleBytes{Data: encoded} - m := types.ScaleDecoder{} - m.Init(bytes, &types.ScaleDecoderOption{ - SubType: "string,string", - }) - var output map[string]interface{} - err := utiles.UnmarshalAny(m.ProcessAndUpdateData("SignEthereumError").(interface{}), &output) - - if err != nil { - fmt.Println("Unmarshall error!") - fmt.Println(err) - } - return output -} - -func read_response(c websocket.Conn) response { - _, message, r_err := c.ReadMessage() - if r_err != nil { - fmt.Println("Error reading message") - fmt.Println(r_err) - } - - res := response{} - if err := json.Unmarshal(message, &res); err != nil { - panic(err) - } - return res -} - -func create_conn(port string) *websocket.Conn { - - dialer := *websocket.DefaultDialer - url := "wss://localhost:" + port - fmt.Println("Connecting to worker:") - fmt.Println(url) - - // this is not secure, use with caution - dialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} - c, _, err := dialer.Dial(url, nil) - if err != nil { - fmt.Println("Could not connect to worker") - fmt.Println(err) - } - fmt.Println("connected to worker") - return c -} - -func registerCustomTypes() { - def, read_err := ioutil.ReadFile("definitions.json") - if read_err != nil { - fmt.Println("Error while reading definitions file") - fmt.Println(read_err) - } - types.RegCustomTypes(source.LoadTypeRegistry(def)) - types.TypeRegistry["[u8; 4]"] = &types.FixedU8{FixedLength: 4} - types.TypeRegistry["[u8; 12]"] = &types.FixedU8{FixedLength: 12} - types.TypeRegistry["[u8; 32]"] = &types.FixedU8{FixedLength: 32} - types.TypeRegistry["[u8; 20]"] = &types.FixedU8{FixedLength: 20} - types.TypeRegistry["[u8; 65]"] = &types.FixedU8{FixedLength: 65} -} diff --git a/tee-worker/bitacross/example/client/go.mod b/tee-worker/bitacross/example/client/go.mod deleted file mode 100644 index adfe3d768a..0000000000 --- a/tee-worker/bitacross/example/client/go.mod +++ /dev/null @@ -1,20 +0,0 @@ -module example - -go 1.21 - -require ( - github.com/ethereum/go-ethereum v1.13.15 - github.com/gorilla/websocket v1.5.1 - github.com/itering/scale.go v1.8.2 -) - -require ( - github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect - github.com/holiman/uint256 v1.2.4 // indirect - github.com/huandu/xstrings v1.4.0 // indirect - github.com/shopspring/decimal v1.3.1 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/sys v0.18.0 // indirect -) diff --git a/tee-worker/bitacross/example/client/go.sum b/tee-worker/bitacross/example/client/go.sum deleted file mode 100644 index 80b37129c8..0000000000 --- a/tee-worker/bitacross/example/client/go.sum +++ /dev/null @@ -1,70 +0,0 @@ -github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= -github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= -github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/ethereum/go-ethereum v1.13.15 h1:U7sSGYGo4SPjP6iNIifNoyIAiNjrmQkz6EwQG+/EZWo= -github.com/ethereum/go-ethereum v1.13.15/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU= -github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= -github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= -github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= -github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= -github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= -github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/itering/scale.go v1.8.2 h1:g+1XCSIdZlrzQDhg0S7gs2HtF6LlH9kmHPzLwqnwlRg= -github.com/itering/scale.go v1.8.2/go.mod h1:mEyWsfihftdLjhpeAo13wnGEM7XE42ZPvFVpC5Lsgqo= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= -github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tee-worker/bitacross/extract_identity b/tee-worker/bitacross/extract_identity deleted file mode 100755 index 2c79268c15..0000000000 --- a/tee-worker/bitacross/extract_identity +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/python3 - -import argparse - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--mrsigner', action="store_true") - args = parser.parse_args() - - line = "" - - searched_header = "enclave_hash.m" - output_header = "MRENCLAVE" - if args.mrsigner: - searched_header = "mrsigner->value" - output_header = "MRSIGNER" - while searched_header not in line: - line = input() - value = list() - line = input() - while line.startswith("0x"): - value += line.strip().split() - try: - line = input() - except: - break - value = "".join(map(lambda x: x.replace("0x",""), value)) -print("{}: {}".format(output_header, value)) diff --git a/tee-worker/bitacross/lib/readme.txt b/tee-worker/bitacross/lib/readme.txt deleted file mode 100644 index 7951405f85..0000000000 --- a/tee-worker/bitacross/lib/readme.txt +++ /dev/null @@ -1 +0,0 @@ -lib \ No newline at end of file diff --git a/tee-worker/bitacross/license_header_scs.txt b/tee-worker/bitacross/license_header_scs.txt deleted file mode 100644 index 6ded8ce2fd..0000000000 --- a/tee-worker/bitacross/license_header_scs.txt +++ /dev/null @@ -1,16 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ \ No newline at end of file diff --git a/tee-worker/bitacross/litentry/core/direct-call/Cargo.toml b/tee-worker/bitacross/litentry/core/direct-call/Cargo.toml deleted file mode 100644 index 749225754d..0000000000 --- a/tee-worker/bitacross/litentry/core/direct-call/Cargo.toml +++ /dev/null @@ -1,59 +0,0 @@ -[package] -authors = ["Trust Computing GmbH "] -edition = "2021" -name = "lc-direct-call" -version = "0.1.0" - -[dependencies] -codec = { package = "parity-scale-codec", workspace = true } -log = { workspace = true } -sp-core = { workspace = true } -sp-io = { workspace = true } - -# internal dependencies -bc-enclave-registry = { path = "../../../bitacross/core/bc-enclave-registry", default-features = false } -bc-musig2-ceremony = { path = "../../../bitacross/core/bc-musig2-ceremony", default-features = false } -bc-relayer-registry = { path = "../../../bitacross/core/bc-relayer-registry", default-features = false } -bc-signer-registry = { path = "../../../bitacross/core/bc-signer-registry", default-features = false } - -itp-sgx-crypto = { workspace = true } -itp-stf-primitives = { workspace = true } -litentry-primitives = { workspace = true } - -sgx_tstd = { workspace = true, optional = true } - -[dev-dependencies] -k256 = { workspace = true, features = ["ecdsa-core", "schnorr"] } -rand = { workspace = true } -hex = { workspace = true } -itp-sgx-crypto = { workspace = true, features = ["std", "mocks"] } - -[features] -default = ["std"] -development = [ - "litentry-primitives/development", -] -sgx = [ - "sgx_tstd", - "bc-musig2-ceremony/sgx", - "bc-enclave-registry/sgx", - "bc-relayer-registry/sgx", - "bc-signer-registry/sgx", - "litentry-primitives/sgx", - "itp-sgx-crypto/sgx", -] -std = [ - "bc-musig2-ceremony/std", - "bc-enclave-registry/std", - "bc-relayer-registry/std", - "bc-signer-registry/std", - "itp-stf-primitives/std", - "itp-sgx-crypto/std", - "litentry-primitives/std", - "sp-core/std", - "sp-io/std", - "codec/std", -] -test = [ - "itp-sgx-crypto/mocks", -] diff --git a/tee-worker/bitacross/litentry/core/direct-call/src/handler/kill_ceremony.rs b/tee-worker/bitacross/litentry/core/direct-call/src/handler/kill_ceremony.rs deleted file mode 100644 index ed7b01502d..0000000000 --- a/tee-worker/bitacross/litentry/core/direct-call/src/handler/kill_ceremony.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -use bc_enclave_registry::EnclaveRegistryLookup; -use bc_musig2_ceremony::CeremonyCommand; -use codec::Encode; -use litentry_primitives::Identity; - -#[derive(Encode, Debug)] -pub enum KillCeremonyError { - InvalidSigner, -} - -pub fn handle( - signer: Identity, - enclave_registry: &ER, -) -> Result { - let is_valid_signer = match signer { - Identity::Substrate(address) => enclave_registry.contains_key(&address), - _ => false, - }; - if !is_valid_signer { - return Err(KillCeremonyError::InvalidSigner) - } - - match signer { - Identity::Substrate(_) => Ok(CeremonyCommand::KillCeremony), - _ => Err(KillCeremonyError::InvalidSigner), - } -} diff --git a/tee-worker/bitacross/litentry/core/direct-call/src/handler/mod.rs b/tee-worker/bitacross/litentry/core/direct-call/src/handler/mod.rs deleted file mode 100644 index 4c7a8d3c03..0000000000 --- a/tee-worker/bitacross/litentry/core/direct-call/src/handler/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -pub mod kill_ceremony; -pub mod nonce_share; -pub mod partial_signature_share; -pub mod sign_bitcoin; -pub mod sign_ethereum; -pub mod sign_ton; diff --git a/tee-worker/bitacross/litentry/core/direct-call/src/handler/nonce_share.rs b/tee-worker/bitacross/litentry/core/direct-call/src/handler/nonce_share.rs deleted file mode 100644 index ba039aea9c..0000000000 --- a/tee-worker/bitacross/litentry/core/direct-call/src/handler/nonce_share.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -use crate::handler::nonce_share::NonceShareError::InvalidSigner; -use bc_enclave_registry::EnclaveRegistryLookup; -use bc_musig2_ceremony::{CeremonyCommand, CeremonyId, PubNonce}; -use codec::Encode; -use litentry_primitives::Identity; -use log::debug; -use std::sync::Arc; - -#[derive(Encode, Debug)] -pub enum NonceShareError { - InvalidSigner, - InvalidNonce, -} - -pub fn handle( - signer: Identity, - ceremony_id: &CeremonyId, - payload: [u8; 66], - enclave_registry: Arc, -) -> Result { - debug!("Received nonce share from: {:?} for ceremony {:?}", signer, ceremony_id); - let is_valid_signer = match signer { - Identity::Substrate(address) => enclave_registry.contains_key(&address), - _ => false, - }; - if !is_valid_signer { - return Err(InvalidSigner) - } - - let nonce = - PubNonce::from_bytes(payload.as_slice()).map_err(|_| NonceShareError::InvalidNonce)?; - - match signer { - Identity::Substrate(address) => Ok(CeremonyCommand::SaveNonce(*address.as_ref(), nonce)), - _ => Err(InvalidSigner), - } -} - -#[cfg(test)] -pub mod test { - use crate::handler::nonce_share::{handle, NonceShareError}; - use alloc::sync::Arc; - use bc_enclave_registry::{EnclaveRegistry, EnclaveRegistryUpdater}; - use bc_musig2_ceremony::SignBitcoinPayload; - use itp_sgx_crypto::{key_repository::AccessKey, schnorr::Pair as SchnorrPair, Error}; - use litentry_primitives::Identity; - use sp_core::{sr25519, Pair}; - - struct SignerAccess {} - - impl AccessKey for SignerAccess { - type KeyType = SchnorrPair; - - fn retrieve_key(&self) -> itp_sgx_crypto::Result { - Err(Error::LockPoisoning) - } - } - - #[test] - pub fn it_should_return_ok_for_enclave_signer() { - // given - let alice_key_pair = sr25519::Pair::from_string("//Alice", None).unwrap(); - let signer_account = Identity::Substrate(alice_key_pair.public().into()); - let ceremony_id = SignBitcoinPayload::Derived(vec![]); - let enclave_registry = Arc::new(EnclaveRegistry::default()); - let _ = - enclave_registry.update(alice_key_pair.public().into(), "localhost:2000".to_string()); - - // when - let result = handle( - signer_account, - &ceremony_id, - [ - 2, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, - 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, 3, 45, 226, - 102, 38, 40, 201, 11, 3, 245, 231, 32, 40, 78, 181, 47, 247, 215, 31, 66, 132, 246, - 39, 182, 138, 133, 61, 120, 199, 142, 31, 254, 147, - ], - enclave_registry, - ); - - // then - assert!(result.is_ok()) - } - - #[test] - pub fn it_should_return_err_for_non_enclave_signer() { - // given - let alice_key_pair = sr25519::Pair::from_string("//Alice", None).unwrap(); - let signer_account = Identity::Substrate(alice_key_pair.public().into()); - let ceremony_id = SignBitcoinPayload::Derived(vec![]); - let enclave_registry = Arc::new(EnclaveRegistry::default()); - - // when - let result = handle( - signer_account, - &ceremony_id, - [ - 2, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, - 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, 3, 45, 226, - 102, 38, 40, 201, 11, 3, 245, 231, 32, 40, 78, 181, 47, 247, 215, 31, 66, 132, 246, - 39, 182, 138, 133, 61, 120, 199, 142, 31, 254, 147, - ], - enclave_registry, - ); - - // then - assert!(matches!(result, Err(NonceShareError::InvalidSigner))) - } -} diff --git a/tee-worker/bitacross/litentry/core/direct-call/src/handler/partial_signature_share.rs b/tee-worker/bitacross/litentry/core/direct-call/src/handler/partial_signature_share.rs deleted file mode 100644 index 38ec1d5b12..0000000000 --- a/tee-worker/bitacross/litentry/core/direct-call/src/handler/partial_signature_share.rs +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -use crate::handler::partial_signature_share::PartialSignatureShareError::InvalidSignature; -use bc_enclave_registry::EnclaveRegistryLookup; -use bc_musig2_ceremony::{CeremonyCommand, CeremonyId, PartialSignature}; -use codec::Encode; -use litentry_primitives::Identity; -use log::debug; -use std::sync::Arc; - -#[derive(Encode, Debug)] -pub enum PartialSignatureShareError { - InvalidSigner, - SignatureSaveError, - InvalidSignature, -} - -pub fn handle( - signer: Identity, - ceremony_id: &CeremonyId, - signature: [u8; 32], - enclave_registry: Arc, -) -> Result { - debug!("Received partial signature share from: {:?} for ceremony {:?}", signer, ceremony_id); - let is_valid_signer = match signer { - Identity::Substrate(address) => enclave_registry.contains_key(&address), - _ => false, - }; - if !is_valid_signer { - return Err(PartialSignatureShareError::InvalidSigner) - } - - match signer { - Identity::Substrate(address) => Ok(CeremonyCommand::SavePartialSignature( - *address.as_ref(), - PartialSignature::from_slice(&signature).map_err(|_| InvalidSignature)?, - )), - _ => Err(PartialSignatureShareError::InvalidSigner), - } -} - -#[cfg(test)] -pub mod test { - use crate::handler::partial_signature_share::{handle, PartialSignatureShareError}; - use alloc::sync::Arc; - use bc_enclave_registry::{EnclaveRegistry, EnclaveRegistryUpdater}; - use bc_musig2_ceremony::SignBitcoinPayload; - use itp_sgx_crypto::{key_repository::AccessKey, schnorr::Pair as SchnorrPair, Error}; - use litentry_primitives::Identity; - use sp_core::{sr25519, Pair}; - - struct SignerAccess {} - - impl AccessKey for SignerAccess { - type KeyType = SchnorrPair; - - fn retrieve_key(&self) -> itp_sgx_crypto::Result { - Err(Error::LockPoisoning) - } - } - - #[test] - pub fn it_should_return_ok_for_enclave_signer() { - // given - let alice_key_pair = sr25519::Pair::from_string("//Alice", None).unwrap(); - let signer_account = Identity::Substrate(alice_key_pair.public().into()); - let ceremony_id = SignBitcoinPayload::Derived(vec![]); - let enclave_registry = Arc::new(EnclaveRegistry::default()); - let _ = - enclave_registry.update(alice_key_pair.public().into(), "localhost:2000".to_string()); - - // when - let result = handle( - signer_account, - &ceremony_id, - [ - 137, 19, 147, 124, 98, 243, 46, 98, 24, 93, 239, 14, 218, 117, 49, 69, 110, 245, - 176, 150, 209, 28, 241, 70, 195, 172, 198, 5, 12, 146, 251, 228, - ], - enclave_registry, - ); - - // then - assert!(result.is_ok()) - } - - #[test] - pub fn it_should_return_err_for_non_enclave_signer() { - // given - let alice_key_pair = sr25519::Pair::from_string("//Alice", None).unwrap(); - let signer_account = Identity::Substrate(alice_key_pair.public().into()); - let ceremony_id = SignBitcoinPayload::Derived(vec![]); - let enclave_registry = Arc::new(EnclaveRegistry::default()); - - // when - let result = handle( - signer_account, - &ceremony_id, - [ - 137, 19, 147, 124, 98, 243, 46, 98, 24, 93, 239, 14, 218, 117, 49, 69, 110, 245, - 176, 150, 209, 28, 241, 70, 195, 172, 198, 5, 12, 146, 251, 228, - ], - enclave_registry, - ); - - // then - assert!(matches!(result, Err(PartialSignatureShareError::InvalidSigner))) - } -} diff --git a/tee-worker/bitacross/litentry/core/direct-call/src/handler/sign_bitcoin.rs b/tee-worker/bitacross/litentry/core/direct-call/src/handler/sign_bitcoin.rs deleted file mode 100644 index 5dfaafa362..0000000000 --- a/tee-worker/bitacross/litentry/core/direct-call/src/handler/sign_bitcoin.rs +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -use bc_enclave_registry::EnclaveRegistryLookup; -use bc_musig2_ceremony::{CeremonyCommand, PublicKey, SignBitcoinPayload, SignersWithKeys}; -use bc_relayer_registry::RelayerRegistryLookup; -use bc_signer_registry::SignerRegistryLookup; -use codec::Encode; -use litentry_primitives::Identity; -use std::sync::Arc; - -#[derive(Encode, Debug)] -pub enum SignBitcoinError { - InvalidSigner, - CeremonyError, -} - -#[allow(clippy::too_many_arguments)] -pub fn handle( - signer: Identity, - payload: SignBitcoinPayload, - relayer_registry: &RRL, - signer_registry: Arc, - enclave_registry: &ER, - check_run: bool, -) -> Result { - if relayer_registry.contains_key(&signer) - || match &signer { - Identity::Substrate(address) => enclave_registry.contains_key(address), - _ => false, - } { - let signers: Result = signer_registry - .get_all() - .iter() - .map(|(address, pub_key)| { - let public_key = PublicKey::from_sec1_bytes(pub_key) - .map_err(|_| SignBitcoinError::CeremonyError)?; - Ok((*address.as_ref(), public_key)) - }) - .collect(); - - Ok(CeremonyCommand::InitCeremony(signers?, payload, check_run)) - } else { - Err(SignBitcoinError::InvalidSigner) - } -} - -#[cfg(test)] -pub mod test { - use crate::handler::sign_bitcoin::{handle, SignBitcoinError}; - use alloc::sync::Arc; - use bc_enclave_registry::{EnclaveRegistry, EnclaveRegistryUpdater}; - use bc_musig2_ceremony::SignBitcoinPayload; - use bc_relayer_registry::{RelayerRegistry, RelayerRegistryUpdater}; - use bc_signer_registry::{PubKey, SignerRegistryLookup}; - use itp_sgx_crypto::{key_repository::AccessKey, schnorr::Pair as SchnorrPair, Error}; - use litentry_primitives::{Address32, Identity}; - use sp_core::{sr25519, Pair}; - - struct SignersRegistryMock {} - - impl SignerRegistryLookup for SignersRegistryMock { - fn contains_key(&self, _account: &Address32) -> bool { - true - } - - fn get_all(&self) -> Vec<(Address32, PubKey)> { - vec![ - ( - Address32::from([0u8; 32]), - [ - 2, 58, 165, 169, 140, 84, 151, 130, 21, 185, 32, 243, 101, 89, 29, 51, 56, - 38, 233, 110, 219, 75, 23, 37, 81, 20, 189, 129, 185, 104, 46, 113, 33, - ], - ), - ( - Address32::from([1u8; 32]), - [ - 2, 33, 158, 56, 188, 136, 36, 56, 255, 109, 228, 17, 179, 63, 196, 98, 40, - 57, 207, 209, 184, 120, 220, 9, 54, 115, 189, 207, 56, 230, 136, 48, 51, - ], - ), - ( - Address32::from([2u8; 32]), - [ - 2, 167, 108, 241, 140, 166, 89, 112, 114, 58, 251, 60, 114, 93, 85, 16, - 221, 20, 31, 40, 78, 234, 124, 2, 156, 166, 18, 246, 230, 29, 49, 229, 58, - ], - ), - ] - } - } - - struct SignerAccess {} - - impl AccessKey for SignerAccess { - type KeyType = SchnorrPair; - - fn retrieve_key(&self) -> itp_sgx_crypto::Result { - Err(Error::LockPoisoning) - } - } - - #[test] - pub fn it_should_return_ok_for_relayer_signer() { - // given - let relayer_registry = RelayerRegistry::default(); - let enclave_registry = EnclaveRegistry::default(); - let alice_key_pair = sr25519::Pair::from_string("//Alice", None).unwrap(); - let relayer_account = Identity::Substrate(alice_key_pair.public().into()); - relayer_registry.update(relayer_account.clone()).unwrap(); - let signers_registry = Arc::new(SignersRegistryMock {}); - - // when - let result = handle( - relayer_account, - SignBitcoinPayload::Derived(vec![]), - &relayer_registry, - signers_registry, - &enclave_registry, - false, - ); - - // then - assert!(result.is_ok()) - } - - #[test] - pub fn it_should_return_ok_for_enclave_signer() { - // given - let relayer_registry = RelayerRegistry::default(); - let enclave_registry = EnclaveRegistry::default(); - let alice_key_pair = sr25519::Pair::from_string("//Alice", None).unwrap(); - let enclave_account = Identity::Substrate(alice_key_pair.public().into()); - enclave_registry.update(alice_key_pair.public().into(), "".to_string()).unwrap(); - let signers_registry = Arc::new(SignersRegistryMock {}); - - // when - let result = handle( - enclave_account, - SignBitcoinPayload::Derived(vec![]), - &relayer_registry, - signers_registry, - &enclave_registry, - false, - ); - - // then - assert!(result.is_ok()) - } - - #[test] - pub fn it_should_return_err_for_non_relayer_and_non_enclave_signer() { - //given - let relayer_registry = RelayerRegistry::default(); - let enclave_registry = EnclaveRegistry::default(); - - let alice_key_pair = sr25519::Pair::from_string("//Alice", None).unwrap(); - let non_relayer_account = Identity::Substrate(alice_key_pair.public().into()); - let signers_registry = Arc::new(SignersRegistryMock {}); - - //when - let result = handle( - non_relayer_account, - SignBitcoinPayload::Derived(vec![]), - &relayer_registry, - signers_registry, - &enclave_registry, - false, - ); - - //then - assert!(matches!(result, Err(SignBitcoinError::InvalidSigner))) - } -} diff --git a/tee-worker/bitacross/litentry/core/direct-call/src/handler/sign_ethereum.rs b/tee-worker/bitacross/litentry/core/direct-call/src/handler/sign_ethereum.rs deleted file mode 100644 index eb07926a7d..0000000000 --- a/tee-worker/bitacross/litentry/core/direct-call/src/handler/sign_ethereum.rs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -use crate::PrehashedEthereumMessage; -use bc_relayer_registry::RelayerRegistryLookup; -use codec::Encode; -use itp_sgx_crypto::{ecdsa::Pair, key_repository::AccessKey}; -use litentry_primitives::Identity; -use log::error; - -#[derive(Encode, Debug)] -pub enum SignEthereumError { - InvalidSigner, - SigningError, -} - -pub fn handle>( - signer: Identity, - msg: PrehashedEthereumMessage, - relayer_registry: &RRL, - key_repository: &EKR, -) -> Result<[u8; 65], SignEthereumError> { - if relayer_registry.contains_key(&signer) { - let key = key_repository.retrieve_key().map_err(|e| { - error!("Could not retrieve ethereum signing key: {}", e); - SignEthereumError::SigningError - })?; - let sig = key.sign_prehash_recoverable(&msg).map_err(|e| { - error!("Could not sign: {}", e); - SignEthereumError::SigningError - })?; - Ok(sig) - } else { - Err(SignEthereumError::InvalidSigner) - } -} - -#[cfg(test)] -pub mod test { - use crate::handler::sign_ethereum::handle; - use bc_relayer_registry::{RelayerRegistry, RelayerRegistryUpdater}; - use itp_sgx_crypto::{ecdsa::Pair as EcdsaPair, mocks::KeyRepositoryMock}; - use k256::{ecdsa::SigningKey, elliptic_curve::rand_core}; - use litentry_primitives::Identity; - use sp_core::{sr25519, Pair}; - - #[test] - pub fn it_should_return_ok_for_relayer_signer() { - //given - let relayer_registry = RelayerRegistry::default(); - let alice_key_pair = sr25519::Pair::from_string("//Alice", None).unwrap(); - let relayer_account = Identity::Substrate(alice_key_pair.public().into()); - relayer_registry.update(relayer_account.clone()).unwrap(); - - let private = SigningKey::random(&mut rand_core::OsRng); - let signing_key = EcdsaPair::new(private); - - let key_repository = KeyRepositoryMock::new(signing_key); - - //when - let result = - handle(relayer_account, Default::default(), &relayer_registry, &key_repository); - - //then - assert!(result.is_ok()) - } - - #[test] - pub fn it_should_return_err_for_non_relayer_signer() { - //given - let relayer_registry = RelayerRegistry::default(); - let alice_key_pair = sr25519::Pair::from_string("//Alice", None).unwrap(); - let non_relayer_account = Identity::Substrate(alice_key_pair.public().into()); - - let private = SigningKey::random(&mut rand_core::OsRng); - let signing_key = EcdsaPair::new(private); - - let key_repository = KeyRepositoryMock::new(signing_key); - - //when - let result = - handle(non_relayer_account, Default::default(), &relayer_registry, &key_repository); - - //then - assert!(result.is_err()) - } - - #[test] - pub fn sign_ethereum_works() { - // test vector from bc team, verified with sp_core::ecdsa::Pair::sign_prehashed - let private_key = - hex::decode("038a5c907573ea7f61a7dcce5ebb2e233a6e9376e5a6f077729bd732d6cab620") - .unwrap(); - let key_pair = EcdsaPair::from_bytes(&private_key).unwrap(); - let payload = - hex::decode("3b08e117290fdd2617ea0e457a8eeebe373c456ecd3f6dc6dc4089380f486516") - .unwrap(); - let result = key_pair.sign_prehash_recoverable(&payload).unwrap(); - let expected_result = hex::decode("e733e8e3cd4f90d8fc10c2f8eeb7183623451b8e1d55b5ab6c4724c5428264955289fac3da7ce2095e12f19b4eb157c55be5c58a09ac8ae3358af0b7ec266a7201").unwrap(); - - assert_eq!(&result, expected_result.as_slice()) - } -} diff --git a/tee-worker/bitacross/litentry/core/direct-call/src/handler/sign_ton.rs b/tee-worker/bitacross/litentry/core/direct-call/src/handler/sign_ton.rs deleted file mode 100644 index 8ab4cee10e..0000000000 --- a/tee-worker/bitacross/litentry/core/direct-call/src/handler/sign_ton.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -use bc_relayer_registry::RelayerRegistryLookup; -use codec::Encode; -use itp_sgx_crypto::key_repository::AccessKey; -use litentry_primitives::Identity; -use log::error; -use sp_core::{ed25519::Pair as Ed25519Pair, Pair}; -use std::vec::Vec; - -#[derive(Encode, Debug)] -pub enum SignTonError { - InvalidSigner, - SigningError, -} - -pub fn handle>( - signer: Identity, - msg: Vec, - relayer_registry: &RRL, - key_repository: &EKR, -) -> Result<[u8; 64], SignTonError> { - if relayer_registry.contains_key(&signer) { - let key = key_repository.retrieve_key().map_err(|e| { - error!("Could not retrieve ton signing key: {}", e); - SignTonError::SigningError - })?; - let sig = key.sign(&msg); - Ok(sig.into()) - } else { - Err(SignTonError::InvalidSigner) - } -} - -#[cfg(test)] -pub mod test { - use crate::handler::sign_ton::handle; - use bc_relayer_registry::{RelayerRegistry, RelayerRegistryUpdater}; - use itp_sgx_crypto::{ecdsa::Pair as EcdsaPair, mocks::KeyRepositoryMock}; - use k256::{ecdsa::SigningKey, elliptic_curve::rand_core}; - use litentry_primitives::Identity; - use sp_core::{sr25519, Pair}; - - #[test] - pub fn it_should_return_ok_for_relayer_signer() { - //given - let relayer_registry = RelayerRegistry::default(); - let alice_key_pair = sr25519::Pair::from_string("//Alice", None).unwrap(); - let relayer_account = Identity::Substrate(alice_key_pair.public().into()); - relayer_registry.update(relayer_account.clone()).unwrap(); - - let signing_key = Pair::from_seed(&[ - 135, 174, 141, 248, 62, 107, 189, 100, 181, 60, 54, 229, 76, 255, 248, 189, 240, 238, - 171, 149, 56, 144, 67, 122, 222, 52, 26, 118, 79, 121, 33, 37, - ]); - - let key_repository = KeyRepositoryMock::new(signing_key); - - //when - let result = - handle(relayer_account, Default::default(), &relayer_registry, &key_repository); - - //then - assert!(result.is_ok()) - } - - #[test] - pub fn it_should_return_err_for_non_relayer_signer() { - //given - let relayer_registry = RelayerRegistry::default(); - let alice_key_pair = sr25519::Pair::from_string("//Alice", None).unwrap(); - let non_relayer_account = Identity::Substrate(alice_key_pair.public().into()); - - let private = SigningKey::random(&mut rand_core::OsRng); - let signing_key = Pair::from_seed(&[ - 135, 174, 141, 248, 62, 107, 189, 100, 181, 60, 54, 229, 76, 255, 248, 189, 240, 238, - 171, 149, 56, 144, 67, 122, 222, 52, 26, 118, 79, 121, 33, 37, - ]); - - let key_repository = KeyRepositoryMock::new(signing_key); - - //when - let result = - handle(non_relayer_account, Default::default(), &relayer_registry, &key_repository); - - //then - assert!(result.is_err()) - } -} diff --git a/tee-worker/bitacross/litentry/core/direct-call/src/lib.rs b/tee-worker/bitacross/litentry/core/direct-call/src/lib.rs deleted file mode 100644 index d2043ec53c..0000000000 --- a/tee-worker/bitacross/litentry/core/direct-call/src/lib.rs +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; -#[cfg(all(not(feature = "std"), feature = "sgx"))] -extern crate sgx_tstd as std; - -use bc_musig2_ceremony::SignBitcoinPayload; -use codec::{Decode, Encode}; -use itp_stf_primitives::types::KeyPair; -use litentry_primitives::{Identity, LitentryMultiSignature, ShardIdentifier}; -use sp_io::hashing::blake2_256; -use std::vec::Vec; - -pub mod handler; - -pub type PrehashedEthereumMessage = [u8; 32]; - -#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] -pub struct DirectCallSigned { - pub call: DirectCall, - pub signature: LitentryMultiSignature, -} - -impl DirectCallSigned { - pub fn verify_signature(&self, mrenclave: &[u8; 32], shard: &ShardIdentifier) -> bool { - let mut payload = self.call.encode(); - payload.append(&mut mrenclave.encode()); - payload.append(&mut shard.encode()); - - self.signature.verify(blake2_256(&payload).as_slice(), self.call.signer()) - } -} - -#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] -pub enum DirectCall { - SignBitcoin(Identity, SignBitcoinPayload), - SignEthereum(Identity, PrehashedEthereumMessage), - SignTon(Identity, Vec), - CheckSignBitcoin(Identity), -} - -impl DirectCall { - pub fn signer(&self) -> &Identity { - match self { - Self::SignBitcoin(signer, ..) => signer, - Self::SignEthereum(signer, ..) => signer, - Self::SignTon(signer, ..) => signer, - Self::CheckSignBitcoin(signer) => signer, - } - } - - pub fn sign( - &self, - pair: &KeyPair, - mrenclave: &[u8; 32], - shard: &ShardIdentifier, - ) -> DirectCallSigned { - let mut payload = self.encode(); - payload.append(&mut mrenclave.encode()); - payload.append(&mut shard.encode()); - - DirectCallSigned { - call: self.clone(), - signature: pair.sign(blake2_256(&payload).as_slice()), - } - } -} - -#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] -pub struct CeremonyRoundCallSigned { - pub call: CeremonyRoundCall, - pub signature: LitentryMultiSignature, -} - -impl CeremonyRoundCallSigned { - pub fn verify_signature(&self, mrenclave: &[u8; 32], shard: &ShardIdentifier) -> bool { - let mut payload = self.call.encode(); - payload.append(&mut mrenclave.encode()); - payload.append(&mut shard.encode()); - - self.signature.verify(blake2_256(&payload).as_slice(), self.call.signer()) - } -} - -#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq)] -pub enum CeremonyRoundCall { - NonceShare(Identity, SignBitcoinPayload, [u8; 66]), - PartialSignatureShare(Identity, SignBitcoinPayload, [u8; 32]), - KillCeremony(Identity, SignBitcoinPayload), -} - -impl CeremonyRoundCall { - pub fn signer(&self) -> &Identity { - match self { - Self::NonceShare(signer, ..) => signer, - Self::PartialSignatureShare(signer, ..) => signer, - Self::KillCeremony(signer, ..) => signer, - } - } - - pub fn sign( - &self, - pair: &KeyPair, - mrenclave: &[u8; 32], - shard: &ShardIdentifier, - ) -> CeremonyRoundCallSigned { - let mut payload = self.encode(); - payload.append(&mut mrenclave.encode()); - payload.append(&mut shard.encode()); - - CeremonyRoundCallSigned { - call: self.clone(), - signature: pair.sign(blake2_256(&payload).as_slice()), - } - } -} diff --git a/tee-worker/bitacross/rust-sgx-sdk/Readme.md b/tee-worker/bitacross/rust-sgx-sdk/Readme.md deleted file mode 100644 index 4c71699c10..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/Readme.md +++ /dev/null @@ -1,5 +0,0 @@ -# RUST-SGX-SDK - -This folder contains only the neccessary parts from the [RUST-SGX-SDK](https://github.com/baidu/rust-sgx-sdk). - -All the crates are directly fetched from github. \ No newline at end of file diff --git a/tee-worker/bitacross/rust-sgx-sdk/buildenv.mk b/tee-worker/bitacross/rust-sgx-sdk/buildenv.mk deleted file mode 100644 index ce28be4e55..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/buildenv.mk +++ /dev/null @@ -1,179 +0,0 @@ -# -# Copyright (C) 2017-2018 Baidu, Inc. All Rights Reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Baidu, Inc., nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# -# - -CP := /bin/cp -f -MKDIR := mkdir -p -STRIP := strip -OBJCOPY := objcopy - -# clean the content of 'INCLUDE' - this variable will be set by vcvars32.bat -# thus it will cause build error when this variable is used by our Makefile, -# when compiling the code under Cygwin tainted by MSVC environment settings. -INCLUDE := - -# turn on stack protector for SDK -COMMON_FLAGS += -fstack-protector - -ifdef DEBUG - COMMON_FLAGS += -O0 -g -DDEBUG -UNDEBUG -else - COMMON_FLAGS += -O2 -D_FORTIFY_SOURCE=2 -UDEBUG -DNDEBUG -endif - -# turn on compiler warnings as much as possible -COMMON_FLAGS += -Wall -Wextra -Winit-self -Wpointer-arith -Wreturn-type \ - -Waddress -Wsequence-point -Wformat-security \ - -Wmissing-include-dirs -Wfloat-equal -Wundef -Wshadow \ - -Wcast-align -Wconversion -Wredundant-decls - -# additional warnings flags for C -CFLAGS += -Wjump-misses-init -Wstrict-prototypes -Wunsuffixed-float-constants - -# additional warnings flags for C++ -CXXFLAGS += -Wnon-virtual-dtor - -# for static_assert() -CXXFLAGS += -std=c++0x - -.DEFAULT_GOAL := all -# this turns off the RCS / SCCS implicit rules of GNU Make -% : RCS/%,v -% : RCS/% -% : %,v -% : s.% -% : SCCS/s.% - -# If a rule fails, delete $@. -.DELETE_ON_ERROR: - -HOST_FILE_PROGRAM := file - -UNAME := $(shell uname -m) -ifneq (,$(findstring 86,$(UNAME))) - HOST_ARCH := x86 - ifneq (,$(shell $(HOST_FILE_PROGRAM) -L $(SHELL) | grep 'x86[_-]64')) - HOST_ARCH := x86_64 - endif -else - $(info Unknown host CPU architecture $(UNAME)) - $(error Aborting) -endif - - -ifeq "$(findstring __INTEL_COMPILER, $(shell $(CC) -E -dM -xc /dev/null))" "__INTEL_COMPILER" - ifeq ($(shell test -f /usr/bin/dpkg; echo $$?), 0) - ADDED_INC := -I /usr/include/$(shell dpkg-architecture -qDEB_BUILD_MULTIARCH) - endif -endif - -ARCH := $(HOST_ARCH) -ifeq "$(findstring -m32, $(CXXFLAGS))" "-m32" - ARCH := x86 -endif - -ifeq ($(ARCH), x86) -COMMON_FLAGS += -DITT_ARCH_IA32 -else -COMMON_FLAGS += -DITT_ARCH_IA64 -endif - -CFLAGS += $(COMMON_FLAGS) -CXXFLAGS += $(COMMON_FLAGS) - -# Enable the security flags -COMMON_LDFLAGS := -Wl,-z,relro,-z,now,-z,noexecstack - -# mitigation options -MITIGATION_INDIRECT ?= 0 -MITIGATION_RET ?= 0 -MITIGATION_C ?= 0 -MITIGATION_ASM ?= 0 -MITIGATION_AFTERLOAD ?= 0 -MITIGATION_LIB_PATH := - -ifeq ($(MITIGATION-CVE-2020-0551), LOAD) - MITIGATION_C := 1 - MITIGATION_ASM := 1 - MITIGATION_INDIRECT := 1 - MITIGATION_RET := 1 - MITIGATION_AFTERLOAD := 1 - MITIGATION_LIB_PATH := cve_2020_0551_load -else ifeq ($(MITIGATION-CVE-2020-0551), CF) - MITIGATION_C := 1 - MITIGATION_ASM := 1 - MITIGATION_INDIRECT := 1 - MITIGATION_RET := 1 - MITIGATION_AFTERLOAD := 0 - MITIGATION_LIB_PATH := cve_2020_0551_cf -endif - -MITIGATION_CFLAGS := -MITIGATION_ASFLAGS := -ifeq ($(MITIGATION_C), 1) -ifeq ($(MITIGATION_INDIRECT), 1) - MITIGATION_CFLAGS += -mindirect-branch-register -endif -ifeq ($(MITIGATION_RET), 1) - MITIGATION_CFLAGS += -mfunction-return=thunk-extern -endif -endif - -ifeq ($(MITIGATION_ASM), 1) - MITIGATION_ASFLAGS += -fno-plt -ifeq ($(MITIGATION_AFTERLOAD), 1) - MITIGATION_ASFLAGS += -Wa,-mlfence-after-load=yes -else - MITIGATION_ASFLAGS += -Wa,-mlfence-before-indirect-branch=register -endif -ifeq ($(MITIGATION_RET), 1) - MITIGATION_ASFLAGS += -Wa,-mlfence-before-ret=not -endif -endif - -MITIGATION_CFLAGS += $(MITIGATION_ASFLAGS) - -# Compiler and linker options for an Enclave -# -# We are using '--export-dynamic' so that `g_global_data_sim' etc. -# will be exported to dynamic symbol table. -# -# When `pie' is enabled, the linker (both BFD and Gold) under Ubuntu 14.04 -# will hide all symbols from dynamic symbol table even if they are marked -# as `global' in the LD version script. -ENCLAVE_CFLAGS = -ffreestanding -nostdinc -fvisibility=hidden -fpie -fno-strict-overflow -fno-delete-null-pointer-checks -ENCLAVE_CXXFLAGS = $(ENCLAVE_CFLAGS) -nostdinc++ -ENCLAVE_LDFLAGS = $(COMMON_LDFLAGS) -Wl,-Bstatic -Wl,-Bsymbolic -Wl,--no-undefined \ - -Wl,-pie,-eenclave_entry -Wl,--export-dynamic \ - -Wl,--gc-sections \ - -Wl,--defsym,__ImageBase=0 - -ENCLAVE_CFLAGS += $(MITIGATION_CFLAGS) -ENCLAVE_ASFLAGS = $(MITIGATION_ASFLAGS) \ No newline at end of file diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/assert.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/assert.h deleted file mode 100644 index a153995416..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/assert.h +++ /dev/null @@ -1,63 +0,0 @@ -/* $OpenBSD: assert.h,v 1.12 2006/01/31 10:53:51 hshoexer Exp $ */ -/* $NetBSD: assert.h,v 1.6 1994/10/26 00:55:44 cgd Exp $ */ - -/*- - * Copyright (c) 1992, 1993 - * The Regents of the University of California. All rights reserved. - * (c) UNIX System Laboratories, Inc. - * All or some portions of this file are derived from material licensed - * to the University of California by American Telephone and Telegraph - * Co. or Unix System Laboratories, Inc. and are reproduced herein with - * the permission of UNIX System Laboratories, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)assert.h 8.2 (Berkeley) 1/21/94 - */ - -/* - * Unlike other ANSI header files, may usefully be included - * multiple times, with and without NDEBUG defined. - */ - -#include - -#undef assert - -#ifdef NDEBUG -# define assert(e) ((void)0) -#else -# define assert(e) ((e) ? (void)0 : __assert(__FILE__, __LINE__, __func__, #e)) -#endif - -#ifndef _ASSERT_H_DECLS -#define _ASSERT_H_DECLS -__BEGIN_DECLS - -void _TLIBC_CDECL_ __assert(const char *, int, const char *, const char *); - -__END_DECLS -#endif /* Not _ASSERT_H_DECLS */ - diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/complex.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/complex.h deleted file mode 100644 index 904cb31fbf..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/complex.h +++ /dev/null @@ -1,134 +0,0 @@ -/* $OpenBSD: complex.h,v 1.3 2010/07/24 22:17:03 guenther Exp $ */ -/* - * Copyright (c) 2008 Martynas Venckus - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _COMPLEX_H_ -#define _COMPLEX_H_ - -#include - -/* - * C99 - */ -#ifdef __GNUC__ -#if __STDC_VERSION__ < 199901 -#define _Complex __complex__ -#endif -#define _Complex_I 1.0fi -#elif defined(lint) -#define _Complex_I 1.0fi -#endif - -#define complex _Complex - -/* XXX switch to _Imaginary_I */ -#undef I -#define I _Complex_I - -__BEGIN_DECLS -/* - * Double versions of C99 functions - */ -double complex cacos(double complex); -double complex casin(double complex); -double complex catan(double complex); -double complex ccos(double complex); -double complex csin(double complex); -double complex ctan(double complex); -double complex cacosh(double complex); -double complex casinh(double complex); -double complex catanh(double complex); -double complex ccosh(double complex); -double complex csinh(double complex); -double complex ctanh(double complex); -double complex cexp(double complex); -double complex clog(double complex); -double cabs(double complex); -double complex cpow(double complex, double complex); -double complex csqrt(double complex); -double carg(double complex); -double cimag(double complex); -double complex conj(double complex); -double complex cproj(double complex); -double creal(double complex); -/* - * C99 reserved - */ -double complex clog10(double complex); - -/* - * Float versions of C99 functions - */ -float complex cacosf(float complex); -float complex casinf(float complex); -float complex catanf(float complex); -float complex ccosf(float complex); -float complex csinf(float complex); -float complex ctanf(float complex); -float complex cacoshf(float complex); -float complex casinhf(float complex); -float complex catanhf(float complex); -float complex ccoshf(float complex); -float complex csinhf(float complex); -float complex ctanhf(float complex); -float complex cexpf(float complex); -float complex clogf(float complex); -float cabsf(float complex); -float complex cpowf(float complex, float complex); -float complex csqrtf(float complex); -float cargf(float complex); -float cimagf(float complex); -float complex conjf(float complex); -float complex cprojf(float complex); -float crealf(float complex); -/* - * C99 reserved - */ -float complex clog10f(float complex); - -/* - * Long double versions of C99 functions - */ -long double complex cacosl(long double complex); -long double complex casinl(long double complex); -long double complex catanl(long double complex); -long double complex ccosl(long double complex); -long double complex csinl(long double complex); -long double complex ctanl(long double complex); -long double complex cacoshl(long double complex); -long double complex casinhl(long double complex); -long double complex catanhl(long double complex); -long double complex ccoshl(long double complex); -long double complex csinhl(long double complex); -long double complex ctanhl(long double complex); -long double complex cexpl(long double complex); -long double complex clogl(long double complex); -long double cabsl(long double complex); -long double complex cpowl(long double complex, long double complex); -long double complex csqrtl(long double complex); -long double cargl(long double complex); -long double cimagl(long double complex); -long double complex conjl(long double complex); -long double complex cprojl(long double complex); -long double creall(long double complex); -/* - * C99 reserved - */ -long double complex clog10l(long double complex); - -__END_DECLS - -#endif /* !_COMPLEX_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/ctype.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/ctype.h deleted file mode 100644 index 57ac70ff11..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/ctype.h +++ /dev/null @@ -1,65 +0,0 @@ -/* $OpenBSD: ctype.h,v 1.22 2010/10/01 20:10:24 guenther Exp $ */ -/* $NetBSD: ctype.h,v 1.14 1994/10/26 00:55:47 cgd Exp $ */ - -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * (c) UNIX System Laboratories, Inc. - * All or some portions of this file are derived from material licensed - * to the University of California by American Telephone and Telegraph - * Co. or Unix System Laboratories, Inc. and are reproduced herein with - * the permission of UNIX System Laboratories, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)ctype.h 5.3 (Berkeley) 4/3/91 - */ - -#ifndef _CTYPE_H_ -#define _CTYPE_H_ - -#include - -__BEGIN_DECLS - -int _TLIBC_CDECL_ isalnum(int); -int _TLIBC_CDECL_ isalpha(int); -int _TLIBC_CDECL_ iscntrl(int); -int _TLIBC_CDECL_ isdigit(int); -int _TLIBC_CDECL_ isgraph(int); -int _TLIBC_CDECL_ islower(int); -int _TLIBC_CDECL_ isprint(int); -int _TLIBC_CDECL_ ispunct(int); -int _TLIBC_CDECL_ isspace(int); -int _TLIBC_CDECL_ isupper(int); -int _TLIBC_CDECL_ isxdigit(int); -int _TLIBC_CDECL_ tolower(int); -int _TLIBC_CDECL_ toupper(int); -int _TLIBC_CDECL_ isblank(int); -int _TLIBC_CDECL_ isascii(int); - -__END_DECLS - -#endif /* _CTYPE_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/dirent.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/dirent.h deleted file mode 100644 index a0ede0375c..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/dirent.h +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright © 2005-2020 Rich Felker, et al. -// Licensed under the MIT license.s -// - -/* Copyright © 2005-2020 Rich Felker, et al. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#ifndef _DIRENT_H_ -#define _DIRENT_H_ - -struct dirent { - __ino_t d_ino; - __off_t d_off; - unsigned short d_reclen; - unsigned char d_type; - char d_name[256]; -}; - -struct dirent64 { - __ino64_t d_ino; - __off64_t d_off; - unsigned short d_reclen; - unsigned char d_type; - char d_name[256]; -}; - -#define d_fileno d_ino - -#endif /* _DIRENT_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/endian.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/endian.h deleted file mode 100644 index 2620c5898f..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/endian.h +++ /dev/null @@ -1,33 +0,0 @@ -/* $OpenBSD: endian.h,v 1.18 2006/03/27 07:09:24 otto Exp $ */ - -/*- - * Copyright (c) 1997 Niklas Hallqvist. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _ENDIAN_H_ -#define _ENDIAN_H_ - -#include - -#endif /* _ENDIAN_H_ */ - diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/errno.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/errno.h deleted file mode 100644 index dbe293cb9e..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/errno.h +++ /dev/null @@ -1,187 +0,0 @@ -/* $OpenBSD: errno.h,v 1.1 2005/12/28 16:33:56 millert Exp $ */ - -/* - * Copyright (c) 1982, 1986, 1989, 1993 - * The Regents of the University of California. All rights reserved. - * (c) UNIX System Laboratories, Inc. - * All or some portions of this file are derived from material licensed - * to the University of California by American Telephone and Telegraph - * Co. or Unix System Laboratories, Inc. and are reproduced herein with - * the permission of UNIX System Laboratories, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)errno.h 8.5 (Berkeley) 1/21/94 - */ - -#ifndef _ERRNO_H_ -#define _ERRNO_H_ - -#include - -#define EPERM 1 -#define ENOENT 2 -#define ESRCH 3 -#define EINTR 4 -#define EIO 5 -#define ENXIO 6 -#define E2BIG 7 -#define ENOEXEC 8 -#define EBADF 9 -#define ECHILD 10 -#define EAGAIN 11 -#define ENOMEM 12 -#define EACCES 13 -#define EFAULT 14 -#define ENOTBLK 15 -#define EBUSY 16 -#define EEXIST 17 -#define EXDEV 18 -#define ENODEV 19 -#define ENOTDIR 20 -#define EISDIR 21 -#define EINVAL 22 -#define ENFILE 23 -#define EMFILE 24 -#define ENOTTY 25 -#define ETXTBSY 26 -#define EFBIG 27 -#define ENOSPC 28 -#define ESPIPE 29 -#define EROFS 30 -#define EMLINK 31 -#define EPIPE 32 -#define EDOM 33 -#define ERANGE 34 -#define EDEADLK 35 -#define ENAMETOOLONG 36 -#define ENOLCK 37 -#define ENOSYS 38 -#define ENOTEMPTY 39 -#define ELOOP 40 -#define EWOULDBLOCK EAGAIN -#define ENOMSG 42 -#define EIDRM 43 -#define ECHRNG 44 -#define EL2NSYNC 45 -#define EL3HLT 46 -#define EL3RST 47 -#define ELNRNG 48 -#define EUNATCH 49 -#define ENOCSI 50 -#define EL2HLT 51 -#define EBADE 52 -#define EBADR 53 -#define EXFULL 54 -#define ENOANO 55 -#define EBADRQC 56 -#define EBADSLT 57 -#define EDEADLOCK EDEADLK -#define EBFONT 59 -#define ENOSTR 60 -#define ENODATA 61 -#define ETIME 62 -#define ENOSR 63 -#define ENONET 64 -#define ENOPKG 65 -#define EREMOTE 66 -#define ENOLINK 67 -#define EADV 68 -#define ESRMNT 69 -#define ECOMM 70 -#define EPROTO 71 -#define EMULTIHOP 72 -#define EDOTDOT 73 -#define EBADMSG 74 -#define EOVERFLOW 75 -#define ENOTUNIQ 76 -#define EBADFD 77 -#define EREMCHG 78 -#define ELIBACC 79 -#define ELIBBAD 80 -#define ELIBSCN 81 -#define ELIBMAX 82 -#define ELIBEXEC 83 -#define EILSEQ 84 -#define ERESTART 85 -#define ESTRPIPE 86 -#define EUSERS 87 -#define ENOTSOCK 88 -#define EDESTADDRREQ 89 -#define EMSGSIZE 90 -#define EPROTOTYPE 91 -#define ENOPROTOOPT 92 -#define EPROTONOSUPPORT 93 -#define ESOCKTNOSUPPORT 94 -#define EOPNOTSUPP 95 -#define EPFNOSUPPORT 96 -#define EAFNOSUPPORT 97 -#define EADDRINUSE 98 -#define EADDRNOTAVAIL 99 -#define ENETDOWN 100 -#define ENETUNREACH 101 -#define ENETRESET 102 -#define ECONNABORTED 103 -#define ECONNRESET 104 -#define ENOBUFS 105 -#define EISCONN 106 -#define ENOTCONN 107 -#define ESHUTDOWN 108 -#define ETOOMANYREFS 109 -#define ETIMEDOUT 110 -#define ECONNREFUSED 111 -#define EHOSTDOWN 112 -#define EHOSTUNREACH 113 -#define EALREADY 114 -#define EINPROGRESS 115 -#define ESTALE 116 -#define EUCLEAN 117 -#define ENOTNAM 118 -#define ENAVAIL 119 -#define EISNAM 120 -#define EREMOTEIO 121 -#define EDQUOT 122 -#define ENOMEDIUM 123 -#define EMEDIUMTYPE 124 -#define ECANCELED 125 -#define ENOKEY 126 -#define EKEYEXPIRED 127 -#define EKEYREVOKED 128 -#define EKEYREJECTED 129 -#define EOWNERDEAD 130 -#define ENOTRECOVERABLE 131 -#define ERFKILL 132 -#define EHWPOISON 133 -#define ENOTSUP EOPNOTSUPP - -__BEGIN_DECLS - -#ifndef errno -int * _TLIBC_CDECL_ __errno(void); -#define errno (*__errno()) -#endif /* errno */ -__END_DECLS - -#endif /* _ERRNO_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/fenv.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/fenv.h deleted file mode 100644 index a233172a41..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/fenv.h +++ /dev/null @@ -1,139 +0,0 @@ -/* $OpenBSD: fenv.h,v 1.2 2011/05/25 21:46:49 martynas Exp $ */ -/* $NetBSD: fenv.h,v 1.2.4.1 2011/02/08 16:18:55 bouyer Exp $ */ - -/* - * Copyright (c) 2010 The NetBSD Foundation, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _FENV_H_ -#define _FENV_H_ - -#include - -/* - * Each symbol representing a floating point exception expands to an integer - * constant expression with values, such that bitwise-inclusive ORs of _all - * combinations_ of the constants result in distinct values. - * - * We use such values that allow direct bitwise operations on FPU/SSE registers. - */ -#define FE_INVALID 0x01 -#define FE_DENORMAL 0x02 -#define FE_DIVBYZERO 0x04 -#define FE_OVERFLOW 0x08 -#define FE_UNDERFLOW 0x10 -#define FE_INEXACT 0x20 - -/* - * The following symbol is simply the bitwise-inclusive OR of all floating-point - * exception constants defined above. - */ -#define FE_ALL_EXCEPT (FE_INVALID | FE_DENORMAL | FE_DIVBYZERO | \ - FE_OVERFLOW | FE_UNDERFLOW | FE_INEXACT) -#define _SSE_MASK_SHIFT 7 - -/* - * Each symbol representing the rounding direction, expands to an integer - * constant expression whose value is distinct non-negative value. - * - * We use such values that allow direct bitwise operations on FPU/SSE registers. - */ -#define FE_TONEAREST 0x000 -#define FE_DOWNWARD 0x400 -#define FE_UPWARD 0x800 -#define FE_TOWARDZERO 0xc00 - -/* - * The following symbol is simply the bitwise-inclusive OR of all floating-point - * rounding direction constants defined above. - */ -#define _X87_ROUND_MASK (FE_TONEAREST | FE_DOWNWARD | FE_UPWARD | \ - FE_TOWARDZERO) -#define _SSE_ROUND_SHIFT 3 - -/* - * fenv_t represents the entire floating-point environment. - */ -typedef struct { - struct { - unsigned int __control; /* Control word register */ - unsigned int __status; /* Status word register */ - unsigned int __tag; /* Tag word register */ - unsigned int __others[4]; /* EIP, Pointer Selector, etc */ - } __x87; - unsigned int __mxcsr; /* Control, status register */ -} fenv_t; - -/* - * The following constant represents the default floating-point environment - * (that is, the one installed at program startup) and has type pointer to - * const-qualified fenv_t. - * - * It can be used as an argument to the functions within the header - * that manage the floating-point environment, namely fesetenv() and - * feupdateenv(). - */ -__BEGIN_DECLS -extern fenv_t __fe_dfl_env; -__END_DECLS -#define FE_DFL_ENV ((const fenv_t *)&__fe_dfl_env) - -/* - * fexcept_t represents the floating-point status flags collectively, including - * any status the implementation associates with the flags. - * - * A floating-point status flag is a system variable whose value is set (but - * never cleared) when a floating-point exception is raised, which occurs as a - * side effect of exceptional floating-point arithmetic to provide auxiliary - * information. - * - * A floating-point control mode is a system variable whose value may be set by - * the user to affect the subsequent behavior of floating-point arithmetic. - */ -typedef unsigned int fexcept_t; - -__BEGIN_DECLS - -int feclearexcept(int); -int fegetexceptflag(fexcept_t *, int); -int feraiseexcept(int); -int fesetexceptflag(const fexcept_t *, int); -int fetestexcept(int); - -int fegetround(void); -int fesetround(int); - -int fegetenv(fenv_t *); -int feholdexcept(fenv_t *); -int fesetenv(const fenv_t *); -int feupdateenv(const fenv_t *); - -int feenableexcept(int); -int fedisableexcept(int); -int fegetexcept(void); - -__END_DECLS - -#endif /* ! _FENV_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/float.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/float.h deleted file mode 100644 index e38a7c6a9f..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/float.h +++ /dev/null @@ -1,84 +0,0 @@ -/* $OpenBSD: float.h,v 1.3 2008/07/21 20:50:54 martynas Exp $ */ -/* $NetBSD: float.h,v 1.8 1995/06/20 20:45:37 jtc Exp $ */ - -/* - * Copyright (c) 1989 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)float.h 7.1 (Berkeley) 5/8/90 - */ - -#ifndef _FLOAT_H_ -#define _FLOAT_H_ - -#include - -#define FLT_RADIX 2 /* b */ - -// The rounding direction can be specified by fesetround() in -#define FLT_ROUNDS 1 /* addition rounding: near */ -#define DECIMAL_DIG 21 /* max precision in decimal digits */ - -// NOTE: FLT_EVAL_METHOD is -1 under FREEBSD x86. -#ifdef __i386__ -#define FLT_EVAL_METHOD 2 /* long double */ -#else -#define FLT_EVAL_METHOD 0 /* no promotions */ -#endif - -#define DBL_MANT_DIG 53 -#define DBL_EPSILON 2.2204460492503131E-16 -#define DBL_DIG 15 -#define DBL_MIN_EXP (-1021) -#define DBL_MIN 2.2250738585072014E-308 -#define DBL_MIN_10_EXP (-307) -#define DBL_MAX_EXP 1024 -#define DBL_MAX_10_EXP 308 - -#define FLT_MANT_DIG 24 /* p */ -#define FLT_DIG 6 /* floor((p-1)*log10(b))+(b == 10) */ -#define FLT_MIN_EXP (-125) /* emin */ -#define FLT_MIN_10_EXP (-37) /* ceil(log10(b**(emin-1))) */ -#define FLT_MAX_EXP 128 /* emax */ -#define FLT_MAX_10_EXP 38 /* floor(log10((1-b**(-p))*b**emax)) */ - -#define DBL_MAX 1.7976931348623157E+308 -#define FLT_EPSILON 1.19209290E-07F /* b**(1-p) */ -#define FLT_MIN 1.17549435E-38F /* b**(emin-1) */ -#define FLT_MAX 3.40282347E+38F /* (1-b**(-p))*b**emax */ - -#define LDBL_MANT_DIG 64 -#define LDBL_EPSILON 1.08420217248550443401e-19L -#define LDBL_DIG 18 -#define LDBL_MIN_EXP (-16381) -#define LDBL_MIN 3.36210314311209350626e-4932L -#define LDBL_MIN_10_EXP (-4931) -#define LDBL_MAX_EXP 16384 -#define LDBL_MAX 1.18973149535723176502e+4932L -#define LDBL_MAX_10_EXP 4932 - -#endif /* _FLOAT_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/inttypes.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/inttypes.h deleted file mode 100644 index fbc009c975..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/inttypes.h +++ /dev/null @@ -1,330 +0,0 @@ -/* $OpenBSD: inttypes.h,v 1.10 2009/01/13 18:13:51 kettenis Exp $ */ - -/* - * Copyright (c) 1997, 2005 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _INTTYPES_H_ -#define _INTTYPES_H_ - -#include - -/* - * 7.8.1 Macros for format specifiers - * - * Each of the following object-like macros expands to a string - * literal containing a conversion specifier, possibly modified by - * a prefix such as hh, h, l, or ll, suitable for use within the - * format argument of a formatted input/output function when - * converting the corresponding integer type. These macro names - * have the general form of PRI (character string literals for the - * fprintf family) or SCN (character string literals for the fscanf - * family), followed by the conversion specifier, followed by a - * name corresponding to a similar typedef name. For example, - * PRIdFAST32 can be used in a format string to print the value of - * an integer of type int_fast32_t. - */ - -/* fprintf macros for signed integers */ -#define PRId8 "d" /* int8_t */ -#define PRId16 "d" /* int16_t */ -#define PRId32 "d" /* int32_t */ -#ifdef __x86_64__ -#define PRId64 "ld" /* int64_t */ -#else -#define PRId64 "lld" /* int64_t */ -#endif - -#define PRIdLEAST8 "d" /* int_least8_t */ -#define PRIdLEAST16 "d" /* int_least16_t */ -#define PRIdLEAST32 "d" /* int_least32_t */ -#ifdef __x86_64__ -#define PRIdLEAST64 "ld" /* int_least64_t */ -#else -#define PRIdLEAST64 "lld" /* int_least64_t */ -#endif - -#define PRIdFAST8 "d" /* int_fast8_t */ -#ifdef __x86_64__ -#define PRIdFAST16 "ld" /* int_fast16_t */ -#define PRIdFAST32 "ld" /* int_fast32_t */ -#define PRIdFAST64 "ld" /* int_fast64_t */ -#else -#define PRIdFAST16 "d" /* int_fast16_t */ -#define PRIdFAST32 "d" /* int_fast32_t */ -#define PRIdFAST64 "lld" /* int_fast64_t */ -#endif - -#ifdef __x86_64__ -#define PRIdMAX "ld" /* intmax_t */ -#else -#if defined(__i386__) -#define PRIdMAX "lld" /* intmax_t */ -#else -#define PRIdMAX "jd" /* intmax_t */ -#endif -#endif - -#ifdef __i386__ -#define PRIdPTR "d" /* intptr_t */ -#else -#define PRIdPTR "ld" /* intptr_t */ -#endif - -#define PRIi8 "i" /* int8_t */ -#define PRIi16 "i" /* int16_t */ -#define PRIi32 "i" /* int32_t */ -#ifdef __x86_64__ -#define PRIi64 "li" /* int64_t */ -#else -#define PRIi64 "lli" /* int64_t */ -#endif - -#define PRIiLEAST8 "i" /* int_least8_t */ -#define PRIiLEAST16 "i" /* int_least16_t */ -#define PRIiLEAST32 "i" /* int_least32_t */ -#ifdef __x86_64__ -#define PRIiLEAST64 "li" /* int_least64_t */ -#else -#define PRIiLEAST64 "lli" /* int_least64_t */ -#endif - -#define PRIiFAST8 "i" /* int_fast8_t */ -#ifdef __x86_64__ -#define PRIiFAST16 "li" /* int_fast16_t */ -#define PRIiFAST32 "li" /* int_fast32_t */ -#define PRIiFAST64 "li" /* int_fast64_t */ -#else -#define PRIiFAST16 "i" /* int_fast16_t */ -#define PRIiFAST32 "i" /* int_fast32_t */ -#define PRIiFAST64 "lli" /* int_fast64_t */ -#endif - -#ifdef __x86_64__ -#define PRIiMAX "li" /* intmax_t */ -#else -#if defined(__i386__) -#define PRIiMAX "lli" /* intmax_t */ -#else -#define PRIiMAX "ji" /* intmax_t */ -#endif -#endif - -#ifdef __i386__ -#define PRIiPTR "i" /* intptr_t */ -#else -#define PRIiPTR "li" /* intptr_t */ -#endif - -/* fprintf macros for unsigned integers */ -#define PRIo8 "o" /* int8_t */ -#define PRIo16 "o" /* int16_t */ -#define PRIo32 "o" /* int32_t */ -#ifdef __x86_64__ -#define PRIo64 "lo" /* int64_t */ -#else -#define PRIo64 "llo" /* int64_t */ -#endif - -#define PRIoLEAST8 "o" /* int_least8_t */ -#define PRIoLEAST16 "o" /* int_least16_t */ -#define PRIoLEAST32 "o" /* int_least32_t */ -#ifdef __x86_64__ -#define PRIoLEAST64 "lo" /* int_least64_t */ -#else -#define PRIoLEAST64 "llo" /* int_least64_t */ -#endif - -#define PRIoFAST8 "o" /* int_fast8_t */ -#ifdef __x86_64__ -#define PRIoFAST16 "lo" /* int_fast16_t */ -#define PRIoFAST32 "lo" /* int_fast32_t */ -#define PRIoFAST64 "lo" /* int_fast64_t */ -#else -#define PRIoFAST16 "o" /* int_fast16_t */ -#define PRIoFAST32 "o" /* int_fast32_t */ -#define PRIoFAST64 "llo" /* int_fast64_t */ -#endif - -#ifdef __x86_64__ -#define PRIoMAX "lo" /* intmax_t */ -#else -#if defined(__i386__) -#define PRIoMAX "llo" /* intmax_t */ -#else -#define PRIoMAX "jo" /* intmax_t */ -#endif -#endif - -#ifdef __i386__ -#define PRIoPTR "o" /* intptr_t */ -#else -#define PRIoPTR "lo" /* intptr_t */ -#endif - -#define PRIu8 "u" /* uint8_t */ -#define PRIu16 "u" /* uint16_t */ -#define PRIu32 "u" /* uint32_t */ - -#ifdef __x86_64__ -#define PRIu64 "lu" /* uint64_t */ -#else -#define PRIu64 "llu" /* uint64_t */ -#endif - -#define PRIuLEAST8 "u" /* uint_least8_t */ -#define PRIuLEAST16 "u" /* uint_least16_t */ -#define PRIuLEAST32 "u" /* uint_least32_t */ - -#ifdef __x86_64__ -#define PRIuLEAST64 "lu" /* uint_least64_t */ -#else -#define PRIuLEAST64 "llu" /* uint_least64_t */ -#endif - -#define PRIuFAST8 "u" /* uint_fast8_t */ - -#ifdef __x86_64__ -#define PRIuFAST16 "lu" /* uint_fast16_t */ -#define PRIuFAST32 "lu" /* uint_fast32_t */ -#define PRIuFAST64 "lu" /* uint_fast64_t */ -#else -#define PRIuFAST16 "u" /* uint_fast16_t */ -#define PRIuFAST32 "u" /* uint_fast32_t */ -#define PRIuFAST64 "llu" /* uint_fast64_t */ -#endif - -#ifdef __x86_64__ -#define PRIuMAX "lu" /* uintmax_t */ -#else -#if defined(__i386__) -#define PRIuMAX "llu" /* uintmax_t */ -#else -#define PRIuMAX "ju" /* uintmax_t */ -#endif -#endif - -#ifdef __i386__ -#define PRIuPTR "u" /* uintptr_t */ -#else -#define PRIuPTR "lu" /* uintptr_t */ -#endif - -#define PRIx8 "x" /* uint8_t */ -#define PRIx16 "x" /* uint16_t */ -#define PRIx32 "x" /* uint32_t */ -#ifdef __x86_64__ -#define PRIx64 "lx" /* uint64_t */ -#else -#define PRIx64 "llx" /* uint64_t */ -#endif - -#define PRIxLEAST8 "x" /* uint_least8_t */ -#define PRIxLEAST16 "x" /* uint_least16_t */ -#define PRIxLEAST32 "x" /* uint_least32_t */ -#ifdef __x86_64__ -#define PRIxLEAST64 "lx" /* uint_least64_t */ -#else -#define PRIxLEAST64 "llx" /* uint_least64_t */ -#endif - -#define PRIxFAST8 "x" /* uint_fast8_t */ -#ifdef __x86_64__ -#define PRIxFAST16 "lx" /* uint_fast16_t */ -#define PRIxFAST32 "lx" /* uint_fast32_t */ -#define PRIxFAST64 "lx" /* uint_fast64_t */ -#else -#define PRIxFAST16 "x" /* uint_fast16_t */ -#define PRIxFAST32 "x" /* uint_fast32_t */ -#define PRIxFAST64 "llx" /* uint_fast64_t */ -#endif - -#ifdef __x86_64__ -#define PRIxMAX "lx" /* uintmax_t */ -#else -#if defined(__i386__) -#define PRIxMAX "llx" /* uintmax_t */ -#else -#define PRIxMAX "jx" /* uintmax_t */ -#endif -#endif - -#ifdef __i386__ -#define PRIxPTR "x" /* uintptr_t */ -#else -#define PRIxPTR "lx" /* uintptr_t */ -#endif - -#define PRIX8 "X" /* uint8_t */ -#define PRIX16 "X" /* uint16_t */ -#define PRIX32 "X" /* uint32_t */ - -#ifdef __x86_64__ -#define PRIX64 "lX" /* uint64_t */ -#else -#define PRIX64 "llX" /* uint64_t */ -#endif - -#define PRIXLEAST8 "X" /* uint_least8_t */ -#define PRIXLEAST16 "X" /* uint_least16_t */ -#define PRIXLEAST32 "X" /* uint_least32_t */ -#ifdef __x86_64__ -#define PRIXLEAST64 "lX" /* uint_least64_t */ -#else -#define PRIXLEAST64 "llX" /* uint_least64_t */ -#endif - -#define PRIXFAST8 "X" /* uint_fast8_t */ -#ifdef __x86_64__ -#define PRIXFAST16 "lX" /* uint_fast16_t */ -#define PRIXFAST32 "lX" /* uint_fast32_t */ -#define PRIXFAST64 "lX" /* uint_fast64_t */ -#else -#define PRIXFAST16 "X" /* uint_fast16_t */ -#define PRIXFAST32 "X" /* uint_fast32_t */ -#define PRIXFAST64 "llX" /* uint_fast64_t */ -#endif - -#ifdef __x86_64__ -#define PRIXMAX "lX" /* uintmax_t */ -#else -#if defined(__i386__) -#define PRIXMAX "llX" /* uintmax_t */ -#else -#define PRIXMAX "jX" /* uintmax_t */ -#endif -#endif - -#ifdef __i386__ -#define PRIXPTR "X" /* uintptr_t */ -#else -#define PRIXPTR "lX" /* uintptr_t */ -#endif - -typedef struct { - intmax_t quot; /* quotient */ - intmax_t rem; /* remainder */ -} imaxdiv_t; - -__BEGIN_DECLS - -intmax_t _TLIBC_CDECL_ imaxabs(intmax_t); -imaxdiv_t _TLIBC_CDECL_ imaxdiv(intmax_t, intmax_t); -intmax_t _TLIBC_CDECL_ strtoimax(const char *, char **, int); -uintmax_t _TLIBC_CDECL_ strtoumax(const char *, char **, int); - -__END_DECLS - -#endif /* _INTTYPES_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/iso646.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/iso646.h deleted file mode 100644 index a0c341b658..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/iso646.h +++ /dev/null @@ -1,26 +0,0 @@ -/* $OpenBSD: iso646.h,v 1.3 2001/10/11 00:05:21 espie Exp $ */ -/* $NetBSD: iso646.h,v 1.1 1995/02/17 09:08:10 jtc Exp $ */ - -/* - * Written by J.T. Conklin 02/16/95. - * Public domain. - */ - -#ifndef _ISO646_H_ -#define _ISO646_H_ - -#ifndef __cplusplus -#define and && -#define and_eq &= -#define bitand & -#define bitor | -#define compl ~ -#define not ! -#define not_eq != -#define or || -#define or_eq |= -#define xor ^ -#define xor_eq ^= -#endif - -#endif /* !_ISO646_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/limits.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/limits.h deleted file mode 100644 index 9d42cb545c..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/limits.h +++ /dev/null @@ -1,41 +0,0 @@ -/* $OpenBSD: limits.h,v 1.15 2008/02/10 09:59:54 kettenis Exp $ */ -/* $NetBSD: limits.h,v 1.7 1994/10/26 00:56:00 cgd Exp $ */ - -/* - * Copyright (c) 1988 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)limits.h 5.9 (Berkeley) 4/3/91 - */ - - -#ifndef _LIMITS_H_ -#define _LIMITS_H_ - -#include - -#endif /* !_LIMITS_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/math.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/math.h deleted file mode 100644 index 6ea425b840..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/math.h +++ /dev/null @@ -1,430 +0,0 @@ -/* $OpenBSD: math.h,v 1.27 2010/12/14 11:16:15 martynas Exp $ */ -/* - * ==================================================== - * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. - * - * Developed at SunPro, a Sun Microsystems, Inc. business. - * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice - * is preserved. - * ==================================================== - */ - -/* - * from: @(#)fdlibm.h 5.1 93/09/24 - */ - -#ifndef _MATH_H_ -#define _MATH_H_ - -#include -#include -#include - -#include - -typedef __float_t float_t; -typedef __double_t double_t; - -#define FP_NAN 0x00 -#define FP_INFINITE 0x01 -#define FP_ZERO 0x02 -#define FP_SUBNORMAL 0x03 -#define FP_NORMAL 0x04 - -#define FP_ILOGB0 (-INT_MAX - 1) -#define FP_ILOGBNAN (-INT_MAX - 1) - -#define fpclassify(x) \ - ((sizeof (x) == sizeof (float)) ? \ - __fpclassifyf(x) \ - : (sizeof (x) == sizeof (double)) ? \ - __fpclassify(x) \ - : __fpclassifyl(x)) -#define isfinite(x) \ - ((sizeof (x) == sizeof (float)) ? \ - __isfinitef(x) \ - : (sizeof (x) == sizeof (double)) ? \ - __isfinite(x) \ - : __isfinitel(x)) -#define isnormal(x) \ - ((sizeof (x) == sizeof (float)) ? \ - __isnormalf(x) \ - : (sizeof (x) == sizeof (double)) ? \ - __isnormal(x) \ - : __isnormall(x)) -#define signbit(x) \ - ((sizeof (x) == sizeof (float)) ? \ - __signbitf(x) \ - : (sizeof (x) == sizeof (double)) ? \ - __signbit(x) \ - : __signbitl(x)) -#define isinf(x) \ - ((sizeof (x) == sizeof (float)) ? \ - __isinff(x) \ - : (sizeof (x) == sizeof (double)) ? \ - __isinf(x) \ - : __isinfl(x)) -#define isnan(x) \ - ((sizeof (x) == sizeof (float)) ? \ - __isnanf(x) \ - : (sizeof (x) == sizeof (double)) ? \ - __isnan(x) \ - : __isnanl(x)) - -#define isgreater(x, y) (!isunordered((x), (y)) && (x) > (y)) -#define isgreaterequal(x, y) (!isunordered((x), (y)) && (x) >= (y)) -#define isless(x, y) (!isunordered((x), (y)) && (x) < (y)) -#define islessequal(x, y) (!isunordered((x), (y)) && (x) <= (y)) -#define islessgreater(x, y) (!isunordered((x), (y)) && ((x) > (y) || (y) > (x))) -#define isunordered(x, y) (isnan(x) || isnan(y)) - -__BEGIN_DECLS - -extern char __infinity[]; -#define HUGE_VAL (*(double *)(void *)__infinity) -#define HUGE_VALF ((float)HUGE_VAL) -#define HUGE_VALL ((long double)HUGE_VAL) -#define INFINITY HUGE_VALF -extern char __nan[]; -#define NAN (*(float *)(void *)__nan) - -/* - * ANSI/POSIX - */ -double _TLIBC_CDECL_ acos(double); -double _TLIBC_CDECL_ asin(double); -double _TLIBC_CDECL_ atan(double); -double _TLIBC_CDECL_ atan2(double, double); -double _TLIBC_CDECL_ cos(double); -double _TLIBC_CDECL_ sin(double); -double _TLIBC_CDECL_ tan(double); - -double _TLIBC_CDECL_ cosh(double); -double _TLIBC_CDECL_ sinh(double); -double _TLIBC_CDECL_ tanh(double); - -double _TLIBC_CDECL_ exp(double); -double _TLIBC_CDECL_ frexp(double, int *); -double _TLIBC_CDECL_ ldexp(double, int); -double _TLIBC_CDECL_ log(double); -double _TLIBC_CDECL_ log10(double); -double _TLIBC_CDECL_ modf(double, double *); - -double _TLIBC_CDECL_ pow(double, double); -double _TLIBC_CDECL_ sqrt(double); - -double _TLIBC_CDECL_ ceil(double); -double _TLIBC_CDECL_ fabs(double); -double _TLIBC_CDECL_ floor(double); -double _TLIBC_CDECL_ fmod(double, double); - -/* - * C99 - */ -double _TLIBC_CDECL_ acosh(double); -double _TLIBC_CDECL_ asinh(double); -double _TLIBC_CDECL_ atanh(double); - -double _TLIBC_CDECL_ exp2(double); -double _TLIBC_CDECL_ expm1(double); -int _TLIBC_CDECL_ ilogb(double); -double _TLIBC_CDECL_ log1p(double); -double _TLIBC_CDECL_ log2(double); -double _TLIBC_CDECL_ logb(double); -double _TLIBC_CDECL_ scalbn(double, int); -double _TLIBC_CDECL_ scalbln(double, long int); - -double _TLIBC_CDECL_ cbrt(double); -double _TLIBC_CDECL_ hypot(double, double); - -double _TLIBC_CDECL_ erf(double); -double _TLIBC_CDECL_ erfc(double); -double _TLIBC_CDECL_ lgamma(double); -double _TLIBC_CDECL_ tgamma(double); - -double _TLIBC_CDECL_ nearbyint(double); -double _TLIBC_CDECL_ rint(double); -long int _TLIBC_CDECL_ lrint(double); -long long int _TLIBC_CDECL_ llrint(double); -double _TLIBC_CDECL_ round(double); -long int _TLIBC_CDECL_ lround(double); -long long int _TLIBC_CDECL_ llround(double); -double _TLIBC_CDECL_ trunc(double); - -double _TLIBC_CDECL_ remainder(double, double); -double _TLIBC_CDECL_ remquo(double, double, int *); - -double _TLIBC_CDECL_ copysign(double, double); -double _TLIBC_CDECL_ nan(const char *); -double _TLIBC_CDECL_ nextafter(double, double); - -double _TLIBC_CDECL_ fdim(double, double); -double _TLIBC_CDECL_ fmax(double, double); -double _TLIBC_CDECL_ fmin(double, double); - -double _TLIBC_CDECL_ fma(double, double, double); - -/* - * Float versions of C99 functions - */ - -float _TLIBC_CDECL_ acosf(float); -float _TLIBC_CDECL_ asinf(float); -float _TLIBC_CDECL_ atanf(float); -float _TLIBC_CDECL_ atan2f(float, float); -float _TLIBC_CDECL_ cosf(float); -float _TLIBC_CDECL_ sinf(float); -float _TLIBC_CDECL_ tanf(float); - -float _TLIBC_CDECL_ acoshf(float); -float _TLIBC_CDECL_ asinhf(float); -float _TLIBC_CDECL_ atanhf(float); -float _TLIBC_CDECL_ coshf(float); -float _TLIBC_CDECL_ sinhf(float); -float _TLIBC_CDECL_ tanhf(float); - -float _TLIBC_CDECL_ expf(float); -float _TLIBC_CDECL_ exp2f(float); -float _TLIBC_CDECL_ expm1f(float); -float _TLIBC_CDECL_ frexpf(float, int *); -int _TLIBC_CDECL_ ilogbf(float); -float _TLIBC_CDECL_ ldexpf(float, int); -float _TLIBC_CDECL_ logf(float); -float _TLIBC_CDECL_ log10f(float); -float _TLIBC_CDECL_ log1pf(float); -float _TLIBC_CDECL_ log2f(float); -float _TLIBC_CDECL_ logbf(float); -float _TLIBC_CDECL_ modff(float, float *); -float _TLIBC_CDECL_ scalbnf(float, int); -float _TLIBC_CDECL_ scalblnf(float, long int); - -float _TLIBC_CDECL_ cbrtf(float); -float _TLIBC_CDECL_ fabsf(float); -float _TLIBC_CDECL_ hypotf(float, float); -float _TLIBC_CDECL_ powf(float, float); -float _TLIBC_CDECL_ sqrtf(float); - -float _TLIBC_CDECL_ erff(float); -float _TLIBC_CDECL_ erfcf(float); -float _TLIBC_CDECL_ lgammaf(float); -float _TLIBC_CDECL_ tgammaf(float); - -float _TLIBC_CDECL_ ceilf(float); -float _TLIBC_CDECL_ floorf(float); -float _TLIBC_CDECL_ nearbyintf(float); - -float _TLIBC_CDECL_ rintf(float); -long int _TLIBC_CDECL_ lrintf(float); -long long int _TLIBC_CDECL_ llrintf(float); -float _TLIBC_CDECL_ roundf(float); -long int _TLIBC_CDECL_ lroundf(float); -long long int _TLIBC_CDECL_ llroundf(float); -float _TLIBC_CDECL_ truncf(float); - -float _TLIBC_CDECL_ fmodf(float, float); -float _TLIBC_CDECL_ remainderf(float, float); -float _TLIBC_CDECL_ remquof(float, float, int *); - -float _TLIBC_CDECL_ copysignf(float, float); -float _TLIBC_CDECL_ nanf(const char *); -float _TLIBC_CDECL_ nextafterf(float, float); - -float _TLIBC_CDECL_ fdimf(float, float); -float _TLIBC_CDECL_ fmaxf(float, float); -float _TLIBC_CDECL_ fminf(float, float); - -float _TLIBC_CDECL_ fmaf(float, float, float); - -/* - * Long double versions of C99 functions - */ - -/* Macros defining long double functions to be their double counterparts - * (long double is synonymous with double in this implementation). - */ - -long double _TLIBC_CDECL_ acosl(long double); -long double _TLIBC_CDECL_ asinl(long double); -long double _TLIBC_CDECL_ atanl(long double); -long double _TLIBC_CDECL_ atan2l(long double, long double); -long double _TLIBC_CDECL_ cosl(long double); -long double _TLIBC_CDECL_ sinl(long double); -long double _TLIBC_CDECL_ tanl(long double); - -long double _TLIBC_CDECL_ acoshl(long double); -long double _TLIBC_CDECL_ asinhl(long double); -long double _TLIBC_CDECL_ atanhl(long double); -long double _TLIBC_CDECL_ coshl(long double); -long double _TLIBC_CDECL_ sinhl(long double); -long double _TLIBC_CDECL_ tanhl(long double); - -long double _TLIBC_CDECL_ expl(long double); -long double _TLIBC_CDECL_ exp2l(long double); -long double _TLIBC_CDECL_ expm1l(long double); -long double _TLIBC_CDECL_ frexpl(long double, int *); -int _TLIBC_CDECL_ ilogbl(long double); -long double _TLIBC_CDECL_ ldexpl(long double, int); -long double _TLIBC_CDECL_ logl(long double); -long double _TLIBC_CDECL_ log10l(long double); -long double _TLIBC_CDECL_ log1pl(long double); -long double _TLIBC_CDECL_ log2l(long double); -long double _TLIBC_CDECL_ logbl(long double); -long double _TLIBC_CDECL_ modfl(long double, long double *); -long double _TLIBC_CDECL_ scalbnl(long double, int); -long double _TLIBC_CDECL_ scalblnl(long double, long int); - -long double _TLIBC_CDECL_ cbrtl(long double); -long double _TLIBC_CDECL_ fabsl(long double); -long double _TLIBC_CDECL_ hypotl(long double, long double); -long double _TLIBC_CDECL_ powl(long double, long double); -long double _TLIBC_CDECL_ sqrtl(long double); - -long double _TLIBC_CDECL_ erfl(long double); -long double _TLIBC_CDECL_ erfcl(long double); -long double _TLIBC_CDECL_ lgammal(long double); -long double _TLIBC_CDECL_ tgammal(long double); - -long double _TLIBC_CDECL_ ceill(long double); -long double _TLIBC_CDECL_ floorl(long double); -long double _TLIBC_CDECL_ nearbyintl(long double); -long double _TLIBC_CDECL_ rintl(long double); -long int _TLIBC_CDECL_ lrintl(long double); -long long int _TLIBC_CDECL_ llrintl(long double); -long double _TLIBC_CDECL_ roundl(long double); -long int _TLIBC_CDECL_ lroundl(long double); -long long int _TLIBC_CDECL_ llroundl(long double); -long double _TLIBC_CDECL_ truncl(long double); - -long double _TLIBC_CDECL_ fmodl(long double, long double); -long double _TLIBC_CDECL_ remainderl(long double, long double); -long double _TLIBC_CDECL_ remquol(long double, long double, int *); - -long double _TLIBC_CDECL_ copysignl(long double, long double); -long double _TLIBC_CDECL_ nanl(const char *); -long double _TLIBC_CDECL_ nextafterl(long double, long double); - -long double _TLIBC_CDECL_ fdiml(long double, long double); -long double _TLIBC_CDECL_ fmaxl(long double, long double); -long double _TLIBC_CDECL_ fminl(long double, long double); -long double _TLIBC_CDECL_ fmal(long double, long double, long double); - -/* nexttoward(): -* The implementation in Intel math library is incompatible with MSVC. -* Because sizeof(long double) is 8bytes with MSVC, -* but the expected long double size is 10bytes. -* And by default, MSVC doesn't provide nexttoward(). -* So we only provide Linux version here. -*/ -double _TLIBC_CDECL_ nexttoward(double, long double); -float _TLIBC_CDECL_ nexttowardf(float, long double); - -long double _TLIBC_CDECL_ nexttowardl(long double, long double); - -/* - * Library implementation - */ -int _TLIBC_CDECL_ __fpclassify(double); -int _TLIBC_CDECL_ __fpclassifyf(float); -int _TLIBC_CDECL_ __isfinite(double); -int _TLIBC_CDECL_ __isfinitef(float); -int _TLIBC_CDECL_ __isinf(double); -int _TLIBC_CDECL_ __isinff(float); -int _TLIBC_CDECL_ __isnan(double); -int _TLIBC_CDECL_ __isnanf(float); -int _TLIBC_CDECL_ __isnormal(double); -int _TLIBC_CDECL_ __isnormalf(float); -int _TLIBC_CDECL_ __signbit(double); -int _TLIBC_CDECL_ __signbitf(float); - -int _TLIBC_CDECL_ __fpclassifyl(long double); -int _TLIBC_CDECL_ __isfinitel(long double); -int _TLIBC_CDECL_ __isinfl(long double); -int _TLIBC_CDECL_ __isnanl(long double); -int _TLIBC_CDECL_ __isnormall(long double); -int _TLIBC_CDECL_ __signbitl(long double); - -/* - * Non-C99 functions. - */ -double _TLIBC_CDECL_ drem(double, double); -double _TLIBC_CDECL_ exp10(double); -double _TLIBC_CDECL_ gamma(double); -double _TLIBC_CDECL_ gamma_r(double, int *); -double _TLIBC_CDECL_ j0(double); -double _TLIBC_CDECL_ j1(double); -double _TLIBC_CDECL_ jn(int, double); -double _TLIBC_CDECL_ lgamma_r(double, int *); -double _TLIBC_CDECL_ pow10(double); -double _TLIBC_CDECL_ scalb(double, double); -/* C99 Macro signbit.*/ -double _TLIBC_CDECL_ significand(double); -void _TLIBC_CDECL_ sincos(double, double *, double *); -double _TLIBC_CDECL_ y0(double); -double _TLIBC_CDECL_ y1(double); -double _TLIBC_CDECL_ yn(int, double); -/* C99 Macro isinf.*/ -/* C99 Macro isnan.*/ -int _TLIBC_CDECL_ finite(double); - -float _TLIBC_CDECL_ dremf(float, float); -float _TLIBC_CDECL_ exp10f(float); -float _TLIBC_CDECL_ gammaf(float); -float _TLIBC_CDECL_ gammaf_r(float, int *); -float _TLIBC_CDECL_ j0f(float); -float _TLIBC_CDECL_ j1f(float); -float _TLIBC_CDECL_ jnf(int, float); -float _TLIBC_CDECL_ lgammaf_r(float, int *); -float _TLIBC_CDECL_ pow10f(float); -float _TLIBC_CDECL_ scalbf(float, float); -int _TLIBC_CDECL_ signbitf(float); -float _TLIBC_CDECL_ significandf(float); -void _TLIBC_CDECL_ sincosf(float, float *, float *); -float _TLIBC_CDECL_ y0f(float); -float _TLIBC_CDECL_ y1f(float); -float _TLIBC_CDECL_ ynf(int, float); -int _TLIBC_CDECL_ finitef(float); -int _TLIBC_CDECL_ isinff(float); -int _TLIBC_CDECL_ isnanf(float); - -long double _TLIBC_CDECL_ dreml(long double, long double); -long double _TLIBC_CDECL_ exp10l(long double); -long double _TLIBC_CDECL_ gammal(long double); -long double _TLIBC_CDECL_ gammal_r(long double, int *); -long double _TLIBC_CDECL_ j0l(long double); -long double _TLIBC_CDECL_ j1l(long double); -long double _TLIBC_CDECL_ jnl(int, long double); -long double _TLIBC_CDECL_ lgammal_r(long double, int *); -long double _TLIBC_CDECL_ pow10l(long double); -long double _TLIBC_CDECL_ scalbl(long double, long double); -int _TLIBC_CDECL_ signbitl(long double); -long double _TLIBC_CDECL_ significandl(long double); -void _TLIBC_CDECL_ sincosl(long double, long double *, long double *); -long double _TLIBC_CDECL_ y1l(long double); -long double _TLIBC_CDECL_ y0l(long double); -long double _TLIBC_CDECL_ ynl(int, long double); -int _TLIBC_CDECL_ finitel(long double); -int _TLIBC_CDECL_ isinfl(long double); -int _TLIBC_CDECL_ isnanl(long double); - -/* - * TODO: From Intel Decimal Floating-Point Math Library - * signbitd32/signbitd64/signbitd128, finited32/finited64/finited128 - * isinfd32/isinfd64/isinfd128, isnand32/isnand64/isnand128 - */ -#if defined(__cplusplus) -/* Clang does not support decimal floating point types. - * - * c.f.: - * http://clang.llvm.org/docs/UsersManual.html#gcc-extensions-not-implemented-yet - */ -#if !defined(__clang__) -typedef float _Decimal32 __attribute__((mode(SD))); -typedef float _Decimal64 __attribute__((mode(DD))); -typedef float _Decimal128 __attribute__((mode(TD))); -#endif -#endif - -__END_DECLS - -#endif /* !_MATH_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/mbusafecrt.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/mbusafecrt.h deleted file mode 100644 index 91d888b3f8..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/mbusafecrt.h +++ /dev/null @@ -1,85 +0,0 @@ -// -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -/*** -* mbusafecrt.h - public declarations for SafeCRT lib -* - -* -* Purpose: -* This file contains the public declarations SafeCRT -* functions ported to MacOS. These are the safe versions of -* functions standard functions banned by SWI -* - -****/ - -/* shields! */ - -#ifndef MBUSAFECRT_H -#define MBUSAFECRT_H -#include -#include -#include -typedef wchar_t WCHAR; - -#ifdef __cplusplus - extern "C" { -#endif - -extern errno_t strcat_s( char* ioDest, size_t inDestBufferSize, const char* inSrc ); -extern errno_t wcscat_s( WCHAR* ioDest, size_t inDestBufferSize, const WCHAR* inSrc ); - -extern errno_t strncat_s( char* ioDest, size_t inDestBufferSize, const char* inSrc, size_t inCount ); -extern errno_t wcsncat_s( WCHAR* ioDest, size_t inDestBufferSize, const WCHAR* inSrc, size_t inCount ); - -extern errno_t strcpy_s( char* outDest, size_t inDestBufferSize, const char* inSrc ); -extern errno_t wcscpy_s( WCHAR* outDest, size_t inDestBufferSize, const WCHAR* inSrc ); - -extern errno_t strncpy_s( char* outDest, size_t inDestBufferSize, const char* inSrc, size_t inCount ); -extern errno_t wcsncpy_s( WCHAR* outDest, size_t inDestBufferSize, const WCHAR* inSrc, size_t inCount ); - -extern char* strtok_s( char* inString, const char* inControl, char** ioContext ); -extern WCHAR* wcstok_s( WCHAR* inString, const WCHAR* inControl, WCHAR** ioContext ); - -extern size_t wcsnlen( const WCHAR* inString, size_t inMaxSize ); - -extern errno_t _itoa_s( int inValue, char* outBuffer, size_t inDestBufferSize, int inRadix ); -extern errno_t _itow_s( int inValue, WCHAR* outBuffer, size_t inDestBufferSize, int inRadix ); - -extern errno_t _ltoa_s( long inValue, char* outBuffer, size_t inDestBufferSize, int inRadix ); -extern errno_t _ltow_s( long inValue, WCHAR* outBuffer, size_t inDestBufferSize, int inRadix ); - -extern errno_t _ultoa_s( unsigned long inValue, char* outBuffer, size_t inDestBufferSize, int inRadix ); -extern errno_t _ultow_s( unsigned long inValue, WCHAR* outBuffer, size_t inDestBufferSize, int inRadix ); - -extern errno_t _i64toa_s( long long inValue, char* outBuffer, size_t inDestBufferSize, int inRadix ); -extern errno_t _i64tow_s( long long inValue, WCHAR* outBuffer, size_t inDestBufferSize, int inRadix ); - -extern errno_t _ui64toa_s( unsigned long long inValue, char* outBuffer, size_t inDestBufferSize, int inRadix ); -extern errno_t _ui64tow_s( unsigned long long inValue, WCHAR* outBuffer, size_t inDestBufferSize, int inRadix ); - -extern int sprintf_s( char *string, size_t sizeInBytes, const char *format, ... ); -extern int swprintf_s( WCHAR *string, size_t sizeInWords, const WCHAR *format, ... ); - -extern int _snprintf_s( char *string, size_t sizeInBytes, size_t count, const char *format, ... ); -extern int _snwprintf_s( WCHAR *string, size_t sizeInWords, size_t count, const WCHAR *format, ... ); - -extern int _vsprintf_s( char* string, size_t sizeInBytes, const char* format, va_list arglist ); -extern int _vsnprintf_s( char* string, size_t sizeInBytes, size_t count, const char* format, va_list arglist ); - -extern int _vswprintf_s( WCHAR* string, size_t sizeInWords, const WCHAR* format, va_list arglist ); -extern int _vsnwprintf_s( WCHAR* string, size_t sizeInWords, size_t count, const WCHAR* format, va_list arglist ); - -extern errno_t memcpy_s( void * dst, size_t sizeInBytes, const void * src, size_t count ); -extern errno_t memcpy_verw_s( void * dst, size_t sizeInBytes, const void * src, size_t count ); -extern errno_t memmove_s( void * dst, size_t sizeInBytes, const void * src, size_t count ); -extern errno_t memmove_verw_s( void * dst, size_t sizeInBytes, const void * src, size_t count ); - -#ifdef __cplusplus - } -#endif - -#endif /* MBUSAFECRT_H */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/netdb.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/netdb.h deleted file mode 100644 index 264f90ff39..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/netdb.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// Copyright © 2005-2020 Rich Felker, et al. -// Licensed under the MIT license.s -// - -/* Copyright © 2005-2020 Rich Felker, et al. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#ifndef _NETDB_H -#define _NETDB_H - -struct addrinfo { - int ai_flags; - int ai_family; - int ai_socktype; - int ai_protocol; - socklen_t ai_addrlen; - struct sockaddr *ai_addr; - char *ai_canonname; - struct addrinfo *ai_next; -}; - -#endif diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/poll.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/poll.h deleted file mode 100644 index fc786fc279..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/poll.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright © 2005-2020 Rich Felker, et al. -// Licensed under the MIT license. -// - -/* Copyright © 2005-2020 Rich Felker, et al. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#ifndef _POLL_H_ -#define _POLL_H_ - -typedef unsigned long nfds_t; - -struct pollfd { - int fd; - short int events; - short int revents; -}; - -#endif diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/pthread.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/pthread.h deleted file mode 100644 index e79668ffd6..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/pthread.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef _SYS_THREAD_H_ -#define _SYS_THREAD_H_ - -/* Thread identifiers. The structure of the attribute type is not - exposed on purpose. */ -typedef unsigned long int pthread_t; - -#if defined __x86_64__ && !defined __ILP32__ -# define __WORDSIZE 64 -#else -# define __WORDSIZE 32 -#define __WORDSIZE32_SIZE_ULONG 0 -#define __WORDSIZE32_PTRDIFF_LONG 0 -#endif - -#ifdef __x86_64__ -# if __WORDSIZE == 64 -# define __SIZEOF_PTHREAD_ATTR_T 56 -# else -# define __SIZEOF_PTHREAD_ATTR_T 32 -#endif - -union pthread_attr_t -{ - char __size[__SIZEOF_PTHREAD_ATTR_T]; - long int __align; -}; -#ifndef __have_pthread_attr_t -typedef union pthread_attr_t pthread_attr_t; -# define __have_pthread_attr_t 1 -#endif - -#endif -#endif diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/pwd.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/pwd.h deleted file mode 100644 index a45b145a94..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/pwd.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// Copyright © 2005-2020 Rich Felker, et al. -// Licensed under the MIT license. -// - -/* Copyright © 2005-2020 Rich Felker, et al. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#ifndef _PWD_H -#define _PWD_H - -struct passwd { - char *pw_name; - char *pw_passwd; - __uid_t pw_uid; - __gid_t pw_gid; - char *pw_gecos; - char *pw_dir; - char *pw_shell; -}; - -#endif diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sched.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/sched.h deleted file mode 100644 index 4d237c4044..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sched.h +++ /dev/null @@ -1,62 +0,0 @@ -// -// Copyright © 2005-2020 Rich Felker, et al. -// Licensed under the MIT license.s -// - -/* Copyright © 2005-2020 Rich Felker, et al. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#ifndef _SCHED_H -#define _SCHED_H -#include - -typedef struct { - unsigned long __bits[128/sizeof(long)]; -} cpu_set_t; - -#define __CPU_op_S(i, size, set, op) ( (i)/8U >= (size) ? 0 : \ - (((unsigned long *)(set))[(i)/8/sizeof(long)] op (1UL<<((i)%(8*sizeof(long))))) ) - -#define CPU_SET_S(i, size, set) __CPU_op_S(i, size, set, |=) -#define CPU_CLR_S(i, size, set) __CPU_op_S(i, size, set, &=~) -#define CPU_ISSET_S(i, size, set) __CPU_op_S(i, size, set, &) - -#define __CPU_op_func_S(func, op) \ -static __inline void __CPU_##func##_S(size_t __size, cpu_set_t *__dest, \ - const cpu_set_t *__src1, const cpu_set_t *__src2) \ -{ \ - size_t __i; \ - for (__i=0; __i<__size/sizeof(long); __i++) \ - ((unsigned long *)__dest)[__i] = ((unsigned long *)__src1)[__i] \ - op ((unsigned long *)__src2)[__i] ; \ -} - -__CPU_op_func_S(AND, &) -__CPU_op_func_S(OR, |) -__CPU_op_func_S(XOR, ^) - -#define CPU_AND_S(a,b,c,d) __CPU_AND_S(a,b,c,d) -#define CPU_OR_S(a,b,c,d) __CPU_OR_S(a,b,c,d) -#define CPU_XOR_S(a,b,c,d) __CPU_XOR_S(a,b,c,d) - -typedef __pid_t pid_t; - -#endif diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/setjmp.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/setjmp.h deleted file mode 100644 index 752f0cf763..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/setjmp.h +++ /dev/null @@ -1,65 +0,0 @@ -/* $NetBSD: setjmp.h,v 1.26 2011/11/05 09:27:06 joerg Exp $ */ - -/*- - * Copyright (c) 1990, 1993 - * The Regents of the University of California. All rights reserved. - * (c) UNIX System Laboratories, Inc. - * All or some portions of this file are derived from material licensed - * to the University of California by American Telephone and Telegraph - * Co. or Unix System Laboratories, Inc. and are reproduced herein with - * the permission of UNIX System Laboratories, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)setjmp.h 8.2 (Berkeley) 1/21/94 - */ - -#ifndef _SETJMP_H_ -#define _SETJMP_H_ - -#ifndef _JB_ATTRIBUTES -#define _JB_ATTRIBUTES /**/ -#else -#endif -#ifndef _BSD_JBSLOT_T_ -#define _BSD_JBSLOT_T_ long -#endif - -#define _JBLEN 8 - -typedef _BSD_JBSLOT_T_ jmp_buf[_JBLEN] _JB_ATTRIBUTES; - -#include -#define __returns_twice __attribute__((__returns_twice__)) -#define __dead - - -__BEGIN_DECLS -int setjmp(jmp_buf) __returns_twice; -void longjmp(jmp_buf, int) __dead; -__END_DECLS - -#endif /* !_SETJMP_H_ */ - diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/signal.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/signal.h deleted file mode 100644 index c0da74f456..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/signal.h +++ /dev/null @@ -1,104 +0,0 @@ -// -// Copyright © 2005-2020 Rich Felker, et al. -// Licensed under the MIT license.s -// - -/* Copyright © 2005-2020 Rich Felker, et al. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#ifndef _SIGNAL_H -#define _SIGNAL_H - -#include - -typedef struct { - unsigned long _bits[128/sizeof(long)]; -} __sigset_t; - -typedef __sigset_t sigset_t; - -union sigval { - int sival_int; - void *sival_ptr; -}; - -typedef struct { - int si_signo; - int si_errno; - int si_code; - union { - char __pad[128 - 2*sizeof(int) - sizeof(long)]; - struct { - union { - struct { - __pid_t si_pid; - __uid_t si_uid; - } __piduid; - struct { - int si_timerid; - int si_overrun; - } __timer; - } __first; - union { - union sigval si_value; - struct { - int si_status; - __clock_t si_utime, si_stime; - } __sigchld; - } __second; - } __si_common; - struct { - void *si_addr; - short si_addr_lsb; - union { - struct { - void *si_lower; - void *si_upper; - } __addr_bnd; - unsigned si_pkey; - } __first; - } __sigfault; - struct { - long si_band; - int si_fd; - } __sigpoll; - struct { - void *si_call_addr; - int si_syscall; - unsigned si_arch; - } __sigsys; - } __si_fields; -} siginfo_t; - -struct sigaction { - union { - void (*sa_handler) (int); - void (*sa_sigaction) (int, siginfo_t *, void *); - } __sa_handler; - __sigset_t sa_mask; - int sa_flags; - void (*sa_restorer) (void); -}; - -#define sa_handler __sa_handler.sa_handler -#define sa_sigaction __sa_handler.sa_sigaction - -#endif diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdalign.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdalign.h deleted file mode 100644 index 93b8f6016e..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdalign.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _STDALIGN_H -#define _STDALIGN_H -#ifndef __cplusplus -/* this whole header only works in C11 or with compiler extensions */ -#if __STDC_VERSION__ < 201112L && defined( __GNUC__) -#define _Alignas(t) __attribute__((__aligned__(t))) -#define _Alignof(t) __alignof__(t) -#endif -#define alignas _Alignas -#define alignof _Alignof -#endif -#define __alignas_is_defined 1 -#define __alignof_is_defined 1 -#endif - diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdarg.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdarg.h deleted file mode 100644 index b2a5d36e82..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdarg.h +++ /dev/null @@ -1,48 +0,0 @@ -/* $OpenBSD: stdarg.h,v 1.14 2010/12/30 05:01:36 tedu Exp $ */ -/* $NetBSD: stdarg.h,v 1.12 1995/12/25 23:15:31 mycroft Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)stdarg.h 8.1 (Berkeley) 6/10/93 - */ - -#ifndef _STDARG_H_ -#define _STDARG_H_ - -#include -#include - -typedef __va_list va_list; - -#define va_start(ap, last) __builtin_va_start((ap), last) -#define va_end __builtin_va_end -#define va_arg __builtin_va_arg -#define va_copy(dst, src) __builtin_va_copy((dst),(src)) - -#endif /* !_STDARG_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdbool.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdbool.h deleted file mode 100644 index 86b866d5d7..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdbool.h +++ /dev/null @@ -1,44 +0,0 @@ -/* $OpenBSD: stdbool.h,v 1.5 2010/07/24 22:17:03 guenther Exp $ */ - -/* - * Written by Marc Espie, September 25, 1999 - * Public domain. - */ - -#ifndef _STDBOOL_H_ -#define _STDBOOL_H_ - -#ifndef __cplusplus - -#ifndef __GNUC__ -/* Support for _C99: type _Bool is already built-in. */ -/* `_Bool' type must promote to `int' or `unsigned int'. */ -typedef enum { - false = 0, - true = 1 -} _Bool; - -/* And those constants must also be available as macros. */ -# define false false -# define true true -#else /* __GNUC__ */ -# define false 0 -# define true 1 -#endif - -/* User visible type `bool' is provided as a macro which may be redefined */ -#define bool _Bool - -#else /* __cplusplus */ - -# define _Bool bool -# define bool bool -# define false false -# define true true - -#endif - -/* Inform that everything is fine */ -#define __bool_true_false_are_defined 1 - -#endif /* _STDBOOL_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/stddef.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/stddef.h deleted file mode 100644 index 62d653029d..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/stddef.h +++ /dev/null @@ -1,70 +0,0 @@ -/* $OpenBSD: stddef.h,v 1.10 2009/09/22 21:40:02 jsg Exp $ */ -/* $NetBSD: stddef.h,v 1.4 1994/10/26 00:56:26 cgd Exp $ */ - -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)stddef.h 5.5 (Berkeley) 4/3/91 - */ - -#ifndef _STDDEF_H_ -#define _STDDEF_H_ - -#include -#include - -#ifndef _PTRDIFF_T_DEFINED_ -#define _PTRDIFF_T_DEFINED_ -typedef __ptrdiff_t ptrdiff_t; -#endif - -#ifndef _SIZE_T_DEFINED_ -#define _SIZE_T_DEFINED_ -typedef __size_t size_t; -#endif - -#if !defined(_WCHAR_T_DEFINED_) && !defined(__cplusplus) -#define _WCHAR_T_DEFINED_ -#ifndef __WCHAR_TYPE__ -#define __WCHAR_TYPE__ int -#endif -typedef __WCHAR_TYPE__ wchar_t; -#endif - -#ifndef NULL -#ifdef __cplusplus -#define NULL 0 -#else -#define NULL ((void *)0) -#endif -#endif - -#define offsetof(type, member) __builtin_offsetof (type, member) - -#endif /* _STDDEF_H_ */ - diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdint.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdint.h deleted file mode 100644 index e574484062..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdint.h +++ /dev/null @@ -1,24 +0,0 @@ -/* $OpenBSD: stdint.h,v 1.4 2006/12/10 22:17:55 deraadt Exp $ */ - -/* - * Copyright (c) 1997, 2005 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _STDINT_H_ -#define _STDINT_H_ - -#include - -#endif /* _STDINT_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdio.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdio.h deleted file mode 100644 index 92d01a0d9e..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdio.h +++ /dev/null @@ -1,95 +0,0 @@ -/* $OpenBSD: stdio.h,v 1.38 2009/11/09 00:18:27 kurt Exp $ */ -/* $NetBSD: stdio.h,v 1.18 1996/04/25 18:29:21 jtc Exp $ */ - -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Chris Torek. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)stdio.h 5.17 (Berkeley) 6/3/91 - */ - -#ifndef _STDIO_H_ -#define _STDIO_H_ - -#include -#include - -#include - -#ifndef _SIZE_T_DEFINED_ -typedef __size_t size_t; -#define _SIZE_T_DEFINED_ -#endif - -#ifndef NULL -# ifdef __cplusplus -# define NULL 0 -# else -# define NULL ((void *)0) -# endif -#endif - -# define BUFSIZ 8192 - -#define EOF (-1) - -__BEGIN_DECLS - -int _TLIBC_CDECL_ snprintf(char *, size_t, const char *, ...) _GCC_PRINTF_FORMAT_(3, 4); -int _TLIBC_CDECL_ vsnprintf(char *, size_t, const char *, __va_list) _GCC_PRINTF_FORMAT_(3, 0); - -/* - * Deprecated definitions. - */ -#if 0 /* No FILE */ -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, fprintf, FILE *, const char *, ...); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, putc, int, FILE *); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, fputc, int, FILE *); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, fputs, const char *, FILE *); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, fscanf, FILE *, const char *, ...); -_TLIBC_DEPRECATED_FUNCTION_(size_t _TLIBC_CDECL_, fwrite, const void *, size_t, size_t, FILE *); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, printf, const char *, ...); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, putchar, int); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, puts, const char *); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, scanf, const char *, ...); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, sprintf, char *, const char *, ...); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, sscanf, const char *, const char *, ...); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, vfprintf, FILE *, const char *, __va_list); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, vfscanf, FILE *, const char *, __va_list); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, vprintf, const char *, __va_list); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, vscanf, const char *, __va_list); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, vsprintf, char *, const char *, __va_list); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, vsscanf, const char *, const char *, __va_list); -#endif - -__END_DECLS - - -#endif /* !_STDIO_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdlib.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdlib.h deleted file mode 100644 index 8128e0d56d..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/stdlib.h +++ /dev/null @@ -1,159 +0,0 @@ -/* $OpenBSD: stdlib.h,v 1.47 2010/05/18 22:24:55 tedu Exp $ */ -/* $NetBSD: stdlib.h,v 1.25 1995/12/27 21:19:08 jtc Exp $ */ - -/*- -* Copyright (c) 1990 The Regents of the University of California. -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions -* are met: -* 1. Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* 2. Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* 3. Neither the name of the University nor the names of its contributors -* may be used to endorse or promote products derived from this software -* without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -* SUCH DAMAGE. -* -* @(#)stdlib.h 5.13 (Berkeley) 6/4/91 -*/ - -#ifndef _STDLIB_H_ -#define _STDLIB_H_ - -#include -#include - -#ifndef _SIZE_T_DEFINED_ -#define _SIZE_T_DEFINED_ -typedef __size_t size_t; -#endif - -#if !defined(_WCHAR_T_DEFINED_) && !defined(__cplusplus) -#define _WCHAR_T_DEFINED_ -#ifndef __WCHAR_TYPE__ -#define __WCHAR_TYPE__ int -#endif -typedef __WCHAR_TYPE__ wchar_t; -#endif - -#ifndef _DIV_T_DEFINED -typedef struct { - int quot; /* quotient */ - int rem; /* remainder */ -} div_t; - -typedef struct { - long quot; /* quotient */ - long rem; /* remainder */ -} ldiv_t; - -typedef struct { - long long quot; /* quotient */ - long long rem; /* remainder */ -} lldiv_t; -#define _DIV_T_DEFINED -#endif - -#ifndef NULL -#ifdef __cplusplus -#define NULL 0 -#else -#define NULL ((void *)0) -#endif -#endif - -#define EXIT_FAILURE 1 -#define EXIT_SUCCESS 0 - -#define RAND_MAX 0x7fffffff -#define MB_CUR_MAX 1 - -__BEGIN_DECLS - -_TLIBC_NORETURN_ void _TLIBC_CDECL_ abort(void); -int _TLIBC_CDECL_ atexit(void (*)(void)); -int _TLIBC_CDECL_ abs(int); -double _TLIBC_CDECL_ atof(const char *); -int _TLIBC_CDECL_ atoi(const char *); -long _TLIBC_CDECL_ atol(const char *); -void * _TLIBC_CDECL_ bsearch(const void *, const void *, size_t, size_t, int (*)(const void *, const void *)); -void * _TLIBC_CDECL_ calloc(size_t, size_t); -div_t _TLIBC_CDECL_ div(int, int); -void _TLIBC_CDECL_ free(void *); -long _TLIBC_CDECL_ labs(long); -ldiv_t _TLIBC_CDECL_ ldiv(long, long); -void * _TLIBC_CDECL_ malloc(size_t); -void * _TLIBC_CDECL_ memalign(size_t, size_t); -#ifndef __cplusplus -int _TLIBC_CDECL_ posix_memalign(void **, size_t, size_t); -#else -int _TLIBC_CDECL_ posix_memalign(void **, size_t, size_t) throw (); -#endif -void * _TLIBC_CDECL_ aligned_alloc(size_t, size_t); -void _TLIBC_CDECL_ qsort(void *, size_t, size_t, int (*)(const void *, const void *)); -void * _TLIBC_CDECL_ realloc(void *, size_t); -double _TLIBC_CDECL_ strtod(const char *, char **); -long _TLIBC_CDECL_ strtol(const char *, char **, int); -float _TLIBC_CDECL_ strtof(const char *, char **); - -long long - _TLIBC_CDECL_ atoll(const char *); -long long - _TLIBC_CDECL_ llabs(long long); -lldiv_t - _TLIBC_CDECL_ lldiv(long long, long long); -long long - _TLIBC_CDECL_ strtoll(const char *, char **, int); -unsigned long - _TLIBC_CDECL_ strtoul(const char *, char **, int); -long double - _TLIBC_CDECL_ strtold(const char *, char **); -unsigned long long - _TLIBC_CDECL_ strtoull(const char *, char **, int); - -int _TLIBC_CDECL_ mblen(const char *, size_t); -size_t _TLIBC_CDECL_ mbstowcs(wchar_t *, const char *, size_t); -int _TLIBC_CDECL_ wctomb(char *, wchar_t); -int _TLIBC_CDECL_ mbtowc(wchar_t *, const char *, size_t); -size_t _TLIBC_CDECL_ wcstombs(char *, const wchar_t *, size_t); - - -/* - * Deprecated C99. - */ -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, atexit, void (_TLIBC_CDECL_ *)(void)); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, rand, void); -_TLIBC_DEPRECATED_FUNCTION_(void _TLIBC_CDECL_, srand, unsigned); -_TLIBC_DEPRECATED_FUNCTION_(void _TLIBC_CDECL_, exit, int); -_TLIBC_DEPRECATED_FUNCTION_(void _TLIBC_CDECL_, _Exit, int); -_TLIBC_DEPRECATED_FUNCTION_(char * _TLIBC_CDECL_, getenv, const char *); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, system, const char *); - -/* - * Non-C99 Functions. - */ -void * _TLIBC_CDECL_ alloca(size_t); - -/* - * Deprecated Non-C99. - */ -//_TLIBC_DEPRECATED_FUNCTION_(void _TLIBC_CDECL_, _exit, int); - -__END_DECLS - -#endif /* !_STDLIB_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/string.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/string.h deleted file mode 100644 index 00a89fde77..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/string.h +++ /dev/null @@ -1,130 +0,0 @@ -/* $OpenBSD: string.h,v 1.20 2010/09/24 13:33:00 matthew Exp $ */ -/* $NetBSD: string.h,v 1.6 1994/10/26 00:56:30 cgd Exp $ */ - -/*- - * Copyright (c) 1990 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)string.h 5.10 (Berkeley) 3/9/91 - */ - -#ifndef _STRING_H_ -#define _STRING_H_ - -#include -#include - -#ifndef _SIZE_T_DEFINED_ -typedef __size_t size_t; -#define _SIZE_T_DEFINED_ -#endif - -#ifndef _ERRNO_T_DEFINED -#define _ERRNO_T_DEFINED -typedef int errno_t; -#endif - -#ifndef NULL -#ifdef __cplusplus -#define NULL 0 -#else -#define NULL ((void *)0) -#endif -#endif - -__BEGIN_DECLS - -void * _TLIBC_CDECL_ memchr(const void *, int, size_t); -int _TLIBC_CDECL_ memcmp(const void *, const void *, size_t); -void * _TLIBC_CDECL_ memcpy_nochecks(void *, const void *, size_t); -void * _TLIBC_CDECL_ memcpy(void *, const void *, size_t); -void * _TLIBC_CDECL_ memcpy_verw(void *, const void *, size_t); -void * _TLIBC_CDECL_ memmove(void *, const void *, size_t); -void * _TLIBC_CDECL_ memmove_verw(void *, const void *, size_t); -void * _TLIBC_CDECL_ memset(void *, int, size_t); -void * _TLIBC_CDECL_ memset_verw(void *, int, size_t); -char * _TLIBC_CDECL_ strchr(const char *, int); -int _TLIBC_CDECL_ strcmp(const char *, const char *); -int _TLIBC_CDECL_ strcoll(const char *, const char *); -size_t _TLIBC_CDECL_ strcspn(const char *, const char *); -char * _TLIBC_CDECL_ strerror(int); -size_t _TLIBC_CDECL_ strlen(const char *); -char * _TLIBC_CDECL_ strncat(char *, const char *, size_t); -int _TLIBC_CDECL_ strncmp(const char *, const char *, size_t); -char * _TLIBC_CDECL_ strncpy(char *, const char *, size_t); -char * _TLIBC_CDECL_ strpbrk(const char *, const char *); -char * _TLIBC_CDECL_ strrchr(const char *, int); -size_t _TLIBC_CDECL_ strspn(const char *, const char *); -char * _TLIBC_CDECL_ strstr(const char *, const char *); -char * _TLIBC_CDECL_ strtok(char *, const char *); -size_t _TLIBC_CDECL_ strxfrm(char *, const char *, size_t); -size_t _TLIBC_CDECL_ strlcpy(char *, const char *, size_t); -errno_t _TLIBC_CDECL_ memset_s(void *s, size_t smax, int c, size_t n); -errno_t _TLIBC_CDECL_ memset_verw_s(void *s, size_t smax, int c, size_t n); - -/* - * Deprecated C99. - */ -_TLIBC_DEPRECATED_FUNCTION_(char * _TLIBC_CDECL_, strcat, char *, const char *); -_TLIBC_DEPRECATED_FUNCTION_(char * _TLIBC_CDECL_, strcpy, char *, const char *); - -/* - * Common used non-C99 functions. - */ -char * _TLIBC_CDECL_ strndup(const char *, size_t); -size_t _TLIBC_CDECL_ strnlen(const char *, size_t); -int _TLIBC_CDECL_ consttime_memequal(const void *b1, const void *b2, size_t len); - -/* - * Non-C99 - */ -int _TLIBC_CDECL_ bcmp(const void *, const void *, size_t); -void _TLIBC_CDECL_ bcopy(const void *, void *, size_t); -void _TLIBC_CDECL_ bzero(void *, size_t); -char * _TLIBC_CDECL_ index(const char *, int); -void * _TLIBC_CDECL_ mempcpy(void *, const void *, size_t); -char * _TLIBC_CDECL_ rindex(const char *, int); -char * _TLIBC_CDECL_ stpncpy(char *dest, const char *src, size_t n); -int _TLIBC_CDECL_ strcasecmp(const char *, const char *); -int _TLIBC_CDECL_ strncasecmp(const char *, const char *, size_t); - -int _TLIBC_CDECL_ ffs(int); -int _TLIBC_CDECL_ ffsl(long int); -int _TLIBC_CDECL_ ffsll(long long int); - -char * _TLIBC_CDECL_ strtok_r(char *, const char *, char **); -int _TLIBC_CDECL_ strerror_r(int, char *, size_t); - -/* - * Deprecated Non-C99. - */ -_TLIBC_DEPRECATED_FUNCTION_(char * _TLIBC_CDECL_, strdup, const char *); -_TLIBC_DEPRECATED_FUNCTION_(char * _TLIBC_CDECL_, stpcpy, char *dest, const char *src); - -__END_DECLS - -#endif /* _STRING_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/_types.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/_types.h deleted file mode 100644 index 5dc6d5bbfb..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/_types.h +++ /dev/null @@ -1,168 +0,0 @@ -/* $OpenBSD: _types.h,v 1.2 2008/03/16 19:42:57 otto Exp $ */ - -/*- - * Copyright (c) 1990, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)types.h 8.3 (Berkeley) 1/5/94 - */ - -#ifndef _SYS__TYPES_H_ -#define _SYS__TYPES_H_ - -#include -/* 7.18.1.1 Exact-width integer types */ -typedef signed char __int8_t; -typedef unsigned char __uint8_t; -typedef short __int16_t; -typedef unsigned short __uint16_t; -typedef int __int32_t; -typedef unsigned int __uint32_t; -#ifdef __x86_64__ -typedef long __int64_t; -typedef unsigned long __uint64_t; -#else -typedef long long __int64_t; -typedef unsigned long long __uint64_t; -#endif - -/* 7.18.1.2 Minimum-width integer types */ -typedef __int8_t __int_least8_t; -typedef __uint8_t __uint_least8_t; -typedef __int16_t __int_least16_t; -typedef __uint16_t __uint_least16_t; -typedef __int32_t __int_least32_t; -typedef __uint32_t __uint_least32_t; -typedef __int64_t __int_least64_t; -typedef __uint64_t __uint_least64_t; - -/* 7.18.1.3 Fastest minimum-width integer types */ -typedef __int8_t __int_fast8_t; -typedef __uint8_t __uint_fast8_t; -#ifdef __x86_64__ -/* Linux x86_64, from stdint.h */ -typedef long int __int_fast16_t; -typedef unsigned long int __uint_fast16_t; -typedef long int __int_fast32_t; -typedef unsigned long int __uint_fast32_t; -typedef long int __int_fast64_t; -typedef unsigned long int __uint_fast64_t; -#else -/* Android x86, and Linux x86 */ -typedef __int32_t __int_fast16_t; -typedef __uint32_t __uint_fast16_t; -typedef __int32_t __int_fast32_t; -typedef __uint32_t __uint_fast32_t; -typedef __int64_t __int_fast64_t; -typedef __uint64_t __uint_fast64_t; -#endif - -typedef long __off_t; -#ifdef __x86_64__ -typedef long int __off64_t; -#else -typedef long long int __off64_t; -#endif - -/* 7.18.1.4 Integer types capable of holding object pointers */ -#ifdef __i386__ -typedef __int32_t __intptr_t; -typedef __uint32_t __uintptr_t; -typedef __int32_t __ptrdiff_t; -/* Standard system types */ -typedef __uint32_t __size_t; -typedef __int32_t __ssize_t; -typedef long double __double_t; -typedef long double __float_t; -#else -typedef __int64_t __intptr_t; -typedef __uint64_t __uintptr_t; -typedef __int64_t __ptrdiff_t; - -/* Standard system types */ -typedef unsigned long __size_t; -typedef long __ssize_t; -typedef double __double_t; -typedef float __float_t; - -#endif /* !__i386__ */ - -typedef long __clock_t; - -typedef long __time_t; -typedef __builtin_va_list __va_list; -typedef unsigned int __wint_t; -/* wctype_t and wctrans_t are defined in wchar.h */ -typedef unsigned long int __wctype_t; -typedef int * __wctrans_t; - -/* - * mbstate_t is an opaque object to keep conversion state, during multibyte - * stream conversions. The content must not be referenced by user programs. - */ -/* For Linux, __mbstate_t is defined in wchar.h */ -typedef struct { - int __c; - union { - __wint_t __wc; - char __wcb[4]; - } __v; -} __mbstate_t; - -/* 7.18.1.5 Greatest-width integer types */ -typedef __int64_t __intmax_t; -typedef __uint64_t __uintmax_t; - - -typedef unsigned long int __ino_t; -typedef unsigned int __mode_t; -typedef unsigned int __uid_t; -typedef unsigned int __gid_t; -typedef long int __blksize_t; -typedef long int __blkcnt_t; - -#ifdef __x86_64__ -typedef unsigned long int __dev_t; -typedef long int __off64_t; -typedef unsigned long int __nlink_t; -typedef long int __blkcnt64_t; -typedef unsigned long int __ino64_t; -#else -typedef unsigned long long int __dev_t; -typedef long long int __off64_t; -typedef unsigned int __nlink_t; -typedef long long int __blkcnt64_t; -typedef unsigned long long int __ino64_t; -#endif - -typedef unsigned int __socklen_t; -typedef int __pid_t; -typedef long __cpu_mask; -#endif /* !_SYS__TYPES_H_ */ - - - diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/cdefs.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/cdefs.h deleted file mode 100644 index 71c3c1ce22..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/cdefs.h +++ /dev/null @@ -1,132 +0,0 @@ -/* $OpenBSD: cdefs.h,v 1.34 2012/08/14 20:11:37 matthew Exp $ */ -/* $NetBSD: cdefs.h,v 1.16 1996/04/03 20:46:39 christos Exp $ */ - -/* - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Berkeley Software Design, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)cdefs.h 8.7 (Berkeley) 1/21/94 - */ - -#ifndef _SYS_CDEFS_H_ -#define _SYS_CDEFS_H_ - -/* Declaration field in C/C++ headers */ -#if defined(__cplusplus) -# define __BEGIN_DECLS extern "C" { -# define __END_DECLS } -#else -# define __BEGIN_DECLS -# define __END_DECLS -#endif - -#if defined(__STDC__) || defined(__cplusplus) -# define __CONCAT(x,y) x ## y -# define __STRING(x) #x -#else -# define __CONCAT(x,y) x/**/y -# define __STRING(x) "x" -#endif -/* - * Macro to test if we're using a specific version of gcc or later. - */ -#if defined __GNUC__ && defined __GNUC_MINOR_ -# define __GNUC_PREREQ__(ma, mi) \ - ((__GNUC__ > (ma)) || (__GNUC__ == (ma) && __GNUC_MINOR__ >= (mi))) -#else -# define __GNUC_PREREQ__(ma, mi) 0 -#endif - -/* Calling Convention: cdecl */ -#define _TLIBC_CDECL_ - -/* Thread Directive */ -#define _TLIBC_THREAD_ /* __thread */ - -/* Deprecated Warnings */ -#define _TLIBC_DEPRECATED_MSG(x) __STRING(x)" is deprecated in tlibc." -#define _TLIBC_DEPRECATED_(x) __attribute__((deprecated(_TLIBC_DEPRECATED_MSG(x)))) - -#ifndef _TLIBC_WARN_DEPRECATED_FUNCTIONS_ -# define _TLIBC_DEPRECATED_FUNCTION_(__ret, __func, ...) -#else -# define _TLIBC_DEPRECATED_FUNCTION_(__ret, __func, ...) \ - _TLIBC_DEPRECATED_(__func) \ - __ret __func(__VA_ARGS__) -#endif - -/* Static analysis for printf format strings. - * _MSC_PRINTF_FORMAT_: MSVC SAL annotation for specifying format strings. - * _GCC_PRINTF_FORMAT_(x, y): GCC declaring attribute for checking format strings. - * x - index of the format string. In C++ non-static method, index 1 is reseved for 'this'. - * y - index of first variadic agrument in '...'. - */ -#define _GCC_PRINTF_FORMAT_(x, y) __attribute__((__format__ (printf, x, y))) - -/* Attribute - noreturn */ -#define _TLIBC_NORETURN_ __attribute__ ((__noreturn__)) - -/* - * GNU C version 2.96 adds explicit branch prediction so that - * the CPU back-end can hint the processor and also so that - * code blocks can be reordered such that the predicted path - * sees a more linear flow, thus improving cache behavior, etc. - * - * The following two macros provide us with a way to utilize this - * compiler feature. Use __predict_true() if you expect the expression - * to evaluate to true, and __predict_false() if you expect the - * expression to evaluate to false. - * - * A few notes about usage: - * - * * Generally, __predict_false() error condition checks (unless - * you have some _strong_ reason to do otherwise, in which case - * document it), and/or __predict_true() `no-error' condition - * checks, assuming you want to optimize for the no-error case. - * - * * Other than that, if you don't know the likelihood of a test - * succeeding from empirical or other `hard' evidence, don't - * make predictions. - * - * * These are meant to be used in places that are run `a lot'. - * It is wasteful to make predictions in code that is run - * seldomly (e.g. at subsystem initialization time) as the - * basic block reordering that this affects can often generate - * larger code. - */ -#if defined(__GNUC__) && __GNUC_PREREQ__(2, 96) -#define __predict_true(exp) __builtin_expect(((exp) != 0), 1) -#define __predict_false(exp) __builtin_expect(((exp) != 0), 0) -#else -#define __predict_true(exp) ((exp) != 0) -#define __predict_false(exp) ((exp) != 0) -#endif - -#endif /* !_SYS_CDEFS_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/endian.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/endian.h deleted file mode 100644 index 1cd7b810c3..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/endian.h +++ /dev/null @@ -1,54 +0,0 @@ -/* $OpenBSD: endian.h,v 1.18 2006/03/27 07:09:24 otto Exp $ */ - -/*- - * Copyright (c) 1997 Niklas Hallqvist. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * Generic definitions for little- and big-endian systems. Other endianesses - * has to be dealt with in the specific machine/endian.h file for that port. - * - * This file is meant to be included from a little- or big-endian port's - * machine/endian.h after setting _BYTE_ORDER to either 1234 for little endian - * or 4321 for big.. - */ - -#ifndef _SYS_ENDIAN_H_ -#define _SYS_ENDIAN_H_ - -#define _LITTLE_ENDIAN 1234 -#define _BIG_ENDIAN 4321 -#define _PDP_ENDIAN 3412 -#define _BYTE_ORDER _LITTLE_ENDIAN - -#define LITTLE_ENDIAN _LITTLE_ENDIAN -#define BIG_ENDIAN _BIG_ENDIAN -#define PDP_ENDIAN _PDP_ENDIAN -#define BYTE_ORDER _BYTE_ORDER - -#define __BYTE_ORDER _BYTE_ORDER -#define __BIG_ENDIAN _BIG_ENDIAN -#define __LITTLE_ENDIAN _LITTLE_ENDIAN - -#endif /* _SYS_ENDIAN_H_ */ - diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/epoll.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/epoll.h deleted file mode 100644 index 958a4c4fb0..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/epoll.h +++ /dev/null @@ -1,42 +0,0 @@ -// -// Copyright © 2005-2020 Rich Felker, et al. -// Licensed under the MIT license. -// - -/* Copyright © 2005-2020 Rich Felker, et al. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#ifndef _SYS_EPOLL_H -#define _SYS_EPOLL_H - -typedef union epoll_data { - void *ptr; - int fd; - uint32_t u32; - uint64_t u64; -} epoll_data_t; - -struct epoll_event { - uint32_t events; - epoll_data_t data; -} __attribute__ ((__packed__)); - -#endif diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/fpu.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/fpu.h deleted file mode 100644 index 4c218a91b6..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/fpu.h +++ /dev/null @@ -1,99 +0,0 @@ -/* $OpenBSD: fpu.h,v 1.16 2018/10/07 22:43:06 guenther Exp $ */ -/* $NetBSD: fpu.h,v 1.1 2003/04/26 18:39:40 fvdl Exp $ */ - -#ifndef _MACHINE_FPU_H_ -#define _MACHINE_FPU_H_ - -#include - -/* - * If the CPU supports xsave/xrstor then we use them so that we can provide - * AVX support. Otherwise we require fxsave/fxrstor, as the SSE registers - * are part of the ABI for passing floating point values. - * While fxsave/fxrstor only required 16-byte alignment for the save area, - * xsave/xrstor requires the save area to have 64-byte alignment. - */ - -struct fxsave64 { - u_int16_t fx_fcw; - u_int16_t fx_fsw; - u_int8_t fx_ftw; - u_int8_t fx_unused1; - u_int16_t fx_fop; - u_int64_t fx_rip; - u_int64_t fx_rdp; - u_int32_t fx_mxcsr; - u_int32_t fx_mxcsr_mask; - u_int64_t fx_st[8][2]; /* 8 normal FP regs */ - u_int64_t fx_xmm[16][2]; /* 16 SSE2 registers */ - u_int8_t fx_unused3[96]; -} __packed; - -struct xstate_hdr { - uint64_t xstate_bv; - uint64_t xstate_xcomp_bv; - uint8_t xstate_rsrv0[0]; - uint8_t xstate_rsrv[40]; -} ___packed; - -struct savefpu { - struct fxsave64 fp_fxsave; /* see above */ - struct xstate_hdr fp_xstate; - u_int64_t fp_ymm[16][2]; - u_int16_t fp_ex_sw; /* saved status from last exception */ - u_int16_t fp_ex_tw; /* saved tag from last exception */ -}; - -/* - * The i387 defaults to Intel extended precision mode and round to nearest, - * with all exceptions masked. - */ -#define __INITIAL_NPXCW__ 0x037f -#define __INITIAL_MXCSR__ 0x1f80 -#define __INITIAL_MXCSR_MASK__ 0xffbf - -#ifdef _KERNEL -/* - * XXX - */ -struct trapframe; -struct cpu_info; - -extern size_t fpu_save_len; -extern uint32_t fpu_mxcsr_mask; -extern uint64_t xsave_mask; - -void fpuinit(struct cpu_info *); -int fputrap(int _type); -void fpusave(struct savefpu *); -void fpusavereset(struct savefpu *); -void fpu_kernel_enter(void); -void fpu_kernel_exit(void); - -int xrstor_user(struct savefpu *_addr, uint64_t _mask); -#define fpureset() \ - xrstor_user(&proc0.p_addr->u_pcb.pcb_savefpu, xsave_mask) -int xsetbv_user(uint32_t _reg, uint64_t _mask); - -#define fninit() __asm("fninit") -#define fwait() __asm("fwait") -/* should be fxsave64, but where we use this it doesn't matter */ -#define fxsave(addr) __asm("fxsave %0" : "=m" (*addr)) -#define ldmxcsr(addr) __asm("ldmxcsr %0" : : "m" (*addr)) -#define fldcw(addr) __asm("fldcw %0" : : "m" (*addr)) - -static inline void -xsave(struct savefpu *addr, uint64_t mask) -{ - uint32_t lo, hi; - - lo = mask; - hi = mask >> 32; - /* should be xsave64, but where we use this it doesn't matter */ - __asm volatile("xsave %0" : "=m" (*addr) : "a" (lo), "d" (hi) : - "memory"); -} - -#endif - -#endif /* _MACHINE_FPU_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/ieee.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/ieee.h deleted file mode 100644 index 47379b28ed..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/ieee.h +++ /dev/null @@ -1,170 +0,0 @@ -/* $OpenBSD: ieee.h,v 1.2 2008/09/07 20:36:06 martynas Exp $ */ -/* $NetBSD: ieee.h,v 1.1 1996/09/30 16:34:25 ws Exp $ */ - -/* - * Copyright (c) 1992, 1993 - * The Regents of the University of California. All rights reserved. - * - * This software was developed by the Computer Systems Engineering group - * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and - * contributed to Berkeley. - * - * All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Lawrence Berkeley Laboratory. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)ieee.h 8.1 (Berkeley) 6/11/93 - */ - -/* - * ieee.h defines the machine-dependent layout of the machine's IEEE - * floating point. It does *not* define (yet?) any of the rounding - * mode bits, exceptions, and so forth. - */ - -/* - * Define the number of bits in each fraction and exponent. - * - * k k+1 - * Note that 1.0 x 2 == 0.1 x 2 and that denorms are represented - * - * (-exp_bias+1) - * as fractions that look like 0.fffff x 2 . This means that - * - * -126 - * the number 0.10000 x 2 , for instance, is the same as the normalized - * - * -127 -128 - * float 1.0 x 2 . Thus, to represent 2 , we need one leading zero - * - * -129 - * in the fraction; to represent 2 , we need two, and so on. This - * - * (-exp_bias-fracbits+1) - * implies that the smallest denormalized number is 2 - * - * for whichever format we are talking about: for single precision, for - * - * -126 -149 - * instance, we get .00000000000000000000001 x 2 , or 1.0 x 2 , and - * - * -149 == -127 - 23 + 1. - */ - -#include -#include - -#define SNG_EXPBITS 8 -#define SNG_FRACBITS 23 - -#define DBL_EXPBITS 11 -#define DBL_FRACHBITS 20 -#define DBL_FRACLBITS 32 -#define DBL_FRACBITS 52 - -#define EXT_EXPBITS 15 -#define EXT_FRACHBITS 32 -#define EXT_FRACLBITS 32 -#define EXT_FRACBITS 64 - -#define EXT_TO_ARRAY32(p, a) do { \ - (a)[0] = (uint32_t)(p)->ext_fracl; \ - (a)[1] = (uint32_t)(p)->ext_frach; \ -} while(0) - -struct ieee_single { - u_int sng_frac:23; - u_int sng_exp:8; - u_int sng_sign:1; -}; - -struct ieee_double { - u_int dbl_fracl; - u_int dbl_frach:20; - u_int dbl_exp:11; - u_int dbl_sign:1; -}; - -struct ieee_ext { - u_int ext_fracl; - u_int ext_frach; - u_int ext_exp:15; - u_int ext_sign:1; - u_int ext_padl:16; - u_int ext_padh; -}; - -/* - * Floats whose exponent is in [1..INFNAN) (of whatever type) are - * `normal'. Floats whose exponent is INFNAN are either Inf or NaN. - * Floats whose exponent is zero are either zero (iff all fraction - * bits are zero) or subnormal values. - * - * A NaN is a `signalling NaN' if its QUIETNAN bit is clear in its - * high fraction; if the bit is set, it is a `quiet NaN'. - */ -#define SNG_EXP_INFNAN 255 -#define DBL_EXP_INFNAN 2047 -#define EXT_EXP_INFNAN 32767 - -#if 0 -#define SNG_QUIETNAN (1 << 22) -#define DBL_QUIETNAN (1 << 19) -#define EXT_QUIETNAN (1 << 15) -#endif - -/* - * Exponent biases. - */ -#define SNG_EXP_BIAS 127 -#define DBL_EXP_BIAS 1023 -#define EXT_EXP_BIAS 16383 - -typedef int fp_except; -#define FP_X_INV 0x01 /* invalid operation exception */ -#define FP_X_DNML 0x02 /* denormalization exception */ -#define FP_X_DZ 0x04 /* divide-by-zero exception */ -#define FP_X_OFL 0x08 /* overflow exception */ -#define FP_X_UFL 0x10 /* underflow exception */ -#define FP_X_IMP 0x20 /* imprecise (loss of precision) */ - -typedef enum { - FP_RN=0, /* round to nearest representable number */ - FP_RM=1, /* round toward negative infinity */ - FP_RP=2, /* round toward positive infinity */ - FP_RZ=3 /* round to zero (truncate) */ -} fp_rnd; - -__BEGIN_DECLS -extern fp_rnd fpgetround(void); -extern fp_rnd fpsetround(fp_rnd); -extern fp_except fpgetmask(void); -extern fp_except fpsetmask(fp_except); -extern fp_except fpgetsticky(void); -extern fp_except fpsetsticky(fp_except); -__END_DECLS diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/limits.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/limits.h deleted file mode 100644 index 3d1f9673ad..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/limits.h +++ /dev/null @@ -1,77 +0,0 @@ -/* $OpenBSD: limits.h,v 1.8 2009/11/27 19:54:35 guenther Exp $ */ -/* - * Copyright (c) 2002 Marc Espie. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD - * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef _SYS_LIMITS_H_ -#define _SYS_LIMITS_H_ - -#include - -/* Common definitions for limits.h. */ - -#define CHAR_BIT 8 /* number of bits in a char */ - -#define SCHAR_MAX 0x7f /* max value for a signed char */ -#define SCHAR_MIN (-0x7f - 1) /* min value for a signed char */ - -#define UCHAR_MAX 0xff /* max value for an unsigned char */ -#ifdef __CHAR_UNSIGNED__ -# define CHAR_MIN 0 /* min value for a char */ -# define CHAR_MAX 0xff /* max value for a char */ -#else -# define CHAR_MAX 0x7f -# define CHAR_MIN (-0x7f-1) -#endif - -#define MB_LEN_MAX 1 /* Allow UTF-8 (RFC 3629) */ - -#define USHRT_MAX 0xffff /* max value for an unsigned short */ -#define SHRT_MAX 0x7fff /* max value for a short */ -#define SHRT_MIN (-0x7fff-1) /* min value for a short */ - -#define UINT_MAX 0xffffffffU /* max value for an unsigned int */ -#define INT_MAX 0x7fffffff /* max value for an int */ -#define INT_MIN (-0x7fffffff-1) /* min value for an int */ - -#ifdef __x86_64__ -# define ULONG_MAX 0xffffffffffffffffUL /* max value for unsigned long */ -# define LONG_MAX 0x7fffffffffffffffL /* max value for a signed long */ -# define LONG_MIN (-0x7fffffffffffffffL-1) /* min value for a signed long */ -#else -# define ULONG_MAX 0xffffffffUL /* max value for an unsigned long */ -# define LONG_MAX 0x7fffffffL /* max value for a long */ -# define LONG_MIN (-0x7fffffffL-1) /* min value for a long */ -#endif - -#define ULLONG_MAX 0xffffffffffffffffULL /* max value for unsigned long long */ -#define LLONG_MAX 0x7fffffffffffffffLL /* max value for a signed long long */ -#define LLONG_MIN (-0x7fffffffffffffffLL-1) /* min value for a signed long long */ - -#ifdef __x86_64__ -# define LONG_BIT 64 -#else -# define LONG_BIT 32 -#endif - -#endif /* !_SYS_LIMITS_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/sockaddr.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/sockaddr.h deleted file mode 100644 index ba6811cbf7..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/sockaddr.h +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright © 2005-2020 Rich Felker, et al. -// Licensed under the MIT license. -// - -/* Copyright © 2005-2020 Rich Felker, et al. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#ifndef _SYS_SOCKADDR_H_ -#define _SYS_SOCKADDR_H_ - -typedef unsigned short int sa_family_t; - -#endif diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/socket.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/socket.h deleted file mode 100644 index 0b16699cc6..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/socket.h +++ /dev/null @@ -1,54 +0,0 @@ -// -// Copyright © 2005-2020 Rich Felker, et al. -// Licensed under the MIT license. -// - -/* Copyright © 2005-2020 Rich Felker, et al. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#ifndef _SYS_SOCKET_H_ -#define _SYS_SOCKET_H_ - -#include -#include -#include - -typedef __socklen_t socklen_t; - -struct sockaddr { - sa_family_t sa_family; - char sa_data[14]; -}; - -struct msghdr { - void *msg_name; - socklen_t msg_namelen; - - struct iovec *msg_iov; - size_t msg_iovlen; - - void *msg_control; - size_t msg_controllen; - - int msg_flags; -}; - -#endif diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/stat.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/stat.h deleted file mode 100644 index 1cf090a7a1..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/stat.h +++ /dev/null @@ -1,127 +0,0 @@ -// -// Copyright © 2005-2020 Rich Felker, et al. -// Licensed under the MIT license. -// - -/* Copyright © 2005-2020 Rich Felker, et al. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - - -#ifndef _SYS_STAT_H_ -#define _SYS_STAT_H_ - -#include -#include -#include - -typedef __dev_t dev_t; -typedef __ino_t ino_t; -typedef __ino64_t ino64_t; -typedef __mode_t mode_t; -typedef __nlink_t nlink_t; -typedef __uid_t uid_t; -typedef __gid_t gid_t; -typedef __blksize_t blksize_t; -typedef __blkcnt_t blkcnt_t; -typedef __blkcnt64_t blkcnt64_t; - -struct stat { - dev_t st_dev; - ino_t st_ino; - nlink_t st_nlink; - - mode_t st_mode; - uid_t st_uid; - gid_t st_gid; - unsigned int __pad0; - dev_t st_rdev; - off_t st_size; - blksize_t st_blksize; - blkcnt_t st_blocks; - - struct timespec st_atim; - struct timespec st_mtim; - struct timespec st_ctim; - long __unused[3]; -}; - -struct stat64 { - dev_t st_dev; - ino64_t st_ino; - nlink_t st_nlink; - - mode_t st_mode; - uid_t st_uid; - gid_t st_gid; - unsigned int __pad0; - dev_t st_rdev; - off_t st_size; - blksize_t st_blksize; - blkcnt64_t st_blocks; - - struct timespec st_atim; - struct timespec st_mtim; - struct timespec st_ctim; - long __unused[3]; -}; - -#define S_IFMT 0170000 - -#define S_IFDIR 0040000 -#define S_IFCHR 0020000 -#define S_IFBLK 0060000 -#define S_IFREG 0100000 -#define S_IFIFO 0010000 -#define S_IFLNK 0120000 -#define S_IFSOCK 0140000 - -#define S_TYPEISMQ(buf) 0 -#define S_TYPEISSEM(buf) 0 -#define S_TYPEISSHM(buf) 0 -#define S_TYPEISTMO(buf) 0 - -#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) -#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) -#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) -#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) -#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) -#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) -#define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) - -#ifndef S_IRUSR -#define S_ISUID 04000 -#define S_ISGID 02000 -#define S_ISVTX 01000 -#define S_IRUSR 0400 -#define S_IWUSR 0200 -#define S_IXUSR 0100 -#define S_IRWXU 0700 -#define S_IRGRP 0040 -#define S_IWGRP 0020 -#define S_IXGRP 0010 -#define S_IRWXG 0070 -#define S_IROTH 0004 -#define S_IWOTH 0002 -#define S_IXOTH 0001 -#define S_IRWXO 0007 -#endif - -#endif /* _SYS_STAT_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/stdint.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/stdint.h deleted file mode 100644 index 51599456d5..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/stdint.h +++ /dev/null @@ -1,260 +0,0 @@ -/* $OpenBSD: stdint.h,v 1.4 2006/12/10 22:17:55 deraadt Exp $ */ - -/* - * Copyright (c) 1997, 2005 Todd C. Miller - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _SYS_STDINT_H_ -#define _SYS_STDINT_H_ - -#include -#include - -/* 7.18.1.1 Exact-width integer types (also in sys/types.h) */ -#ifndef _INT8_T_DEFINED_ -#define _INT8_T_DEFINED_ -typedef __int8_t int8_t; -#endif - -#ifndef _UINT8_T_DEFINED_ -#define _UINT8_T_DEFINED_ -typedef __uint8_t uint8_t; -#endif - -#ifndef _INT16_T_DEFINED_ -#define _INT16_T_DEFINED_ -typedef __int16_t int16_t; -#endif - -#ifndef _UINT16_T_DEFINED_ -#define _UINT16_T_DEFINED_ -typedef __uint16_t uint16_t; -#endif - -#ifndef _INT32_T_DEFINED_ -#define _INT32_T_DEFINED_ -typedef __int32_t int32_t; -#endif - -#ifndef _UINT32_T_DEFINED_ -#define _UINT32_T_DEFINED_ -typedef __uint32_t uint32_t; -#endif - -#ifndef _INT64_T_DEFINED_ -#define _INT64_T_DEFINED_ -typedef __int64_t int64_t; -#endif - -#ifndef _UINT64_T_DEFINED_ -#define _UINT64_T_DEFINED_ -typedef __uint64_t uint64_t; -#endif - -/* 7.18.1.2 Minimum-width integer types */ -typedef __int_least8_t int_least8_t; -typedef __uint_least8_t uint_least8_t; -typedef __int_least16_t int_least16_t; -typedef __uint_least16_t uint_least16_t; -typedef __int_least32_t int_least32_t; -typedef __uint_least32_t uint_least32_t; -typedef __int_least64_t int_least64_t; -typedef __uint_least64_t uint_least64_t; - -/* 7.18.1.3 Fastest minimum-width integer types */ -typedef __int_fast8_t int_fast8_t; -typedef __uint_fast8_t uint_fast8_t; -typedef __int_fast16_t int_fast16_t; -typedef __uint_fast16_t uint_fast16_t; -typedef __int_fast32_t int_fast32_t; -typedef __uint_fast32_t uint_fast32_t; -typedef __int_fast64_t int_fast64_t; -typedef __uint_fast64_t uint_fast64_t; - -/* 7.18.1.4 Integer types capable of holding object pointers */ -#ifndef _INTPTR_T_DEFINED_ -#define _INTPTR_T_DEFINED_ -typedef __intptr_t intptr_t; -#endif - -#ifndef _UINTPTR_T_DEFINED_ -#define _UINTPTR_T_DEFINED_ -typedef __uintptr_t uintptr_t; -#endif - -/* 7.18.1.5 Greatest-width integer types */ -typedef __intmax_t intmax_t; -typedef __uintmax_t uintmax_t; - -//#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) -/* - * 7.18.2 Limits of specified-width integer types. - * - * The following object-like macros specify the minimum and maximum limits - * of integer types corresponding to the typedef names defined above. - */ - -/* 7.18.2.1 Limits of exact-width integer types */ -#define INT8_MIN (-0x7f - 1) -#define INT16_MIN (-0x7fff - 1) -#define INT32_MIN (-0x7fffffff - 1) -#ifdef __x86_64__ -#define INT64_MIN (-0x7fffffffffffffffL - 1) -#else -#define INT64_MIN (-0x7fffffffffffffffLL - 1) -#endif - -#define INT8_MAX 0x7f -#define INT16_MAX 0x7fff -#define INT32_MAX 0x7fffffff -#ifdef __x86_64__ -#define INT64_MAX 0x7fffffffffffffffL -#else -#define INT64_MAX 0x7fffffffffffffffLL -#endif - -#define UINT8_MAX 0xff -#define UINT16_MAX 0xffff -#define UINT32_MAX 0xffffffffU -#ifdef __x86_64__ -#define UINT64_MAX 0xffffffffffffffffUL -#else -#define UINT64_MAX 0xffffffffffffffffULL -#endif - -/* 7.18.2.2 Limits of minimum-width integer types */ -#define INT_LEAST8_MIN INT8_MIN -#define INT_LEAST16_MIN INT16_MIN -#define INT_LEAST32_MIN INT32_MIN -#define INT_LEAST64_MIN INT64_MIN - -#define INT_LEAST8_MAX INT8_MAX -#define INT_LEAST16_MAX INT16_MAX -#define INT_LEAST32_MAX INT32_MAX -#define INT_LEAST64_MAX INT64_MAX - -#define UINT_LEAST8_MAX UINT8_MAX -#define UINT_LEAST16_MAX UINT16_MAX -#define UINT_LEAST32_MAX UINT32_MAX -#define UINT_LEAST64_MAX UINT64_MAX - -/* 7.18.2.3 Limits of fastest minimum-width integer types */ -#define INT_FAST8_MIN INT8_MIN -#define INT_FAST16_MIN INT16_MIN -#define INT_FAST32_MIN INT32_MIN -#define INT_FAST64_MIN INT64_MIN - -#define INT_FAST8_MAX INT8_MAX -#ifdef __x86_64__ -#define INT_FAST16_MAX INT64_MAX -#define INT_FAST32_MAX INT64_MAX -#else -#define INT_FAST16_MAX INT32_MAX -#define INT_FAST32_MAX INT32_MAX -#endif -#define INT_FAST64_MAX INT64_MAX - -#define UINT_FAST8_MAX UINT8_MAX -#ifdef __x86_64__ -#define UINT_FAST16_MAX UINT64_MAX -#define UINT_FAST32_MAX UINT64_MAX -#else -#define UINT_FAST16_MAX UINT32_MAX -#define UINT_FAST32_MAX UINT32_MAX -#endif -#define UINT_FAST64_MAX UINT64_MAX - -/* 7.18.2.4 Limits of integer types capable of holding object pointers */ -#ifdef __x86_64__ -#define INTPTR_MIN INT64_MIN -#define INTPTR_MAX INT64_MAX -#define UINTPTR_MAX UINT64_MAX -#else -#define INTPTR_MIN INT32_MIN -#define INTPTR_MAX INT32_MAX -#define UINTPTR_MAX UINT32_MAX -#endif - -/* 7.18.2.5 Limits of greatest-width integer types */ -#define INTMAX_MIN INT64_MIN -#define INTMAX_MAX INT64_MAX -#define UINTMAX_MAX UINT64_MAX - -/* - * 7.18.3 Limits of other integer types. - * - * The following object-like macros specify the minimum and maximum limits - * of integer types corresponding to types specified in other standard - * header files. - */ - -/* Limits of ptrdiff_t */ -#define PTRDIFF_MIN INTPTR_MIN -#define PTRDIFF_MAX INTPTR_MAX - -/* Limits of size_t (also in limits.h) */ -#ifndef SIZE_MAX -#define SIZE_MAX UINTPTR_MAX -#endif - -/* Limits of wchar_t */ -# ifdef __WCHAR_MAX__ -# define WCHAR_MAX __WCHAR_MAX__ -# else -# define WCHAR_MAX (2147483647) -# endif -# ifdef __WCHAR_MIN__ -# define WCHAR_MIN __WCHAR_MIN__ -# elif L'\0' - 1 > 0 -# define WCHAR_MIN L'\0' -# else -# define WCHAR_MIN (-WCHAR_MAX - 1) -# endif - -/* Limits of wint_t */ -# define WINT_MIN (0u) -# define WINT_MAX (4294967295u) - -//#endif /* __cplusplus || __STDC_LIMIT_MACROS */ - -//#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) -/* - * 7.18.4 Macros for integer constants. - * - * The following function-like macros expand to integer constants - * suitable for initializing objects that have integer types corresponding - * to types defined in . The argument in any instance of - * these macros shall be a decimal, octal, or hexadecimal constant with - * a value that does not exceed the limits for the corresponding type. - */ - -/* 7.18.4.1 Macros for minimum-width integer constants. */ -#define INT8_C(_c) (_c) -#define INT16_C(_c) (_c) -#define INT32_C(_c) (_c) -#define INT64_C(_c) __CONCAT(_c, LL) - -#define UINT8_C(_c) (_c) -#define UINT16_C(_c) (_c) -#define UINT32_C(_c) __CONCAT(_c, U) -#define UINT64_C(_c) __CONCAT(_c, ULL) - -/* 7.18.4.2 Macros for greatest-width integer constants. */ -#define INTMAX_C(_c) __CONCAT(_c, LL) -#define UINTMAX_C(_c) __CONCAT(_c, ULL) - -//#endif /* __cplusplus || __STDC_CONSTANT_MACROS */ - -#endif /* _SYS_STDINT_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/struct_timespec.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/struct_timespec.h deleted file mode 100644 index bca02c8809..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/struct_timespec.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// Copyright © 2005-2020 Rich Felker, et al. -// Licensed under the MIT license. -// - -/* Copyright © 2005-2020 Rich Felker, et al. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#ifndef _SYS_TIMESPEC_H_ -#define _SYS_TIMESPEC_H_ - -#include - -struct timespec { - __time_t tv_sec; - long tv_nsec; -}; - -#endif diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/types.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/types.h deleted file mode 100644 index b64f89df04..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/types.h +++ /dev/null @@ -1,129 +0,0 @@ -/* $OpenBSD: types.h,v 1.31 2008/03/16 19:42:57 otto Exp $ */ -/* $NetBSD: types.h,v 1.29 1996/11/15 22:48:25 jtc Exp $ */ - -/*- - * Copyright (c) 1982, 1986, 1991, 1993 - * The Regents of the University of California. All rights reserved. - * (c) UNIX System Laboratories, Inc. - * All or some portions of this file are derived from material licensed - * to the University of California by American Telephone and Telegraph - * Co. or Unix System Laboratories, Inc. and are reproduced herein with - * the permission of UNIX System Laboratories, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)types.h 8.4 (Berkeley) 1/21/94 - */ - -#ifndef _SYS_TYPES_H_ -#define _SYS_TYPES_H_ - -#include -#include - -typedef unsigned char u_char; -typedef unsigned short u_short; -typedef unsigned int u_int; -typedef unsigned long u_long; - -typedef unsigned char unchar; /* Sys V compatibility */ -typedef unsigned short ushort; /* Sys V compatibility */ -typedef unsigned int uint; /* Sys V compatibility */ -typedef unsigned long ulong; /* Sys V compatibility */ - -#ifndef _INT8_T_DEFINED_ -#define _INT8_T_DEFINED_ -typedef __int8_t int8_t; -#endif - -#ifndef _UINT8_T_DEFINED_ -#define _UINT8_T_DEFINED_ -typedef __uint8_t uint8_t; -#endif - -#ifndef _INT16_T_DEFINED_ -#define _INT16_T_DEFINED_ -typedef __int16_t int16_t; -#endif - -#ifndef _UINT16_T_DEFINED_ -#define _UINT16_T_DEFINED_ -typedef __uint16_t uint16_t; -#endif - -#ifndef _INT32_T_DEFINED_ -#define _INT32_T_DEFINED_ -typedef __int32_t int32_t; -#endif - -#ifndef _UINT32_T_DEFINED_ -#define _UINT32_T_DEFINED_ -typedef __uint32_t uint32_t; -#endif - -#ifndef _INT64_T_DEFINED_ -#define _INT64_T_DEFINED_ -typedef __int64_t int64_t; -#endif - -#ifndef _UINT64_T_DEFINED_ -#define _UINT64_T_DEFINED_ -typedef __uint64_t uint64_t; -#endif - -#ifndef _INTPTR_T_DEFINED_ -#define _INTPTR_T_DEFINED_ -typedef __intptr_t intptr_t; -#endif - -#ifndef _UINTPTR_T_DEFINED_ -#define _UINTPTR_T_DEFINED_ -typedef __uintptr_t uintptr_t; -#endif - -/* BSD-style unsigned bits types */ -typedef __uint8_t u_int8_t; -typedef __uint16_t u_int16_t; -typedef __uint32_t u_int32_t; -typedef __uint64_t u_int64_t; - - -#ifndef _SIZE_T_DEFINED_ -#define _SIZE_T_DEFINED_ -typedef __size_t size_t; -#endif - -#ifndef _SSIZE_T_DEFINED_ -#define _SSIZE_T_DEFINED_ -typedef __ssize_t ssize_t; -#endif - -#ifndef _OFF_T_DEFINED_ -#define _OFF_T_DEFINED_ -typedef __off_t off_t; -typedef __off64_t off64_t; -#endif - -#endif /* !_SYS_TYPES_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/uio.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/uio.h deleted file mode 100644 index 2544f06a7d..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/sys/uio.h +++ /dev/null @@ -1,35 +0,0 @@ -// -// Copyright © 2005-2020 Rich Felker, et al. -// Licensed under the MIT license. -// - -/* Copyright © 2005-2020 Rich Felker, et al. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -#ifndef _SYS_UIO_H_ -#define _SYS_UIO_H_ - -struct iovec { - void *iov_base; - size_t iov_len; -}; - -#endif diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/time.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/time.h deleted file mode 100644 index 01cfd6e4e9..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/time.h +++ /dev/null @@ -1,105 +0,0 @@ -/* $OpenBSD: time.h,v 1.18 2006/01/06 18:53:04 millert Exp $ */ -/* $NetBSD: time.h,v 1.9 1994/10/26 00:56:35 cgd Exp $ */ - -/* - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. - * - * (c) UNIX System Laboratories, Inc. - * All or some portions of this file are derived from material licensed - * to the University of California by American Telephone and Telegraph - * Co. or Unix System Laboratories, Inc. and are reproduced herein with - * the permission of UNIX System Laboratories, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)time.h 5.12 (Berkeley) 3/9/91 - */ - -#ifndef _TIME_H_ -#define _TIME_H_ - -#include -#include -#include - -#ifndef NULL -#ifdef __cplusplus -#define NULL 0 -#else -#define NULL ((void *)0) -#endif -#endif - -#if !defined (_CLOCK_T_DEFINED_) && !defined (_CLOCK_T_DEFINED) -#define _CLOCK_T_DEFINED_ -#define _CLOCK_T_DEFINED -typedef __clock_t clock_t; -#endif - -#if !defined (_TIME_T_DEFINED_) && !defined (_TIME_T_DEFINED) -#define _TIME_T_DEFINED_ -#define _TIME_T_DEFINED -typedef __time_t time_t; -#endif - -#if !defined (_SIZE_T_DEFINED_) && !defined (_SIZE_T_DEFINED) -#define _SIZE_T_DEFINED_ -#define _SIZE_T_DEFINED -typedef __size_t size_t; -#endif - -#if !defined (_TM_DEFINED) -#define _TM_DEFINED -struct tm { - int tm_sec; /* seconds after the minute [0-60] */ - int tm_min; /* minutes after the hour [0-59] */ - int tm_hour; /* hours since midnight [0-23] */ - int tm_mday; /* day of the month [1-31] */ - int tm_mon; /* months since January [0-11] */ - int tm_year; /* years since 1900 */ - int tm_wday; /* days since Sunday [0-6] */ - int tm_yday; /* days since January 1 [0-365] */ - int tm_isdst; /* Daylight Saving Time flag */ - /* FIXME: naming issue exists on Fedora/Ubuntu */ - long tm_gmtoff; /* offset from UTC in seconds */ - char *tm_zone; /* timezone abbreviation */ -}; -#endif - -__BEGIN_DECLS - -double _TLIBC_CDECL_ difftime(time_t, time_t); -char * _TLIBC_CDECL_ asctime(const struct tm *); -size_t _TLIBC_CDECL_ strftime(char *, size_t, const char *, const struct tm *); - -/* - * Non-C99 - */ -char * _TLIBC_CDECL_ asctime_r(const struct tm *, char *); - -__END_DECLS - -#endif /* !_TIME_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/unistd.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/unistd.h deleted file mode 100644 index 2ab3a9a042..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/unistd.h +++ /dev/null @@ -1,59 +0,0 @@ -/* $OpenBSD: unistd.h,v 1.62 2008/06/25 14:58:54 millert Exp $ */ -/* $NetBSD: unistd.h,v 1.26.4.1 1996/05/28 02:31:51 mrg Exp $ */ - -/*- - * Copyright (c) 1991 The Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)unistd.h 5.13 (Berkeley) 6/17/91 - */ - -#ifndef _UNISTD_H_ -#define _UNISTD_H_ - -#include -#include - -__BEGIN_DECLS - -void * _TLIBC_CDECL_ sbrk(intptr_t); - -/* - * Deprecated Non-C99. - */ -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, execl, const char *, const char *, ...); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, execlp, const char *, const char *, ...); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, execle, const char *, const char *, ...); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, execv, const char *, char * const *); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, execve, const char *, char * const *, char * const *); -_TLIBC_DEPRECATED_FUNCTION_(int _TLIBC_CDECL_, execvp, const char *, char * const *); - -//_TLIBC_DEPRECATED_FUNCTION_(pid_t _TLIBC_CDECL_, fork, void); /* no pid_t */ - -__END_DECLS - -#endif /* !_UNISTD_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/wchar.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/wchar.h deleted file mode 100644 index 2db86f28eb..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/wchar.h +++ /dev/null @@ -1,143 +0,0 @@ -/* $OpenBSD: wchar.h,v 1.11 2010/07/24 09:58:39 guenther Exp $ */ -/* $NetBSD: wchar.h,v 1.16 2003/03/07 07:11:35 tshiozak Exp $ */ - -/*- - * Copyright (c)1999 Citrus Project, - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/*- - * Copyright (c) 1999, 2000 The NetBSD Foundation, Inc. - * All rights reserved. - * - * This code is derived from software contributed to The NetBSD Foundation - * by Julian Coleman. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _WCHAR_H_ -#define _WCHAR_H_ - -#include -#include -#include /* WCHAR_MAX/WCHAR_MIN */ - -#ifndef NULL -#ifdef __cplusplus -#define NULL 0 -#else -#define NULL ((void *)0) -#endif -#endif - -#if !defined(_WCHAR_T_DEFINED_) && !defined(__cplusplus) -#define _WCHAR_T_DEFINED_ -#ifndef __WCHAR_TYPE__ -#define __WCHAR_TYPE__ int -#endif -typedef __WCHAR_TYPE__ wchar_t; -#endif - -#ifndef _MBSTATE_T_DEFINED_ -#define _MBSTATE_T_DEFINED_ -typedef __mbstate_t mbstate_t; -#endif - -#ifndef _WINT_T_DEFINED_ -#define _WINT_T_DEFINED_ -typedef __wint_t wint_t; -#endif - -#ifndef _SIZE_T_DEFINED_ -#define _SIZE_T_DEFINED_ -typedef __size_t size_t; -#endif - -#ifndef WEOF -#define WEOF ((wint_t)-1) -#endif - -__BEGIN_DECLS - -wint_t _TLIBC_CDECL_ btowc(int); -int _TLIBC_CDECL_ wctob(wint_t); -size_t _TLIBC_CDECL_ mbrlen(const char *, size_t, mbstate_t *); -size_t _TLIBC_CDECL_ mbrtowc(wchar_t *, const char *, size_t, mbstate_t *); -int _TLIBC_CDECL_ mbsinit(const mbstate_t *); -size_t _TLIBC_CDECL_ mbsrtowcs(wchar_t *, const char **, size_t, mbstate_t *); -size_t _TLIBC_CDECL_ wcrtomb(char *, wchar_t, mbstate_t *); -wchar_t * _TLIBC_CDECL_ wcschr(const wchar_t *, wchar_t); -int _TLIBC_CDECL_ wcscmp(const wchar_t *, const wchar_t *); -int _TLIBC_CDECL_ wcscoll(const wchar_t *, const wchar_t *); -size_t _TLIBC_CDECL_ wcscspn(const wchar_t *, const wchar_t *); -size_t _TLIBC_CDECL_ wcslen(const wchar_t *); -wchar_t * _TLIBC_CDECL_ wcsncat(wchar_t *, const wchar_t *, size_t); -int _TLIBC_CDECL_ wcsncmp(const wchar_t *, const wchar_t *, size_t); -wchar_t * _TLIBC_CDECL_ wcsncpy(wchar_t *, const wchar_t *, size_t); -wchar_t * _TLIBC_CDECL_ wcspbrk(const wchar_t *, const wchar_t *); -wchar_t * _TLIBC_CDECL_ wcsrchr(const wchar_t *, wchar_t); -size_t _TLIBC_CDECL_ wcsrtombs(char *, const wchar_t **, size_t, mbstate_t *); -size_t _TLIBC_CDECL_ wcsspn(const wchar_t *, const wchar_t *); -wchar_t * _TLIBC_CDECL_ wcsstr(const wchar_t *, const wchar_t *); -wchar_t * _TLIBC_CDECL_ wcstok(wchar_t *, const wchar_t *, wchar_t **); -size_t _TLIBC_CDECL_ wcsxfrm(wchar_t *, const wchar_t *, size_t); -wchar_t * _TLIBC_CDECL_ wmemchr(const wchar_t *, wchar_t, size_t); -int _TLIBC_CDECL_ wmemcmp(const wchar_t *, const wchar_t *, size_t); -wchar_t * _TLIBC_CDECL_ wmemcpy(wchar_t *, const wchar_t *, size_t); -wchar_t * _TLIBC_CDECL_ wmemmove(wchar_t *, const wchar_t *, size_t); -wchar_t * _TLIBC_CDECL_ wmemset(wchar_t *, wchar_t, size_t); - -int _TLIBC_CDECL_ swprintf(wchar_t *, size_t, const wchar_t *, ...); -int _TLIBC_CDECL_ vswprintf(wchar_t *, size_t, const wchar_t *, __va_list); - -long double _TLIBC_CDECL_ wcstold (const wchar_t *, wchar_t **); -long long _TLIBC_CDECL_ wcstoll (const wchar_t *, wchar_t **, int); -unsigned long long _TLIBC_CDECL_ wcstoull (const wchar_t *, wchar_t **, int); - -/* leagcy version of wcsstr */ -wchar_t * _TLIBC_CDECL_ wcswcs(const wchar_t *, const wchar_t *); - -__END_DECLS - -#endif /* !_WCHAR_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/common/inc/wctype.h b/tee-worker/bitacross/rust-sgx-sdk/common/inc/wctype.h deleted file mode 100644 index 0ab9497d78..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/common/inc/wctype.h +++ /dev/null @@ -1,80 +0,0 @@ -/* $OpenBSD: wctype.h,v 1.5 2006/01/06 18:53:04 millert Exp $ */ -/* $NetBSD: wctype.h,v 1.5 2003/03/02 22:18:11 tshiozak Exp $ */ - -/*- - * Copyright (c)1999 Citrus Project, - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * citrus Id: wctype.h,v 1.4 2000/12/21 01:50:21 itojun Exp - */ - -#ifndef _WCTYPE_H_ -#define _WCTYPE_H_ - -#include -#include - -#ifndef _WINT_T_DEFINED_ -#define _WINT_T_DEFINED_ -typedef __wint_t wint_t; -#endif - -#ifndef _WCTRANS_T_DEFINED_ -#define _WCTRANS_T_DEFINED_ -typedef __wctrans_t wctrans_t; -#endif - -#ifndef _WCTYPE_T_DEFINED_ -#define _WCTYPE_T_DEFINED_ -typedef __wctype_t wctype_t; -#endif - -#ifndef WEOF -#define WEOF ((wint_t)-1) -#endif - -__BEGIN_DECLS - -int _TLIBC_CDECL_ iswalnum(wint_t); -int _TLIBC_CDECL_ iswalpha(wint_t); -int _TLIBC_CDECL_ iswblank(wint_t); -int _TLIBC_CDECL_ iswcntrl(wint_t); -int _TLIBC_CDECL_ iswdigit(wint_t); -int _TLIBC_CDECL_ iswgraph(wint_t); -int _TLIBC_CDECL_ iswlower(wint_t); -int _TLIBC_CDECL_ iswprint(wint_t); -int _TLIBC_CDECL_ iswpunct(wint_t); -int _TLIBC_CDECL_ iswspace(wint_t); -int _TLIBC_CDECL_ iswupper(wint_t); -int _TLIBC_CDECL_ iswxdigit(wint_t); -int _TLIBC_CDECL_ iswctype(wint_t, wctype_t); -wint_t _TLIBC_CDECL_ towctrans(wint_t, wctrans_t); -wint_t _TLIBC_CDECL_ towlower(wint_t); -wint_t _TLIBC_CDECL_ towupper(wint_t); -wctrans_t _TLIBC_CDECL_ wctrans(const char *); -wctype_t _TLIBC_CDECL_ wctype(const char *); - -__END_DECLS - -#endif /* _WCTYPE_H_ */ diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/inc/dirent.h b/tee-worker/bitacross/rust-sgx-sdk/edl/inc/dirent.h deleted file mode 100644 index be63f8332d..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/inc/dirent.h +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License.. - -#ifndef _EDL_DIRENT_H -#define _EDL_DIRENT_H - -struct dirent_t -{ - uint64_t d_ino; - int64_t d_off; - unsigned short int d_reclen; - unsigned char d_type; - char d_name[256]; -}; - -struct dirent64_t -{ - uint64_t d_ino; - int64_t d_off; - unsigned short int d_reclen; - unsigned char d_type; - char d_name[256]; -}; - -#endif diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/inc/stat.h b/tee-worker/bitacross/rust-sgx-sdk/edl/inc/stat.h deleted file mode 100644 index 7f04c3cec9..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/inc/stat.h +++ /dev/null @@ -1,65 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License.. - -#ifndef _EDL_STAT_H -#define _EDL_STAT_H - -struct stat_t -{ - uint64_t st_dev; - uint64_t st_ino; - uint64_t st_nlink; - uint32_t st_mode; - uint32_t st_uid; - uint32_t st_gid; - int __pad0; - uint64_t st_rdev; - uint64_t st_size; - int64_t st_blksize; - int64_t st_blocks; - int64_t st_atime; - int64_t st_atime_nsec; - int64_t st_mtime; - int64_t st_mtime_nsec; - int64_t st_ctime; - int64_t st_ctime_nsec; - int64_t __reserved[3]; -}; - -struct stat64_t -{ - uint64_t st_dev; - uint64_t st_ino; - uint64_t st_nlink; - uint32_t st_mode; - uint32_t st_uid; - uint32_t st_gid; - int __pad0; - uint64_t st_rdev; - uint64_t st_size; - int64_t st_blksize; - int64_t st_blocks; - int64_t st_atime; - int64_t st_atime_nsec; - int64_t st_mtime; - int64_t st_mtime_nsec; - int64_t st_ctime; - int64_t st_ctime_nsec; - int64_t __reserved[3]; -}; - -#endif diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_dcap_tvl.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_dcap_tvl.edl deleted file mode 100644 index 7c5c0d8c69..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_dcap_tvl.edl +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2011-2020 Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -enclave { - - include "sgx_qve_header.h" - include "sgx_ql_quote.h" - - - trusted { - - /** - * Verify QvE Report and Identity - * - * @param p_quote[IN] - Pointer to SGX Quote. - * @param quote_size[IN] - Size of the buffer pointed to by p_quote (in bytes). - * @param p_qve_report_info[IN] - The output of API "sgx_qv_verify_quote", it should contain QvE report and nonce - * @param expiration_check_date[IN] - This is the date to verify QvE report data, you should use same value for this API and "sgx_qv_verify_quote" - * @param collateral_expiration_status[IN] - The output of API "sgx_qv_verify_quote" about quote verification collateral's expiration status - * @param quote_verification_result[IN] - The output of API "sgx_qv_verify_quote" about quote verification result - * @param p_supplemental_data[IN] - The output of API "sgx_qv_verify_quote", the pointer to supplemental data - * @param supplemental_data_size[IN] - Size of the buffer pointed to by p_quote (in bytes) - * @param qve_isvsvn_threshold [IN] - The threshold of QvE ISVSVN, the ISVSVN of QvE used to verify quote must be greater or equal to this threshold. You can get latest QvE ISVSVN in QvE Identity (JSON) from Intel PCS. - * - * @return Status code of the operation, one of: - * - SGX_QL_SUCCESS - * - SGX_QL_ERROR_INVALID_PARAMETER - * - SGX_QL_ERROR_REPORT // Error when verifying QvE report - * - SGX_QL_ERROR_UNEXPECTED // Error when comparing QvE report data - * - SGX_QL_QVEIDENTITY_MISMATCH // Error when comparing QvE identity - * - SGX_QL_QVE_OUT_OF_DATE // QvE ISVSVN is smaller than input QvE ISV SVN threshold - **/ - - public quote3_error_t sgx_tvl_verify_qve_report_and_identity( - [in, size=quote_size] const uint8_t *p_quote, - uint32_t quote_size, - [in, count=1] const sgx_ql_qe_report_info_t *p_qve_report_info, - time_t expiration_check_date, - uint32_t collateral_expiration_status, - sgx_ql_qv_result_t quote_verification_result, - [in, size=supplemental_data_size] const uint8_t *p_supplemental_data, - uint32_t supplemental_data_size, - sgx_isv_svn_t qve_isvsvn_threshold); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_pthread.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_pthread.edl deleted file mode 100644 index 7a097a7396..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_pthread.edl +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2011-2019 Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -enclave { - untrusted { - [cdecl] int pthread_wait_timeout_ocall (unsigned long long waiter, unsigned long long timeout); - [cdecl] int pthread_create_ocall(unsigned long long self); - [cdecl] int pthread_wakeup_ocall(unsigned long long waiter); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_tkey_exchange.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_tkey_exchange.edl deleted file mode 100644 index 3e18c89582..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_tkey_exchange.edl +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2011-2019 Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -enclave { - trusted { - public sgx_status_t sgx_ra_get_ga(sgx_ra_context_t context, - [out] sgx_ec256_public_t *g_a); - - public sgx_status_t sgx_ra_proc_msg2_trusted(sgx_ra_context_t context, - [in]const sgx_ra_msg2_t *p_msg2, /*copy msg2 except quote into enclave */ - [in] const sgx_target_info_t *p_qe_target, - [out] sgx_report_t *p_report, - [out] sgx_quote_nonce_t *p_nonce); - - public sgx_status_t sgx_ra_get_msg3_trusted(sgx_ra_context_t context, - uint32_t quote_size, - [in]sgx_report_t* qe_report, - [user_check]sgx_ra_msg3_t *p_msg3, - uint32_t msg3_size); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_tprotected_fs.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_tprotected_fs.edl deleted file mode 100644 index 2dfad370a9..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_tprotected_fs.edl +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2011-2019 Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -enclave { - from "sgx_tstdc.edl" import *; - untrusted { - void* u_sgxprotectedfs_exclusive_file_open([in, string] const char* filename, uint8_t read_only, [out] int64_t* file_size, [out] int32_t* error_code); - uint8_t u_sgxprotectedfs_check_if_file_exists([in, string] const char* filename); - int32_t u_sgxprotectedfs_fread_node([user_check] void* f, uint64_t node_number, [out, size=node_size] uint8_t* buffer, uint32_t node_size); - int32_t u_sgxprotectedfs_fwrite_node([user_check] void* f, uint64_t node_number, [in, size=node_size] uint8_t* buffer, uint32_t node_size); - int32_t u_sgxprotectedfs_fclose([user_check] void* f); - uint8_t u_sgxprotectedfs_fflush([user_check] void* f); - int32_t u_sgxprotectedfs_remove([in, string] const char* filename); - - void* u_sgxprotectedfs_recovery_file_open([in, string] const char* filename); - uint8_t u_sgxprotectedfs_fwrite_recovery_node([user_check] void* f, [in, count=data_length] uint8_t* data, uint32_t data_length); - int32_t u_sgxprotectedfs_do_file_recovery([in, string] const char* filename, [in, string] const char* recovery_filename, uint32_t node_size); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_tstdc.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_tstdc.edl deleted file mode 100644 index 4124debcfb..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_tstdc.edl +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2011-2019 Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -enclave { - untrusted { - [cdecl] void sgx_oc_cpuidex([out] int cpuinfo[4], int leaf, int subleaf); - - /* Go outside and wait on my untrusted event */ - [cdecl] int sgx_thread_wait_untrusted_event_ocall([user_check] const void *self); - - /* Wake a thread waiting on its untrusted event */ - [cdecl] int sgx_thread_set_untrusted_event_ocall([user_check] const void *waiter); - - /* Wake a thread waiting on its untrusted event, and wait on my untrusted event */ - [cdecl] int sgx_thread_setwait_untrusted_events_ocall([user_check] const void *waiter, [user_check] const void *self); - - /* Wake multiple threads waiting on their untrusted events */ - [cdecl] int sgx_thread_set_multiple_untrusted_events_ocall([in, count = total] const void **waiters, size_t total); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_tswitchless.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_tswitchless.edl deleted file mode 100644 index a20669ab59..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_tswitchless.edl +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2011-2019 Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -enclave { - - trusted { - public sgx_status_t sl_init_switchless([user_check]void* sl_data); - public sgx_status_t sl_run_switchless_tworker(); - }; - -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_ttls.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_ttls.edl deleted file mode 100644 index ca0906f578..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/intel/sgx_ttls.edl +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2011-2021 Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -enclave{ - include "sgx_report.h" - include "sgx_qve_header.h" - include "sgx_ql_lib_common.h" - include "sgx_ql_quote.h" - - untrusted { - quote3_error_t sgx_tls_get_qe_target_info_ocall([size = target_info_size, out] sgx_target_info_t *p_target_info, - size_t target_info_size); - - quote3_error_t sgx_tls_get_quote_size_ocall([out] uint32_t *p_quote_size); - - quote3_error_t sgx_tls_get_quote_ocall([size = report_size, in] sgx_report_t* p_report, - size_t report_size, - [size = quote_size, out] uint8_t *p_quote, - uint32_t quote_size); - - quote3_error_t sgx_tls_get_supplemental_data_size_ocall([out] uint32_t *p_supplemental_data_size); - - quote3_error_t sgx_tls_verify_quote_ocall( - [size = quote_size, in] const uint8_t *p_quote, - uint32_t quote_size, - time_t expiration_check_date, - [out] sgx_ql_qv_result_t *p_quote_verification_result, - [size = qve_report_info_size, in, out] sgx_ql_qe_report_info_t *p_qve_report_info, - size_t qve_report_info_size, - [size = supplemental_data_size, out] uint8_t *p_supplemental_data, - uint32_t supplemental_data_size); - - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_asyncio.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_asyncio.edl deleted file mode 100644 index f46373894e..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_asyncio.edl +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -enclave { - - include "sys/epoll.h" - include "poll.h" - - trusted { - /* define ECALLs here. */ - }; - - untrusted { - int u_poll_ocall([out] int *error, [in, out, count=nfds] struct pollfd *fds, nfds_t nfds, int timeout); - int u_epoll_create1_ocall([out] int *error, int flags); - int u_epoll_ctl_ocall([out] int *error, int epfd, int op, int fd, [in] struct epoll_event *event); - int u_epoll_wait_ocall([out] int *error, int epfd, [out, count=maxevents] struct epoll_event *events, int maxevents, int timeout); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_backtrace.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_backtrace.edl deleted file mode 100644 index 4a9e7ef8c4..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_backtrace.edl +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -enclave { - - from "sgx_fd.edl" import *; - from "sgx_file.edl" import *; - from "sgx_mem.edl" import *; - - trusted { - /* define ECALLs here. */ - }; - - untrusted { - /* define OCALLs here. */ - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_env.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_env.edl deleted file mode 100644 index d4a77cc816..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_env.edl +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -enclave { - - include "pwd.h" - - trusted { - /* define ECALLs here. */ - }; - - untrusted { - char **u_environ_ocall(); - char *u_getenv_ocall([in, string] const char *name); - int u_setenv_ocall([out] int *error, [in, string] const char *name, [in, string] const char *value, int overwrite); - int u_unsetenv_ocall([out] int *error, [in, string] const char *name); - int u_chdir_ocall([out] int *error, [in, string] const char *dir); - char *u_getcwd_ocall([out] int *error, [out, size=buflen] char *buf, size_t buflen); - int u_getpwuid_r_ocall(unsigned int uid, - [out] struct passwd *pwd, - [out, size=buflen] char *buf, - size_t buflen, - [out] struct passwd **passwd_result); - unsigned int u_getuid_ocall(); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_fd.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_fd.edl deleted file mode 100644 index cd668b71c0..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_fd.edl +++ /dev/null @@ -1,57 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -enclave { - - include "inc/stat.h" - include "sys/uio.h" - include "time.h" - - from "sgx_mem.edl" import *; - - trusted { - /* define ECALLs here. */ - }; - - untrusted { - size_t u_read_ocall([out] int *error, int fd, [user_check] void *buf, size_t count); - size_t u_pread64_ocall([out] int *error, int fd, [user_check] void *buf, size_t count, int64_t offset); - size_t u_readv_ocall([out] int *error, int fd, [in, count=iovcnt] const struct iovec *iov, int iovcnt); - size_t u_preadv64_ocall([out] int *error, int fd, [in, count=iovcnt] const struct iovec *iov, int iovcnt, int64_t offset); - - size_t u_write_ocall([out] int *error, int fd, [user_check] const void *buf, size_t count); - size_t u_pwrite64_ocall([out] int *error, int fd, [user_check] const void *buf, size_t count, int64_t offset); - size_t u_writev_ocall([out] int *error, int fd, [in, count=iovcnt] const struct iovec *iov, int iovcnt); - size_t u_pwritev64_ocall([out] int *error, int fd, [in, count=iovcnt] const struct iovec *iov, int iovcnt, int64_t offset); - - size_t u_sendfile_ocall([out] int *error, int out_fd, int in_fd, [in, out] int64_t *offset, size_t count); - size_t u_copy_file_range_ocall([out] int *error, int fd_in, [in, out] int64_t *off_in, int fd_out, [in, out] int64_t *off_out, size_t len, unsigned int flags); - size_t u_splice_ocall([out] int *error, int fd_in, [in, out] int64_t *off_in, int fd_out, [in, out] int64_t *off_out, size_t len, unsigned int flags); - - int u_fcntl_arg0_ocall([out] int *error, int fd, int cmd); - int u_fcntl_arg1_ocall([out] int *error, int fd, int cmd, int arg); - int u_ioctl_arg0_ocall([out] int *error, int fd, int request); - int u_ioctl_arg1_ocall([out] int *error, int fd, int request, [in, out] int *arg); - - int u_close_ocall([out] int *error, int fd); - int u_isatty_ocall([out] int *error, int fd); - int u_dup_ocall([out] int *error, int oldfd); - int u_eventfd_ocall([out] int *error, unsigned int initval, int flags); - - int u_futimens_ocall([out] int *error, int fd, [in, count=2] const struct timespec *times); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_file.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_file.edl deleted file mode 100644 index c70ec599a2..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_file.edl +++ /dev/null @@ -1,66 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -enclave { - - include "inc/stat.h" - include "inc/dirent.h" - - trusted { - /* define ECALLs here. */ - }; - - untrusted { - int u_open_ocall([out] int *error, [in, string] const char *pathname, int flags); - int u_open64_ocall([out] int *error, [in, string] const char *path, int oflag, int mode); - int u_openat_ocall([out] int *error, int dirfd, [in, string] const char *pathname, int flags); - - int u_fstat_ocall([out] int *error, int fd, [out] struct stat_t *buf); - int u_fstat64_ocall([out] int *error, int fd, [out] struct stat64_t *buf); - int u_stat_ocall([out] int *error, [in, string] const char *path, [out] struct stat_t *buf); - int u_stat64_ocall([out] int *error, [in, string] const char *path, [out] struct stat64_t *buf); - int u_lstat_ocall([out] int *error, [in, string] const char *path, [out] struct stat_t *buf); - int u_lstat64_ocall([out] int *error, [in, string] const char *path, [out] struct stat64_t *buf); - uint64_t u_lseek_ocall([out] int *error, int fd, int64_t offset, int whence); - int64_t u_lseek64_ocall([out] int *error, int fd, int64_t offset, int whence); - int u_ftruncate_ocall([out] int *error, int fd, int64_t length); - int u_ftruncate64_ocall([out] int *error, int fd, int64_t length); - int u_truncate_ocall([out] int *error, [in, string] const char *path, int64_t length); - int u_truncate64_ocall([out] int *error, [in, string] const char *path, int64_t length); - - int u_fsync_ocall([out] int *error, int fd); - int u_fdatasync_ocall([out] int *error, int fd); - int u_fchmod_ocall([out] int *error, int fd, uint32_t mode); - int u_unlink_ocall([out] int *error, [in, string] const char *pathname); - int u_link_ocall([out] int *error, [in, string] const char *oldpath, [in, string] const char *newpath); - int u_unlinkat_ocall([out] int *error, int dirfd, [in, string] const char *pathname, int flags); - int u_linkat_ocall([out] int *error, int olddirfd, [in, string] const char *oldpath, int newdirfd, [in, string] const char *newpath, int flags); - int u_rename_ocall([out] int *error, [in, string] const char *oldpath, [in, string] const char *newpath); - int u_chmod_ocall([out] int *error, [in, string] const char *path, uint32_t mode); - size_t u_readlink_ocall([out] int *error, [in, string] const char *path, [out, size=bufsz] char *buf, size_t bufsz); - int u_symlink_ocall([out] int *error, [in, string] const char *path1, [in, string] const char *path2); - char *u_realpath_ocall([out] int *error, [in, string] const char *pathname); - int u_mkdir_ocall([out] int *error, [in, string] const char *pathname, uint32_t mode); - int u_rmdir_ocall([out] int *error, [in, string] const char *pathname); - void *u_fdopendir_ocall([out] int *error, int fd); - void *u_opendir_ocall([out] int *error, [in, string] const char *pathname); - int u_readdir64_r_ocall([user_check] void *dirp, [in, out] struct dirent64_t *entry, [out] struct dirent64_t **result); - int u_closedir_ocall([out] int *error, [user_check] void *dirp); - int u_dirfd_ocall([out] int *error, [user_check] void *dirp); - int u_fstatat64_ocall([out] int *error, int dirfd, [in, string] const char *pathname, [out] struct stat64_t *buf, int flags); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_fs.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_fs.edl deleted file mode 100644 index 2618be9352..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_fs.edl +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -enclave { - - from "sgx_mem.edl" import *; - from "sgx_fd.edl" import *; - from "sgx_file.edl" import *; - - trusted { - /* define ECALLs here. */ - }; - - untrusted { - /* define OCALLs here. */ - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_mem.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_mem.edl deleted file mode 100644 index db55802755..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_mem.edl +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -enclave { - - trusted { - /* define ECALLs here. */ - }; - - untrusted { - void *u_malloc_ocall([out] int *error, size_t size); - void u_free_ocall([user_check] void *p); - - void *u_mmap_ocall([out] int *error, - [user_check] void *start, - size_t length, - int prot, - int flags, - int fd, - int64_t offset); - int u_munmap_ocall([out] int *error, [user_check] void *start, size_t length); - - int u_msync_ocall([out] int *error, [user_check] void *addr, size_t length, int flags); - int u_mprotect_ocall([out] int *error, [user_check] void *addr, size_t length, int prot); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_net.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_net.edl deleted file mode 100644 index a803b53ac2..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_net.edl +++ /dev/null @@ -1,41 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -enclave { - - include "sys/socket.h" - include "netdb.h" - - from "sgx_socket.edl" import *; - from "sgx_asyncio.edl" import *; - from "sgx_fd.edl" import *; - from "sgx_time.edl" import *; - - trusted { - /* define ECALLs here. */ - }; - - untrusted { - int u_getaddrinfo_ocall([out] int *error, - [in, string] const char *node, - [in, string] const char *service, - [in] const struct addrinfo *hints, - [out] struct addrinfo **res); - void u_freeaddrinfo_ocall([user_check] struct addrinfo *res); - char *u_gai_strerror_ocall(int errcode); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_net_switchless.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_net_switchless.edl deleted file mode 100644 index ec5c500cfc..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_net_switchless.edl +++ /dev/null @@ -1,92 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -enclave { - - include "sys/socket.h" - include "poll.h" - from "sgx_fs.edl" import *; - from "sgx_time.edl" import *; - from "sgx_mem.edl" import *; - - trusted { - /* define ECALLs here. */ - }; - - untrusted { - - int u_net_socket_ocall([out] int *error, int domain, int ty, int protocol) transition_using_threads; - int u_net_socketpair_ocall([out] int *error, int domain, int ty, int protocol, [out] int sv[2]) transition_using_threads; - int u_net_bind_ocall([out] int *error, int sockfd, [in, size=addrlen] const struct sockaddr *addr, socklen_t addrlen) transition_using_threads; - int u_net_listen_ocall([out] int *error, int sockfd, int backlog) transition_using_threads; - int u_net_accept4_ocall([out] int *error, - int sockfd, - [in, out, size=addrlen_in] struct sockaddr *addr, - socklen_t addrlen_in, - [out] socklen_t *addrlen_out, - int flags) transition_using_threads; - int u_net_connect_ocall([out] int *error, - int sockfd, - [in, size=addrlen] const struct sockaddr *addr, - socklen_t addrlen) transition_using_threads; - size_t u_net_recv_ocall([out] int *error, int sockfd, [out, size=len] void *buf, size_t len, int flags) transition_using_threads; - size_t u_net_recvfrom_ocall([out] int *error, - int sockfd, - [out, size=len] void *buf, - size_t len, - int flags, - [out, size=addrlen_in] struct sockaddr *src_addr, - socklen_t addrlen_in, - [out] socklen_t *addrlen_out) transition_using_threads; - size_t u_net_recvmsg_ocall([out] int *error, int sockfd, [in, out] struct msghdr *msg, int flags) transition_using_threads; - size_t u_net_send_ocall([out] int *error, int sockfd, [in, size=len] const void *buf, size_t len, int flags) transition_using_threads; - size_t u_net_sendto_ocall([out] int *error, - int sockfd, - [in, size=len] const void *buf, - size_t len, - int flags, - [in, size=addrlen] const struct sockaddr *dest_addr, - socklen_t addrlen) transition_using_threads; - size_t u_sendmsg_ocall([out] int *error, int sockfd, [in] const struct msghdr *msg, int flags) transition_using_threads; - int u_net_getsockopt_ocall([out] int *error, - int sockfd, - int level, - int optname, - [out, size=optlen_in] void *optval, - socklen_t optlen_in, - [out] socklen_t *optlen_out) transition_using_threads; - int u_net_setsockopt_ocall([out] int *error, - int sockfd, - int level, - int optname, - [in, size=optlen] const void *optval, - socklen_t optlen) transition_using_threads; - int u_net_getsockname_ocall([out] int *error, - int sockfd, - [out, size=addrlen_in] struct sockaddr *addr, - socklen_t addrlen_in, - [out] socklen_t *addrlen_out) transition_using_threads; - int u_net_getpeername_ocall([out] int *error, - int sockfd, - [out, size=addrlen_in] struct sockaddr *addr, - socklen_t addrlen_in, - [out] socklen_t *addrlen_out) transition_using_threads; - int u_net_shutdown_ocall([out] int *error, int sockfd, int how) transition_using_threads; - int u_net_ioctl_ocall([out] int *error, int fd, int request, [in, out] int *arg) transition_using_threads; - int u_net_poll_ocall([out] int *error, [in, out, count=nfds] struct pollfd *fds, nfds_t nfds, int timeout) transition_using_threads; - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_pipe.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_pipe.edl deleted file mode 100644 index 00c12f5e7c..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_pipe.edl +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -enclave { - - from "sgx_fd.edl" import *; - from "sgx_asyncio.edl" import *; - - trusted { - /* define ECALLs here. */ - }; - - untrusted { - int u_pipe_ocall([out] int *error, [out, count=2] int *pipefd); - int u_pipe2_ocall([out] int *error, [out, count=2] int *pipefd, int flags); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_process.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_process.edl deleted file mode 100644 index 69123df5d8..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_process.edl +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -enclave { - - trusted { - /* define ECALLs here. */ - - }; - - untrusted { - int u_getpid_ocall(); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_signal.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_signal.edl deleted file mode 100644 index fd9b0f0d14..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_signal.edl +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -enclave { - include "signal.h" - - trusted { - /* define ECALLs here. */ - public int t_signal_handler_ecall([in]const siginfo_t *info); - }; - - untrusted { - int u_sigaction_ocall([out]int *error, - int signum, - [in] const struct sigaction *act, - [out] struct sigaction *oldact, - uint64_t enclave_id); - - int u_sigprocmask_ocall([out]int *error, - int signum, - [in] const sigset_t *set, - [out] sigset_t *oldset); - - int u_raise_ocall(int signum) allow(t_signal_handler_ecall); - - void u_signal_clear_ocall(uint64_t enclave_id); - }; -}; - diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_socket.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_socket.edl deleted file mode 100644 index 6fc8ff7c85..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_socket.edl +++ /dev/null @@ -1,111 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -enclave { - - include "sys/socket.h" - - from "sgx_mem.edl" import *; - - trusted { - /* define ECALLs here. */ - }; - - untrusted { - int u_socket_ocall([out] int *error, int domain, int ty, int protocol); - int u_socketpair_ocall([out] int *error, int domain, int ty, int protocol, [out] int sv[2]); - int u_bind_ocall([out] int *error, int sockfd, [in, size=addrlen] const struct sockaddr *addr, socklen_t addrlen); - int u_listen_ocall([out] int *error, int sockfd, int backlog); - int u_accept_ocall([out] int *error, - int sockfd, - [in, out, size=addrlen_in] struct sockaddr *addr, - socklen_t addrlen_in, - [out] socklen_t *addrlen_out); - int u_accept4_ocall([out] int *error, - int sockfd, - [in, out, size=addrlen_in] struct sockaddr *addr, - socklen_t addrlen_in, - [out] socklen_t *addrlen_out, - int flags); - int u_connect_ocall([out] int *error, - int sockfd, - [in, size=addrlen] const struct sockaddr *addr, - socklen_t addrlen); - size_t u_recv_ocall([out] int *error, int sockfd,[user_check] void *buf, size_t len, int flags); - size_t u_recvfrom_ocall([out] int *error, - int sockfd, - [user_check] void *buf, - size_t len, - int flags, - [out, size=addrlen_in] struct sockaddr *src_addr, - socklen_t addrlen_in, - [out] socklen_t *addrlen_out); - size_t u_recvmsg_ocall([out] int *error, - int sockfd, - [out, size=msg_namelen] void *msg_name, - socklen_t msg_namelen, - [out] socklen_t* msg_namelen_out, - [in, count=msg_iovlen] struct iovec* msg_iov, - size_t msg_iovlen, - [out, size=msg_controllen] void *msg_control, - size_t msg_controllen, - [out] size_t* msg_controllen_out, - [out] int* msg_flags, - int flags); - size_t u_send_ocall([out] int *error, int sockfd, [user_check] const void *buf, size_t len, int flags); - size_t u_sendto_ocall([out] int *error, - int sockfd, - [user_check] const void *buf, - size_t len, - int flags, - [in, size=addrlen] const struct sockaddr *dest_addr, - socklen_t addrlen); - size_t u_sendmsg_ocall([out] int *error, - int sockfd, - [in, size=msg_namelen] const void* msg_name, - socklen_t msg_namelen, - [in, count=msg_iovlen] const struct iovec* msg_iov, - size_t msg_iovlen, - [in, size=msg_controllen] const void* msg_control, - size_t msg_controllen, - int flags); - int u_getsockopt_ocall([out] int *error, - int sockfd, - int level, - int optname, - [out, size=optlen_in] void *optval, - socklen_t optlen_in, - [out] socklen_t *optlen_out); - int u_setsockopt_ocall([out] int *error, - int sockfd, - int level, - int optname, - [in, size=optlen] const void *optval, - socklen_t optlen); - int u_getsockname_ocall([out] int *error, - int sockfd, - [out, size=addrlen_in] struct sockaddr *addr, - socklen_t addrlen_in, - [out] socklen_t *addrlen_out); - int u_getpeername_ocall([out] int *error, - int sockfd, - [out, size=addrlen_in] struct sockaddr *addr, - socklen_t addrlen_in, - [out] socklen_t *addrlen_out); - int u_shutdown_ocall([out] int *error, int sockfd, int how); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_stdio.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_stdio.edl deleted file mode 100644 index 5367d9ab97..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_stdio.edl +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -enclave { - - from "sgx_fd.edl" import *; - - trusted { - /* define ECALLs here. */ - }; - - untrusted { - /* define OCALLs here. */ - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_sys.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_sys.edl deleted file mode 100644 index bc74b96843..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_sys.edl +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -enclave { - - include "sched.h" - - trusted { - /* define ECALLs here. */ - }; - - untrusted { - long u_sysconf_ocall([out] int *error, int name); - int u_prctl_ocall([out] int *error, int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5); - int u_sched_setaffinity_ocall([out] int *error, pid_t pid, size_t cpusetsize, [in, size=cpusetsize] cpu_set_t *mask); - int u_sched_getaffinity_ocall([out] int *error, pid_t pid, size_t cpusetsize, [out, size=cpusetsize] cpu_set_t *mask); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_thread.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_thread.edl deleted file mode 100644 index 71512f0e56..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_thread.edl +++ /dev/null @@ -1,32 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. -enclave { - - include "time.h" - - from "intel/sgx_pthread.edl" import *; - from "sgx_sys.edl" import *; - - trusted { - /* define ECALLs here. */ - }; - - untrusted { - int u_sched_yield_ocall([out]int *error); - int u_nanosleep_ocall([out]int *error, [in]const struct timespec *req, [out]struct timespec *rem); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_time.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_time.edl deleted file mode 100644 index adeeeccf92..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_time.edl +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -enclave { - - include "time.h" - - trusted { - /* define ECALLs here. */ - }; - - untrusted { - int u_clock_gettime_ocall([out] int *error, int clk_id, [out] struct timespec *tp); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_tstd.edl b/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_tstd.edl deleted file mode 100644 index 9b74272f50..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/edl/sgx_tstd.edl +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -enclave { - - from "sgx_time.edl" import *; - - trusted { - /* define ECALLs here. */ - public void t_global_init_ecall(uint64_t id, [in, size=len] const uint8_t *path, size_t len); - public void t_global_exit_ecall(); - }; - - untrusted { - /* define OCALLs here. */ - int u_thread_set_event_ocall([out] int *error, [user_check] const void *tcs); - int u_thread_wait_event_ocall([out] int *error, [user_check] const void *tcs, [in] const struct timespec *timeout); - int u_thread_set_multiple_events_ocall([out] int *error, [in, count=total] const void **tcss, int total); - int u_thread_setwait_events_ocall([out] int *error, - [user_check] const void *waiter_tcs, - [user_check] const void *self_tcs, - [in] const struct timespec *timeout); - }; -}; diff --git a/tee-worker/bitacross/rust-sgx-sdk/version b/tee-worker/bitacross/rust-sgx-sdk/version deleted file mode 100644 index 78e68ab976..0000000000 --- a/tee-worker/bitacross/rust-sgx-sdk/version +++ /dev/null @@ -1 +0,0 @@ -27bd225ae6dbcd1d0a6d4d9590acc4d73c5195c2 diff --git a/tee-worker/bitacross/scripts/benchmark_local-setup.sh b/tee-worker/bitacross/scripts/benchmark_local-setup.sh deleted file mode 100644 index 40bc700f05..0000000000 --- a/tee-worker/bitacross/scripts/benchmark_local-setup.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -set -e - -pushd .. - -pushd bin -./litentry-worker mrenclave | tee ~/mrenclave.b58 -popd - -ulimit -S -n 4096 - -python3 local-setup/launch.py local-setup/config/benchmark.json & -PID=$! -echo $PID > ./benchmark.pid -echo "Benchmark PID: $PID" - -sleep 40s - -pushd bin -./bitacross-cli -p 9930 -P 2030 trusted --direct --mrenclave "$(cat ~/mrenclave.b58)" benchmark 20 100 -w -popd - -sleep 10s - -if test -f "./benchmark.pid"; then - echo "Killing benchmark process" - kill -s SIGTERM "$(cat ./benchmark.pid)" - rm benchmark.pid -fi - -popd diff --git a/tee-worker/bitacross/scripts/changelog/.gitignore b/tee-worker/bitacross/scripts/changelog/.gitignore deleted file mode 100644 index 4fbcc523b0..0000000000 --- a/tee-worker/bitacross/scripts/changelog/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -changelog.md -*.json -release*.md -.env diff --git a/tee-worker/bitacross/scripts/changelog/Gemfile b/tee-worker/bitacross/scripts/changelog/Gemfile deleted file mode 100644 index f2d7c3bd71..0000000000 --- a/tee-worker/bitacross/scripts/changelog/Gemfile +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -source 'https://rubygems.org' - -git_source(:github) { |repo_name| "https://github.com/#{repo_name}" } - -gem 'octokit', '~> 4' - -gem 'git_diff_parser', '~> 3' - -gem 'toml', '~> 0.3.0' - -gem 'rake', group: :dev - -gem 'optparse', '~> 0.1.1' - -gem 'logger', '~> 1.4' - -gem 'test-unit', group: :dev - -gem 'rubocop', group: :dev, require: false diff --git a/tee-worker/bitacross/scripts/changelog/Gemfile.lock b/tee-worker/bitacross/scripts/changelog/Gemfile.lock deleted file mode 100644 index 6c22948a8e..0000000000 --- a/tee-worker/bitacross/scripts/changelog/Gemfile.lock +++ /dev/null @@ -1,82 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) - ast (2.4.2) - faraday (1.8.0) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0.1) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.1) - faraday-patron (~> 1.0) - faraday-rack (~> 1.0) - multipart-post (>= 1.2, < 3) - ruby2_keywords (>= 0.0.4) - faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) - faraday-excon (1.1.0) - faraday-httpclient (1.0.1) - faraday-net_http (1.0.1) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) - faraday-rack (1.0.0) - git_diff_parser (3.2.0) - logger (1.4.4) - multipart-post (2.1.1) - octokit (4.21.0) - faraday (>= 0.9) - sawyer (~> 0.8.0, >= 0.5.3) - optparse (0.1.1) - parallel (1.21.0) - parser (3.0.2.0) - ast (~> 2.4.1) - parslet (2.0.0) - power_assert (2.0.1) - public_suffix (4.0.6) - rainbow (3.0.0) - rake (13.0.6) - regexp_parser (2.1.1) - rexml (3.3.6) - strscan - rubocop (1.23.0) - parallel (~> 1.10) - parser (>= 3.0.0.0) - rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml - rubocop-ast (>= 1.12.0, < 2.0) - ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.13.0) - parser (>= 3.0.1.1) - ruby-progressbar (1.11.0) - ruby2_keywords (0.0.5) - sawyer (0.8.2) - addressable (>= 2.3.5) - faraday (> 0.8, < 2.0) - strscan (3.1.0) - test-unit (3.5.1) - power_assert - toml (0.3.0) - parslet (>= 1.8.0, < 3.0.0) - unicode-display_width (2.1.0) - -PLATFORMS - x86_64-darwin-20 - x86_64-linux - -DEPENDENCIES - git_diff_parser (~> 3) - logger (~> 1.4) - octokit (~> 4) - optparse (~> 0.1.1) - rake - rubocop - test-unit - toml (~> 0.3.0) - -BUNDLED WITH - 2.2.22 diff --git a/tee-worker/bitacross/scripts/changelog/README.md b/tee-worker/bitacross/scripts/changelog/README.md deleted file mode 100644 index 4776277e70..0000000000 --- a/tee-worker/bitacross/scripts/changelog/README.md +++ /dev/null @@ -1,3 +0,0 @@ -## License - -Everything in this folder is GPL 3.0 licensed. The original has been authored by parity and was taken from here: https://github.com/paritytech/polkadot/tree/master/scripts/ci/changelog. \ No newline at end of file diff --git a/tee-worker/bitacross/scripts/changelog/bin/changelog b/tee-worker/bitacross/scripts/changelog/bin/changelog deleted file mode 100755 index 15b17d6166..0000000000 --- a/tee-worker/bitacross/scripts/changelog/bin/changelog +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env ruby - -# frozen_string_literal: true - -# call for instance as: -# ./bin/changelog [] [] -# for instance, for the release notes of v1.2.3: -# ./bin/changelog v1.2.3 -# or -# ./bin/changelog v1.2.3 v1.2.2 -# -# You may set the ENV NO_CACHE to force fetching from Github -# You should also ensure you set the ENV: GITHUB_TOKEN - -require_relative '../lib/changelog' -require 'logger' - -logger = Logger.new($stdout) -logger.level = Logger::DEBUG -logger.debug('Starting') - -owner = 'integritee-network' -repo = 'worker' - -gh_worker = SubRef.new(format('%s/%s', { owner: owner, repo: repo })) -last_release_ref = gh_worker.get_last_ref() - -worker_ref2 = ARGV[0] || 'HEAD' -worker_ref1 = ARGV[1] || last_release_ref - -output = ARGV[2] || 'release-notes.md' - -ENV['REF1'] = worker_ref1 -ENV['REF2'] = worker_ref2 - -pallets_ref1 = gh_worker.get_dependency_reference(worker_ref1, 'pallet-teerex') -pallets_ref2 = gh_worker.get_dependency_reference(worker_ref2, 'pallet-teerex') - -logger.debug("Worker from: #{worker_ref1}") -logger.debug("Worker to: #{worker_ref2}") - -logger.debug("Pallets from: #{pallets_ref1}") -logger.debug("Pallets to: #{pallets_ref2}") - -pallets_data = 'pallets.json' -worker_data = 'worker.json' - -logger.debug("Using PALLETS: #{pallets_data}") -logger.debug("Using WORKER: #{worker_data}") - -logger.warn('NO_CACHE set') if ENV['NO_CACHE'] - -if ENV['NO_CACHE'] || !File.file?(worker_data) - logger.debug(format('Fetching data for Worker into %s', worker_data)) - cmd = format('changelogerator %s/%s -f %s -t %s > %s', - { owner: owner, repo: 'worker', from: worker_ref1, to: worker_ref2, output: worker_data }) - system(cmd) -else - logger.debug("Re-using:#{worker_data}") -end - -if ENV['NO_CACHE'] || !File.file?(pallets_data) - logger.debug(format('Fetching data for Pallets into %s', pallets_data)) - cmd = format('changelogerator %s/%s -f %s -t %s > %s', - { owner: owner, repo: 'pallets', from: pallets_ref1, to: pallets_ref2, output: pallets_data }) - system(cmd) -else - logger.debug("Re-using:#{pallets_data}") -end - -# Here we compose all the pieces together into one -# single big json file. -cmd = format('jq \ - --slurpfile pallets %s \ - --slurpfile worker %s \ - -n \'{ - pallets: $pallets[0], - worker: $worker[0], - }\' > context.json', pallets_data, worker_data) -system(cmd) - -cmd = format('tera --env --env-key env --include-path templates \ - --template templates/template.md.tera context.json > %s', output) -system(cmd) diff --git a/tee-worker/bitacross/scripts/changelog/digests/.gitignore b/tee-worker/bitacross/scripts/changelog/digests/.gitignore deleted file mode 100644 index a6c57f5fb2..0000000000 --- a/tee-worker/bitacross/scripts/changelog/digests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.json diff --git a/tee-worker/bitacross/scripts/changelog/digests/.gitkeep b/tee-worker/bitacross/scripts/changelog/digests/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tee-worker/bitacross/scripts/changelog/lib/changelog.rb b/tee-worker/bitacross/scripts/changelog/lib/changelog.rb deleted file mode 100644 index d7cf92e7d2..0000000000 --- a/tee-worker/bitacross/scripts/changelog/lib/changelog.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -# A Class to find Substrate references -class SubRef - require 'octokit' - require 'toml' - - attr_reader :client, :repository - - def initialize(github_repo) - @client = Octokit::Client.new( - access_token: ENV['GITHUB_TOKEN'] - ) - @repository = @client.repository(github_repo) - end - - # This function checks the Cargo.lock of a given - # Rust project, for a given package, and fetches - # the dependency git ref. - def get_dependency_reference(ref, package) - cargo = TOML::Parser.new( - Base64.decode64( - @client.contents( - @repository.full_name, - path: 'Cargo.lock', - query: { ref: ref.to_s } - ).content - ) - ).parsed - cargo['package'].find { |p| p['name'] == package }['source'].split('#').last - end - - # Get the git ref of the last release for the repo. - # repo is given in the form integritee-network/worker - def get_last_ref() - 'refs/tags/' + @client.latest_release(@repository.full_name).tag_name - end -end diff --git a/tee-worker/bitacross/scripts/changelog/templates/_free_notes.md.tera b/tee-worker/bitacross/scripts/changelog/templates/_free_notes.md.tera deleted file mode 100644 index c4a841a992..0000000000 --- a/tee-worker/bitacross/scripts/changelog/templates/_free_notes.md.tera +++ /dev/null @@ -1,10 +0,0 @@ - -{# This file uses the Markdown format with additional templating such as this comment. -#} -{# Such a comment will not show up in the rendered release notes. -#} -{# The content of this file (if any) will be inserted at the top of the release notes -#} -{# and generated for each new release candidate. -#} -{# Ensure you leave an empty line at both top and bottom of this file. -#} - - - - diff --git a/tee-worker/bitacross/scripts/changelog/templates/challenge_level.md.tera b/tee-worker/bitacross/scripts/changelog/templates/challenge_level.md.tera deleted file mode 100644 index c4a8934fd4..0000000000 --- a/tee-worker/bitacross/scripts/changelog/templates/challenge_level.md.tera +++ /dev/null @@ -1,37 +0,0 @@ -{%- import "change.md.tera" as m_c -%} - -{# This macro convert a merge challenge level into readable output #} -{%- macro challenge_level(e, changes) -%} - -{%- if e >= 5 -%} - {%- set level = "‼️ Breaking Changes" -%} - {%- set text = "This release contains **breaking changes**. Be sure to upgrade the affected interfaces." -%} -{%- elif e >= 3 -%} - {%- set level = "❗️ Attention" -%} - {%- set text = "This release contains some non-trivial updates. Be mindful when upgrading." -%} -{%- else -%} - {%- set level = "Trivial" -%} - {%- set text = "This release contains relatively small updates." -%} -{%- endif %} - - - - -{%- if level %} -{{level}}: {{text}} - -{% if e >= 3 %} -The changes motivating this challenge level are: -{% for pr in changes | sort(attribute="merged_at") -%} - {%- if pr.meta.E -%} - {%- if pr.meta.E.value == e %} -- {{ m_c::change(c=pr) }} - {%- endif -%} - {%- endif -%} -{%- endfor -%} -{%- else -%} - -{%- endif -%} -{%- endif -%} - -{%- endmacro level -%} diff --git a/tee-worker/bitacross/scripts/changelog/templates/change.md.tera b/tee-worker/bitacross/scripts/changelog/templates/change.md.tera deleted file mode 100644 index 25cc04edec..0000000000 --- a/tee-worker/bitacross/scripts/changelog/templates/change.md.tera +++ /dev/null @@ -1,42 +0,0 @@ -{# This macro shows ONE change #} -{%- macro change(c, cml="[C]", pal="[P]", wor="[W]") -%} - -{%- if c.meta.C and c.meta.C.value >= 7 -%} -{%- set prio = " ‼️ HIGH" -%} -{%- elif c.meta.C and c.meta.C.value >= 3 -%} -{%- set prio = " ❗️ Medium" -%} -{%- elif c.meta.C and c.meta.C.value < 3 -%} -{%- set prio = " Low" -%} -{%- else -%} -{%- set prio = "" -%} -{%- endif -%} - - -{%- if c.html_url is containing("worker") -%} -{%- set repo = wor -%} -{%- elif c.html_url is containing("pallets") -%} -{%- set repo = pal -%} -{%- else -%} -{%- set repo = " " -%} -{%- endif -%} - -{# For now don't show pallets or worker #} -{%- set repo = " " -%} - -{%- if c.meta.E and c.meta.E.value >= 7 -%} -{%- set challenge = " 💥 breaking changes " -%} -{%- elif c.meta.E and c.meta.E.value == 6 -%} -{%- set challenge = " ⚡ breaks parentchain interface " -%} -{%- elif c.meta.E and c.meta.E.value == 5 -%} -{%- set challenge = " 🔥 breaks public rpc api " -%} -{%- elif c.meta.E and c.meta.E.value >= 3 -%} -{%- set challenge = " 📢 attention required " -%} -{%- elif c.meta.E and c.meta.E.value < 3 -%} -{%- set challenge = " ✅ easy merge " -%} -{%- else -%} -{%- set challenge = "" -%} -{%- endif -%} - - -{{- repo }} {{ challenge }}[`#{{c.number}}`]({{c.html_url}}) {{- prio }} - {{ c.title | capitalize | truncate(length=120, end="…") }} -{%- endmacro change -%} \ No newline at end of file diff --git a/tee-worker/bitacross/scripts/changelog/templates/changes.md.tera b/tee-worker/bitacross/scripts/changelog/templates/changes.md.tera deleted file mode 100644 index 1dcb6ea978..0000000000 --- a/tee-worker/bitacross/scripts/changelog/templates/changes.md.tera +++ /dev/null @@ -1,23 +0,0 @@ -{# This include generates the section showing the changes #} -## Changes - -{# for not now printed until pallet is actually included #} -{# ### Legend #} - -{# - {{ WOR }} Worker #} -{# - {{ PAL }} Pallet #} - -{% include "changes_applibs.md.tera" %} - -{% include "changes_client.md.tera" %} - -{% include "changes_core.md.tera" %} - -{% include "changes_evm.md.tera" %} - -{% include "changes_offchain.md.tera" %} - -{% include "changes_sidechain.md.tera" %} - - -{% include "changes_misc.md.tera" %} diff --git a/tee-worker/bitacross/scripts/changelog/templates/changes_applibs.md.tera b/tee-worker/bitacross/scripts/changelog/templates/changes_applibs.md.tera deleted file mode 100644 index db393f764e..0000000000 --- a/tee-worker/bitacross/scripts/changelog/templates/changes_applibs.md.tera +++ /dev/null @@ -1,17 +0,0 @@ -{% import "change.md.tera" as m_c -%} -### App-Libs - -{#- The changes are sorted by merge date #} -{%- for pr in changes | sort(attribute="merged_at") %} - -{%- if pr.meta.B %} - {%- if pr.meta.B.value == 0 %} - {#- We skip silent ones -#} - {%- else -%} - - {%- if pr.meta.A.value == 2 %} -- {{ m_c::change(c=pr) }} - {%- endif -%} - {% endif -%} - {% endif -%} -{% endfor %} diff --git a/tee-worker/bitacross/scripts/changelog/templates/changes_client.md.tera b/tee-worker/bitacross/scripts/changelog/templates/changes_client.md.tera deleted file mode 100644 index 5e96861812..0000000000 --- a/tee-worker/bitacross/scripts/changelog/templates/changes_client.md.tera +++ /dev/null @@ -1,17 +0,0 @@ -{% import "change.md.tera" as m_c -%} -### Client - -{#- The changes are sorted by merge date #} -{%- for pr in changes | sort(attribute="merged_at") %} - -{%- if pr.meta.B %} - {%- if pr.meta.B.value == 0 %} - {#- We skip silent ones -#} - {%- else -%} - - {%- if pr.meta.A.value == 1 %} -- {{ m_c::change(c=pr) }} - {%- endif -%} - {% endif -%} - {% endif -%} -{% endfor %} diff --git a/tee-worker/bitacross/scripts/changelog/templates/changes_core.md.tera b/tee-worker/bitacross/scripts/changelog/templates/changes_core.md.tera deleted file mode 100644 index f88447b9e9..0000000000 --- a/tee-worker/bitacross/scripts/changelog/templates/changes_core.md.tera +++ /dev/null @@ -1,17 +0,0 @@ -{% import "change.md.tera" as m_c -%} -### Core - -{#- The changes are sorted by merge date #} -{%- for pr in changes | sort(attribute="merged_at") %} - -{%- if pr.meta.B %} - {%- if pr.meta.B.value == 0 %} - {#- We skip silent ones -#} - {%- else -%} - - {%- if pr.meta.A.value == 0 %} -- {{ m_c::change(c=pr) }} - {%- endif -%} - {% endif -%} - {% endif -%} -{% endfor %} diff --git a/tee-worker/bitacross/scripts/changelog/templates/changes_evm.md.tera b/tee-worker/bitacross/scripts/changelog/templates/changes_evm.md.tera deleted file mode 100644 index 92747435fd..0000000000 --- a/tee-worker/bitacross/scripts/changelog/templates/changes_evm.md.tera +++ /dev/null @@ -1,17 +0,0 @@ -{% import "change.md.tera" as m_c -%} -### EVM Feature - -{#- The changes are sorted by merge date #} -{%- for pr in changes | sort(attribute="merged_at") %} - -{%- if pr.meta.B %} - {%- if pr.meta.B.value == 0 %} - {#- We skip silent ones -#} - {%- else -%} - - {%- if pr.meta.A.value == 6 %} -- {{ m_c::change(c=pr) }} - {%- endif -%} - {% endif -%} - {% endif -%} -{% endfor %} diff --git a/tee-worker/bitacross/scripts/changelog/templates/changes_misc.md.tera b/tee-worker/bitacross/scripts/changelog/templates/changes_misc.md.tera deleted file mode 100644 index 1beb2efd91..0000000000 --- a/tee-worker/bitacross/scripts/changelog/templates/changes_misc.md.tera +++ /dev/null @@ -1,37 +0,0 @@ -{%- import "change.md.tera" as m_c -%} - -{%- set_global misc_count = 0 -%} -{#- First pass to count #} -{%- for pr in changes -%} - {%- if pr.meta.B %} - {%- if pr.meta.B.value == 0 -%} - {#- We skip silent ones -#} - {%- else -%} -{%- set_global misc_count = misc_count + 1 -%} - {% endif -%} - {% endif -%} -{% endfor -%} - -### Misc - -{% if misc_count > 10 %} -There are other misc. changes. You can expand the list below to view them all. -
    Other misc. changes -{% endif -%} - -{#- The changes are sorted by merge date #} -{%- for pr in changes | sort(attribute="merged_at") %} - {%- if pr.meta.B %} - {%- if pr.meta.B.value == 0 %} - {#- We skip silent ones -#} - {%- else -%} - {%- if pr.meta.B.value >= 1 %} -- {{ m_c::change(c=pr) }} - {%- endif -%} - {% endif -%} - {% endif -%} -{% endfor %} - -{% if misc_count > 10 %} -
    -{% endif -%} diff --git a/tee-worker/bitacross/scripts/changelog/templates/changes_offchain.md.tera b/tee-worker/bitacross/scripts/changelog/templates/changes_offchain.md.tera deleted file mode 100644 index d298752043..0000000000 --- a/tee-worker/bitacross/scripts/changelog/templates/changes_offchain.md.tera +++ /dev/null @@ -1,17 +0,0 @@ -{% import "change.md.tera" as m_c -%} -### Offchain - -{#- The changes are sorted by merge date #} -{%- for pr in changes | sort(attribute="merged_at") %} - -{%- if pr.meta.B %} - {%- if pr.meta.B.value == 0 %} - {#- We skip silent ones -#} - {%- else -%} - - {%- if pr.meta.A.value == 4 %} -- {{ m_c::change(c=pr) }} - {%- endif -%} - {% endif -%} - {% endif -%} -{% endfor %} diff --git a/tee-worker/bitacross/scripts/changelog/templates/changes_sidechain.md.tera b/tee-worker/bitacross/scripts/changelog/templates/changes_sidechain.md.tera deleted file mode 100644 index f953cfbcdf..0000000000 --- a/tee-worker/bitacross/scripts/changelog/templates/changes_sidechain.md.tera +++ /dev/null @@ -1,17 +0,0 @@ -{% import "change.md.tera" as m_c -%} -### Sidechain - -{#- The changes are sorted by merge date #} -{%- for pr in changes | sort(attribute="merged_at") %} - -{%- if pr.meta.B %} - {%- if pr.meta.B.value == 0 %} - {#- We skip silent ones -#} - {%- else -%} - - {%- if pr.meta.A.value == 3 %} -- {{ m_c::change(c=pr) }} - {%- endif -%} - {% endif -%} - {% endif -%} -{% endfor %} diff --git a/tee-worker/bitacross/scripts/changelog/templates/debug.md.tera b/tee-worker/bitacross/scripts/changelog/templates/debug.md.tera deleted file mode 100644 index 41f3702d7c..0000000000 --- a/tee-worker/bitacross/scripts/changelog/templates/debug.md.tera +++ /dev/null @@ -1,8 +0,0 @@ -{%- set to_ignore = changes | filter(attribute="meta.B.value", value=0) %} - - diff --git a/tee-worker/bitacross/scripts/changelog/templates/global_challenge_level.md.tera b/tee-worker/bitacross/scripts/changelog/templates/global_challenge_level.md.tera deleted file mode 100644 index d2108dce4d..0000000000 --- a/tee-worker/bitacross/scripts/changelog/templates/global_challenge_level.md.tera +++ /dev/null @@ -1,26 +0,0 @@ -{% import "challenge_level.md.tera" as m_p -%} -## Upgrade Challenge Level - -{%- set worker_prio = 0 -%} -{%- set pallet_prio = 0 -%} - -{# We fetch the various levels #} -{%- if worker.meta.E -%} -{%- set worker_level = worker.meta.E.max -%} -{%- else -%} -{%- set worker_level = 0 -%} -{%- endif -%} -{%- if pallet.meta.E -%} -{%- set pallet_level = pallet.meta.E.max -%} -{%- else -%} -{%- set pallet_level = 0 -%} -{%- endif -%} - -{# We compute the global level #} -{%- set global_level = worker_level -%} -{%- if pallet_level > global_level -%} -{%- set global_level = pallet_level -%} -{%- endif -%} - -{#- We show the result #} -{{ m_p::challenge_level(e=global_level, changes=changes) }} diff --git a/tee-worker/bitacross/scripts/changelog/templates/global_priority.md.tera b/tee-worker/bitacross/scripts/changelog/templates/global_priority.md.tera deleted file mode 100644 index 87a6d52aaf..0000000000 --- a/tee-worker/bitacross/scripts/changelog/templates/global_priority.md.tera +++ /dev/null @@ -1,27 +0,0 @@ -{% import "high_priority.md.tera" as m_p -%} -## Upgrade Priority - -{%- set worker_prio = 0 -%} -{%- set pallet_prio = 0 -%} - -{# We fetch the various priorities #} -{%- if worker.meta.C -%} -{%- set worker_prio = worker.meta.C.max -%} -{%- else -%} -{%- set worker_prio = 0 -%} -{%- endif -%} -{%- if pallet.meta.C -%} -{%- set pallet_prio = pallet.meta.C.max -%} -{%- else -%} -{%- set pallet_prio = 0 -%} -{%- endif -%} - -{# We compute the global priority #} -{%- set global_prio = worker_prio -%} -{%- if pallet_prio > global_prio -%} -{%- set global_prio = pallet_prio -%} -{%- endif -%} - - -{#- We show the result #} -{{ m_p::high_priority(p=global_prio, changes=changes) }} diff --git a/tee-worker/bitacross/scripts/changelog/templates/high_priority.md.tera b/tee-worker/bitacross/scripts/changelog/templates/high_priority.md.tera deleted file mode 100644 index 117d335efd..0000000000 --- a/tee-worker/bitacross/scripts/changelog/templates/high_priority.md.tera +++ /dev/null @@ -1,38 +0,0 @@ -{%- import "change.md.tera" as m_c -%} - -{# This macro convert a priority level into readable output #} -{%- macro high_priority(p, changes) -%} - -{%- if p >= 7 -%} - {%- set prio = "‼️ HIGH" -%} - {%- set text = "This is a **high priority** release and you must upgrade as as soon as possible." -%} -{%- elif p >= 3 -%} - {%- set prio = "❗️ Medium" -%} - {%- set text = "This is a medium priority release and you should upgrade in a timely manner." -%} -{%- else -%} - {%- set prio = "Low" -%} - {%- set text = "This is a low priority release and you may upgrade at your convenience." -%} -{%- endif %} - - - -{%- if prio %} -{{prio}}: {{text}} - -{% if p >= 3 %} -The changes motivating this priority level are: -{% for pr in changes | sort(attribute="merged_at") -%} - {%- if pr.meta.C -%} - {%- if pr.meta.C.value == p %} -- {{ m_c::change(c=pr) }} -{%- if pr.meta.B and pr.meta.B.value == 7 %} (RUNTIME) -{% endif %} - {%- endif -%} - {%- endif -%} -{%- endfor -%} -{%- else -%} - -{%- endif -%} -{%- endif -%} - -{%- endmacro priority -%} diff --git a/tee-worker/bitacross/scripts/changelog/templates/pre_release.md.tera b/tee-worker/bitacross/scripts/changelog/templates/pre_release.md.tera deleted file mode 100644 index 7d4ad42dd8..0000000000 --- a/tee-worker/bitacross/scripts/changelog/templates/pre_release.md.tera +++ /dev/null @@ -1,11 +0,0 @@ -{%- if env.PRE_RELEASE == "true" -%} -
    ⚠️ This is a pre-release - -**Release candidates** are **pre-releases** and may not be final. -Although they are reasonably tested, there may be additional changes or issues -before an official release is tagged. Use at your own discretion, and consider -only using final releases on critical production infrastructure. -
    -{% else -%} - -{%- endif %} diff --git a/tee-worker/bitacross/scripts/changelog/templates/template.md.tera b/tee-worker/bitacross/scripts/changelog/templates/template.md.tera deleted file mode 100644 index 2c61f3d5a1..0000000000 --- a/tee-worker/bitacross/scripts/changelog/templates/template.md.tera +++ /dev/null @@ -1,33 +0,0 @@ -{# This is the entry point of the template -#} - -{% include "pre_release.md.tera" -%} - -{% if env.PRE_RELEASE == "true" -%} -This pre-release contains the changes from `{{ env.REF1 | replace(from="refs/tags/", to="") }}` to `{{ env.REF2 | -replace(from="refs/tags/", to="") }}`. -{%- else -%} -This release contains the changes from `{{ env.REF1 | replace(from="refs/tags/", to="") }}` to `{{ env.REF2 | -replace(from="refs/tags/", to="") }}`. -{% endif -%} - -{# -- For now no pallet changes included -- #} -{# {%- set changes = worker.changes | concat(with=pallet.changes) -%}##} -{%- set changes = worker.changes -%} -{%- include "debug.md.tera" -%} - -{%- set CML = "[C]" -%} -{%- set WOR = "[W]" -%} -{%- set PAL = "[P]" -%} - -{# -- Manual free notes section -- #} -{% include "_free_notes.md.tera" -%} - -{# -- Important automatic section -- #} -{% include "global_priority.md.tera" -%} - -{# -- Important automatic section -- #} -{% include "global_challenge_level.md.tera" -%} - -{# --------------------------------- #} - -{% include "changes.md.tera" -%} diff --git a/tee-worker/bitacross/scripts/init_env.sh b/tee-worker/bitacross/scripts/init_env.sh deleted file mode 100755 index 9b68a64b22..0000000000 --- a/tee-worker/bitacross/scripts/init_env.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -# script that sets the correct environment variables to execute other scripts - -export SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -export PROJ_ROOT="$(dirname "$SCRIPT_DIR")" -export CLIENT_DIR="$PROJ_ROOT/cli" -export LOG_DIR="$PROJ_ROOT/log" -export CI_DIR="$PROJ_ROOT/ci" -export RUST_LOG=info,ws=warn,substrate_api_client=warn,ac_node_api=warn - -echo "Set environment variables:" -echo " BASH_SCRIPT_DIR: $SCRIPT_DIR" -echo " PROJ_ROOT: $PROJ_ROOT" -echo " CLIENT_DIR: $CLIENT_DIR" \ No newline at end of file diff --git a/tee-worker/bitacross/scripts/launch.sh b/tee-worker/bitacross/scripts/launch.sh deleted file mode 100755 index f91405f3d3..0000000000 --- a/tee-worker/bitacross/scripts/launch.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -PARACHAIN="paseo" -ROOTDIR=$(git rev-parse --show-toplevel) -ROOTDIR="${ROOTDIR}/tee-worker" - -function usage() { - echo "Usage: $0 " - echo "" - echo " All mode apply to ${PARACHAIN} context." - echo " dev: start worker(s) together with local ${PARACHAIN} for development" - echo " staging: start worker(s) sync with staging ${PARACHAIN} on tee-staging server" - echo " prod: start worker(s) sync with production ${PARACHAIN} on polkadot.js" - echo " mock: start worker(s) together with local ${PARACHAIN} for development" -} - -function start_local_parachain() { - cd ${ROOTDIR} - echo "------------------------------------------------------------" - echo "Start local parachain: ${PARACHAIN} ..." - # TODO: only `paseo` is supported for the moment. And it's hard-coded inside `start_parachain.sh` - ./../local-setup/start_parachain.sh - if [ $? -ne 0 ]; then - exit 1 - fi -} - -function start_worker_for_dev() { - start_local_parachain - cd ${ROOTDIR} - worker_num=2 - echo "------------------------------------------------------------" - echo "Start ${worker_num} workers with dev ${PARACHAIN} ..." - ./scripts/launch_local_worker.sh -c true -n ${worker_num} -m "dev" -} - -function start_worker_for_staging() { - cd ${ROOTDIR} - worker_num=2 - # staging_parachain_url - url="wss://tee-staging.litentry.io" - # staging_parachain_port - port=443 - echo "------------------------------------------------------------" - echo "Start ${worker_num} workers with staging ${PARACHAIN} ..." - ./scripts/launch_local_worker.sh -c true -n ${worker_num} -u ${url} -p ${port} -m "staging" -} - -function start_worker_for_prod() { - cd ${ROOTDIR} - worker_num=2 - # production_parachain_url - url="wss://rpc.${PARACHAIN}-parachain-sg.litentry.io" - # production_parachain_port - port=443 - echo "------------------------------------------------------------" - echo "Start ${worker_num} workers with production ${PARACHAIN} ..." - ./scripts/launch_local_worker.sh -c true -n ${worker_num} -u ${url} -p ${port} -m "prod" -} - -function start_worker_for_mock() { - start_local_parachain - cd ${ROOTDIR} - worker_num=2 - echo "------------------------------------------------------------" - echo "Start ${worker_num} workers with local ${PARACHAIN} ..." - ./scripts/launch_local_worker.sh -c true -n ${worker_num} -m "mock" -} - - -[ $# -ne 1 ] && (usage; exit 1) -MODE=$1 - -if [ "$MODE" = "dev" ] || [ "$MODE" = "staging" ] || [ "$MODE" = "prod" ] || [ "$MODE" = "mock" ]; then - echo "Launch in $MODE mode" - start_worker_for_$MODE -else - echo "Unknow mode: $MODE" - usage; exit 1 -fi - -echo "Done" - - - - - - diff --git a/tee-worker/bitacross/scripts/launch_local_worker.sh b/tee-worker/bitacross/scripts/launch_local_worker.sh deleted file mode 100755 index b1dfe788f6..0000000000 --- a/tee-worker/bitacross/scripts/launch_local_worker.sh +++ /dev/null @@ -1,138 +0,0 @@ -#!/usr/bin/env bash - -# TODO: Sanity check of parameters -while getopts ":c:n:u:p:m:" opt; do - case $opt in - c) - cleanup_flag=$OPTARG - ;; - n) - worker_num=$OPTARG - ;; - u) - node_url=$OPTARG - ;; - p) - node_port=$OPTARG - ;; - m) - mode=$OPTARG - ;; - esac -done - -CLEANUP=${cleanup_flag:-true} -WORKER_NUM=${worker_num:-1} - -NODE_URL=${node_url:-"ws://127.0.0.1"} # "ws://host.docker.internal" -NODE_PORT=${node_port:-"9944"} # "9946" - -# Fixed values: -ENCLAVE_ENDPOINT="localhost" -MU_RA_PORT="3443" -UNTRUSTED_HTTP_PORT="4545" -TRUSTED_WORKER_PORT="2000" -UNTRUSTED_WORKER_PORT="3000" - -F_CLEAN="" -FSUBCMD_DEV="" -FSUBCMD_REQ_STATE="" - -WAIT_INTERVAL_SECONDS=10 -WAIT_ROUNDS=20 - -if [ "${CLEANUP}" = 'true' ]; then - F_CLEAN="--clean-reset" - FSUBCMD_DEV="--dev" -fi - -function wait_worker_is_initialized() -{ - for index in $(seq 1 $WAIT_ROUNDS); do - state=$(curl -s http://localhost:$1/is_initialized) - if [ "$state" == "I am initialized." ]; then - echo "Initialization successful: $state" - return - else - echo "sleep $WAIT_INTERVAL_SECONDS" - sleep $WAIT_INTERVAL_SECONDS - fi - done - echo - echo "Worker initialization failed" - exit 1 -} - -echo "Number of WORKER_NUM: ${WORKER_NUM}" -############################################################################## -### Start execution -############################################################################## - -ROOTDIR=$(git rev-parse --show-toplevel) -ROOTDIR="${ROOTDIR}/tee-worker" -RUST_LOG="info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,\ -itc_parentchain_light_client=info,\ -jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,\ -its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,\ -its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,\ -itp_attestation_handler=debug,http_req=debug,itc_rest_client=debug,\ -itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug" - -# Create the log directory, in case not existed. -mkdir -p ${ROOTDIR}/log - -for ((i = 0; i < ${WORKER_NUM}; i++)); do - worker_name="worker${i}" - echo "" - echo "--------------------setup worker(${worker_name})----------------------------------------" - - if ((i > 0)); then - FSUBCMD_REQ_STATE="--request-state" - fi - - if [ "${CLEANUP}" = 'true' ]; then - echo "clear dir: ${ROOTDIR}/tmp/${worker_name}" - rm -rf "${ROOTDIR}"/tmp/"${worker_name}" - fi - mkdir -p "${ROOTDIR}"/tmp/"${worker_name}" - for Item in 'enclave.signed.so' 'key.txt' 'spid.txt' 'litentry-worker' 'bitacross-cli'; do - cp "${ROOTDIR}/bin/${Item}" "${ROOTDIR}"/tmp/"${worker_name}" - done - - cd "${ROOTDIR}"/tmp/${worker_name} || exit - echo "enter ${ROOTDIR}/tmp/${worker_name}" - - mu_ra_port=$((${MU_RA_PORT} + i)) - untrusted_http_port=$((${UNTRUSTED_HTTP_PORT} + i)) - trusted_worker_port=$((${TRUSTED_WORKER_PORT} + i)) - untrusted_worker_port=$((${UNTRUSTED_WORKER_PORT} + i)) - echo "${worker_name} ports: - mu-ra-port: ${mu_ra_port} - untrusted-http-port: ${untrusted_http_port} - trusted-worker-port: ${trusted_worker_port} - untrusted-worker-port: ${untrusted_worker_port} - " - - launch_command="RUST_LOG=${RUST_LOG} ./litentry-worker ${F_CLEAN} --ws-external \ ---mu-ra-external-address ${ENCLAVE_ENDPOINT} \ ---mu-ra-port ${mu_ra_port} \ ---node-port ${NODE_PORT} \ ---node-url ${NODE_URL} \ ---trusted-external-address wss://${ENCLAVE_ENDPOINT} \ ---trusted-worker-port ${trusted_worker_port} \ ---untrusted-external-address ws://${ENCLAVE_ENDPOINT} \ ---untrusted-http-port ${untrusted_http_port} \ ---untrusted-worker-port ${untrusted_worker_port} \ -run --skip-ra ${FSUBCMD_DEV} ${FSUBCMD_REQ_STATE}" - - echo "${worker_name} command: ${launch_command}" - eval "${launch_command}" > "${ROOTDIR}"/log/${worker_name}.log 2>&1 & - echo "${worker_name}(litentry-worker) started successfully. log: ${ROOTDIR}/log/${worker_name}.log" - - if ((${WORKER_NUM} > 0)); then - wait_worker_is_initialized ${untrusted_http_port} - fi -done - -echo "" -echo "--- Setup work(s) done ---" diff --git a/tee-worker/bitacross/scripts/litentry/release/ReadMe.md b/tee-worker/bitacross/scripts/litentry/release/ReadMe.md deleted file mode 100644 index 3faea84187..0000000000 --- a/tee-worker/bitacross/scripts/litentry/release/ReadMe.md +++ /dev/null @@ -1,106 +0,0 @@ - -# Release package - - -## Step 0: Preparation - -This package is generated from [litentry-parachain](https://github.com/litentry/litentry-parachain) -From the root folder ~/litentry-parachain/tee-worker/: -``` -make release-pkg -``` -A release package will be generated, within which there are: - -- enclave.sign.so -- litentry-worker -- config.json.eg -- prepare.sh - -
    - -## Step 1: Deploy on production - -Before starting the workers, please make sure the target parachain is already up and accessable. As well as the following directory/files: - -| Name | Value | Comment | -|-----|------|---| -| WORKER_DIR | /opt/worker | Working directory of workers | -| CONFIG_DIR | /opt/configs | Config directory which contains the following 4 secret files | -| -| CONFIG | config.json | Configs for twitter/discord/data provider/etc. url/keys. Take reference from config.json.eg | -| ACCOUNT | account.json | Substrate account exported json file | -| INTEL_KEY | key_production.txt | Intel SGX production key. Need to apply from Intel | -| INTEL_SPI | spid_production.txt | Intel SGX production spid. Need to apply from Intel | - -
    - -1. Extract the release package to one target location. Worker will be executed from there. Then execute `prepare.sh`: - ``` - ./prepare.sh - ``` - This script will generate out `MRENCLAVE` hex value (mrenclave.txt) and `Enclave Account` info (account.txt). They will be used later by ts scripts to setup enclave account. -
    - -2. Startup options. - - The service will start up like this example: - ``` - RUST_LOG=info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,itc_parentchain_light_client=info,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,itp_attestation_handler=debug,http_req=debug,lc_mock_server=warn,itc_rest_client=debug,lc_credentials=debug,lc_identity_verification=debug,lc_stf_task_receiver=debug,lc_stf_task_sender=debug,lc_data_providers=debug,itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug ./litentry-worker --clean-reset --ws-external --mu-ra-external-address localhost --mu-ra-port 3443 --node-port 9944 --node-url ws://127.0.0.1 --trusted-external-address wss://localhost --trusted-worker-port 2000 --untrusted-external-address ws://localhost --untrusted-http-port 4545 --untrusted-worker-port 3000 run --skip-ra --dev - ``` - The first part is RUST_LOG info. In production env, most of them will be disabled. Or `RUST_LOG=info` is enough. - - Starting from `./litentry-worker`, the following is the real startup options: - - ``` - USAGE: - litentry-worker [FLAGS] [OPTIONS] - - FLAGS: - -c, --clean-reset Cleans and purges any previous state and key files and generates them anew before starting. - --enable-metrics Enable the metrics HTTP server to serve metrics - --help Prints help information - -V, --version Prints version information - --ws-external Set this flag in case the worker should listen to external requests. - - OPTIONS: - -i, --metrics-port - Set the port on which the metrics are served. [default: 8787] - - -M, --mu-ra-external-address - Set the mutual remote attestation worker address to be retrieved by a trusted rpc call. If no port is given, the same as in `mu-ra-port` will be used. - -r, --mu-ra-port - Set the websocket port to listen for mu-ra requests [default: 3443] - - -p, --node-port - Set the websocket port to listen for substrate events [default: 9944] - - -u, --node-url - Set the node server protocol and IP address [default: ws://127.0.0.1] - - -T, --trusted-external-address - Set the trusted worker address to be advertised on the parentchain. If no port is given, the same as in - `trusted-worker-port` will be used. - -P, --trusted-worker-port - Set the trusted websocket port of the worker, running directly in the enclave. [default: 2000] - - -U, --untrusted-external-address - Set the untrusted worker address to be retrieved by a trusted rpc call. If no port is given, the same as in - `untrusted-worker-port` will be used. - -h, --untrusted-http-port Set the port for the untrusted HTTP server - -w, --untrusted-worker-port - Set the untrusted websocket port of the worker [default: 2001] - - SUBCOMMANDS: - dump-ra Perform RA and dump cert to disk - help Prints this message or the help of the given subcommand(s) - init-shard Initialize new shard (do this only if you run the first worker for that shard). if shard is not - specified, the MRENCLAVE is used instead - migrate-shard Migrate shard - mrenclave Dump mrenclave to stdout. base58 encoded. - request-state join a shard by requesting key provisioning from another worker - run Start the litentry-worker - shielding-key Get the public RSA3072 key from the TEE to be used to encrypt requests - signing-key Get the public ed25519 key the TEE uses to sign messages and extrinsics - test Run tests involving the enclave - ``` - diff --git a/tee-worker/bitacross/scripts/litentry/release/build.sh b/tee-worker/bitacross/scripts/litentry/release/build.sh deleted file mode 100755 index aafd70210d..0000000000 --- a/tee-worker/bitacross/scripts/litentry/release/build.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -# this script builds the release artefacts for TEE client and/or the enclave - -set -euo pipefail - -function usage() { - echo "Usage: $0 if-build-worker if-build-enclave" - echo "Example:" - echo " $0 true true" -} - -[ $# -ne 2 ] && (usage; exit 1) - -echo "build worker: $1" -echo "build enclave: $2" - -ROOTDIR=$(git rev-parse --show-toplevel) -WORKERDIR="$ROOTDIR/tee-worker" - -# hardcoded sgx signing key, adjust it accordingly if you call the script manually -SGX_COMMERCIAL_KEY="/opt/enclave_release/sgx_sign_key.pem" - -if [ ! -f "$SGX_COMMERCIAL_KEY" ]; then - echo "Cannot find SGX sign key under $SGX_COMMERCIAL_KEY" - exit 1 -fi - -DESTDIR="$WORKERDIR/enclave_release" -[ -d "$DESTDIR" ] && rm -rf "$DESTDIR" -mkdir -p "$DESTDIR" - -cd "$WORKERDIR" - -make clean - -export SGX_PRODUCTION=1 -export SGX_COMMERCIAL_KEY="$SGX_COMMERCIAL_KEY" -if [ "$1" = "true" ]; then - make service - cp bin/litentry-worker "$DESTDIR" -fi -if [ "$2" = "true" ]; then - make bin/enclave.signed.so - cp bin/enclave.signed.so "$DESTDIR" - make mrenclave 2>&1 | grep MRENCLAVE | awk '{print $2}' > "$DESTDIR/mrenclave.txt" -fi - -echo "Build tee done" -ls -l "$DESTDIR" diff --git a/tee-worker/bitacross/scripts/litentry/release/deploy.sh b/tee-worker/bitacross/scripts/litentry/release/deploy.sh deleted file mode 100755 index efd6b329c4..0000000000 --- a/tee-worker/bitacross/scripts/litentry/release/deploy.sh +++ /dev/null @@ -1,559 +0,0 @@ -#!/bin/bash - -set -eo pipefail - -# This script is used to perform actions on the target host, including: -# - generate: generate the systemd service files from the template -# - restart: restart the parachain, or the worker, or both -# - upgrade-worker: uprade the worker0 to the rev in local repo -# -# TODO: -# the combinations of flags are not yet well verified/organised, especially the following: -# --only-worker -# --build -# --discard - -# ------------------------------ -# path setting -# ------------------------------ - -ROOTDIR=$(git rev-parse --show-toplevel) -BASEDIR=/opt/litentry -PARACHAIN_BASEDIR="$BASEDIR/parachain" -WORKER_BASEDIR="$BASEDIR/worker" -BACKUP_BASEDIR="$BASEDIR/backup" -LOG_BACKUP_BASEDIR="$BACKUP_BASEDIR/log" -WORKER_BACKUP_BASEDIR="$BACKUP_BASEDIR/worker" -RELAYCHAIN_ALICE_BASEDIR="$PARACHAIN_BASEDIR/relay-alice" -RELAYCHAIN_BOB_BASEDIR="$PARACHAIN_BASEDIR/relay-bob" -PARACHAIN_ALICE_BASEDIR="$PARACHAIN_BASEDIR/para-alice" - -# ------------------------------ -# default arg setting -# ------------------------------ - -BUILD=false -DISCARD=false -WORKER_CONFIG= -CHAIN=paseo -ONLY_WORKER=false -PARACHAIN_HOST=localhost -PARACHAIN_PORT=9944 -DOCKER_IMAGE=litentry/litentry-parachain:tee-prod -COPY_FROM_DOCKER=false -PRODUCTION=false -ACTION= - -# ------------------------------ -# Some global setting -# ------------------------------ - -WORKER_COUNT= -PARACHAIN_ID= -OLD_MRENCLAVE= -NEW_MRENCLAVE= -OLD_SHARD= -LATEST_FINALIZED_BLOCK= - -SGX_SDK=/opt/intel/sgxsdk -SGX_ENCLAVE_SIGNER=$SGX_SDK/bin/x64/sgx_sign - -# ------------------------------ -# main() -# ------------------------------ - -function main { - # 0/ check if $USER has sudo - if sudo -l -U $USER 2>/dev/null | grep -q 'may run the following'; then - source "$SGX_SDK/environment" - else - echo "$USER doesn't have sudo permission" - exit 1 - fi - - # 1/ create folders if missing - sudo mkdir -p "$BASEDIR" - sudo chown $USER:$GROUPS "$BASEDIR" - for d in "$LOG_BACKUP_BASEDIR" "$WORKER_BACKUP_BASEDIR" "$RELAYCHAIN_ALICE_BASEDIR" "$RELAYCHAIN_BOB_BASEDIR" \ - "$PARACHAIN_ALICE_BASEDIR" "$WORKER_BASEDIR"; do - mkdir -p "$d" - done - - # 2/ parse command lines - echo "Parsing command line ..." - while [ $# -gt 0 ]; do - case "$1" in - -h|--help) - display_help - exit 0 - ;; - -b|--build) - BUILD=true - shift - ;; - -d|--discard) - DISCARD=true - shift - ;; - -c|--config) - WORKER_CONFIG="$(realpath -s $2)" - shift 2 - ;; - -a|--only-worker) - ONLY_WORKER=true - shift - ;; - -x|--chain) - CHAIN="$2" - shift 2 - ;; - -p|--parachain-port) - PARACHAIN_PORT="$2" - shift 2 - ;; - -z|--parachain-host) - PARACHAIN_HOST="$2" - shift 2 - ;; - -v|--copy-from-docker) - COPY_FROM_DOCKER=true - DOCKER_IMAGE="$2" - shift 2 - ;; - --prod) - PRODUCTION=true - shift - ;; - generate|restart|upgrade-worker) - ACTION="$1" - shift - ;; - *) - echo "Error: unknown option or subcommand $1" - display_help - exit 1 - ;; - esac - done - - # 3/ sanity checks - if [ ! -f "$WORKER_CONFIG" ]; then - echo "Worker config not found: $WORKER_CONFIG" - exit 1 - fi - - WORKER_COUNT=$(cat "$WORKER_CONFIG" | jq '.workers | length') - echo "Worker count: $WORKER_COUNT" - - # TODO: check flags conflict, e.g. - # - having `--discard` together with `upgrade-worker` doesn't make sense - # - `upgrade-worker` should ignore the `--only-worker` flag - - # 4/ main business logic - case "$ACTION" in - generate) - backup_services - generate_services - exit - ;; - restart) - backup_logs - backup_workers - stop_services - prune - build - setup_working_dir - if [ "$ONLY_WORKER" = true ]; then - remove_clean_reset - fi - restart_services - exit - ;; - upgrade-worker) - # build the new worker, the code must be under $ROOTDIR/tee-worker already - build_worker - # update the schedule - set_scheduled_enclave - - # wait until sidechain stalls - wait_for_sidechain - backup_workers - stop_worker_services - get_old_mrenclave - # TODO: actually we only need the copy-up - setup_working_dir - migrate_shard - remove_clean_reset - restart_services - exit - ;; - *) - echo "Unknown action: $ACTION" - exit 1 ;; - esac -} - -# ------------------------------ -# helper functions -# ------------------------------ - -function print_divider { - echo "------------------------------------------------------------" -} - -function display_help { - echo "usage: ./deploy.sh [options]" - echo "" - echo "subcommands:" - echo " generate Generate the parachain and worker systemd files" - echo " restart Restart the services" - echo " upgrade-worker Upgrade the worker" - echo "" - echo "options:" - echo " -h, --help Display this help message and exit" - echo " -b, --build Build the parachain and worker binaries (default: false)" - echo " -d, --discard Clean the existing state for parachain and worker (default: false)" - echo " -c, --config Config file for the worker" - echo " -a, --only-worker Start only the worker (default: false)" - echo " -x, --chain Chain type for launching the parachain network (default: paseo)" - echo " -h, --parachain-host Parachain ws URL (default: localhost)" - echo " -p, --parachain-port Parachain ws port (default: 9944)" - echo " -v, --copy-from-docker Copy the parachain binary from a docker image (default: litentry/litentry-parachain:tee-prod)" - echo " --prod Use a prod configuration to build and run the worker (default: false)" - echo "" - echo "examples:" - echo " ./deploy.sh generate --config tmp.json" - echo " ./deploy.sh restart --config tmp.json --discard --build" - echo " ./deploy.sh restart --config tmp.json --only-worker" - echo " ./deploy.sh upgrade-worker --config tmp.json --only-worker" - echo "" - echo "notes:" - echo " - This script requires an OS that supports systemd." - echo " - It is mandatory to provide a JSON config file for the worker." - echo " - jq is required to be installed on the system " - echo "" - echo "For more information or assistance, please contact Litentry parachain team." -} - -# TODO: in fact, this function only backs up the parachain logs -# maybe we want to remove it as it's not so critical anyway -function backup_logs { - echo "Backing up logs ..." - now=$(date +"%Y%m%d-%H%M%S") - outdir="$LOG_BACKUP_BASEDIR/log-$now" - mkdir -p "$outdir" - cp "$PARACHAIN_BASEDIR"/*.log "$outdir" || true - echo "Logs backed up into $outdir" -} - -function backup_workers { - echo "Backing up workers ..." - now=$(date +"%Y%m%d-%H%M%S") - cd "$WORKER_BASEDIR" || exit - for i in $(ls -d * 2>/dev/null); do - outdir="$WORKER_BACKUP_BASEDIR/$i-$now" - cp -rf "$i" "$outdir" - echo "Worker backed up into $outdir" - done -} - -function backup_services { - echo "Backing up services ..." - now=$(date +"%Y%m%d-%H%M%S") - cd /etc/systemd/system || exit - outdir="$WORKER_BACKUP_BASEDIR/service-$now" - mkdir -p "$outdir" - for f in para-alice.service relay-alice.service relay-bob.service $(ls worker*.service 2>/dev/null); do - cp "$f" "$outdir" || true - done -} - -function prune { - if [ "$DISCARD" = true ]; then - echo "Pruning the existing state ..." - rm -rf "$PARACHAIN_BASEDIR"/* - rm -rf "$WORKER_BASEDIR"/* - fi -} - -function generate_services { - echo "Generating systemd service files ..." - cd "$ROOTDIR/tee-worker/scripts/litentry/release" - cp template/* . - sed -i "s/CHAIN/$CHAIN/g" *.service - sed -i "s/USER/$USER/g" *.service - for ((i = 0; i < WORKER_COUNT; i++)); do - cp worker.service worker$i.service - sed -i "s/NUMBER/$i/g" worker$i.service - # populate args - flags=$(cat "$WORKER_CONFIG" | jq -r ".workers[$i].flags[]") - subcommand_flags=$(cat "$WORKER_CONFIG" | jq -r ".workers[$i].subcommand_flags[]") - args= - for flag in $flags; do - args+=" $flag" - done - args+=" run" - for subcommand_flag in $subcommand_flags; do - args+=" $subcommand_flag" - done - sed -i "s;ARGS;$args;" worker$i.service - done - rm worker.service - sudo cp *.service -f /etc/systemd/system/ - rm *.service - sudo systemctl daemon-reload - echo "Done, please check files under /etc/systemd/system/" - echo "Restart the services to take effect" -} - -function build_worker { - echo "Building worker ..." - cd $ROOTDIR/tee-worker/ || exit - if [ "$PRODUCTION" = true ]; then - # we will get an error if SGX_COMMERCIAL_KEY is not set for prod - SGX_PRODUCTION=1 make - else - # use SW mode for dev - SGX_MODE=SW make - fi -} - -# TODO: take github rev into consideration -function build { - if [ "$BUILD" = true ]; then - echo "Building the parachain and worker binaries ..." - - # download polkadot - echo "Downloading polkadot binary ..." - - for f in polkadot-execute-worker polkadot-prepare-worker polkadot; do - url="https://github.com/paritytech/polkadot-sdk/releases/download/polkadot-v1.1.0/$f" - polkadot_bin="$PARACHAIN_BASEDIR/$f" - wget -O "$polkadot_bin" -q "$url" - chmod a+x "$polkadot_bin" - if [ ! -s "$polkadot_bin" ]; then - echo "$polkadot_bin is 0 bytes, download URL: $url" && exit 1 - fi - done - - if ! "$polkadot_bin" --version &> /dev/null; then - echo "Cannot execute $polkadot_bin, wrong executable?" && exit 1 - fi - - # pull or build parachain - if [ "$COPY_FROM_DOCKER" = true ]; then - echo "Pulling binary from $DOCKER_IMAGE ..." - docker pull "$DOCKER_IMAGE" - docker cp "$(docker create --rm $DOCKER_IMAGE):/usr/local/bin/litentry-collator" "$PARACHAIN_BASEDIR" - else - echo "Building parachain binary ..." - cd "$ROOTDIR" || exit - if [ "$PRODUCTION" = true ]; then - cargo build --locked --profile production - else - pwd - make build-node - fi - cp "$ROOTDIR/target/release/litentry-collator" "$PARACHAIN_BASEDIR" - fi - chmod a+x "$PARACHAIN_BASEDIR/litentry-collator" - fi -} - -function restart_services { - sudo systemctl daemon-reload - if [ "$ONLY_WORKER" = false ]; then - echo "Restarting parachain services ..." - - cd "$PARACHAIN_BASEDIR" || exit - ./polkadot build-spec --chain rococo-local --disable-default-bootnode --raw > rococo-local-chain-spec.json - ./litentry-collator export-genesis-state --chain $CHAIN-dev > genesis-state - ./litentry-collator export-genesis-wasm --chain $CHAIN-dev > genesis-wasm - - sudo systemctl restart relay-alice.service - sleep 5 - sudo systemctl restart relay-bob.service - sleep 5 - sudo systemctl restart para-alice.service - sleep 5 - register_parachain - fi - - echo "Restarting worker services ..." - for ((i = 0; i < WORKER_COUNT; i++)); do - sudo systemctl restart "worker$i.service" - sleep 5 - done - echo "Done" -} - -function stop_worker_services { - echo "Stopping worker services ..." - for ((i = 0; i < WORKER_COUNT; i++)); do - sudo systemctl stop "worker$i.service" - sleep 5 - done -} - -function stop_parachain_services { - echo "Stopping parachain services ..." - sudo systemctl stop para-alice.service relay-alice.service relay-bob.service -} - -function stop_services { - stop_worker_services - - # TODO: it means we can't stop parachain service alone - # this needs to be done directly via `systemctl` - if [ "$ONLY_WORKER" = false ]; then - stop_parachain_services - fi -} - -function register_parachain { - echo "Register parathread now ..." - cd "$ROOTDIR" || exit - export PARACHAIN_ID=$(grep -i "${CHAIN}_para_id" primitives/core/src/lib.rs | sed 's/.* = //;s/\;.*//') - cd "$ROOTDIR/ts-tests" || exit - if [[ -z "$NODE_ENV" ]]; then - echo "NODE_ENV=ci" > .env - else - echo "NODE_ENV=$NODE_ENV" > .env - fi - # The genesis state path file needs to be updated as it is hardcoded to be /tmp/parachain_dev - jq --arg genesis_state "$PARACHAIN_BASEDIR/genesis-state" --arg genesis_wasm "$PARACHAIN_BASEDIR/genesis-wasm" '.genesis_state_path = $genesis_state | .genesis_wasm_path = $genesis_wasm' config.ci.json > config.ci.json.1 - mv config.ci.json.1 config.ci.json - pnpm install - pnpm run register-parathread 2>&1 | tee "$PARACHAIN_BASEDIR/register-parathread.log" - print_divider - - echo "Upgrade parathread to parachain now ..." - # Wait for 90s to allow onboarding finish, after that we do the upgrade - sleep 90 - pnpm run upgrade-parathread 2>&1 | tee "$PARACHAIN_BASEDIR/upgrade-parathread.log" - print_divider - - echo "done. please check $PARACHAIN_BASEDIR for generated files if need" - print_divider - git restore config.ci.json -} - -function setup_working_dir { - echo "Setting up working dir ..." - cd "$ROOTDIR/tee-worker/bin" || exit - - if [ "$PRODUCTION" = false ]; then - for f in 'key.txt' 'spid.txt'; do - [ -f "$f" ] || touch "$f" - done - fi - - for ((i = 0; i < WORKER_COUNT; i++)); do - worker_dir="$WORKER_BASEDIR/w$i" - mkdir -p "$worker_dir" - for f in 'key.txt' 'spid.txt' 'enclave.signed.so' 'litentry-worker'; do - [ -f "$f" ] && cp -f "$f" "$worker_dir" - done - - cd "$worker_dir" - [ -f light_client_db.bin/db.bin.backup ] && cp -f light_client_db.bin/db.bin.backup light_client_db.bin/db.bin - - enclave_account=$(./litentry-worker signing-key | grep -oP '^Enclave account: \K.*$$') - - if [ "$PRODUCTION" = true ]; then - echo "Transferring balance to the enclave account $enclave_account ..." - cd $ROOTDIR/scripts/ts-utils/ || exit - pnpm install - pnpm exec ts-node transfer.ts $enclave_account - fi - done -} - -function get_old_mrenclave { - cd "$WORKER_BASEDIR/w0" || exit - OLD_SHARD=$(./litentry-worker mrenclave) - $SGX_ENCLAVE_SIGNER dump -enclave ./enclave.signed.so -dumpfile df.out - OLD_MRENCLAVE=$($ROOTDIR/tee-worker/extract_identity < df.out | awk '{print $2}') - rm df.out - echo "old shard: $OLD_SHARD" - echo "old mrenclave: $OLD_MRENCLAVE" -} - -function set_scheduled_enclave { - echo "Setting scheduled enclave ..." - cd $ROOTDIR/tee-worker || exit - NEW_MRENCLAVE=$(make mrenclave 2>&1 | grep MRENCLAVE | awk '{print $2}') - echo "new mrenclave: $NEW_MRENCLAVE" - - latest_sidechain_block - - echo "Setting up the new worker on chain ..." - cd $ROOTDIR/ts-tests/ || exit - pnpm install - pnpm run setup-enclave $NEW_MRENCLAVE $SCHEDULED_UPDATE_BLOCK -} - -function wait_for_sidechain { - echo "Waiting for sidechain to reach block $SCHEDULED_UPDATE_BLOCK ..." - found=false - for _ in $(seq 1 30); do - sleep 20 - block_number=$(grep -F 'Enclave produced sidechain blocks' $WORKER_BASEDIR/w0/worker.log | tail -n 1 | sed 's/.*\[//;s/]//') - echo "current sidechain block: $block_number" - if [ $((block_number+1)) -eq $SCHEDULED_UPDATE_BLOCK ]; then - echo "we should stall soon ..." - fi - if tail -n 50 $WORKER_BASEDIR/w0/worker.log | grep -q "Skipping sidechain block $SCHEDULED_UPDATE_BLOCK due to mismatch MRENCLAVE"; then - echo "we reach $SCHEDULED_UPDATE_BLOCK now" - found=true - break - fi - done - if [ $found = false ]; then - echo "not reached, timeout" - exit 1 - fi -} - -function migrate_shard { - echo "Migrating shards for workers ..." - for ((i = 0; i < WORKER_COUNT; i++)); do - cd "$WORKER_BASEDIR/w$i" || exit - echo "old MRENCLAVE: $OLD_MRENCLAVE" - echo "new MRENCLAVE: $NEW_MRENCLAVE" - ./litentry-worker migrate-shard --old-shard $OLD_MRENCLAVE --new-shard $NEW_MRENCLAVE - - cd shards || exit - rm -rf $OLD_SHARD - done - echo "Done" -} - -function remove_clean_reset { - echo "Removing --clean-reset flag for workers ..." - for ((i = 0; i < WORKER_COUNT; i++)); do - sudo sed -i 's/--clean-reset//' /etc/systemd/system/worker$i.service - done - echo "Done" -} - -# TODO: here we only read worker0 logs here -function latest_sidechain_block { - block_number=$(grep -F 'Enclave produced sidechain blocks' $WORKER_BASEDIR/w0/worker.log | tail -n 1 | sed 's/.*\[//;s/]//') - SCHEDULED_UPDATE_BLOCK=$((block_number + 30)) - echo "Current sidechain block: $block_number, scheduled update block: $SCHEDULED_UPDATE_BLOCK" -} - -# TODO: unused -function _latest_parentchain_block { - # JSON-RPC request payload - request='{"jsonrpc":"2.0","id":1,"method":"chain_getHeader","params":[]}' - - # Make the JSON-RPC request and retrieve the latest finalized block - response=$(curl -s -H "Content-Type: application/json" -d "$request" http://$PARACHAIN_HOST:$PARACHAIN_PORT) - hex_number=$(echo "$response" | grep -oP '(?<="number":")[^"]+') - LATEST_FINALIZED_BLOCK=$(printf "%d" "$hex_number") - echo "Current parachain block: $LATEST_FINALIZED_BLOCK" -} - -main "$@" diff --git a/tee-worker/bitacross/scripts/litentry/release/prepare.sh b/tee-worker/bitacross/scripts/litentry/release/prepare.sh deleted file mode 100755 index e9817e8d71..0000000000 --- a/tee-worker/bitacross/scripts/litentry/release/prepare.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -set -euo pipefail - - -# This WORKER_DIR is the directory where worker will start from. -WORKER_DIR=/opt/worker/ - -# CONFIG_DIR provides all the necessary private secret files. -# They should only exist on the running machine. -CONFIG_DIR=/opt/configs/ -CONFIG=$CONFIG_DIR/config.json -ACCOUNT=$CONFIG_DIR/private_account.json -INTEL_KEY=$CONFIG_DIR/key_production.txt -INTEL_SPID=$CONFIG_DIR/spid_production.txt - -############################################################################## -# Don't edit anything from here -if [[ ! -e "$WORKER_DIR" ]]; then - mkdir -p $WORKER_DIR -fi - -for Item in $CONFIG $ACCOUNT $INTEL_KEY $INTEL_SPID; do - if [[ ! -e "$Item" ]]; then - echo "Error: $Item is not a valid path." - exit 1 - fi -done - -# Generate keys and copy around. -SRC_DIR=$(dirname "$0") -cd $SRC_DIR - -./litentry-worker signing-key | grep -oP '^Enclave account: \K.*$$' > enclave_account.txt -echo "Enclave account is prepared inside enclave_account.txt" - -./litentry-worker shielding-key - -for Item in 'enclave.signed.so' 'litentry-worker' 'aes_key_sealed.bin' 'ed25519_key_sealed.bin' 'enclave-shielding-pubkey.json' 'enclave-signing-pubkey.bin' 'rsa3072_key_sealed.bin' 'sidechain_db'; do - cp -r "${Item}" "${WORKER_DIR}" -done - -cp $CONFIG "${WORKER_DIR}/config.json" -cp $INTEL_KEY "${WORKER_DIR}/key_production.txt" -cp $INTEL_SPID "${WORKER_DIR}/spid_production.txt" - -# Comment out for the moment. Need to adapt together with PR-1587 ts-utils. -cp $ACCOUNT "${WORKER_DIR}/ts-utils/private_account.json" -cp "enclave_account.txt" "${WORKER_DIR}/ts-utils/enclave_account.txt" -cp "mrenclave.txt" "${WORKER_DIR}/ts-utils/mrenclave.txt" - diff --git a/tee-worker/bitacross/scripts/litentry/release/template/para-alice.service b/tee-worker/bitacross/scripts/litentry/release/template/para-alice.service deleted file mode 100644 index cb8eb6659b..0000000000 --- a/tee-worker/bitacross/scripts/litentry/release/template/para-alice.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=Litentry Parachain - -[Service] -Type=simple -User=USER -WorkingDirectory=/opt/litentry/parachain -ExecStart=/opt/litentry/parachain/litentry-collator --base-path /opt/litentry/parachain/para-alice --alice --collator --force-authoring --chain CHAIN-dev --unsafe-rpc-external --rpc-cors=all --ws-max-connections 3000 --port 30333 --rpc-port 9944 --execution wasm --state-pruning archive --blocks-pruning archive -- --execution wasm --chain /opt/litentry/parachain/rococo-local-chain-spec.json --port 30332 --rpc-port 9943 -Restart=always -RestartSec=120 -StandardOutput=append:/opt/litentry/parachain/para.alice.log -StandardError=inherit - -[Install] -WantedBy=multi-user.target diff --git a/tee-worker/bitacross/scripts/litentry/release/template/relay-alice.service b/tee-worker/bitacross/scripts/litentry/release/template/relay-alice.service deleted file mode 100644 index ce87068c59..0000000000 --- a/tee-worker/bitacross/scripts/litentry/release/template/relay-alice.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=Litentry Relaychain Alice - -[Service] -Type=simple -User=USER -WorkingDirectory=/opt/litentry/parachain -ExecStart=/opt/litentry/parachain/polkadot --base-path /opt/litentry/parachain/relay-alice --chain /opt/litentry/parachain/rococo-local-chain-spec.json --alice --port 30336 --rpc-port 9946 -Restart=always -RestartSec=120 -StandardOutput=append:/opt/litentry/parachain/relay.alice.log -StandardError=inherit - -[Install] -WantedBy=multi-user.target diff --git a/tee-worker/bitacross/scripts/litentry/release/template/relay-bob.service b/tee-worker/bitacross/scripts/litentry/release/template/relay-bob.service deleted file mode 100644 index 889f96378e..0000000000 --- a/tee-worker/bitacross/scripts/litentry/release/template/relay-bob.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=Litentry Relaychain Bob - -[Service] -Type=simple -User=USER -WorkingDirectory=/opt/litentry/parachain -ExecStart=/opt/litentry/parachain/polkadot --base-path /opt/litentry/parachain/relay-bob --chain /opt/litentry/parachain/rococo-local-chain-spec.json --bob --port 30337 --rpc-port 9947 -Restart=always -RestartSec=120 -StandardOutput=append:/opt/litentry/parachain/relay.bob.log -StandardError=inherit - -[Install] -WantedBy=multi-user.target diff --git a/tee-worker/bitacross/scripts/litentry/release/template/worker.service b/tee-worker/bitacross/scripts/litentry/release/template/worker.service deleted file mode 100644 index e218d60278..0000000000 --- a/tee-worker/bitacross/scripts/litentry/release/template/worker.service +++ /dev/null @@ -1,14 +0,0 @@ -[Unit] -Description=Litentry TEE worker - -[Service] -Type=simple -User=USER -Environment='RUST_LOG=info,litentry_worker=debug,ws=warn,sp_io=error,substrate_api_client=warn,itc_parentchain_light_client=info,jsonrpsee_ws_client=warn,jsonrpsee_ws_server=warn,enclave_runtime=debug,ita_stf=debug,its_rpc_handler=warn,itc_rpc_client=warn,its_consensus_common=debug,its_state=warn,its_consensus_aura=warn,aura*=warn,its_consensus_slots=warn,itp_attestation_handler=debug,http_req=debug,lc_mock_server=warn,itc_rest_client=debug,lc_credentials=debug,lc_identity_verification=debug,lc_stf_task_receiver=debug,lc_stf_task_sender=debug,lc_data_providers=debug,itp_top_pool=debug,itc_parentchain_indirect_calls_executor=debug' -WorkingDirectory=/opt/litentry/worker/wNUMBER -ExecStart=/bin/bash -c 'cd /opt/litentry/worker/wNUMBER && source /opt/intel/sgxsdk/environment && ./litentry-worker ARGS' -StandardOutput=append:/opt/litentry/worker/wNUMBER/worker.log -StandardError=inherit - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/tee-worker/bitacross/scripts/litentry/ubuntu_setup.sh b/tee-worker/bitacross/scripts/litentry/ubuntu_setup.sh deleted file mode 100755 index ef02a6418e..0000000000 --- a/tee-worker/bitacross/scripts/litentry/ubuntu_setup.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash - -set -eo pipefail - -# most is copied from -# https://github.com/apache/incubator-teaclave-sgx-sdk/blob/v1.1.4/dockerfile/Dockerfile.2004.nightly - -# install rust -curl -s https://sh.rustup.rs -sSf | sh -s -- -y -# shellcheck source=${HOME}/.cargo/env -source ${HOME}/.cargo/env -rustup show - -# install substrate build deps -sudo apt-get update -sudo apt-get install -y cmake pkg-config libssl-dev git clang libclang-dev gnupg2 protobuf-compiler - -# install llvm -sudo apt-get update -wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh 10 - -# override binutils -wget https://download.01.org/intel-sgx/sgx-linux/2.20/as.ld.objdump.r4.tar.gz -tar xzf as.ld.objdump.r4.tar.gz -sudo cp -f external/toolset/ubuntu20.04/* /usr/bin/ - -# install sgx_sdk -SDK_URL="https://download.01.org/intel-sgx/sgx-linux/2.20/distro/ubuntu20.04-server/sgx_linux_x64_sdk_2.20.100.4.bin" -curl -o sdk.sh $SDK_URL -chmod a+x sdk.sh -echo -e 'no\n/opt' | ./sdk.sh -source /opt/sgxsdk/environment - -# install runtime sgx libs (psw) -CODENAME=focal -VERSION=2.20.100.4-focal1 -DCAP_VERSION=1.17.100.4-focal1 - -curl -fsSL https://download.01.org/intel-sgx/sgx_repo/ubuntu/intel-sgx-deb.key | sudo apt-key add - && \ -sudo add-apt-repository "deb https://download.01.org/intel-sgx/sgx_repo/ubuntu $CODENAME main" && \ -sudo apt-get update && \ -sudo apt-get install -y \ - libsgx-headers=$VERSION \ - libsgx-ae-epid=$VERSION \ - libsgx-ae-le=$VERSION \ - libsgx-ae-pce=$VERSION \ - libsgx-aesm-ecdsa-plugin=$VERSION \ - libsgx-aesm-epid-plugin=$VERSION \ - libsgx-aesm-launch-plugin=$VERSION \ - libsgx-aesm-pce-plugin=$VERSION \ - libsgx-aesm-quote-ex-plugin=$VERSION \ - libsgx-enclave-common=$VERSION \ - libsgx-enclave-common-dev=$VERSION \ - libsgx-epid=$VERSION \ - libsgx-epid-dev=$VERSION \ - libsgx-launch=$VERSION \ - libsgx-launch-dev=$VERSION \ - libsgx-quote-ex=$VERSION \ - libsgx-quote-ex-dev=$VERSION \ - libsgx-uae-service=$VERSION \ - libsgx-urts=$VERSION \ - sgx-aesm-service=$VERSION \ - libsgx-ae-qe3=$DCAP_VERSION \ - libsgx-pce-logic=$DCAP_VERSION \ - libsgx-qe3-logic=$DCAP_VERSION \ - libsgx-ra-network=$DCAP_VERSION \ - libsgx-ra-uefi=$DCAP_VERSION -mkdir -p /var/run/aesmd || true - -# store env -echo "$(env)" >> $GITHUB_ENV \ No newline at end of file diff --git a/tee-worker/bitacross/scripts/m6.sh b/tee-worker/bitacross/scripts/m6.sh deleted file mode 100755 index d6ed56786d..0000000000 --- a/tee-worker/bitacross/scripts/m6.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# Runs M6 demo: Either set `CLIENT_DIR` env var directly or run script with: -# -# source ./init_env.sh && ./m6.sh - -echo "$CLIENT_DIR" - -cd "$CLIENT_DIR" || exit - -LOG_1="${LOG_1:-$LOG_DIR/m6_demo_shielding_unshielding_1.log}" -LOG_2="${LOG_2:-$LOG_DIR/m6_demo_shielding_unshielding_2.log}" - -echo "[m6.sh] printing to logs:" -echo " $LOG_1" -echo " $LOG_2" - -touch "$LOG_1" -touch "$LOG_2" - -./demo_shielding_unshielding.sh -p 9944 -P 2000 -C ./../bin/bitacross-cli -t first 2>&1 | tee "$LOG_1" -./demo_shielding_unshielding.sh -p 9944 -P 3000 -C ./../bin/bitacross-cli -t second 2>&1 | tee "$LOG_2" diff --git a/tee-worker/bitacross/scripts/m8.sh b/tee-worker/bitacross/scripts/m8.sh deleted file mode 100755 index 402875a8c8..0000000000 --- a/tee-worker/bitacross/scripts/m8.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# Runs M8 demo: Either set `CLIENT_DIR` env var directly or run script with: -# -# source ./init_env.sh && ./m8.sh - -cd "$CLIENT_DIR" || exit - -LOG_1="${LOG_1:-$LOG_DIR/m8_demo_direct_call_1.log}" -LOG_2="${LOG_2:-$LOG_DIR/m8_demo_direct_call_2.log}" - -echo "[m8.sh] printing to logs:" -echo " $LOG_1" -echo " $LOG_2" - -touch "$LOG_1" -touch "$LOG_2" - -./demo_direct_call.sh -p 9944 -P 2000 -C ./../bin/bitacross-cli -t first 2>&1 | tee "$LOG_1" -./demo_direct_call.sh -p 9944 -P 3000 -C ./../bin/bitacross-cli -t second 2>&1 | tee "$LOG_2" diff --git a/tee-worker/bitacross/scripts/polkadot_update.sh b/tee-worker/bitacross/scripts/polkadot_update.sh deleted file mode 100755 index 0ba52f86e3..0000000000 --- a/tee-worker/bitacross/scripts/polkadot_update.sh +++ /dev/null @@ -1,97 +0,0 @@ -#!/bin/bash - -# A script to automate the polkadot update for our repository as far as possible -# Needs the diener and sd (sed replacement) tool. Install with: -# cargo install diener -# cargo install sd - -# These are the values that need to be adjusted for an update -CHECKOUT_DIR="$HOME/polkadot_update2" -DEVELOPER_ID="tn" -OLD_VERSION_NUMBER="0.9.27" -NEW_VERSION_NUMBER="0.9.28" -NEW_NIGHTLY_VERSION="2022-09-12" - -OLD_POLKADOT_VERSION_NUMBER="polkadot-v${OLD_VERSION_NUMBER}" -NEW_POLKADOT_VERSION_NUMBER="polkadot-v${NEW_VERSION_NUMBER}" -DEVELOPMENT_BRANCH="${DEVELOPER_ID}/${NEW_POLKADOT_VERSION_NUMBER}" - -# Make sure that the directory does not exist. We don't want to mess up existing stuff -if [ -d "${CHECKOUT_DIR}" ]; then - echo "Directory ${CHECKOUT_DIR} already exists. Please delete directory first." - exit 1 -fi - -mkdir "${CHECKOUT_DIR}" -pushd "${CHECKOUT_DIR}" - -git clone https://github.com/integritee-network/integritee-node.git -git clone https://github.com/integritee-network/pallets.git -git clone https://github.com/integritee-network/parachain.git -git clone https://github.com/scs/substrate-api-client.git -git clone https://github.com/integritee-network/worker.git - -declare -a REPO_NAMES=("integritee-node" "pallets" "parachain" "substrate-api-client" "worker" ) - -# Create new branch for all repos -for REPO in ${REPO_NAMES[@]}; do - pushd ${REPO};git checkout -b ${DEVELOPMENT_BRANCH};popd -done - -# Update the polkadot version -# We cannot combine the flags into a single call. Don't use the all flag because it relly changes all dependencies -diener update --cumulus --branch ${NEW_POLKADOT_VERSION_NUMBER} -diener update --substrate --branch ${NEW_POLKADOT_VERSION_NUMBER} -# Polkadot uses another branch pattern, because why not... -diener update --polkadot --branch "release-v${NEW_VERSION_NUMBER}" - -# Add commit for all repos -for REPO in ${REPO_NAMES[@]}; do - pushd ${REPO};git add -A;git commit -m "Update polkadot version (Auto generated commit)";popd -done - -# Execute cargo update for all repos. Currently not active as it is not clear when is the "right moment" to do this -#for REPO in ${REPO_NAMES[@]}; do -# pushd ${REPO};cargo update;popd -#done - -# Add commit for all repos -#for REPO in ${REPO_NAMES[@]}; do -# pushd ${REPO};git add -A;git commit -m "Run cargo update (Auto generated)";popd -#done - -#set -o xtrace -# Update internal dependencies by doing search replace -for REPO in ${REPO_NAMES[@]}; do - SEARCH_STRING_VERSION="${REPO}\", branch = \"${OLD_POLKADOT_VERSION_NUMBER}\"" - SEARCH_STRING_VERSION_GIT="${REPO}.git\", branch = \"${OLD_POLKADOT_VERSION_NUMBER}\"" - SEARCH_STRING_MASTER="${REPO}\", branch = \"master\"" - SEARCH_STRING_MASTER_GIT="${REPO}.git\", branch = \"master\"" - REPLACE_STRING="${REPO}.git\", branch = \"${DEVELOPMENT_BRANCH}\"" - sd "${SEARCH_STRING_VERSION}" "${REPLACE_STRING}" $(find . -type f -name 'Cargo.toml') - sd "${SEARCH_STRING_VERSION_GIT}" "${REPLACE_STRING}" $(find . -type f -name 'Cargo.toml') - sd "${SEARCH_STRING_MASTER}" "${REPLACE_STRING}" $(find . -type f -name 'Cargo.toml') - sd "${SEARCH_STRING_MASTER_GIT}" "${REPLACE_STRING}" $(find . -type f -name 'Cargo.toml') -done - -# Add commit for all repos -for REPO in ${REPO_NAMES[@]}; do - pushd ${REPO};git add -A;git commit -m "Update versions for internal dependencies (Auto generated commit)";popd -done - -NIGHTLY_SEARCH_STRING="channel = \"nightly-.*\"" -NIGHTLY_SEARCH_STRING="channel = \"nightly-${NEW_NIGHTLY_VERSION}\"" -sd "${NIGHTLY_SEARCH_STRING}" "${NIGTHLY_NEW_STRING}" $(find . -type f -name 'rust-toolchain.toml') - -# Add commit for all repos -for REPO in ${REPO_NAMES[@]}; do - pushd ${REPO};git add -A;git commit -m "Update rust toolchain to new nightly version (Auto generated commit)";popd -done - -echo "" -echo "" -echo "Search results for old version number ${OLD_VERSION_NUMBER} in Cargo.toml files:" -# Exclude the lock files as they still refer to the old version -grep -F -r --exclude *.lock "${OLD_VERSION_NUMBER}" . - -popd diff --git a/tee-worker/bitacross/scripts/sidechain.sh b/tee-worker/bitacross/scripts/sidechain.sh deleted file mode 100755 index 908c538eb1..0000000000 --- a/tee-worker/bitacross/scripts/sidechain.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -set -euo pipefail - -# Runs sidechain demo: Either set `CLIENT_DIR` env var directly or run script with: -# -# source ./init_env.sh && ./sidechain.sh - -cd "$CLIENT_DIR" || exit - -LOG="${LOG:-$LOG_DIR/sidechain_demo.log}" - -echo "[sidechain.sh] printing to logs:" -echo " $LOG" - -touch "$LOG" - -./demo_sidechain.sh -p 9944 -A 2000 -B 3000 -C ./../bin/bitacross-cli 2>&1 | tee "$LOG" \ No newline at end of file diff --git a/tee-worker/bitacross/scripts/test_transfer/README.md b/tee-worker/bitacross/scripts/test_transfer/README.md deleted file mode 100644 index 13ff80ca8e..0000000000 --- a/tee-worker/bitacross/scripts/test_transfer/README.md +++ /dev/null @@ -1,6 +0,0 @@ -## Test transfer from Alice to random account - -## Install -```bash -npm install -``` diff --git a/tee-worker/bitacross/scripts/test_transfer/package-lock.json b/tee-worker/bitacross/scripts/test_transfer/package-lock.json deleted file mode 100644 index 237b27764d..0000000000 --- a/tee-worker/bitacross/scripts/test_transfer/package-lock.json +++ /dev/null @@ -1,1322 +0,0 @@ -{ - "name": "test_transfer", - "version": "1.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "test_transfer", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "@polkadot/api": "^10.9.1", - "@polkadot/keyring": "^12.3.2", - "@polkadot/util-crypto": "^12.3.2" - } - }, - "node_modules/@noble/curves": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", - "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", - "dependencies": { - "@noble/hashes": "1.3.1" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/hashes": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", - "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot/api": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-10.9.1.tgz", - "integrity": "sha512-ND/2UqZBWvtt4PfV03OStTKg0mxmPk4UpMAgJKutdgsz/wP9CYJ1KbjwFgPNekL9JnzbKQsWyQNPVrcw7kQk8A==", - "dependencies": { - "@polkadot/api-augment": "10.9.1", - "@polkadot/api-base": "10.9.1", - "@polkadot/api-derive": "10.9.1", - "@polkadot/keyring": "^12.3.1", - "@polkadot/rpc-augment": "10.9.1", - "@polkadot/rpc-core": "10.9.1", - "@polkadot/rpc-provider": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-augment": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/types-create": "10.9.1", - "@polkadot/types-known": "10.9.1", - "@polkadot/util": "^12.3.1", - "@polkadot/util-crypto": "^12.3.1", - "eventemitter3": "^5.0.1", - "rxjs": "^7.8.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/api-augment": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-10.9.1.tgz", - "integrity": "sha512-kRZZvCFVcN4hAH4dJ+Qzfdy27/4EEq3oLDf3ihj0LTVrAezSWcKPGE3EVFy+Mn6Lo4SUc7RVyoKvIUhSk2l4Dg==", - "dependencies": { - "@polkadot/api-base": "10.9.1", - "@polkadot/rpc-augment": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-augment": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/api-base": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-10.9.1.tgz", - "integrity": "sha512-Q3m2KzlceMK2kX8bhnUZWk3RT6emmijeeFZZQgCePpEcrSeNjnqG4qjuTPgkveaOkUT8MAoDc5Avuzcc2jlW9g==", - "dependencies": { - "@polkadot/rpc-core": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/util": "^12.3.1", - "rxjs": "^7.8.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/api-derive": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-10.9.1.tgz", - "integrity": "sha512-mRud1UZCFIc4Z63qAoGSIHh/foyUYADfy1RQYCmPpeFKfIdCIrHpd7xFdJXTOMYOS0BwlM6u4qli/ZT4XigezQ==", - "dependencies": { - "@polkadot/api": "10.9.1", - "@polkadot/api-augment": "10.9.1", - "@polkadot/api-base": "10.9.1", - "@polkadot/rpc-core": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/util": "^12.3.1", - "@polkadot/util-crypto": "^12.3.1", - "rxjs": "^7.8.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/keyring": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-12.3.2.tgz", - "integrity": "sha512-NTdtDeI0DP9l/45hXynNABeP5VB8piw5YR+CbUxK2e36xpJWVXwbcOepzslg5ghE9rs8UKJb30Z/HqTU4sBY0Q==", - "dependencies": { - "@polkadot/util": "12.3.2", - "@polkadot/util-crypto": "12.3.2", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "12.3.2", - "@polkadot/util-crypto": "12.3.2" - } - }, - "node_modules/@polkadot/networks": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-12.3.2.tgz", - "integrity": "sha512-uCkyybKoeEm1daKr0uT/9oNDHDDzCy2/ZdVl346hQqfdR1Ct3BaxMjxqvdmb5N8aCw0cBWSfgsxAYtw8ESmllQ==", - "dependencies": { - "@polkadot/util": "12.3.2", - "@substrate/ss58-registry": "^1.40.0", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/rpc-augment": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-10.9.1.tgz", - "integrity": "sha512-MaLHkNlyqN20ZRYr6uNd1BZr1OsrnX9qLAmsl0mcrri1vPGRH6VHjfFH1RBLkikpWD82v17g0l2hLwdV1ZHMcw==", - "dependencies": { - "@polkadot/rpc-core": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/rpc-core": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-10.9.1.tgz", - "integrity": "sha512-ZtA8B8SfXSAwVkBlCcKRHw0eSM7ec/sbiNOM5GasXPeRujUgT7lOwSH2GbUZSqe9RfRDMp6DvO9c2JoGc3LLWw==", - "dependencies": { - "@polkadot/rpc-augment": "10.9.1", - "@polkadot/rpc-provider": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/util": "^12.3.1", - "rxjs": "^7.8.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/rpc-provider": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-10.9.1.tgz", - "integrity": "sha512-4QzT2QzD+320+eT6b79sGAA85Tt3Bb8fQvse4r5Mom2iiBd2SO81vOhxSAOaIe4GUsw25VzFJmsbe7+OObItdg==", - "dependencies": { - "@polkadot/keyring": "^12.3.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-support": "10.9.1", - "@polkadot/util": "^12.3.1", - "@polkadot/util-crypto": "^12.3.1", - "@polkadot/x-fetch": "^12.3.1", - "@polkadot/x-global": "^12.3.1", - "@polkadot/x-ws": "^12.3.1", - "eventemitter3": "^5.0.1", - "mock-socket": "^9.2.1", - "nock": "^13.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - }, - "optionalDependencies": { - "@substrate/connect": "0.7.26" - } - }, - "node_modules/@polkadot/types": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-10.9.1.tgz", - "integrity": "sha512-AG33i2ZGGfq7u+5rkAdGrXAQHHl844/Yv+junH5ZzX69xiCoWO1bH/yzDUNBdpki2GlACWvF9nLYh3F2tVF93w==", - "dependencies": { - "@polkadot/keyring": "^12.3.1", - "@polkadot/types-augment": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/types-create": "10.9.1", - "@polkadot/util": "^12.3.1", - "@polkadot/util-crypto": "^12.3.1", - "rxjs": "^7.8.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/types-augment": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-10.9.1.tgz", - "integrity": "sha512-OY9/jTMFRFqYdkUnfcGwqMLC64A0Q25bjvCuVQCVjsPFKE3wl0Kt5rNT01eV2UmLXrR6fY0xWbR2w80bLA7CIQ==", - "dependencies": { - "@polkadot/types": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/types-codec": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-10.9.1.tgz", - "integrity": "sha512-mJ5OegKGraY1FLvEa8FopRCr3pQrhDkcn5RNOjmgJQozENVeRaxhk0NwxYz7IojFvSDnKnc6lNQfKaaSe5pLHg==", - "dependencies": { - "@polkadot/util": "^12.3.1", - "@polkadot/x-bigint": "^12.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/types-create": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-10.9.1.tgz", - "integrity": "sha512-OVz50MGTTuiuVnRP/zAx4CTuLioc0hsiwNwqN2lNhmIJGtnQ4Vy/7mQRsIWehiYz6g0Vzzm5B3qWkTXO1NSN5w==", - "dependencies": { - "@polkadot/types-codec": "10.9.1", - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/types-known": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-10.9.1.tgz", - "integrity": "sha512-zCMVWc4pJtkbMFPu72bD4IhvV/gkHXPX3C5uu92WdmCfnn0vEIEsMKWlVXVVvQQZKAqvs/awpqIfrUtEViOGEA==", - "dependencies": { - "@polkadot/networks": "^12.3.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/types-create": "10.9.1", - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/types-support": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-10.9.1.tgz", - "integrity": "sha512-XsieuLDsszvMZQlleacQBfx07i/JkwQV/UxH9q8Hz7Okmaz9pEVEW1h3ka2/cPuC7a4l32JhaORBUYshBZNdJg==", - "dependencies": { - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/util": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-12.3.2.tgz", - "integrity": "sha512-y/JShcGyOamCUiSIg++XZuLHt1ktSKBaSH2K5Nw5NXlgP0+7am+GZzqPB8fQ4qhYLruEOv+YRiz0GC1Zr9S+wg==", - "dependencies": { - "@polkadot/x-bigint": "12.3.2", - "@polkadot/x-global": "12.3.2", - "@polkadot/x-textdecoder": "12.3.2", - "@polkadot/x-textencoder": "12.3.2", - "@types/bn.js": "^5.1.1", - "bn.js": "^5.2.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/util-crypto": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-12.3.2.tgz", - "integrity": "sha512-pTpx+YxolY0BDT4RcGmgeKbHHD/dI6Ll9xRsqmVdIjpcVVY20uDNTyXs81ZNtfKgyod1y9JQkfNv2Dz9iEpTkQ==", - "dependencies": { - "@noble/curves": "1.1.0", - "@noble/hashes": "1.3.1", - "@polkadot/networks": "12.3.2", - "@polkadot/util": "12.3.2", - "@polkadot/wasm-crypto": "^7.2.1", - "@polkadot/wasm-util": "^7.2.1", - "@polkadot/x-bigint": "12.3.2", - "@polkadot/x-randomvalues": "12.3.2", - "@scure/base": "1.1.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "12.3.2" - } - }, - "node_modules/@polkadot/wasm-bridge": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.2.1.tgz", - "integrity": "sha512-uV/LHREDBGBbHrrv7HTki+Klw0PYZzFomagFWII4lp6Toj/VCvRh5WMzooVC+g/XsBGosAwrvBhoModabyHx+A==", - "dependencies": { - "@polkadot/wasm-util": "7.2.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "*", - "@polkadot/x-randomvalues": "*" - } - }, - "node_modules/@polkadot/wasm-crypto": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.2.1.tgz", - "integrity": "sha512-SA2+33S9TAwGhniKgztVN6pxUKpGfN4Tre/eUZGUfpgRkT92wIUT2GpGWQE+fCCqGQgADrNiBcwt6XwdPqMQ4Q==", - "dependencies": { - "@polkadot/wasm-bridge": "7.2.1", - "@polkadot/wasm-crypto-asmjs": "7.2.1", - "@polkadot/wasm-crypto-init": "7.2.1", - "@polkadot/wasm-crypto-wasm": "7.2.1", - "@polkadot/wasm-util": "7.2.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "*", - "@polkadot/x-randomvalues": "*" - } - }, - "node_modules/@polkadot/wasm-crypto-asmjs": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.2.1.tgz", - "integrity": "sha512-z/d21bmxyVfkzGsKef/FWswKX02x5lK97f4NPBZ9XBeiFkmzlXhdSnu58/+b1sKsRAGdW/Rn/rTNRDhW0GqCAg==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "*" - } - }, - "node_modules/@polkadot/wasm-crypto-init": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.2.1.tgz", - "integrity": "sha512-GcEXtwN9LcSf32V9zSaYjHImFw16hCyo2Xzg4GLLDPPeaAAfbFr2oQMgwyDbvBrBjLKHVHjsPZyGhXae831amw==", - "dependencies": { - "@polkadot/wasm-bridge": "7.2.1", - "@polkadot/wasm-crypto-asmjs": "7.2.1", - "@polkadot/wasm-crypto-wasm": "7.2.1", - "@polkadot/wasm-util": "7.2.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "*", - "@polkadot/x-randomvalues": "*" - } - }, - "node_modules/@polkadot/wasm-crypto-wasm": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.2.1.tgz", - "integrity": "sha512-DqyXE4rSD0CVlLIw88B58+HHNyrvm+JAnYyuEDYZwCvzUWOCNos/DDg9wi/K39VAIsCCKDmwKqkkfIofuOj/lA==", - "dependencies": { - "@polkadot/wasm-util": "7.2.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "*" - } - }, - "node_modules/@polkadot/wasm-util": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.2.1.tgz", - "integrity": "sha512-FBSn/3aYJzhN0sYAYhHB8y9JL8mVgxLy4M1kUXYbyo+8GLRQEN5rns8Vcb8TAlIzBWgVTOOptYBvxo0oj0h7Og==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "*" - } - }, - "node_modules/@polkadot/x-bigint": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-12.3.2.tgz", - "integrity": "sha512-JLqLgfGXe/x+hZJETd5ZqfpVsbwyMsH5Nn1Q20ineMMjXN/ig+kVR8Mc15LXBMuw4g7LldFW6UUrotWnuMI8Yw==", - "dependencies": { - "@polkadot/x-global": "12.3.2", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/x-fetch": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-12.3.2.tgz", - "integrity": "sha512-3IEuZ5S+RI/t33NsdPLIIa5COfDCfpUW2sbaByEczn75aD1jLqJZSEDwiBniJ2osyNd4uUxBf6e5jw7LAZeZJg==", - "dependencies": { - "@polkadot/x-global": "12.3.2", - "node-fetch": "^3.3.1", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/x-global": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-12.3.2.tgz", - "integrity": "sha512-yVZq6oIegjlyh5rUZiTklgu+fL+W/DG1ypEa02683tUCB3avV5cA3PAHKptMSlb6FpweHu37lKKrqfAWrraDxg==", - "dependencies": { - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/x-randomvalues": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-12.3.2.tgz", - "integrity": "sha512-ywjIs8CWpvOGmq+3cGCNPOHxAjPHdBUiXyDccftx5BRVdmtbt36gK/V84bKr6Xs73FGu0jprUAOSRRsLZX/3dg==", - "dependencies": { - "@polkadot/x-global": "12.3.2", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "@polkadot/util": "12.3.2", - "@polkadot/wasm-util": "*" - } - }, - "node_modules/@polkadot/x-textdecoder": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-12.3.2.tgz", - "integrity": "sha512-lY5bfA5xArJRWEJlYOlQQMJeTjWD8s0yMhchirVgf5xj8Id9vPGeUoneH+VFDEwgXxrqBvDFJ4smN4T/r6a/fg==", - "dependencies": { - "@polkadot/x-global": "12.3.2", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/x-textencoder": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-12.3.2.tgz", - "integrity": "sha512-iP3qEBiHzBckQ9zeY7ZHRWuu7mCEg5SMpOugs6UODRk8sx6KHzGQYlghBbWLit0uppPDVE0ifEwZ2n73djJHWQ==", - "dependencies": { - "@polkadot/x-global": "12.3.2", - "tslib": "^2.5.3" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@polkadot/x-ws": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-12.3.2.tgz", - "integrity": "sha512-yM9Z64pLNlHpJE43+Xtr+iUXmYpFFY5u5hrke2PJt13O48H8f9Vb9cRaIh94appLyICoS0aekGhDkGH+MCspBA==", - "dependencies": { - "@polkadot/x-global": "12.3.2", - "tslib": "^2.5.3", - "ws": "^8.13.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@scure/base": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", - "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ] - }, - "node_modules/@substrate/connect": { - "version": "0.7.26", - "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.7.26.tgz", - "integrity": "sha512-uuGSiroGuKWj1+38n1kY5HReer5iL9bRwPCzuoLtqAOmI1fGI0hsSI2LlNQMAbfRgr7VRHXOk5MTuQf5ulsFRw==", - "optional": true, - "dependencies": { - "@substrate/connect-extension-protocol": "^1.0.1", - "eventemitter3": "^4.0.7", - "smoldot": "1.0.4" - } - }, - "node_modules/@substrate/connect-extension-protocol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-1.0.1.tgz", - "integrity": "sha512-161JhCC1csjH3GE5mPLEd7HbWtwNSPJBg3p1Ksz9SFlTzj/bgEwudiRN2y5i0MoLGCIJRYKyKGMxVnd29PzNjg==", - "optional": true - }, - "node_modules/@substrate/connect/node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "optional": true - }, - "node_modules/@substrate/ss58-registry": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.40.0.tgz", - "integrity": "sha512-QuU2nBql3J4KCnOWtWDw4n1K4JU0T79j54ZZvm/9nhsX6AIar13FyhsaBfs6QkJ2ixTQAnd7TocJIoJRWbqMZA==" - }, - "node_modules/@types/bn.js": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", - "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "20.3.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz", - "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==" - }, - "node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" - }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" - }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/mock-socket": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.2.1.tgz", - "integrity": "sha512-aw9F9T9G2zpGipLLhSNh6ZpgUyUl4frcVmRN08uE1NWPWg43Wx6+sGPDbQ7E5iFZZDJW5b5bypMeAEHqTbIFag==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/nock": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.3.1.tgz", - "integrity": "sha512-vHnopocZuI93p2ccivFyGuUfzjq2fxNyNurp7816mlT5V5HF4SzXu8lvLrVzBbNqzs+ODooZ6OksuSUNM7Njkw==", - "dependencies": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.21", - "propagate": "^2.0.0" - }, - "engines": { - "node": ">= 10.13" - } - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz", - "integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/pako": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", - "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", - "optional": true - }, - "node_modules/propagate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/smoldot": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-1.0.4.tgz", - "integrity": "sha512-N3TazI1C4GGrseFH/piWyZCCCRJTRx2QhDfrUKRT4SzILlW5m8ayZ3QTKICcz1C/536T9cbHHJyP7afxI6Mi1A==", - "optional": true, - "dependencies": { - "pako": "^2.0.4", - "ws": "^8.8.1" - } - }, - "node_modules/tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" - }, - "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - } - }, - "dependencies": { - "@noble/curves": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", - "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", - "requires": { - "@noble/hashes": "1.3.1" - } - }, - "@noble/hashes": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", - "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" - }, - "@polkadot/api": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-10.9.1.tgz", - "integrity": "sha512-ND/2UqZBWvtt4PfV03OStTKg0mxmPk4UpMAgJKutdgsz/wP9CYJ1KbjwFgPNekL9JnzbKQsWyQNPVrcw7kQk8A==", - "requires": { - "@polkadot/api-augment": "10.9.1", - "@polkadot/api-base": "10.9.1", - "@polkadot/api-derive": "10.9.1", - "@polkadot/keyring": "^12.3.1", - "@polkadot/rpc-augment": "10.9.1", - "@polkadot/rpc-core": "10.9.1", - "@polkadot/rpc-provider": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-augment": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/types-create": "10.9.1", - "@polkadot/types-known": "10.9.1", - "@polkadot/util": "^12.3.1", - "@polkadot/util-crypto": "^12.3.1", - "eventemitter3": "^5.0.1", - "rxjs": "^7.8.1", - "tslib": "^2.5.3" - } - }, - "@polkadot/api-augment": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-10.9.1.tgz", - "integrity": "sha512-kRZZvCFVcN4hAH4dJ+Qzfdy27/4EEq3oLDf3ihj0LTVrAezSWcKPGE3EVFy+Mn6Lo4SUc7RVyoKvIUhSk2l4Dg==", - "requires": { - "@polkadot/api-base": "10.9.1", - "@polkadot/rpc-augment": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-augment": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - } - }, - "@polkadot/api-base": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-10.9.1.tgz", - "integrity": "sha512-Q3m2KzlceMK2kX8bhnUZWk3RT6emmijeeFZZQgCePpEcrSeNjnqG4qjuTPgkveaOkUT8MAoDc5Avuzcc2jlW9g==", - "requires": { - "@polkadot/rpc-core": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/util": "^12.3.1", - "rxjs": "^7.8.1", - "tslib": "^2.5.3" - } - }, - "@polkadot/api-derive": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-10.9.1.tgz", - "integrity": "sha512-mRud1UZCFIc4Z63qAoGSIHh/foyUYADfy1RQYCmPpeFKfIdCIrHpd7xFdJXTOMYOS0BwlM6u4qli/ZT4XigezQ==", - "requires": { - "@polkadot/api": "10.9.1", - "@polkadot/api-augment": "10.9.1", - "@polkadot/api-base": "10.9.1", - "@polkadot/rpc-core": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/util": "^12.3.1", - "@polkadot/util-crypto": "^12.3.1", - "rxjs": "^7.8.1", - "tslib": "^2.5.3" - } - }, - "@polkadot/keyring": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-12.3.2.tgz", - "integrity": "sha512-NTdtDeI0DP9l/45hXynNABeP5VB8piw5YR+CbUxK2e36xpJWVXwbcOepzslg5ghE9rs8UKJb30Z/HqTU4sBY0Q==", - "requires": { - "@polkadot/util": "12.3.2", - "@polkadot/util-crypto": "12.3.2", - "tslib": "^2.5.3" - } - }, - "@polkadot/networks": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-12.3.2.tgz", - "integrity": "sha512-uCkyybKoeEm1daKr0uT/9oNDHDDzCy2/ZdVl346hQqfdR1Ct3BaxMjxqvdmb5N8aCw0cBWSfgsxAYtw8ESmllQ==", - "requires": { - "@polkadot/util": "12.3.2", - "@substrate/ss58-registry": "^1.40.0", - "tslib": "^2.5.3" - } - }, - "@polkadot/rpc-augment": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-10.9.1.tgz", - "integrity": "sha512-MaLHkNlyqN20ZRYr6uNd1BZr1OsrnX9qLAmsl0mcrri1vPGRH6VHjfFH1RBLkikpWD82v17g0l2hLwdV1ZHMcw==", - "requires": { - "@polkadot/rpc-core": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - } - }, - "@polkadot/rpc-core": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-10.9.1.tgz", - "integrity": "sha512-ZtA8B8SfXSAwVkBlCcKRHw0eSM7ec/sbiNOM5GasXPeRujUgT7lOwSH2GbUZSqe9RfRDMp6DvO9c2JoGc3LLWw==", - "requires": { - "@polkadot/rpc-augment": "10.9.1", - "@polkadot/rpc-provider": "10.9.1", - "@polkadot/types": "10.9.1", - "@polkadot/util": "^12.3.1", - "rxjs": "^7.8.1", - "tslib": "^2.5.3" - } - }, - "@polkadot/rpc-provider": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-10.9.1.tgz", - "integrity": "sha512-4QzT2QzD+320+eT6b79sGAA85Tt3Bb8fQvse4r5Mom2iiBd2SO81vOhxSAOaIe4GUsw25VzFJmsbe7+OObItdg==", - "requires": { - "@polkadot/keyring": "^12.3.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-support": "10.9.1", - "@polkadot/util": "^12.3.1", - "@polkadot/util-crypto": "^12.3.1", - "@polkadot/x-fetch": "^12.3.1", - "@polkadot/x-global": "^12.3.1", - "@polkadot/x-ws": "^12.3.1", - "@substrate/connect": "0.7.26", - "eventemitter3": "^5.0.1", - "mock-socket": "^9.2.1", - "nock": "^13.3.1", - "tslib": "^2.5.3" - } - }, - "@polkadot/types": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-10.9.1.tgz", - "integrity": "sha512-AG33i2ZGGfq7u+5rkAdGrXAQHHl844/Yv+junH5ZzX69xiCoWO1bH/yzDUNBdpki2GlACWvF9nLYh3F2tVF93w==", - "requires": { - "@polkadot/keyring": "^12.3.1", - "@polkadot/types-augment": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/types-create": "10.9.1", - "@polkadot/util": "^12.3.1", - "@polkadot/util-crypto": "^12.3.1", - "rxjs": "^7.8.1", - "tslib": "^2.5.3" - } - }, - "@polkadot/types-augment": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-10.9.1.tgz", - "integrity": "sha512-OY9/jTMFRFqYdkUnfcGwqMLC64A0Q25bjvCuVQCVjsPFKE3wl0Kt5rNT01eV2UmLXrR6fY0xWbR2w80bLA7CIQ==", - "requires": { - "@polkadot/types": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - } - }, - "@polkadot/types-codec": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-10.9.1.tgz", - "integrity": "sha512-mJ5OegKGraY1FLvEa8FopRCr3pQrhDkcn5RNOjmgJQozENVeRaxhk0NwxYz7IojFvSDnKnc6lNQfKaaSe5pLHg==", - "requires": { - "@polkadot/util": "^12.3.1", - "@polkadot/x-bigint": "^12.3.1", - "tslib": "^2.5.3" - } - }, - "@polkadot/types-create": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-10.9.1.tgz", - "integrity": "sha512-OVz50MGTTuiuVnRP/zAx4CTuLioc0hsiwNwqN2lNhmIJGtnQ4Vy/7mQRsIWehiYz6g0Vzzm5B3qWkTXO1NSN5w==", - "requires": { - "@polkadot/types-codec": "10.9.1", - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - } - }, - "@polkadot/types-known": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-10.9.1.tgz", - "integrity": "sha512-zCMVWc4pJtkbMFPu72bD4IhvV/gkHXPX3C5uu92WdmCfnn0vEIEsMKWlVXVVvQQZKAqvs/awpqIfrUtEViOGEA==", - "requires": { - "@polkadot/networks": "^12.3.1", - "@polkadot/types": "10.9.1", - "@polkadot/types-codec": "10.9.1", - "@polkadot/types-create": "10.9.1", - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - } - }, - "@polkadot/types-support": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-10.9.1.tgz", - "integrity": "sha512-XsieuLDsszvMZQlleacQBfx07i/JkwQV/UxH9q8Hz7Okmaz9pEVEW1h3ka2/cPuC7a4l32JhaORBUYshBZNdJg==", - "requires": { - "@polkadot/util": "^12.3.1", - "tslib": "^2.5.3" - } - }, - "@polkadot/util": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-12.3.2.tgz", - "integrity": "sha512-y/JShcGyOamCUiSIg++XZuLHt1ktSKBaSH2K5Nw5NXlgP0+7am+GZzqPB8fQ4qhYLruEOv+YRiz0GC1Zr9S+wg==", - "requires": { - "@polkadot/x-bigint": "12.3.2", - "@polkadot/x-global": "12.3.2", - "@polkadot/x-textdecoder": "12.3.2", - "@polkadot/x-textencoder": "12.3.2", - "@types/bn.js": "^5.1.1", - "bn.js": "^5.2.1", - "tslib": "^2.5.3" - } - }, - "@polkadot/util-crypto": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-12.3.2.tgz", - "integrity": "sha512-pTpx+YxolY0BDT4RcGmgeKbHHD/dI6Ll9xRsqmVdIjpcVVY20uDNTyXs81ZNtfKgyod1y9JQkfNv2Dz9iEpTkQ==", - "requires": { - "@noble/curves": "1.1.0", - "@noble/hashes": "1.3.1", - "@polkadot/networks": "12.3.2", - "@polkadot/util": "12.3.2", - "@polkadot/wasm-crypto": "^7.2.1", - "@polkadot/wasm-util": "^7.2.1", - "@polkadot/x-bigint": "12.3.2", - "@polkadot/x-randomvalues": "12.3.2", - "@scure/base": "1.1.1", - "tslib": "^2.5.3" - } - }, - "@polkadot/wasm-bridge": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.2.1.tgz", - "integrity": "sha512-uV/LHREDBGBbHrrv7HTki+Klw0PYZzFomagFWII4lp6Toj/VCvRh5WMzooVC+g/XsBGosAwrvBhoModabyHx+A==", - "requires": { - "@polkadot/wasm-util": "7.2.1", - "tslib": "^2.5.0" - } - }, - "@polkadot/wasm-crypto": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.2.1.tgz", - "integrity": "sha512-SA2+33S9TAwGhniKgztVN6pxUKpGfN4Tre/eUZGUfpgRkT92wIUT2GpGWQE+fCCqGQgADrNiBcwt6XwdPqMQ4Q==", - "requires": { - "@polkadot/wasm-bridge": "7.2.1", - "@polkadot/wasm-crypto-asmjs": "7.2.1", - "@polkadot/wasm-crypto-init": "7.2.1", - "@polkadot/wasm-crypto-wasm": "7.2.1", - "@polkadot/wasm-util": "7.2.1", - "tslib": "^2.5.0" - } - }, - "@polkadot/wasm-crypto-asmjs": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.2.1.tgz", - "integrity": "sha512-z/d21bmxyVfkzGsKef/FWswKX02x5lK97f4NPBZ9XBeiFkmzlXhdSnu58/+b1sKsRAGdW/Rn/rTNRDhW0GqCAg==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@polkadot/wasm-crypto-init": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.2.1.tgz", - "integrity": "sha512-GcEXtwN9LcSf32V9zSaYjHImFw16hCyo2Xzg4GLLDPPeaAAfbFr2oQMgwyDbvBrBjLKHVHjsPZyGhXae831amw==", - "requires": { - "@polkadot/wasm-bridge": "7.2.1", - "@polkadot/wasm-crypto-asmjs": "7.2.1", - "@polkadot/wasm-crypto-wasm": "7.2.1", - "@polkadot/wasm-util": "7.2.1", - "tslib": "^2.5.0" - } - }, - "@polkadot/wasm-crypto-wasm": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.2.1.tgz", - "integrity": "sha512-DqyXE4rSD0CVlLIw88B58+HHNyrvm+JAnYyuEDYZwCvzUWOCNos/DDg9wi/K39VAIsCCKDmwKqkkfIofuOj/lA==", - "requires": { - "@polkadot/wasm-util": "7.2.1", - "tslib": "^2.5.0" - } - }, - "@polkadot/wasm-util": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.2.1.tgz", - "integrity": "sha512-FBSn/3aYJzhN0sYAYhHB8y9JL8mVgxLy4M1kUXYbyo+8GLRQEN5rns8Vcb8TAlIzBWgVTOOptYBvxo0oj0h7Og==", - "requires": { - "tslib": "^2.5.0" - } - }, - "@polkadot/x-bigint": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-12.3.2.tgz", - "integrity": "sha512-JLqLgfGXe/x+hZJETd5ZqfpVsbwyMsH5Nn1Q20ineMMjXN/ig+kVR8Mc15LXBMuw4g7LldFW6UUrotWnuMI8Yw==", - "requires": { - "@polkadot/x-global": "12.3.2", - "tslib": "^2.5.3" - } - }, - "@polkadot/x-fetch": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-12.3.2.tgz", - "integrity": "sha512-3IEuZ5S+RI/t33NsdPLIIa5COfDCfpUW2sbaByEczn75aD1jLqJZSEDwiBniJ2osyNd4uUxBf6e5jw7LAZeZJg==", - "requires": { - "@polkadot/x-global": "12.3.2", - "node-fetch": "^3.3.1", - "tslib": "^2.5.3" - } - }, - "@polkadot/x-global": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-12.3.2.tgz", - "integrity": "sha512-yVZq6oIegjlyh5rUZiTklgu+fL+W/DG1ypEa02683tUCB3avV5cA3PAHKptMSlb6FpweHu37lKKrqfAWrraDxg==", - "requires": { - "tslib": "^2.5.3" - } - }, - "@polkadot/x-randomvalues": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-12.3.2.tgz", - "integrity": "sha512-ywjIs8CWpvOGmq+3cGCNPOHxAjPHdBUiXyDccftx5BRVdmtbt36gK/V84bKr6Xs73FGu0jprUAOSRRsLZX/3dg==", - "requires": { - "@polkadot/x-global": "12.3.2", - "tslib": "^2.5.3" - } - }, - "@polkadot/x-textdecoder": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-12.3.2.tgz", - "integrity": "sha512-lY5bfA5xArJRWEJlYOlQQMJeTjWD8s0yMhchirVgf5xj8Id9vPGeUoneH+VFDEwgXxrqBvDFJ4smN4T/r6a/fg==", - "requires": { - "@polkadot/x-global": "12.3.2", - "tslib": "^2.5.3" - } - }, - "@polkadot/x-textencoder": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-12.3.2.tgz", - "integrity": "sha512-iP3qEBiHzBckQ9zeY7ZHRWuu7mCEg5SMpOugs6UODRk8sx6KHzGQYlghBbWLit0uppPDVE0ifEwZ2n73djJHWQ==", - "requires": { - "@polkadot/x-global": "12.3.2", - "tslib": "^2.5.3" - } - }, - "@polkadot/x-ws": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-12.3.2.tgz", - "integrity": "sha512-yM9Z64pLNlHpJE43+Xtr+iUXmYpFFY5u5hrke2PJt13O48H8f9Vb9cRaIh94appLyICoS0aekGhDkGH+MCspBA==", - "requires": { - "@polkadot/x-global": "12.3.2", - "tslib": "^2.5.3", - "ws": "^8.13.0" - } - }, - "@scure/base": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz", - "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==" - }, - "@substrate/connect": { - "version": "0.7.26", - "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.7.26.tgz", - "integrity": "sha512-uuGSiroGuKWj1+38n1kY5HReer5iL9bRwPCzuoLtqAOmI1fGI0hsSI2LlNQMAbfRgr7VRHXOk5MTuQf5ulsFRw==", - "optional": true, - "requires": { - "@substrate/connect-extension-protocol": "^1.0.1", - "eventemitter3": "^4.0.7", - "smoldot": "1.0.4" - }, - "dependencies": { - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "optional": true - } - } - }, - "@substrate/connect-extension-protocol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-1.0.1.tgz", - "integrity": "sha512-161JhCC1csjH3GE5mPLEd7HbWtwNSPJBg3p1Ksz9SFlTzj/bgEwudiRN2y5i0MoLGCIJRYKyKGMxVnd29PzNjg==", - "optional": true - }, - "@substrate/ss58-registry": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.40.0.tgz", - "integrity": "sha512-QuU2nBql3J4KCnOWtWDw4n1K4JU0T79j54ZZvm/9nhsX6AIar13FyhsaBfs6QkJ2ixTQAnd7TocJIoJRWbqMZA==" - }, - "@types/bn.js": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", - "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", - "requires": { - "@types/node": "*" - } - }, - "@types/node": { - "version": "20.3.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz", - "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==" - }, - "bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" - }, - "data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==" - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" - }, - "fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "requires": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - } - }, - "formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "requires": { - "fetch-blob": "^3.1.2" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "mock-socket": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.2.1.tgz", - "integrity": "sha512-aw9F9T9G2zpGipLLhSNh6ZpgUyUl4frcVmRN08uE1NWPWg43Wx6+sGPDbQ7E5iFZZDJW5b5bypMeAEHqTbIFag==" - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "nock": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.3.1.tgz", - "integrity": "sha512-vHnopocZuI93p2ccivFyGuUfzjq2fxNyNurp7816mlT5V5HF4SzXu8lvLrVzBbNqzs+ODooZ6OksuSUNM7Njkw==", - "requires": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.21", - "propagate": "^2.0.0" - } - }, - "node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" - }, - "node-fetch": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz", - "integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==", - "requires": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - } - }, - "pako": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", - "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", - "optional": true - }, - "propagate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==" - }, - "rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "requires": { - "tslib": "^2.1.0" - } - }, - "smoldot": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-1.0.4.tgz", - "integrity": "sha512-N3TazI1C4GGrseFH/piWyZCCCRJTRx2QhDfrUKRT4SzILlW5m8ayZ3QTKICcz1C/536T9cbHHJyP7afxI6Mi1A==", - "optional": true, - "requires": { - "pako": "^2.0.4", - "ws": "^8.8.1" - } - }, - "tslib": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz", - "integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" - }, - "web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==" - }, - "ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "requires": {} - } - } -} diff --git a/tee-worker/bitacross/scripts/test_transfer/package.json b/tee-worker/bitacross/scripts/test_transfer/package.json deleted file mode 100644 index a3e2b769b8..0000000000 --- a/tee-worker/bitacross/scripts/test_transfer/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "test_transfer", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "dependencies": { - "@polkadot/api": "^10.9.1", - "@polkadot/keyring": "^12.3.2", - "@polkadot/util-crypto": "^12.3.2" - } -} diff --git a/tee-worker/bitacross/scripts/test_transfer/transfer.js b/tee-worker/bitacross/scripts/test_transfer/transfer.js deleted file mode 100644 index ed0801fb68..0000000000 --- a/tee-worker/bitacross/scripts/test_transfer/transfer.js +++ /dev/null @@ -1,53 +0,0 @@ -// Import the API & Provider and some utility functions -const { ApiPromise } = require('@polkadot/api'); - -const { Keyring } = require('@polkadot/keyring'); - -// Utility function for random values -const { randomAsU8a } = require('@polkadot/util-crypto'); - -// Some constants we are using in this sample -const AMOUNT = 1000000000000; - -async function main () { - // Create the API and wait until ready - const api = await ApiPromise.create(); - - // Create an instance of a testing keyring - const keyring = new Keyring({ type: 'sr25519', ss58Format: 42 }); - const alice = keyring.addFromUri('//Alice'); - - // Access the publicKey and address - const { publicKey, address } = alice; - - console.log('Alice Public Key:', publicKey); - console.log('Alice Address:', address); - - const { nonce, data: balance } = await api.query.system.account(publicKey); - - // Create a new random recipient - const recipient = keyring.addFromSeed(randomAsU8a(32)).address; - - console.log('Sending', AMOUNT, 'from', address, 'who has a balance of', balance.free, 'to', recipient, 'with nonce', nonce.toString()); - - api.tx.balances - .transferKeepAlive(recipient, AMOUNT) - .signAndSend(alice, { nonce }, ({ events = [], status }) => { - console.log('Transaction status:', status.type); - - if (status.isInBlock) { - console.log('Included at block hash', status.asInBlock.toHex()); - console.log('Events:'); - - events.forEach(({ event: { data, method, section }, phase }) => { - console.log('\t', phase.toString(), `: ${section}.${method}`, data.toString()); - }); - } else if (status.isFinalized) { - console.log('Finalized block hash', status.asFinalized.toHex()); - - process.exit(0); - } - }); -} - -main().catch(console.error); diff --git a/tee-worker/bitacross/service/Cargo.toml b/tee-worker/bitacross/service/Cargo.toml deleted file mode 100644 index 28aa4f6ecf..0000000000 --- a/tee-worker/bitacross/service/Cargo.toml +++ /dev/null @@ -1,94 +0,0 @@ -[package] -name = 'bitacross-worker' -version = '0.1.0' -authors = ['Trust Computing GmbH ', 'Integritee AG '] -build = 'build.rs' -edition = '2021' - -[dependencies] -async-trait = "0.1.50" -base58 = "0.2" -clap = { version = "2.33", features = ["yaml"] } -codec = { package = "parity-scale-codec", workspace = true } -dirs = "3.0.2" -env_logger = { workspace = true } -futures = { workspace = true, features = ["std"] } -hex = { workspace = true, features = ["std"] } -humantime = "2.1" -jsonrpsee = { version = "0.2.0", features = ["client", "ws-server", "macros"] } -lazy_static = { workspace = true } -log = { workspace = true, features = ["std"] } -parking_lot = "0.12.1" -parse_duration = "2.1.1" -prometheus = { version = "0.13.0", features = ["process"], default-features = false } # Enabling std lead to protobuf dependency conflicts with substrate, and we don't need it. -rayon = "1.10.0" -regex = "1.9.5" -scale-info = { workspace = true } -serde = { workspace = true, features = ["std"] } -serde_derive = { workspace = true } -serde_json = { workspace = true, features = ["std"] } -thiserror = { workspace = true } -tokio = { version = "1.6.1", features = ["full"] } -url = "2.5.0" -warp = "=0.3.5" - -ipfs-api = "0.11.0" - -sgx_crypto_helper = { workspace = true, features = ["ucrypto_help"] } -sgx_types = { workspace = true } - -ita-parentchain-interface = { package = "bc-ita-parentchain-interface", path = "../app-libs/parentchain-interface" } -itc-parentchain = { package = "bc-itc-parentchain", path = "../core/parentchain/parentchain-crate" } -itc-rest-client = { workspace = true, features = ["std"] } -itc-rpc-client = { workspace = true } -itp-api-client-types = { workspace = true, features = ["std"] } -itp-enclave-api = { package = "bc-itp-enclave-api", path = "../core-primitives/enclave-api" } -itp-enclave-metrics = { workspace = true, features = ["std"] } -itp-node-api = { workspace = true, features = ["std"] } -itp-settings = { workspace = true } -itp-stf-interface = { workspace = true, features = ["std"] } -itp-storage = { workspace = true, features = ["std"] } -itp-time-utils = { workspace = true, features = ["std"] } -itp-types = { workspace = true, features = ["std"] } -itp-utils = { workspace = true, features = ["std"] } - -substrate-api-client = { workspace = true } - -frame-support = { workspace = true, features = ["std"] } -sp-consensus-grandpa = { workspace = true, features = ["std"] } -sp-core = { workspace = true, features = ["std", "full_crypto"] } -sp-keyring = { workspace = true } -sp-runtime = { workspace = true, features = ["std"] } - -# litentry -config = "0.13.3" -litentry-primitives = { workspace = true, features = ["std"] } - -[features] -default = [] -offchain-worker = ["itp-settings/offchain-worker"] -development = [ - "itp-settings/development", - "litentry-primitives/development", -] -dcap = [] -attesteer = ["dcap"] -# Must be enabled to build a binary and link it with the enclave successfully. -# This flag is set in the makefile. -# -# Must not be enabled to run cargo test without an sgx-sdk providing environment -# https://github.com/rust-lang/cargo/issues/2549. -# -# It has been chosen to not make this a default feature because this makes test execution -# more ergonomic as we can simply do `cargo test` on the whole workspace like this. -link-binary = [ - "itp-enclave-api/implement-ffi", -] - -[dev-dependencies] -# crates.io -anyhow = "1.0.40" -mockall = "0.11" -# local -itc-parentchain-test = { workspace = true, features = ["std"] } -itp-sgx-crypto = { workspace = true, features = ["std"] } diff --git a/tee-worker/bitacross/service/build.rs b/tee-worker/bitacross/service/build.rs deleted file mode 100644 index 1fb664ecc0..0000000000 --- a/tee-worker/bitacross/service/build.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (C) 2017-2018 Baidu, Inc. All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in -// the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Baidu, Inc., nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -fn main() { - // All the linker options are now defined in `itp-enclave-api-ffi` -} diff --git a/tee-worker/bitacross/service/src/account_funding.rs b/tee-worker/bitacross/service/src/account_funding.rs deleted file mode 100644 index 61a3e8780f..0000000000 --- a/tee-worker/bitacross/service/src/account_funding.rs +++ /dev/null @@ -1,182 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::error::{Error, ServiceResult}; -use codec::Encode; -use itp_node_api::api_client::{AccountApi, ParentchainApi}; -use itp_settings::worker::REGISTERING_FEE_FACTOR_FOR_INIT_FUNDS; -use itp_types::{ - parentchain::{AccountId, Balance, ParentchainId}, - Moment, -}; -use log::*; -use sp_core::{ - crypto::{AccountId32, Ss58Codec}, - Pair, -}; -use sp_keyring::AccountKeyring; -use sp_runtime::{MultiAddress, Saturating}; -use std::{thread, time::Duration}; -use substrate_api_client::{ - ac_compose_macros::compose_extrinsic, ac_primitives::Bytes, extrinsic::BalancesExtrinsics, - GetBalance, GetStorage, GetTransactionPayment, SubmitAndWatch, XtStatus, -}; - -const SGX_RA_PROOF_MAX_LEN: usize = 5000; -const MAX_URL_LEN: usize = 256; -/// Information about the enclave on-chain account. -pub trait EnclaveAccountInfo { - fn free_balance(&self) -> ServiceResult; -} - -pub struct EnclaveAccountInfoProvider { - node_api: ParentchainApi, - account_id: AccountId32, -} - -impl EnclaveAccountInfo for EnclaveAccountInfoProvider { - fn free_balance(&self) -> ServiceResult { - self.node_api.get_free_balance(&self.account_id).map_err(|e| e.into()) - } -} - -impl EnclaveAccountInfoProvider { - pub fn new(node_api: ParentchainApi, account_id: AccountId32) -> Self { - EnclaveAccountInfoProvider { node_api, account_id } - } -} - -/// evaluate if the enclave should have more funds and how much more -/// in --dev mode: let Alice pay for missing funds -/// in production mode: wait for manual transfer before continuing -pub fn setup_reasonable_account_funding( - api: &ParentchainApi, - accountid: &AccountId32, - parentchain_id: ParentchainId, - is_development_mode: bool, -) -> ServiceResult<()> { - loop { - let needed = estimate_funds_needed_to_run_for_a_while(api, accountid, parentchain_id)?; - let free = api.get_free_balance(accountid)?; - let missing_funds = needed.saturating_sub(free); - - if missing_funds < needed * 2 / 3 { - return Ok(()) - } - - if is_development_mode { - info!("[{:?}] Alice will grant {:?} to {:?}", parentchain_id, missing_funds, accountid); - bootstrap_funds_from_alice(api, accountid, missing_funds)?; - } else { - error!( - "[{:?}] Enclave account needs funding. please send at least {:?} to {:?}", - parentchain_id, missing_funds, accountid - ); - thread::sleep(Duration::from_secs(10)); - } - } -} - -fn estimate_funds_needed_to_run_for_a_while( - api: &ParentchainApi, - accountid: &AccountId32, - parentchain_id: ParentchainId, -) -> ServiceResult { - let existential_deposit = api.get_existential_deposit()?; - info!("[{:?}] Existential deposit is = {:?}", parentchain_id, existential_deposit); - - let mut min_required_funds: Balance = existential_deposit; - - let transfer_fee = estimate_transfer_fee(api)?; - info!("[{:?}] a single transfer costs {:?}", parentchain_id, transfer_fee); - min_required_funds += 1000 * transfer_fee; - - // TODO(Litentry P-628): shall we charge RA fee? - info!("[{:?}] not adding RA fees for now", parentchain_id); - - info!( - "[{:?}] we estimate the funding requirement for the primary validateer (worst case) to be {:?}", - parentchain_id, - min_required_funds - ); - Ok(min_required_funds) -} - -pub fn estimate_fee(api: &ParentchainApi, encoded_extrinsic: Vec) -> Result { - let reg_fee_details = api.get_fee_details(&encoded_extrinsic.into(), None)?; - match reg_fee_details { - Some(details) => match details.inclusion_fee { - Some(fee) => Ok(fee.inclusion_fee()), - None => Err(Error::Custom( - "Inclusion fee for the registration of the enclave is None!".into(), - )), - }, - None => - Err(Error::Custom("Fee Details for the registration of the enclave is None !".into())), - } -} - -/// Alice sends some funds to the account. only for dev chains testing -fn bootstrap_funds_from_alice( - api: &ParentchainApi, - accountid: &AccountId32, - funding_amount: u128, -) -> Result<(), Error> { - let alice = AccountKeyring::Alice.pair(); - let alice_acc = AccountId32::from(*alice.public().as_array_ref()); - - let alice_free = api.get_free_balance(&alice_acc)?; - info!(" Alice's free balance = {:?}", alice_free); - let nonce = api.get_account_next_index(&alice_acc)?; - info!(" Alice's Account Nonce is {}", nonce); - - if funding_amount > alice_free { - println!( - "funding amount is too high: please change EXISTENTIAL_DEPOSIT_FACTOR_FOR_INIT_FUNDS ({:?})", - funding_amount - ); - return Err(Error::ApplicationSetup) - } - - let mut alice_signer_api = api.clone(); - alice_signer_api.set_signer(alice.into()); - - println!("[+] send extrinsic: bootstrap funding Enclave from Alice's funds"); - let xt = alice_signer_api - .balance_transfer_allow_death(MultiAddress::Id(accountid.clone()), funding_amount); - let xt_report = alice_signer_api.submit_and_watch_extrinsic_until(xt, XtStatus::Finalized)?; - info!( - "[<] L1 extrinsic success. extrinsic hash: {:?} / status: {:?}", - xt_report.extrinsic_hash, xt_report.status - ); - // Verify funds have arrived. - let free_balance = alice_signer_api.get_free_balance(accountid); - trace!("TEE's NEW free balance = {:?}", free_balance); - - Ok(()) -} - -/// precise estimation of a single transfer fee -pub fn estimate_transfer_fee(api: &ParentchainApi) -> Result { - let encoded_xt: Bytes = api - .balance_transfer_allow_death(AccountId::from([0u8; 32]).into(), 1000000000000) - .encode() - .into(); - let tx_fee = api.get_fee_details(&encoded_xt, None).unwrap().unwrap().inclusion_fee.unwrap(); - let transfer_fee = tx_fee.base_fee + tx_fee.len_fee + tx_fee.adjusted_weight_fee; - Ok(transfer_fee) -} diff --git a/tee-worker/bitacross/service/src/cli.yml b/tee-worker/bitacross/service/src/cli.yml deleted file mode 100644 index f3764fbeef..0000000000 --- a/tee-worker/bitacross/service/src/cli.yml +++ /dev/null @@ -1,212 +0,0 @@ -name: "litentry-worker" -version: "0.0.1" -about: Worker using Intel SGX TEE for litentry parachain node -authors: "Trust Computing GmbH " - -# AppSettings can be defined as a list and are **not** ascii case sensitive -settings: - - ColoredHelp - - SubcommandRequired - -# All subcommands must be listed in the 'subcommand:' object, where the key to -# the list is the name of the subcommand, and all settings for that command are -# part of a Hash -args: - - node-url: - short: u - long: node-url - help: Set the url and the protocol of the RPC endpoint. - takes_value: true - default_value: "ws://127.0.0.1" - - node-port: - short: p - long: node-port - help: Set the port of the RPC endpoint. - takes_value: true - default_value: "9944" - - target-a-parentchain-rpc-url: - long: target-a-parentchain-rpc-url - help: Set the url and the protocol of an optional Target A parentchain RPC endpoint that contains your business logic specific pallets. - takes_value: true - required: false - - target-a-parentchain-rpc-port: - long: target-a-parentchain-rpc-port - help: Set the port of the optional Target A parentchain RPC endpoint. - takes_value: true - required: false - - target-b-parentchain-rpc-url: - long: target-b-parentchain-rpc-url - help: Set the url and the protocol of an optional Target B parentchain RPC endpoint that contains your business logic specific pallets. - takes_value: true - required: false - - target-b-parentchain-rpc-port: - long: target-b-parentchain-rpc-port - help: Set the port of the optional Target B parentchain RPC endpoint. - takes_value: true - required: false - - data-dir: - short: d - long: data-dir - help: Data dir where the worker stores it's keys and other data. - takes_value: true - - ws-external: - long: ws-external - help: Set this flag in case the worker should listen to external requests. - - mu-ra-port: - short: r - long: mu-ra-port - help: Set the websocket port to listen for mu-ra requests - takes_value: true - default_value: "3443" - - trusted-worker-port: - short: P - long: trusted-worker-port - help: Set the trusted websocket port of the worker, running directly in the enclave. - takes_value: true - default_value: "2000" - - untrusted-worker-port: - short: w - long: untrusted-worker-port - help: Set the untrusted websocket port of the worker - takes_value: true - default_value: "2001" - - trusted-external-address: - short: T - long: trusted-external-address - help: Set the trusted worker address to be advertised on the parentchain. If no port is given, the same as in `trusted-worker-port` will be used. - takes_value: true - required: false - - untrusted-external-address: - short: U - long: untrusted-external-address - help: Set the untrusted worker address to be retrieved by a trusted rpc call. If no port is given, the same as in `untrusted-worker-port` will be used. - takes_value: true - required: false - - mu-ra-external-address: - short: M - long: mu-ra-external-address - help: Set the mutual remote attestation worker address to be retrieved by a trusted rpc call. If no port is given, the same as in `mu-ra-port` will be used. - takes_value: true - required: false - - enable-metrics: - long: enable-metrics - help: Enable the metrics HTTP server to serve metrics - - metrics-port: - short: i - long: metrics-port - help: Set the port on which the metrics are served. - takes_value: true - default_value: "8787" - required: false - - untrusted-http-port: - short: h - long: untrusted-http-port - help: Set the port for the untrusted HTTP server - takes_value: true - required: false - - clean-reset: - long: clean-reset - short: c - help: Cleans and purges any previous state and key files and generates them anew before starting. - - parentchain-start-block: - long: parentchain-start-block - help: Set the parentchain block number to start syncing with - takes_value: true - required: false - default_value: "0" - - ceremony-commands-thread-count: - long: ceremony-commands-thread-count - help: Number of threads to spawn for ceremony commands handling - takes_value: true - default_value: "4" - required: false - - ceremony-events-thread-count: - long: ceremony-events-thread-count - help: Number of threads to spawn for ceremony events handling - takes_value: true - default_value: "20" - required: false - -subcommands: - - run: - about: Start the litentry-worker - args: - - skip-ra: - long: skip-ra - help: skip remote attestation. Set this flag if running enclave in SW mode - - shard: - required: false - index: 1 - help: shard identifier base58 encoded. Defines the state that this worker shall operate on. Default is mrenclave - - dev: - long: dev - short: d - help: Set this flag if running in development mode to bootstrap enclave account on parentchain via //Alice. - - request-state: - long: request-state - short: r - help: Run the worker and request key and state provisioning from another worker. - - request-state: - about: (Deprecated - TODO) join a shard by requesting key provisioning from another worker - args: - - shard: - long: shard - required: false - help: shard identifier base58 encoded. Defines the state that this worker shall operate on. Default is mrenclave - - skip-ra: - long: skip-ra - help: skip remote attestation. Set this flag if running enclave in SW mode - - shielding-key: - about: Get the public RSA3072 key from the TEE to be used to encrypt requests - - signing-key: - about: Get the public ed25519 key the TEE uses to sign messages and extrinsics - - dump-ra: - about: Perform RA and dump cert to disk - - wallet: - about: Print the bitcoin and ethereum custodian wallet key information, only works in non-prod - - init-wallet: - about: Init eth, btc, ton wallets from BTC_KEY, ETH_KEY, TON_KEY env variables, only works in non-prod - - mrenclave: - about: Dump mrenclave to stdout. base58 encoded. - - init-shard: - about: Initialize new shard (do this only if you run the first worker for that shard). if shard is not specified, the MRENCLAVE is used instead - args: - - shard: - required: false - multiple: true - index: 1 - help: shard identifier base58 encoded - - migrate-shard: - about: Migrate state from old shards to the new(current) shard, which is identical to mrenclave - - test: - about: Run tests involving the enclave - takes_value: true - args: - - all: - short: a - long: all - help: Run all tests (beware, all corrupts the counter state for some whatever reason...) - takes_value: false - - unit: - short: u - long: unit - help: Run unit tests - takes_value: false - - ecall: - short: e - long: ecall - help: Run enclave ecall tests - takes_value: false - - integration: - short: i - long: integration - help: Run integration tests - takes_value: false - - provisioning-server: - long: provisioning-server - help: Run TEE server for MU-RA key provisioning - takes_value: false - - provisioning-client: - long: provisioning-client - help: Run TEE client for MU-RA key provisioning - takes_value: false diff --git a/tee-worker/bitacross/service/src/config.rs b/tee-worker/bitacross/service/src/config.rs deleted file mode 100644 index 92b37cc262..0000000000 --- a/tee-worker/bitacross/service/src/config.rs +++ /dev/null @@ -1,617 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use clap::ArgMatches; -use itc_rest_client::rest_client::Url; -use itp_types::{parentchain::ParentchainId, ShardIdentifier}; -use parse_duration::parse; -use serde::{Deserialize, Serialize}; -use std::{ - fs, - path::{Path, PathBuf}, - time::Duration, -}; - -static DEFAULT_NODE_URL: &str = "ws://127.0.0.1"; -static DEFAULT_NODE_PORT: &str = "9944"; -static DEFAULT_TRUSTED_PORT: &str = "2000"; -static DEFAULT_UNTRUSTED_PORT: &str = "2001"; -static DEFAULT_MU_RA_PORT: &str = "3443"; -static DEFAULT_METRICS_PORT: &str = "8787"; -static DEFAULT_UNTRUSTED_HTTP_PORT: &str = "4545"; -static DEFAULT_PARENTCHAIN_START_BLOCK: &str = "0"; - -#[derive(Clone, Debug, PartialEq)] -pub struct Config { - pub litentry_rpc_url: String, - pub litentry_rpc_port: String, - pub target_a_parentchain_rpc_url: Option, - pub target_a_parentchain_rpc_port: Option, - pub target_b_parentchain_rpc_url: Option, - pub target_b_parentchain_rpc_port: Option, - pub worker_ip: String, - /// Trusted worker address that will be advertised on the parentchain. - pub trusted_external_worker_address: Option, - /// Port to directly communicate with the trusted tls server inside the enclave. - pub trusted_worker_port: String, - /// Untrusted worker address that will be returned by the dedicated trusted ws rpc call. - pub untrusted_external_worker_address: Option, - /// Port to the untrusted ws of the validateer. - pub untrusted_worker_port: String, - /// Mutual remote attestation address that will be returned by the dedicated trusted ws rpc call. - pub mu_ra_external_address: Option, - /// Port for mutual-remote attestation requests. - pub mu_ra_port: String, - /// Enable the metrics server - pub enable_metrics_server: bool, - /// Port for the metrics server - pub metrics_server_port: String, - /// Port for the untrusted HTTP server (e.g. for `is_initialized`) - pub untrusted_http_port: String, - /// Data directory used by all the services. - pub data_dir: PathBuf, - /// Config of the 'run' subcommand - pub run_config: Option, - - /// the parentchain block number to start syncing with - pub parentchain_start_block: String, - - /// Number of threads to spawn for ceremony commands handling - pub ceremony_commands_thread_count: u8, - - /// Number of threads to spawn for ceremony events handling - pub ceremony_events_thread_count: u8, -} - -#[allow(clippy::too_many_arguments)] -impl Config { - pub fn new( - litentry_rpc_url: String, - litentry_rpc_port: String, - target_a_parentchain_rpc_url: Option, - target_a_parentchain_rpc_port: Option, - target_b_parentchain_rpc_url: Option, - target_b_parentchain_rpc_port: Option, - worker_ip: String, - trusted_external_worker_address: Option, - trusted_worker_port: String, - untrusted_external_worker_address: Option, - untrusted_worker_port: String, - mu_ra_external_address: Option, - mu_ra_port: String, - enable_metrics_server: bool, - metrics_server_port: String, - untrusted_http_port: String, - data_dir: PathBuf, - run_config: Option, - parentchain_start_block: String, - ceremony_commands_thread_count: u8, - ceremony_events_thread_count: u8, - ) -> Self { - Self { - litentry_rpc_url, - litentry_rpc_port, - target_a_parentchain_rpc_url, - target_a_parentchain_rpc_port, - target_b_parentchain_rpc_url, - target_b_parentchain_rpc_port, - worker_ip, - trusted_external_worker_address, - trusted_worker_port, - untrusted_external_worker_address, - untrusted_worker_port, - mu_ra_external_address, - mu_ra_port, - enable_metrics_server, - metrics_server_port, - untrusted_http_port, - data_dir, - run_config, - parentchain_start_block, - ceremony_commands_thread_count, - ceremony_events_thread_count, - } - } - - /// Integritee RPC endpoint (including ws://). - pub fn litentry_rpc_endpoint(&self) -> String { - format!("{}:{}", self.litentry_rpc_url, self.litentry_rpc_port) - } - - pub fn target_a_parentchain_rpc_endpoint(&self) -> Option { - if self.target_a_parentchain_rpc_url.is_some() - && self.target_a_parentchain_rpc_port.is_some() - { - return Some(format!( - "{}:{}", - // Can be done better, but this code is obsolete anyhow with clap v4. - self.target_a_parentchain_rpc_url.clone().unwrap(), - self.target_a_parentchain_rpc_port.clone().unwrap() - )) - }; - - None - } - - pub fn target_b_parentchain_rpc_endpoint(&self) -> Option { - if self.target_b_parentchain_rpc_url.is_some() - && self.target_b_parentchain_rpc_port.is_some() - { - return Some(format!( - "{}:{}", - // Can be done better, but this code is obsolete anyhow with clap v4. - self.target_b_parentchain_rpc_url.clone().unwrap(), - self.target_b_parentchain_rpc_port.clone().unwrap() - )) - }; - - None - } - - pub fn trusted_worker_url_internal(&self) -> String { - // use the same scheme as `trusted_worker_url_external` - let url = url::Url::parse(self.trusted_worker_url_external().as_str()).unwrap(); - format!("{}://{}:{}", url.scheme(), self.worker_ip, self.trusted_worker_port) - } - - /// Returns the trusted worker url that should be addressed by external clients. - pub fn trusted_worker_url_external(&self) -> String { - match &self.trusted_external_worker_address { - Some(external_address) => ensure_ws_or_wss(external_address), - None => format!("wss://{}:{}", self.worker_ip, self.trusted_worker_port), // fallback to wss - } - } - - pub fn untrusted_worker_url(&self) -> String { - // use the same scheme as `untrusted_worker_url_external` - let url = url::Url::parse(self.untrusted_worker_url_external().as_str()).unwrap(); - format!("{}://{}:{}", url.scheme(), self.worker_ip, self.untrusted_worker_port) - } - - /// Returns the untrusted worker url that should be addressed by external clients. - pub fn untrusted_worker_url_external(&self) -> String { - match &self.untrusted_external_worker_address { - Some(external_address) => ensure_ws_or_wss(external_address), - None => format!("ws://{}:{}", self.worker_ip, self.untrusted_worker_port), // fallback to ws - } - } - - pub fn mu_ra_url(&self) -> String { - format!("{}:{}", self.worker_ip, self.mu_ra_port) - } - - /// Returns the mutual remote attestion worker url that should be addressed by external workers. - pub fn mu_ra_url_external(&self) -> String { - match &self.mu_ra_external_address { - Some(external_address) => external_address.to_string(), - None => format!("{}:{}", self.worker_ip, self.mu_ra_port), - } - } - - pub fn data_dir(&self) -> &Path { - self.data_dir.as_path() - } - - pub fn run_config(&self) -> &Option { - &self.run_config - } - - pub fn enable_metrics_server(&self) -> bool { - self.enable_metrics_server - } - - pub fn try_parse_metrics_server_port(&self) -> Option { - self.metrics_server_port.parse::().ok() - } - - pub fn try_parse_untrusted_http_server_port(&self) -> Option { - self.untrusted_http_port.parse::().ok() - } - - pub fn try_parse_parentchain_start_block(&self) -> Option { - self.parentchain_start_block.parse::().ok() - } -} - -impl From<&ArgMatches<'_>> for Config { - fn from(m: &ArgMatches<'_>) -> Self { - let trusted_port = m.value_of("trusted-worker-port").unwrap_or(DEFAULT_TRUSTED_PORT); - let untrusted_port = m.value_of("untrusted-worker-port").unwrap_or(DEFAULT_UNTRUSTED_PORT); - let mu_ra_port = m.value_of("mu-ra-port").unwrap_or(DEFAULT_MU_RA_PORT); - let is_metrics_server_enabled = m.is_present("enable-metrics"); - let metrics_server_port = m.value_of("metrics-port").unwrap_or(DEFAULT_METRICS_PORT); - let untrusted_http_port = - m.value_of("untrusted-http-port").unwrap_or(DEFAULT_UNTRUSTED_HTTP_PORT); - - let data_dir = match m.value_of("data-dir") { - Some(d) => { - let p = PathBuf::from(d); - if !p.exists() { - log::info!("Creating new data-directory for the service {}.", p.display()); - fs::create_dir_all(p.as_path()).unwrap(); - } else { - log::info!("Starting service in existing directory {}.", p.display()); - } - p - }, - None => { - log::warn!("[Config] defaulting to data-dir = PWD because it was previous behaviour. This might change soon.\ - Please pass the data-dir explicitly to ensure nothing breaks in your setup."); - pwd() - }, - }; - - let run_config = m.subcommand_matches("run").map(RunConfig::from); - - let parentchain_start_block = - m.value_of("parentchain-start-block").unwrap_or(DEFAULT_PARENTCHAIN_START_BLOCK); - - let ceremony_commands_thread_count = - m.value_of("ceremony-commands-thread-count").unwrap_or("4").parse().unwrap(); - let ceremony_events_thread_count = - m.value_of("ceremony-events-thread-count").unwrap_or("20").parse().unwrap(); - - Self::new( - m.value_of("node-url").unwrap_or(DEFAULT_NODE_URL).into(), - m.value_of("node-port").unwrap_or(DEFAULT_NODE_PORT).into(), - m.value_of("target-a-parentchain-rpc-url").map(Into::into), - m.value_of("target-a-parentchain-rpc-port").map(Into::into), - m.value_of("target-b-parentchain-rpc-url").map(Into::into), - m.value_of("target-b-parentchain-rpc-port").map(Into::into), - if m.is_present("ws-external") { "0.0.0.0".into() } else { "127.0.0.1".into() }, - m.value_of("trusted-external-address") - .map(|url| add_port_if_necessary(url, trusted_port)), - trusted_port.to_string(), - m.value_of("untrusted-external-address") - .map(|url| add_port_if_necessary(url, untrusted_port)), - untrusted_port.to_string(), - m.value_of("mu-ra-external-address") - .map(|url| add_port_if_necessary(url, mu_ra_port)), - mu_ra_port.to_string(), - is_metrics_server_enabled, - metrics_server_port.to_string(), - untrusted_http_port.to_string(), - data_dir, - run_config, - parentchain_start_block.to_string(), - ceremony_commands_thread_count, - ceremony_events_thread_count, - ) - } -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct RunConfig { - /// Skip remote attestation. Set this flag if running enclave in SW mode - skip_ra: bool, - /// Set this flag if running in development mode to bootstrap enclave account on parentchain via //Alice. - dev: bool, - /// Shard identifier base58 encoded. Defines the shard that this worker operates on. Default is mrenclave. - shard: Option, - /// Marblerun's Prometheus endpoint base URL - marblerun_base_url: Option, - /// parentchain which should be used for shielding/unshielding the stf's native token - pub shielding_target: Option, -} - -impl RunConfig { - pub fn skip_ra(&self) -> bool { - self.skip_ra - } - - pub fn dev(&self) -> bool { - self.dev - } - - pub fn shard(&self) -> Option<&str> { - self.shard.as_deref() - } - - pub fn marblerun_base_url(&self) -> &str { - // This conflicts with the default port of a substrate node, but it is indeed the - // default port of marblerun too: - // https://github.com/edgelesssys/marblerun/blob/master/docs/docs/workflows/monitoring.md?plain=1#L26 - self.marblerun_base_url.as_deref().unwrap_or("http://localhost:9944") - } -} - -impl From<&ArgMatches<'_>> for RunConfig { - fn from(m: &ArgMatches<'_>) -> Self { - let skip_ra = m.is_present("skip-ra"); - let dev = m.is_present("dev"); - let shard = m.value_of("shard").map(|s| s.to_string()); - - let marblerun_base_url = m.value_of("marblerun-url").map(|i| { - Url::parse(i) - .unwrap_or_else(|e| panic!("marblerun-url parsing error: {:?}", e)) - .to_string() - }); - - let shielding_target = m.value_of("shielding-target").map(|i| match i { - "litentry" => ParentchainId::Litentry, - "target_a" => ParentchainId::TargetA, - "target_b" => ParentchainId::TargetB, - _ => panic!( - "failed to parse shielding-target: {} must be one of litentry|target_a|target_b", - i - ), - }); - - Self { skip_ra, dev, shard, marblerun_base_url, shielding_target } - } -} - -fn add_port_if_necessary(url: &str, port: &str) -> String { - // [Option("ws(s)"), ip, Option(port)] - match url.split(':').count() { - 3 => url.to_string(), - 2 => { - if url.contains("ws") { - // url is of format ws://127.0.0.1, no port added - format!("{}:{}", url, port) - } else { - // url is of format 127.0.0.1:4000, port was added - url.to_string() - } - }, - 1 => format!("{}:{}", url, port), - _ => panic!("Invalid worker url format in url input {:?}", url), - } -} - -fn ensure_ws_or_wss(url_str: &str) -> String { - let url = url::Url::parse(url_str) - .map_err(|e| { - println!("Parse url [{}] error: {}", url_str, e); - }) - .unwrap(); - - if url.scheme() != "wss" && url.scheme() != "ws" { - panic!("Parse url [{}] error: expect ws or wss, but get {}", url_str, url.scheme()); - } - url.into() -} - -pub fn pwd() -> PathBuf { - std::env::current_dir().expect("works on all supported platforms; qed.") -} - -#[cfg(test)] -mod test { - use super::*; - use std::{assert_matches::assert_matches, collections::HashMap}; - - #[test] - fn check_correct_config_assignment_for_empty_input() { - let empty_args = ArgMatches::default(); - let config = Config::from(&empty_args); - let expected_worker_ip = "127.0.0.1"; - - assert_eq!(config.litentry_rpc_url, DEFAULT_NODE_URL); - assert_eq!(config.litentry_rpc_port, DEFAULT_NODE_PORT); - assert_eq!(config.target_a_parentchain_rpc_url, None); - assert_eq!(config.target_a_parentchain_rpc_port, None); - assert_eq!(config.target_b_parentchain_rpc_url, None); - assert_eq!(config.target_b_parentchain_rpc_port, None); - assert_eq!(config.trusted_worker_port, DEFAULT_TRUSTED_PORT); - assert_eq!(config.untrusted_worker_port, DEFAULT_UNTRUSTED_PORT); - assert_eq!(config.mu_ra_port, DEFAULT_MU_RA_PORT); - assert_eq!(config.worker_ip, expected_worker_ip); - assert!(config.trusted_external_worker_address.is_none()); - assert!(config.untrusted_external_worker_address.is_none()); - assert!(config.mu_ra_external_address.is_none()); - assert!(!config.enable_metrics_server); - assert_eq!(config.untrusted_http_port, DEFAULT_UNTRUSTED_HTTP_PORT); - assert_eq!(config.data_dir, pwd()); - assert!(config.run_config.is_none()); - assert_eq!(config.parentchain_start_block, DEFAULT_PARENTCHAIN_START_BLOCK); - } - - #[test] - fn worker_ip_is_set_correctly_for_set_ws_external_flag() { - let expected_worker_ip = "0.0.0.0"; - - let mut args = ArgMatches::default(); - args.args = HashMap::from([("ws-external", Default::default())]); - let config = Config::from(&args); - - assert_eq!(config.worker_ip, expected_worker_ip); - } - - #[test] - fn check_correct_config_assignment_for_given_input() { - let node_ip = "ws://12.1.58.1"; - let node_port = "111111"; - let trusted_ext_addr = "wss://1.1.1.2:700"; - let trusted_port = "7119"; - let untrusted_ext_addr = "ws://1.723.3.1:11"; - let untrusted_port = "9119"; - let mu_ra_ext_addr = "1.1.3.1:1000"; - let mu_ra_port = "99"; - let untrusted_http_port = "4321"; - - let parentchain_start_block = "30"; - - let mut args = ArgMatches::default(); - args.args = HashMap::from([ - ("node-url", Default::default()), - ("node-port", Default::default()), - ("ws-external", Default::default()), - ("trusted-external-address", Default::default()), - ("untrusted-external-address", Default::default()), - ("mu-ra-external-address", Default::default()), - ("mu-ra-port", Default::default()), - ("untrusted-worker-port", Default::default()), - ("trusted-worker-port", Default::default()), - ("untrusted-http-port", Default::default()), - ("mock-server-port", Default::default()), - ("parentchain-start-block", Default::default()), - ]); - // Workaround because MatchedArg is private. - args.args.get_mut("node-url").unwrap().vals = vec![node_ip.into()]; - args.args.get_mut("node-port").unwrap().vals = vec![node_port.into()]; - args.args.get_mut("trusted-external-address").unwrap().vals = vec![trusted_ext_addr.into()]; - args.args.get_mut("untrusted-external-address").unwrap().vals = - vec![untrusted_ext_addr.into()]; - args.args.get_mut("mu-ra-external-address").unwrap().vals = vec![mu_ra_ext_addr.into()]; - args.args.get_mut("mu-ra-port").unwrap().vals = vec![mu_ra_port.into()]; - args.args.get_mut("untrusted-worker-port").unwrap().vals = vec![untrusted_port.into()]; - args.args.get_mut("trusted-worker-port").unwrap().vals = vec![trusted_port.into()]; - args.args.get_mut("untrusted-http-port").unwrap().vals = vec![untrusted_http_port.into()]; - args.args.get_mut("parentchain-start-block").unwrap().vals = - vec![parentchain_start_block.into()]; - - let config = Config::from(&args); - - assert_eq!(config.litentry_rpc_url, node_ip); - assert_eq!(config.litentry_rpc_port, node_port); - assert_eq!(config.trusted_worker_port, trusted_port); - assert_eq!(config.untrusted_worker_port, untrusted_port); - assert_eq!(config.mu_ra_port, mu_ra_port); - assert_eq!(config.trusted_external_worker_address, Some(trusted_ext_addr.to_string())); - assert_eq!(config.untrusted_external_worker_address, Some(untrusted_ext_addr.to_string())); - assert_eq!(config.mu_ra_external_address, Some(mu_ra_ext_addr.to_string())); - assert_eq!(config.untrusted_http_port, untrusted_http_port.to_string()); - assert_eq!(config.parentchain_start_block, parentchain_start_block.to_string()); - } - - #[test] - fn default_run_config_is_correct() { - let empty_args = ArgMatches::default(); - let run_config = RunConfig::from(&empty_args); - - assert_eq!(run_config.dev, false); - assert_eq!(run_config.skip_ra, false); - assert!(run_config.shard.is_none()); - } - - #[test] - fn run_config_parsing_works() { - let shard_identifier = "shard-identifier"; - - let mut args = ArgMatches::default(); - args.args = HashMap::from([ - ("dev", Default::default()), - ("skip-ra", Default::default()), - ("shard", Default::default()), - ]); - // Workaround because MatchedArg is private. - args.args.get_mut("shard").unwrap().vals = vec![shard_identifier.into()]; - - let run_config = RunConfig::from(&args); - - assert_eq!(run_config.dev, true); - assert_eq!(run_config.skip_ra, true); - assert_eq!(run_config.shard.unwrap(), shard_identifier.to_string()); - } - - #[test] - fn external_addresses_are_returned_correctly_if_not_set() { - let trusted_port = "7119"; - let untrusted_port = "9119"; - let mu_ra_port = "99"; - let expected_worker_ip = "127.0.0.1"; - - let mut args = ArgMatches::default(); - args.args = HashMap::from([ - ("mu-ra-port", Default::default()), - ("untrusted-worker-port", Default::default()), - ("trusted-worker-port", Default::default()), - ]); - // Workaround because MatchedArg is private. - args.args.get_mut("mu-ra-port").unwrap().vals = vec![mu_ra_port.into()]; - args.args.get_mut("untrusted-worker-port").unwrap().vals = vec![untrusted_port.into()]; - args.args.get_mut("trusted-worker-port").unwrap().vals = vec![trusted_port.into()]; - - let config = Config::from(&args); - - assert_eq!( - config.trusted_worker_url_external(), - format!("wss://{}:{}", expected_worker_ip, trusted_port) - ); - assert_eq!( - config.untrusted_worker_url_external(), - format!("ws://{}:{}", expected_worker_ip, untrusted_port) - ); - assert_eq!(config.mu_ra_url_external(), format!("{}:{}", expected_worker_ip, mu_ra_port)); - } - - #[test] - fn external_addresses_are_returned_correctly_if_set() { - let trusted_ext_addr = "wss://1.1.1.2:700/"; - let untrusted_ext_addr = "ws://1.123.3.1:11/"; - let mu_ra_ext_addr = "1.1.3.1:1000"; - - let mut args = ArgMatches::default(); - args.args = HashMap::from([ - ("trusted-external-address", Default::default()), - ("untrusted-external-address", Default::default()), - ("mu-ra-external-address", Default::default()), - ]); - // Workaround because MatchedArg is private. - args.args.get_mut("trusted-external-address").unwrap().vals = vec![trusted_ext_addr.into()]; - args.args.get_mut("untrusted-external-address").unwrap().vals = - vec![untrusted_ext_addr.into()]; - args.args.get_mut("mu-ra-external-address").unwrap().vals = vec![mu_ra_ext_addr.into()]; - - let config = Config::from(&args); - - assert_eq!(config.trusted_worker_url_external(), trusted_ext_addr); - assert_eq!(config.untrusted_worker_url_external(), untrusted_ext_addr); - assert_eq!(config.mu_ra_url_external(), mu_ra_ext_addr); - } - - #[test] - fn ensure_no_port_is_added_to_url_with_port() { - let url = "ws://hello:4000"; - let port = "0"; - - let resulting_url = add_port_if_necessary(url, port); - - assert_eq!(resulting_url, url); - } - - #[test] - fn ensure_port_is_added_to_url_without_port() { - let url = "wss://hello"; - let port = "0"; - - let resulting_url = add_port_if_necessary(url, port); - - assert_eq!(resulting_url, format!("{}:{}", url, port)); - } - - #[test] - fn ensure_no_port_is_added_to_url_with_port_without_prefix() { - let url = "hello:10001"; - let port = "012"; - - let resulting_url = add_port_if_necessary(url, port); - - assert_eq!(resulting_url, url); - } - - #[test] - fn ensure_port_is_added_to_url_without_port_without_prefix() { - let url = "hello_world"; - let port = "10"; - - let resulting_url = add_port_if_necessary(url, port); - - assert_eq!(resulting_url, format!("{}:{}", url, port)); - } -} diff --git a/tee-worker/bitacross/service/src/enclave/api.rs b/tee-worker/bitacross/service/src/enclave/api.rs deleted file mode 100644 index 86030f8d8d..0000000000 --- a/tee-worker/bitacross/service/src/enclave/api.rs +++ /dev/null @@ -1,116 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::config::Config; -use itp_enclave_api::{enclave_base::EnclaveBase, error::Error as EnclaveApiError, EnclaveResult}; -use itp_settings::files::{ENCLAVE_FILE, ENCLAVE_TOKEN}; -use log::*; -use sgx_types::*; -use std::{ - fs::File, - io::{Read, Write}, - path::PathBuf, -}; - -use itp_enclave_api::{Enclave, SgxEnclave}; - -pub fn enclave_init(config: &Config) -> EnclaveResult { - const LEN: usize = 1024; - let mut launch_token = [0; LEN]; - let mut launch_token_updated = 0; - - // Step 1: try to retrieve the launch token saved by last transaction - // if there is no token, then create a new one. - // - // try to get the token saved in $HOME */ - let mut home_dir = PathBuf::new(); - let use_token = match dirs::home_dir() { - Some(path) => { - info!("[+] Home dir is {}", path.display()); - home_dir = path; - true - }, - None => { - error!("[-] Cannot get home dir"); - false - }, - }; - let token_file = home_dir.join(ENCLAVE_TOKEN); - if use_token { - match File::open(&token_file) { - Err(_) => { - info!( - "[-] Token file {} not found! Will create one.", - token_file.as_path().to_str().unwrap() - ); - }, - Ok(mut f) => { - info!("[+] Open token file success! "); - match f.read(&mut launch_token) { - Ok(LEN) => { - info!("[+] Token file valid!"); - }, - _ => info!("[+] Token file invalid, will create new token file"), - } - }, - } - } - - // Step 2: call sgx_create_enclave to initialize an enclave instance - // Debug Support: 1 = debug mode, 0 = not debug mode - #[cfg(feature = "development")] - let debug = 1; - #[cfg(not(feature = "development"))] - let debug = 0; - - let mut misc_attr = - sgx_misc_attribute_t { secs_attr: sgx_attributes_t { flags: 0, xfrm: 0 }, misc_select: 0 }; - let enclave = (SgxEnclave::create( - ENCLAVE_FILE, - debug, - &mut launch_token, - &mut launch_token_updated, - &mut misc_attr, - )) - .map_err(EnclaveApiError::Sgx)?; - - // Step 3: save the launch token if it is updated - if use_token && launch_token_updated != 0 { - // reopen the file with write capability - match File::create(&token_file) { - Ok(mut f) => match f.write_all(&launch_token) { - Ok(()) => info!("[+] Saved updated launch token!"), - Err(_) => error!("[-] Failed to save updated launch token!"), - }, - Err(_) => { - warn!("[-] Failed to save updated enclave token, but doesn't matter"); - }, - } - } - - // create an enclave API and initialize it - let enclave_api = Enclave::new(enclave); - enclave_api.init( - &config.mu_ra_url_external(), - &config.untrusted_worker_url_external(), - &config.data_dir().display().to_string(), - config.ceremony_commands_thread_count, - config.ceremony_events_thread_count, - )?; - - Ok(enclave_api) -} diff --git a/tee-worker/bitacross/service/src/enclave/mod.rs b/tee-worker/bitacross/service/src/enclave/mod.rs deleted file mode 100644 index bb9ba4fe84..0000000000 --- a/tee-worker/bitacross/service/src/enclave/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(feature = "link-binary")] -pub mod api; -pub mod tls_ra; diff --git a/tee-worker/bitacross/service/src/enclave/tls_ra.rs b/tee-worker/bitacross/service/src/enclave/tls_ra.rs deleted file mode 100644 index f7ff454ecb..0000000000 --- a/tee-worker/bitacross/service/src/enclave/tls_ra.rs +++ /dev/null @@ -1,117 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ -use itp_enclave_api::{ - error::Error, - remote_attestation::{RemoteAttestation, TlsRemoteAttestation}, - EnclaveResult, -}; -use itp_types::ShardIdentifier; -use log::*; -use sgx_types::*; -use std::{ - net::{TcpListener, TcpStream}, - os::unix::io::AsRawFd, - time::Duration, -}; - -pub fn enclave_run_state_provisioning_server( - enclave_api: &E, - sign_type: sgx_quote_sign_type_t, - quoting_enclave_target_info: Option<&sgx_target_info_t>, - quote_size: Option<&u32>, - addr: &str, - skip_ra: bool, -) { - info!("Starting MU-RA-Server on: {}", addr); - let listener = match TcpListener::bind(addr) { - Ok(l) => l, - Err(e) => { - error!("error starting MU-RA server on {}: {}", addr, e); - return - }, - }; - loop { - match listener.accept() { - Ok((socket, addr)) => { - info!("[MU-RA-Server] a worker at {} is requesting key provisioning", addr); - // there is some race condition, lets wait until local state gets updated (signers are registered and updated locally through indirect calls) - std::thread::sleep(Duration::from_secs(3)); - let result = enclave_api.run_state_provisioning_server( - socket.as_raw_fd(), - sign_type, - quoting_enclave_target_info, - quote_size, - skip_ra, - ); - - match result { - Ok(_) => { - debug!("[MU-RA-Server] ECALL success!"); - }, - Err(e) => { - error!("[MU-RA-Server] ECALL Enclave Failed {:?}!", e); - }, - } - }, - Err(e) => error!("couldn't get client: {:?}", e), - } - } -} - -pub fn enclave_request_state_provisioning( - enclave_api: &E, - sign_type: sgx_quote_sign_type_t, - addr: &str, - shard: &ShardIdentifier, - skip_ra: bool, -) -> EnclaveResult<()> { - info!("[MU-RA-Client] Requesting key provisioning from {}", addr); - - let stream = TcpStream::connect(addr).map_err(|e| Error::Other(Box::new(e)))?; - - #[cfg(not(feature = "dcap"))] - let get_quote_data = false; - #[cfg(feature = "dcap")] - let get_quote_data = !skip_ra; - - let quoting_enclave_target_info = if get_quote_data { - match enclave_api.qe_get_target_info() { - Ok(quote_size) => Some(quote_size), - Err(e) => return Err(e), - } - } else { - None - }; - - let quote_size = if get_quote_data { - match enclave_api.qe_get_quote_size() { - Ok(quote_size) => Some(quote_size), - Err(e) => return Err(e), - } - } else { - None - }; - - enclave_api.request_state_provisioning( - stream.as_raw_fd(), - sign_type, - quoting_enclave_target_info.as_ref(), - quote_size.as_ref(), - shard, - skip_ra, - ) -} diff --git a/tee-worker/bitacross/service/src/error.rs b/tee-worker/bitacross/service/src/error.rs deleted file mode 100644 index 975d32f267..0000000000 --- a/tee-worker/bitacross/service/src/error.rs +++ /dev/null @@ -1,65 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -use codec::Error as CodecError; -use itp_node_api::api_client::ApiClientError; -use itp_types::{parentchain::Hash, ShardIdentifier}; - -pub type ServiceResult = Result; - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error("{0}")] - Codec(#[from] CodecError), - #[error("{0:?}")] - ApiClient(ApiClientError), - #[error("Node API terminated subscription unexpectedly")] - ApiSubscriptionDisconnected, - #[error("Enclave API error: {0}")] - EnclaveApi(#[from] itp_enclave_api::error::Error), - #[error("Trusted Rpc Client error: {0}")] - TrustedRpcClient(#[from] itc_rpc_client::error::Error), - #[error("{0}")] - JsonRpSeeClient(#[from] jsonrpsee::types::Error), - #[error("{0}")] - Serialization(#[from] serde_json::Error), - #[error("{0}")] - FromUtf8(#[from] std::string::FromUtf8Error), - #[error("Application setup error!")] - ApplicationSetup, - #[error("Failed to find any peer worker")] - NoPeerWorkerFound, - #[error("No worker for shard {0} found on parentchain")] - NoWorkerForShardFound(ShardIdentifier), - #[error("Returned empty parentchain block vec after sync, even though there have been blocks given as input")] - EmptyChunk, - #[error("Could not find genesis header of the parentchain")] - MissingGenesisHeader, - #[error("Could not find last finalized block of the parentchain")] - MissingLastFinalizedBlock, - #[error("Could not find block in parentchain")] - UnknownBlockHeader(Hash), - #[error("Enclave has not enough funds to send extrinsic")] - LowEnclaveBalance, - #[error("{0}")] - Custom(Box), -} - -impl From for Error { - fn from(error: ApiClientError) -> Self { - Error::ApiClient(error) - } -} diff --git a/tee-worker/bitacross/service/src/globals/mod.rs b/tee-worker/bitacross/service/src/globals/mod.rs deleted file mode 100644 index ee250661c5..0000000000 --- a/tee-worker/bitacross/service/src/globals/mod.rs +++ /dev/null @@ -1,19 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pub mod tokio_handle; diff --git a/tee-worker/bitacross/service/src/globals/tokio_handle.rs b/tee-worker/bitacross/service/src/globals/tokio_handle.rs deleted file mode 100644 index 54e49d985e..0000000000 --- a/tee-worker/bitacross/service/src/globals/tokio_handle.rs +++ /dev/null @@ -1,108 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use lazy_static::lazy_static; -use parking_lot::RwLock; -use tokio::runtime::Handle; - -lazy_static! { - static ref TOKIO_HANDLE: RwLock> = RwLock::new(None); -} - -/// Wrapper for accessing a tokio handle -pub trait GetTokioHandle { - fn get_handle(&self) -> Handle; -} - -/// implementation, using a static global variable internally -/// -pub struct GlobalTokioHandle; - -/// these are the static (global) accessors -/// reduce their usage where possible and use an instance of TokioHandleAccessorImpl or the trait -impl GlobalTokioHandle { - /// this needs to be called once at application startup! - pub fn initialize() { - let rt = tokio::runtime::Builder::new_multi_thread() - .enable_all() - .worker_threads(2) - .build() - .unwrap(); - *TOKIO_HANDLE.write() = Some(rt); - } - - /// static / global getter of the handle (try to keep private!, use trait to access handle) - fn read_handle() -> Handle { - TOKIO_HANDLE - .read() - .as_ref() - .expect("Tokio handle has not been initialized!") - .handle() - .clone() - } -} - -impl GetTokioHandle for GlobalTokioHandle { - fn get_handle(&self) -> Handle { - GlobalTokioHandle::read_handle() - } -} - -/// Implementation for a scoped Tokio handle. -/// -/// -pub struct ScopedTokioHandle { - tokio_runtime: tokio::runtime::Runtime, -} - -impl Default for ScopedTokioHandle { - fn default() -> Self { - ScopedTokioHandle { tokio_runtime: tokio::runtime::Runtime::new().unwrap() } - } -} - -impl GetTokioHandle for ScopedTokioHandle { - fn get_handle(&self) -> Handle { - self.tokio_runtime.handle().clone() - } -} - -#[cfg(test)] -mod tests { - - use super::*; - - #[tokio::test] - async fn given_initialized_tokio_handle_when_runtime_goes_out_of_scope_then_async_handle_is_valid( - ) { - // initialize the global handle - // be aware that if you write more tests here, the global state will be shared across multiple threads - // which cargo test spawns. So it can lead to failing tests. - // solution: either get rid of the global state, or write all test functionality in this single test function - { - GlobalTokioHandle::initialize(); - } - - let handle = GlobalTokioHandle.get_handle(); - - let result = handle.spawn_blocking(|| "now running on a worker thread").await; - - assert!(result.is_ok()); - assert!(!result.unwrap().is_empty()) - } -} diff --git a/tee-worker/bitacross/service/src/initialized_service.rs b/tee-worker/bitacross/service/src/initialized_service.rs deleted file mode 100644 index 2aca3876ac..0000000000 --- a/tee-worker/bitacross/service/src/initialized_service.rs +++ /dev/null @@ -1,109 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Service to determine if the integritee services is initialized and registered on the node, -//! hosted on a http server. - -use crate::error::ServiceResult; -use log::*; -use parking_lot::RwLock; -use std::{default::Default, marker::PhantomData, net::SocketAddr, sync::Arc}; -use warp::Filter; - -pub async fn start_is_initialized_server( - initialization_handler: Arc, - port: u16, -) -> ServiceResult<()> -where - Handler: IsInitialized + Send + Sync + 'static, -{ - let is_initialized_route = warp::path!("is_initialized").and_then(move || { - let handler_clone = initialization_handler.clone(); - async move { - if handler_clone.is_initialized() { - Ok("I am initialized.") - } else { - Err(warp::reject::not_found()) - } - } - }); - - let socket_addr: SocketAddr = ([0, 0, 0, 0], port).into(); - - info!("Running initialized server on: {:?}", socket_addr); - warp::serve(is_initialized_route).run(socket_addr).await; - - info!("Initialized server shut down"); - Ok(()) -} - -/// Trait to query of a worker is considered fully initialized. -pub trait IsInitialized { - fn is_initialized(&self) -> bool; -} - -/// Tracker for initialization. Used by components that ensure these steps were taken. -pub trait TrackInitialization { - fn registered_on_parentchain(&self); - - fn worker_for_shard_registered(&self); -} - -#[derive(Default)] -pub struct InitializationHandler { - registered_on_parentchain: RwLock, - worker_for_shard_registered: RwLock, -} - -impl TrackInitialization for InitializationHandler { - fn registered_on_parentchain(&self) { - let mut registered_lock = self.registered_on_parentchain.write(); - *registered_lock = true; - } - - fn worker_for_shard_registered(&self) { - let mut registered_lock = self.worker_for_shard_registered.write(); - *registered_lock = true; - } -} - -impl IsInitialized for InitializationHandler { - fn is_initialized(&self) -> bool { - *self.registered_on_parentchain.read() - } -} - -#[cfg(test)] -mod tests { - - use super::*; - - #[test] - fn default_handler_is_initialized_returns_false() { - let offchain_worker_handler = InitializationHandler::default(); - - assert!(!offchain_worker_handler.is_initialized()); - } - - #[test] - fn parentchain_registration_is_enough_for_initialized() { - let initialization_handler = InitializationHandler::default(); - initialization_handler.registered_on_parentchain(); - - assert!(initialization_handler.is_initialized()); - } -} diff --git a/tee-worker/bitacross/service/src/main.rs b/tee-worker/bitacross/service/src/main.rs deleted file mode 100644 index 378869c22f..0000000000 --- a/tee-worker/bitacross/service/src/main.rs +++ /dev/null @@ -1,48 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#![cfg_attr(test, feature(assert_matches))] -#![allow(unused)] - -mod account_funding; -mod config; -mod enclave; -mod error; -mod globals; -mod initialized_service; -mod ocall_bridge; -mod parentchain_handler; -mod prometheus_metrics; -mod setup; -mod sync_state; -mod tests; -mod utils; -mod worker; -mod worker_peers_updater; - -#[cfg(feature = "link-binary")] -pub mod main_impl; - -#[cfg(feature = "link-binary")] -fn main() { - main_impl::main(); -} - -#[cfg(not(feature = "link-binary"))] -fn main() { - panic!("tried to run the binary without linking. Make sure to pass `--features link-binary`") -} diff --git a/tee-worker/bitacross/service/src/main_impl.rs b/tee-worker/bitacross/service/src/main_impl.rs deleted file mode 100644 index 663f2df117..0000000000 --- a/tee-worker/bitacross/service/src/main_impl.rs +++ /dev/null @@ -1,942 +0,0 @@ -#[cfg(not(feature = "dcap"))] -use crate::utils::check_files; -use crate::{ - account_funding::{setup_reasonable_account_funding, EnclaveAccountInfoProvider}, - config::Config, - enclave::{ - api::enclave_init, - tls_ra::{enclave_request_state_provisioning, enclave_run_state_provisioning_server}, - }, - error::Error, - globals::tokio_handle::{GetTokioHandle, GlobalTokioHandle}, - initialized_service::{ - start_is_initialized_server, InitializationHandler, IsInitialized, TrackInitialization, - }, - ocall_bridge::{ - bridge_api::Bridge as OCallBridge, component_factory::OCallBridgeComponentFactory, - }, - parentchain_handler::{HandleParentchain, ParentchainHandler}, - prometheus_metrics::{start_metrics_server, EnclaveMetricsReceiver, MetricsHandler}, - setup, sync_state, tests, - utils::extract_shard, - worker::Worker, - worker_peers_updater::WorkerPeersRegistry, -}; -use base58::ToBase58; -use clap::{load_yaml, App, ArgMatches}; -use codec::{Decode, Encode}; -use ita_parentchain_interface::integritee::{Hash, Header}; -use itp_enclave_api::{ - enclave_base::EnclaveBase, - remote_attestation::{RemoteAttestation, TlsRemoteAttestation}, - sidechain::Sidechain, - Enclave, -}; -use itp_node_api::{ - api_client::{AccountApi, PalletTeebagApi, ParentchainApi}, - metadata::NodeMetadata, - node_api_factory::{CreateNodeApi, NodeApiFactory}, -}; -use litentry_primitives::{Enclave as TeebagEnclave, ShardIdentifier, WorkerType}; -use log::*; -use regex::Regex; -use serde_json::Value; -use sgx_types::*; -use sp_runtime::traits::Header as HeaderT; -use substrate_api_client::{ - api::XtStatus, rpc::HandleSubscription, GetAccountInformation, GetBalance, GetChainInfo, - SubmitAndWatch, SubscribeChain, SubscribeEvents, -}; - -#[cfg(feature = "dcap")] -use litentry_primitives::extract_tcb_info_from_raw_dcap_quote; - -use crate::error::ServiceResult; -use itp_types::parentchain::{AccountId, Balance, ParentchainId}; -use sp_core::{ - crypto::{AccountId32, Ss58Codec}, - Pair, -}; -use sp_keyring::AccountKeyring; -use sp_runtime::MultiSigner; -use std::{ - collections::HashSet, fmt::Debug, path::PathBuf, str, str::Utf8Error, sync::Arc, thread, - time::Duration, -}; -use substrate_api_client::ac_node_api::{EventRecord, Phase::ApplyExtrinsic}; - -const VERSION: &str = env!("CARGO_PKG_VERSION"); - -#[cfg(feature = "link-binary")] -pub type EnclaveWorker = Worker; - -pub(crate) fn main() { - // Setup logging - env_logger::builder() - .format_timestamp(Some(env_logger::TimestampPrecision::Millis)) - .init(); - - let yml = load_yaml!("cli.yml"); - let matches = App::from_yaml(yml).get_matches(); - - let config = Config::from(&matches); - - GlobalTokioHandle::initialize(); - - // log this information, don't println because some python scripts for GA rely on the - // stdout from the service - #[cfg(not(feature = "development"))] - info!("*** Starting service in SGX production mode"); - #[cfg(feature = "development")] - info!("*** Starting service in SGX debug mode"); - - let mut lockfile = PathBuf::from(config.data_dir()); - lockfile.push("worker.lock"); - while std::fs::metadata(lockfile.clone()).is_ok() { - println!("lockfile is present, will wait for it to disappear {:?}", lockfile); - thread::sleep(std::time::Duration::from_secs(5)); - } - - let clean_reset = matches.is_present("clean-reset"); - if clean_reset { - crate::setup::purge_files_from_dir(config.data_dir()).unwrap(); - } - - // build the entire dependency tree - let tokio_handle = Arc::new(GlobalTokioHandle {}); - let node_api_factory = - Arc::new(NodeApiFactory::new(config.litentry_rpc_endpoint(), AccountKeyring::Alice.pair())); - let enclave = Arc::new(enclave_init(&config).unwrap()); - let initialization_handler = Arc::new(InitializationHandler::default()); - let worker = Arc::new(EnclaveWorker::new( - config.clone(), - enclave.clone(), - node_api_factory.clone(), - initialization_handler.clone(), - HashSet::new(), - )); - let peer_updater = Arc::new(WorkerPeersRegistry::new(worker)); - let enclave_metrics_receiver = Arc::new(EnclaveMetricsReceiver {}); - - let maybe_target_a_parentchain_api_factory = config - .target_a_parentchain_rpc_endpoint() - .map(|url| Arc::new(NodeApiFactory::new(url, AccountKeyring::Alice.pair()))); - - let maybe_target_b_parentchain_api_factory = config - .target_b_parentchain_rpc_endpoint() - .map(|url| Arc::new(NodeApiFactory::new(url, AccountKeyring::Alice.pair()))); - - // initialize o-call bridge with a concrete factory implementation - OCallBridge::initialize(Arc::new(OCallBridgeComponentFactory::new( - node_api_factory.clone(), - maybe_target_a_parentchain_api_factory, - maybe_target_b_parentchain_api_factory, - enclave.clone(), - peer_updater, - tokio_handle.clone(), - enclave_metrics_receiver, - ))); - - #[cfg(feature = "dcap")] - let quoting_enclave_target_info = match enclave.qe_get_target_info() { - Ok(target_info) => Some(target_info), - Err(e) => { - warn!("Setting up DCAP - qe_get_target_info failed with error: {:?}, continuing.", e); - None - }, - }; - #[cfg(feature = "dcap")] - let quote_size = match enclave.qe_get_quote_size() { - Ok(size) => Some(size), - Err(e) => { - warn!("Setting up DCAP - qe_get_quote_size failed with error: {:?}, continuing.", e); - None - }, - }; - - #[cfg(not(feature = "dcap"))] - let quoting_enclave_target_info = None; - #[cfg(not(feature = "dcap"))] - let quote_size = None; - - if let Some(run_config) = config.run_config() { - let shard = extract_shard(run_config.shard(), enclave.as_ref()); - - println!("Worker Config: {:?}", config); - - if clean_reset { - setup::initialize_shard_and_keys(enclave.as_ref(), &shard).unwrap(); - } - - let node_api = - node_api_factory.create_api().expect("Failed to create parentchain node API"); - - start_worker::<_, _, _>( - config, - &shard, - enclave, - node_api, - tokio_handle, - initialization_handler, - quoting_enclave_target_info, - quote_size, - ); - } else if let Some(smatches) = matches.subcommand_matches("request-state") { - println!("*** Requesting state from a registered worker \n"); - let node_api = - node_api_factory.create_api().expect("Failed to create parentchain node API"); - sync_state::sync_state::<_, _>( - &node_api, - &extract_shard(smatches.value_of("shard"), enclave.as_ref()), - enclave.as_ref(), - smatches.is_present("skip-ra"), - ); - } else if matches.is_present("shielding-key") { - setup::generate_shielding_key_file(enclave.as_ref()); - } else if matches.is_present("signing-key") { - setup::generate_signing_key_file(enclave.as_ref()); - let tee_accountid = enclave_account(enclave.as_ref()); - println!("Enclave signing account: {:}", &tee_accountid.to_ss58check()); - } else if matches.is_present("dump-ra") { - info!("*** Perform RA and dump cert to disk"); - #[cfg(not(feature = "dcap"))] - enclave.dump_ias_ra_cert_to_disk().unwrap(); - #[cfg(feature = "dcap")] - { - let skip_ra = false; - let dcap_quote = enclave.generate_dcap_ra_quote(skip_ra).unwrap(); - let (fmspc, _tcb_info) = extract_tcb_info_from_raw_dcap_quote(&dcap_quote).unwrap(); - enclave.dump_dcap_collateral_to_disk(fmspc).unwrap(); - enclave.dump_dcap_ra_cert_to_disk().unwrap(); - } - } else if matches.is_present("mrenclave") { - let mrenclave = enclave.get_fingerprint().unwrap(); - let hex_value = hex::encode(mrenclave); - println!("MRENCLAVE hex: {}", hex_value); - println!("MRENCLAVE base58: {}", mrenclave.encode().to_base58()); - } else if let Some(sub_matches) = matches.subcommand_matches("init-shard") { - setup::init_shard( - enclave.as_ref(), - &extract_shard(sub_matches.value_of("shard"), enclave.as_ref()), - ); - } else if let Some(sub_matches) = matches.subcommand_matches("test") { - if sub_matches.is_present("provisioning-server") { - println!("*** Running Enclave MU-RA TLS server\n"); - enclave_run_state_provisioning_server( - enclave.as_ref(), - sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE, - quoting_enclave_target_info.as_ref(), - quote_size.as_ref(), - &config.mu_ra_url(), - sub_matches.is_present("skip-ra"), - ); - println!("[+] Done!"); - } else if sub_matches.is_present("provisioning-client") { - println!("*** Running Enclave MU-RA TLS client\n"); - let shard = extract_shard(sub_matches.value_of("shard"), enclave.as_ref()); - enclave_request_state_provisioning( - enclave.as_ref(), - sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE, - &config.mu_ra_url_external(), - &shard, - sub_matches.is_present("skip-ra"), - ) - .unwrap(); - println!("[+] Done!"); - } else { - tests::run_enclave_tests(sub_matches); - } - } else if let Some(sub_matches) = matches.subcommand_matches("migrate-shard") { - let new_shard = extract_shard(None, enclave.as_ref()); - setup::migrate_shard(enclave.as_ref(), &new_shard); - let new_shard_name = new_shard.encode().to_base58(); - setup::remove_old_shards(config.data_dir(), &new_shard_name); - } else if let Some(sub_matches) = matches.subcommand_matches("wallet") { - println!("Bitcoin wallet:"); - let bitcoin_keypair = enclave.get_bitcoin_wallet_pair().unwrap(); - println!("public : 0x{}", hex::encode(bitcoin_keypair.public_bytes())); - println!("private: 0x{}", hex::encode(bitcoin_keypair.private_bytes())); - - println!("Ethereum wallet:"); - let ethereum_keypair = enclave.get_ethereum_wallet_pair().unwrap(); - println!("public : 0x{}", hex::encode(ethereum_keypair.public_bytes())); - println!("private: 0x{}", hex::encode(ethereum_keypair.private_bytes())); - - println!("Ton wallet:"); - let ton_keypair = enclave.get_ton_wallet_pair().unwrap(); - println!("public : 0x{}", hex::encode(ton_keypair.public().0)); - println!("private: 0x{}", hex::encode(ton_keypair.seed())); - } else if let Some(sub_matches) = matches.subcommand_matches("init-wallet") { - println!("Initializing wallets"); - enclave.init_wallets(config.data_dir().to_str().unwrap()).unwrap(); - } else { - println!("For options: use --help"); - } -} - -/// FIXME: needs some discussion (restructuring?) -#[allow(clippy::too_many_arguments)] -fn start_worker( - config: Config, - shard: &ShardIdentifier, - enclave: Arc, - litentry_rpc_api: ParentchainApi, - tokio_handle_getter: Arc, - initialization_handler: Arc, - quoting_enclave_target_info: Option, - quote_size: Option, -) where - T: GetTokioHandle, - E: EnclaveBase + Sidechain + RemoteAttestation + TlsRemoteAttestation + Clone, - InitializationHandler: TrackInitialization + IsInitialized + Sync + Send + 'static, -{ - let run_config = config.run_config().clone().expect("Run config missing"); - let skip_ra = run_config.skip_ra(); - - let flavor_str = "offchain-worker"; - - println!("Litentry Worker for {} v{}", flavor_str, VERSION); - - #[cfg(feature = "dcap")] - println!(" DCAP is enabled"); - #[cfg(not(feature = "dcap"))] - println!(" DCAP is disabled"); - #[cfg(not(feature = "development"))] - println!(" Production Mode is enabled"); - #[cfg(feature = "development")] - println!(" Production Mode is disabled"); - - info!("starting worker on shard {}", shard.encode().to_base58()); - // ------------------------------------------------------------------------ - // check for required files - if !skip_ra { - #[cfg(not(feature = "dcap"))] - check_files(); - } - // ------------------------------------------------------------------------ - // initialize the enclave - let mrenclave = enclave.get_fingerprint().unwrap(); - println!("MRENCLAVE={}", mrenclave.0.to_base58()); - println!("MRENCLAVE in hex {:?}", hex::encode(mrenclave)); - - // ------------------------------------------------------------------------ - // let new workers call us for key provisioning - println!("MU-RA server listening on {}", config.mu_ra_url()); - let is_development_mode = run_config.dev(); - let ra_url = config.mu_ra_url(); - let enclave_api_key_prov = enclave.clone(); - thread::spawn(move || { - enclave_run_state_provisioning_server( - enclave_api_key_prov.as_ref(), - sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE, - quoting_enclave_target_info.as_ref(), - quote_size.as_ref(), - &ra_url, - skip_ra, - ); - info!("State provisioning server stopped."); - }); - - let tokio_handle = tokio_handle_getter.get_handle(); - - // ------------------------------------------------------------------------ - // Get the public key of our TEE. - let tee_accountid = enclave_account(enclave.as_ref()); - println!("Enclave account {:} ", &tee_accountid.to_ss58check()); - - // ------------------------------------------------------------------------ - // Start `is_initialized` server. - let untrusted_http_server_port = config - .try_parse_untrusted_http_server_port() - .expect("untrusted http server port to be a valid port number"); - let initialization_handler_clone = initialization_handler.clone(); - tokio_handle.spawn(async move { - if let Err(e) = - start_is_initialized_server(initialization_handler_clone, untrusted_http_server_port) - .await - { - error!("Unexpected error in `is_initialized` server: {:?}", e); - } - }); - - // ------------------------------------------------------------------------ - // Start prometheus metrics server. - if config.enable_metrics_server() { - let metrics_handler = Arc::new(MetricsHandler {}); - let metrics_server_port = config - .try_parse_metrics_server_port() - .expect("metrics server port to be a valid port number"); - tokio_handle.spawn(async move { - if let Err(e) = start_metrics_server(metrics_handler, metrics_server_port).await { - error!("Unexpected error in Prometheus metrics server: {:?}", e); - } - }); - } - - // ------------------------------------------------------------------------ - // Start trusted worker rpc server - let direct_invocation_server_addr = config.trusted_worker_url_internal(); - let enclave_for_direct_invocation = enclave.clone(); - thread::spawn(move || { - println!( - "[+] Trusted RPC direct invocation server listening on {}", - direct_invocation_server_addr - ); - enclave_for_direct_invocation - .init_direct_invocation_server(direct_invocation_server_addr) - .unwrap(); - println!("[+] RPC direct invocation server shut down"); - }); - - // ------------------------------------------------------------------------ - // Init parentchain specific stuff. Needed early for parentchain communication. - let (integritee_parentchain_handler, integritee_last_synced_header_at_last_run) = - init_parentchain( - &enclave, - &litentry_rpc_api, - &tee_accountid, - ParentchainId::Litentry, - shard, - ); - - #[cfg(feature = "dcap")] - register_collateral(&litentry_rpc_api, &*enclave, &tee_accountid, is_development_mode, skip_ra); - - let trusted_url = config.trusted_worker_url_external(); - - #[cfg(feature = "attesteer")] - fetch_marblerun_events_every_hour( - litentry_rpc_api.clone(), - enclave.clone(), - tee_accountid.clone(), - is_development_mode, - trusted_url.clone(), - run_config.marblerun_base_url().to_string(), - ); - - // ------------------------------------------------------------------------ - // Perform a remote attestation and get an unchecked extrinsic back. - - if skip_ra { - println!( - "[!] skipping remote attestation. Registering enclave without attestation report." - ); - } else { - println!("[!] creating remote attestation report and create enclave register extrinsic."); - }; - - #[cfg(feature = "dcap")] - enclave.set_sgx_qpl_logging().expect("QPL logging setup failed"); - - let enclave2 = enclave.clone(); - let node_api2 = litentry_rpc_api.clone(); - let tee_accountid2 = tee_accountid.clone(); - let trusted_url2 = trusted_url.clone(); - - #[cfg(not(feature = "dcap"))] - let register_xt = move || enclave2.generate_ias_ra_extrinsic(&trusted_url2, skip_ra).unwrap(); - #[cfg(feature = "dcap")] - let register_xt = move || enclave2.generate_dcap_ra_extrinsic(&trusted_url2, skip_ra).unwrap(); - - let send_register_xt = move || { - println!("[+] Send register enclave extrinsic"); - send_litentry_extrinsic(register_xt(), &node_api2, &tee_accountid2, is_development_mode) - }; - - // Litentry: send the registration extrinsic regardless of being registered or not, - // the reason is the mrenclave could change in between, so we rely on the - // on-chain logic to handle everything. - // this is the same behavior as upstream - let register_enclave_block_hash = - send_register_xt().expect("enclave RA registration must be successful to continue"); - - let api_register_enclave_xt_header = - litentry_rpc_api.get_header(Some(register_enclave_block_hash)).unwrap().unwrap(); - - // TODO: #1451: Fix api-client type hacks - let register_enclave_xt_header = - Header::decode(&mut api_register_enclave_xt_header.encode().as_slice()) - .expect("Can decode previously encoded header; qed"); - - println!( - "[+] Enclave registered at block number: {:?}, hash: {:?}", - register_enclave_xt_header.number(), - register_enclave_xt_header.hash() - ); - // double-check - let my_enclave = litentry_rpc_api - .enclave(&tee_accountid, None) - .unwrap() - .expect("our enclave should be registered at this point"); - trace!("verified that our enclave is registered: {:?}", my_enclave); - - // Litentry: - // the logic differs from upstream a bit here (due to different impl in parachain pallet), - // theoretically the `primary_enclave_identifier_for_shard` should never be empty, unless the previous - // registration failed (e.g. due to unexpected mrenclave). In that case it's expected not to continue with anything. - // - // in case it's non-empty, it relies on the check of `enclave.get_shard_creation_info` to tell if this worker - // has run before - this is similar to upstream. - // There're a few cases: - // 1. `--clean-reset` is set, then the shard should have been initalized earlier already and it's empty state anyway - // 2. `--clean-reset` is not set: - // 2a. `get_shard_creation_info` is empty and we are primary worker => it's never run before => init everything - // 2b. `get_shard_creation_info` is empty and we are non-primary worker => it's never run before => request to sync state - // 2c. `get_shard_creation_info` is non-empty it's run before => do nothing - let (we_are_primary_validateer, re_init_parentchain_needed) = - match litentry_rpc_api - .primary_enclave_identifier_for_shard(WorkerType::BitAcross, shard, None) - .unwrap() - { - Some(account) => { - let first_run = enclave - .get_shard_creation_info(shard) - .unwrap() - .for_parentchain(ParentchainId::Litentry) - .is_none(); - if account == tee_accountid { - println!("We are the primary worker, first_run: {}", first_run); - if first_run { - enclave.init_shard(shard.encode()).unwrap(); - enclave - .init_shard_creation_parentchain_header( - shard, - &ParentchainId::Litentry, - ®ister_enclave_xt_header, - ) - .unwrap(); - debug!("shard config should be initialized on litentry network now"); - (true, true) - } else { - (true, false) - } - } else { - println!("We are NOT primary worker, the primary worker is {}", account); - if first_run { - // obtain provisioning from last active worker as this hasn't been done before - info!("my state doesn't know the creation header of the shard. will request provisioning"); - sync_state::sync_state::<_, _>( - &litentry_rpc_api, - &shard, - enclave.as_ref(), - skip_ra, - ); - } - (false, true) - } - }, - None => { - panic!("No primary enclave account is found - was the enclave successfully registered?"); - }, - }; - debug!("getting shard creation: {:?}", enclave.get_shard_creation_info(shard)); - initialization_handler.registered_on_parentchain(); - - let (integritee_parentchain_handler, integritee_last_synced_header_at_last_run) = - if re_init_parentchain_needed { - // re-initialize integritee parentchain to make sure to use creation_header for fast-sync or the provisioned light client state - init_parentchain( - &enclave, - &litentry_rpc_api, - &tee_accountid, - ParentchainId::Litentry, - shard, - ) - } else { - (integritee_parentchain_handler, integritee_last_synced_header_at_last_run) - }; - - println!("[Litentry:OCW] Finished initializing light client, syncing parentchain..."); - - // Litentry: apply skipped parentchain block - let parentchain_start_block = config - .try_parse_parentchain_start_block() - .expect("parentchain start block to be a valid number"); - - // Syncing all parentchain blocks, this might take a while.. - let last_synced_header = integritee_parentchain_handler - .sync_parentchain_until_latest_finalized( - integritee_last_synced_header_at_last_run, - parentchain_start_block, - *shard, - true, - ) - .unwrap(); - - start_parentchain_header_subscription_thread( - integritee_parentchain_handler, - last_synced_header, - *shard, - ); - info!("skipping shard vault check because not yet supported for offchain worker"); - - let maybe_target_a_rpc_api = if let Some(url) = config.target_a_parentchain_rpc_endpoint() { - Some(init_target_parentchain( - &enclave, - &tee_accountid, - url, - shard, - ParentchainId::TargetA, - is_development_mode, - )) - } else { - None - }; - - let maybe_target_b_rpc_api = if let Some(url) = config.target_b_parentchain_rpc_endpoint() { - Some(init_target_parentchain( - &enclave, - &tee_accountid, - url, - shard, - ParentchainId::TargetB, - is_development_mode, - )) - } else { - None - }; - - // Publish generated custiodian wallets - enclave.publish_wallets(); - enclave.finish_enclave_init(); - - ita_parentchain_interface::event_subscriber::subscribe_to_parentchain_events( - &litentry_rpc_api, - ParentchainId::Litentry, - ); -} - -fn init_target_parentchain( - enclave: &Arc, - tee_account_id: &AccountId32, - url: String, - shard: &ShardIdentifier, - parentchain_id: ParentchainId, - is_development_mode: bool, -) -> ParentchainApi -where - E: EnclaveBase + Sidechain, -{ - println!("Initializing parentchain {:?} with url: {}", parentchain_id, url); - let node_api = NodeApiFactory::new(url, AccountKeyring::Alice.pair()) - .create_api() - .unwrap_or_else(|_| panic!("[{:?}] Failed to create parentchain node API", parentchain_id)); - - setup_reasonable_account_funding( - &node_api, - tee_account_id, - parentchain_id, - is_development_mode, - ) - .unwrap_or_else(|_| { - panic!("[{:?}] Could not fund parentchain enclave account", parentchain_id) - }); - - // we attempt to set shard creation for this parentchain in case it hasn't been done before - let api_head = node_api.get_header(None).unwrap().unwrap(); - // TODO: #1451: Fix api-client type hacks - let head = Header::decode(&mut api_head.encode().as_slice()) - .expect("Can decode previously encoded header; qed"); - // we ignore failure - let _ = enclave.init_shard_creation_parentchain_header(shard, &parentchain_id, &head); - - let (parentchain_handler, last_synched_header) = - init_parentchain(enclave, &node_api, tee_account_id, parentchain_id, shard); - - println!("[{:?}] Finished initializing light client, syncing parentchain...", parentchain_id); - - // Syncing all parentchain blocks, this might take a while.. - let last_synched_header = parentchain_handler - .sync_parentchain_until_latest_finalized(last_synched_header, 0, *shard, true) - .unwrap(); - - start_parentchain_header_subscription_thread( - parentchain_handler.clone(), - last_synched_header, - *shard, - ); - - let parentchain_init_params = parentchain_handler.parentchain_init_params.clone(); - - let node_api_clone = node_api.clone(); - thread::Builder::new() - .name(format!("{:?}_parentchain_event_subscription", parentchain_id)) - .spawn(move || { - ita_parentchain_interface::event_subscriber::subscribe_to_parentchain_events( - &node_api_clone, - parentchain_id, - ) - }) - .unwrap(); - node_api -} - -fn init_parentchain( - enclave: &Arc, - node_api: &ParentchainApi, - tee_account_id: &AccountId32, - parentchain_id: ParentchainId, - shard: &ShardIdentifier, -) -> (Arc>, Header) -where - E: EnclaveBase + Sidechain, -{ - let parentchain_handler = Arc::new( - ParentchainHandler::new_with_automatic_light_client_allocation( - node_api.clone(), - enclave.clone(), - parentchain_id, - *shard, - ) - .unwrap(), - ); - let last_synced_header = parentchain_handler.init_parentchain_components().unwrap(); - println!("[{:?}] last synced parentchain block: {}", parentchain_id, last_synced_header.number); - - let nonce = node_api.get_account_next_index(tee_account_id).unwrap(); - info!("[{:?}] Enclave nonce = {:?}", parentchain_id, nonce); - enclave.set_nonce(nonce, parentchain_id).unwrap_or_else(|_| { - panic!("[{:?}] Could not set nonce of enclave. Returning here...", parentchain_id) - }); - - let metadata = node_api.metadata().clone(); - let runtime_spec_version = node_api.runtime_version().spec_version; - let runtime_transaction_version = node_api.runtime_version().transaction_version; - enclave - .set_node_metadata( - NodeMetadata::new(metadata, runtime_spec_version, runtime_transaction_version).encode(), - parentchain_id, - ) - .unwrap_or_else(|_| { - panic!("[{:?}] Could not set the node metadata in the enclave", parentchain_id) - }); - - (parentchain_handler, last_synced_header) -} - -/// Start polling loop to wait until we have a worker for a shard registered on -/// the parentchain (TEEBAG EnclaveIdentifier). This is the pre-requisite to be -/// considered initialized and ready for the next worker to start (in sidechain mode only). -fn spawn_worker_for_shard_polling( - shard: &ShardIdentifier, - node_api: ParentchainApi, - initialization_handler: Arc, -) where - InitializationHandler: TrackInitialization + Sync + Send + 'static, -{ - let shard_for_initialized = *shard; - thread::spawn(move || { - const POLL_INTERVAL_SECS: u64 = 2; - - loop { - info!("Polling for worker for shard ({} seconds interval)", POLL_INTERVAL_SECS); - if let Ok(Some(_account)) = node_api.primary_enclave_identifier_for_shard( - WorkerType::BitAcross, - &shard_for_initialized, - None, - ) { - // Set that the service is initialized. - initialization_handler.worker_for_shard_registered(); - println!("[+] Found `WorkerForShard` on parentchain state",); - break - } - thread::sleep(Duration::from_secs(POLL_INTERVAL_SECS)); - } - }); -} - -#[cfg(feature = "attesteer")] -fn fetch_marblerun_events_every_hour( - api: ParentchainApi, - enclave: Arc, - accountid: AccountId32, - is_development_mode: bool, - url: String, - marblerun_base_url: String, -) where - E: RemoteAttestation + Clone + Sync + Send + 'static, -{ - let enclave = enclave.clone(); - let handle = thread::spawn(move || { - const POLL_INTERVAL_5_MINUTES_IN_SECS: u64 = 5 * 60; - loop { - info!("Polling marblerun events for quotes to register"); - register_quotes_from_marblerun( - &api, - enclave.clone(), - &accountid, - is_development_mode, - url.clone(), - &marblerun_base_url, - ); - - thread::sleep(Duration::from_secs(POLL_INTERVAL_5_MINUTES_IN_SECS)); - } - }); - - handle.join().unwrap() -} -#[cfg(feature = "attesteer")] -fn register_quotes_from_marblerun( - api: &ParentchainApi, - enclave: Arc, - accountid: &AccountId32, - is_development_mode: bool, - url: String, - marblerun_base_url: &str, -) { - let enclave = enclave.as_ref(); - let events = crate::prometheus_metrics::fetch_marblerun_events(marblerun_base_url) - .map_err(|e| { - info!("Fetching events from Marblerun failed with: {:?}, continuing with 0 events.", e); - }) - .unwrap_or_default(); - let quotes: Vec<&[u8]> = - events.iter().map(|event| event.get_quote_without_prepended_bytes()).collect(); - - for quote in quotes { - match enclave.generate_dcap_ra_extrinsic_from_quote(url.clone(), "e) { - Ok(xt) => { - send_litentry_extrinsic(xt, api, accountid, is_development_mode); - }, - Err(e) => { - error!("Extracting information from quote failed: {}", e) - }, - } - } -} -#[cfg(feature = "dcap")] -fn register_collateral( - api: &ParentchainApi, - enclave: &dyn RemoteAttestation, - accountid: &AccountId32, - is_development_mode: bool, - skip_ra: bool, -) { - //TODO generate_dcap_ra_quote() does not really need skip_ra, rethink how many layers skip_ra should be passed along - if !skip_ra { - let dcap_quote = enclave.generate_dcap_ra_quote(skip_ra).unwrap(); - let (fmspc, _tcb_info) = extract_tcb_info_from_raw_dcap_quote(&dcap_quote).unwrap(); - println!("[>] DCAP setup: register QE collateral"); - let uxt = enclave.generate_register_quoting_enclave_extrinsic(fmspc).unwrap(); - send_litentry_extrinsic(uxt, api, accountid, is_development_mode); - - println!("[>] DCAP setup: register TCB info"); - let uxt = enclave.generate_register_tcb_info_extrinsic(fmspc).unwrap(); - send_litentry_extrinsic(uxt, api, accountid, is_development_mode); - } -} - -fn send_litentry_extrinsic( - extrinsic: Vec, - api: &ParentchainApi, - fee_payer: &AccountId32, - is_development_mode: bool, -) -> ServiceResult { - let fee = crate::account_funding::estimate_fee(api, extrinsic.clone())?; - let ed = api.get_existential_deposit()?; - let free = api.get_free_balance(fee_payer)?; - let missing_funds = fee.saturating_add(ed).saturating_sub(free); - info!("[Litentry] send extrinsic"); - debug!("fee: {:?}, ed: {:?}, free: {:?} => missing: {:?}", fee, ed, free, missing_funds); - trace!( - " encoded extrinsic len: {}, payload: 0x{:}", - extrinsic.len(), - hex::encode(extrinsic.clone()) - ); - - if missing_funds > 0 { - setup_reasonable_account_funding( - api, - fee_payer, - ParentchainId::Litentry, - is_development_mode, - )? - } - - match api.submit_and_watch_opaque_extrinsic_until(&extrinsic.into(), XtStatus::Finalized) { - Ok(xt_report) => { - info!( - "[+] L1 extrinsic success. extrinsic hash: {:?} / status: {:?}", - xt_report.extrinsic_hash, xt_report.status - ); - xt_report.block_hash.ok_or(Error::Custom("no extrinsic hash returned".into())) - }, - Err(e) => { - panic!("Extrinsic failed {:?} parentchain genesis: {:?}", e, api.genesis_hash()); - }, - } -} - -fn start_parentchain_header_subscription_thread( - parentchain_handler: Arc>, - last_synced_header: Header, - shard: ShardIdentifier, -) { - let parentchain_id = *parentchain_handler.parentchain_id(); - thread::Builder::new() - .name(format!("{:?}_parentchain_sync_loop", parentchain_id)) - .spawn(move || { - if let Err(e) = - subscribe_to_parentchain_new_headers(parentchain_handler, last_synced_header, shard) - { - error!( - "[{:?}] parentchain block syncing terminated with a failure: {:?}", - parentchain_id, e - ); - } - println!("[!] [{:?}] parentchain block syncing has terminated", parentchain_id); - }) - .unwrap(); -} - -/// Subscribe to the node API finalized heads stream and trigger a parent chain sync -/// upon receiving a new header. -fn subscribe_to_parentchain_new_headers( - parentchain_handler: Arc>, - mut last_synced_header: Header, - shard: ShardIdentifier, -) -> Result<(), Error> { - // TODO: this should be implemented by parentchain_handler directly, and not via - // exposed parentchain_api - let mut subscription = parentchain_handler - .parentchain_api() - .subscribe_finalized_heads() - .map_err(Error::ApiClient)?; - - // TODO(Kai@Litentry): - // originally we had an outer loop to try to handle the disconnection, - // see https://github.com/litentry/litentry-parachain/commit/b8059d0fad928e4bba99178451cd0d473791c437 - // but I reverted it because: - // - no graceful shutdown, we could have many mpsc channel when it doesn't go right - // - we might have multiple `sync_parentchain` running concurrently, which causes chaos in enclave side - // - I still feel it's only a workaround, not a perfect solution - // - // TODO: now the sync will panic if disconnected - it heavily relys on the worker-restart to work (even manually) - let parentchain_id = parentchain_handler.parentchain_id(); - loop { - let new_header = subscription - .next() - .ok_or(Error::ApiSubscriptionDisconnected)? - .map_err(|e| Error::ApiClient(e.into()))?; - - info!( - "[{:?}] Received finalized header update ({}), syncing parent chain...", - parentchain_id, new_header.number - ); - - last_synced_header = parentchain_handler.sync_parentchain_until_latest_finalized( - last_synced_header, - 0, - shard, - false, - )?; - } -} - -/// Get the public signing key of the TEE. -pub fn enclave_account(enclave_api: &E) -> AccountId32 { - let tee_public = enclave_api.get_ecc_signing_pubkey().unwrap(); - trace!("[+] Got ed25519 account of TEE = {}", tee_public.to_ss58check()); - AccountId32::from(*tee_public.as_array_ref()) -} diff --git a/tee-worker/bitacross/service/src/ocall_bridge/bridge_api.rs b/tee-worker/bitacross/service/src/ocall_bridge/bridge_api.rs deleted file mode 100644 index b3d3a68efa..0000000000 --- a/tee-worker/bitacross/service/src/ocall_bridge/bridge_api.rs +++ /dev/null @@ -1,236 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use itp_enclave_api::remote_attestation::QveReport; -use lazy_static::lazy_static; -use log::*; -use parking_lot::RwLock; -use sgx_types::*; -use std::{sync::Arc, vec::Vec}; - -#[cfg(test)] -use mockall::predicate::*; -#[cfg(test)] -use mockall::*; - -lazy_static! { - /// global state for the component factory - /// access is always routed through 'Bridge', do not use directly! - static ref COMPONENT_FACTORY: RwLock>> = - RwLock::new(None); -} - -/// The Bridge is the static/global interface to inject concrete implementations -/// (or rather the factories for them) - this is done at startup of the worker. -/// On the other side, it is used by the o-call FFI to retrieve the state and forward calls -/// to their respective implementation. -pub struct Bridge; - -impl Bridge { - pub fn get_ra_api() -> Arc { - trace!("Requesting RemoteAttestation OCall API instance"); - - COMPONENT_FACTORY - .read() - .as_ref() - .expect("Component factory has not been set. Use `initialize()`") - .get_ra_api() - } - - pub fn get_oc_api() -> Arc { - trace!("Requesting WorkerOnChain OCall API instance"); - - COMPONENT_FACTORY - .read() - .as_ref() - .expect("Component factory has not been set. Use `initialize()`") - .get_oc_api() - } - - pub fn get_ipfs_api() -> Arc { - trace!("Requesting IPFS OCall API instance"); - - COMPONENT_FACTORY - .read() - .as_ref() - .expect("Component factory has not been set. Use `initialize()`") - .get_ipfs_api() - } - - pub fn get_metrics_api() -> Arc { - COMPONENT_FACTORY - .read() - .as_ref() - .expect("Component factory has not been set. Use `initialize()`") - .get_metrics_api() - } - - pub fn initialize(component_factory: Arc) { - trace!("Initializing OCall bridge with component factory"); - - *COMPONENT_FACTORY.write() = Some(component_factory); - } -} - -/// Factory trait (abstract factory) that creates instances -/// of all the components of the OCall Bridge -pub trait GetOCallBridgeComponents { - /// remote attestation OCall API - fn get_ra_api(&self) -> Arc; - - /// on chain (parentchain) OCall API - fn get_oc_api(&self) -> Arc; - - /// ipfs OCall API - fn get_ipfs_api(&self) -> Arc; - - /// Metrics OCall API. - fn get_metrics_api(&self) -> Arc; -} - -/// OCall bridge errors -#[derive(Debug, thiserror::Error)] -pub enum OCallBridgeError { - #[error("GetQuote Error: {0}")] - GetQuote(sgx_status_t), - #[error("InitQuote Error: {0}")] - InitQuote(sgx_status_t), - #[error("GetUpdateInfo Error: {0}")] - GetUpdateInfo(sgx_status_t), - #[error("GetIasSocket Error: {0}")] - GetIasSocket(String), - #[error("UpdateMetric Error: {0}")] - UpdateMetric(String), - #[error("Propose sidechain block failed: {0}")] - ProposeSidechainBlock(String), - #[error("Failed to fetch sidechain blocks from peer: {0}")] - FetchSidechainBlocksFromPeer(String), - #[error("Sending extrinsics to parentchain failed: {0}")] - SendExtrinsicsToParentchain(String), - #[error("IPFS Error: {0}")] - IpfsError(String), - #[error("DirectInvocation Error: {0}")] - DirectInvocationError(String), - #[error(transparent)] - Codec(#[from] codec::Error), - #[error("Node API factory error: {0}")] - NodeApiFactory(#[from] itp_node_api::node_api_factory::NodeApiFactoryError), - #[error("Target A parentchain not initialized")] - TargetAParentchainNotInitialized, - #[error("Target B parentchain not initialized")] - TargetBParentchainNotInitialized, -} - -impl From for sgx_status_t { - fn from(o: OCallBridgeError) -> sgx_status_t { - match o { - OCallBridgeError::GetQuote(s) => s, - OCallBridgeError::InitQuote(s) => s, - OCallBridgeError::GetUpdateInfo(s) => s, - _ => sgx_status_t::SGX_ERROR_UNEXPECTED, - } - } -} - -pub type OCallBridgeResult = Result; - -/// Trait for all the OCalls related to remote attestation -#[cfg_attr(test, automock)] -pub trait RemoteAttestationBridge { - /// initialize the quote - fn init_quote(&self) -> OCallBridgeResult<(sgx_target_info_t, sgx_epid_group_id_t)>; - - /// get the intel attestation service socket - fn get_ias_socket(&self) -> OCallBridgeResult; - - /// retrieve the quote from intel - fn get_quote( - &self, - revocation_list: Vec, - report: sgx_report_t, - quote_type: sgx_quote_sign_type_t, - spid: sgx_spid_t, - quote_nonce: sgx_quote_nonce_t, - ) -> OCallBridgeResult<(sgx_report_t, Vec)>; - - /// retrieve the quote from dcap server - fn get_dcap_quote(&self, report: sgx_report_t, quote_size: u32) -> OCallBridgeResult>; - - // Retrieve verification of quote - fn get_qve_report_on_quote( - &self, - quote: Vec, - current_time: i64, - quote_collateral: &sgx_ql_qve_collateral_t, - qve_report_info: sgx_ql_qe_report_info_t, - supplemental_data_size: u32, - ) -> OCallBridgeResult; - - /// -- - fn get_update_info( - &self, - platform_blob: sgx_platform_info_t, - enclave_trusted: i32, - ) -> OCallBridgeResult; -} - -/// Trait for all the OCalls related to parentchain operations -#[cfg_attr(test, automock)] -pub trait WorkerOnChainBridge { - fn worker_request( - &self, - request: Vec, - parentchain_id: Vec, - ) -> OCallBridgeResult>; - - fn send_to_parentchain( - &self, - extrinsics_encoded: Vec, - parentchain_id: Vec, - watch_until: Vec, - ) -> OCallBridgeResult>; -} - -/// Trait for updating metrics from inside the enclave. -#[cfg_attr(test, automock)] -pub trait MetricsBridge { - fn update_metric(&self, metric_encoded: Vec) -> OCallBridgeResult<()>; -} - -/// type for IPFS -pub type Cid = [u8; 46]; - -/// Trait for all the OCalls related to IPFS -#[cfg_attr(test, automock)] -pub trait IpfsBridge { - fn write_to_ipfs(&self, data: &'static [u8]) -> OCallBridgeResult; - - fn read_from_ipfs(&self, cid: Cid) -> OCallBridgeResult<()>; -} - -/// Trait for the direct invocation OCalls -#[cfg_attr(test, automock)] -pub trait DirectInvocationBridge { - fn update_status_event( - &self, - hash_vec: Vec, - status_update_vec: Vec, - ) -> OCallBridgeResult<()>; - - fn send_status(&self, hash_vec: Vec, status_vec: Vec) -> OCallBridgeResult<()>; -} diff --git a/tee-worker/bitacross/service/src/ocall_bridge/component_factory.rs b/tee-worker/bitacross/service/src/ocall_bridge/component_factory.rs deleted file mode 100644 index d82d108937..0000000000 --- a/tee-worker/bitacross/service/src/ocall_bridge/component_factory.rs +++ /dev/null @@ -1,117 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - globals::tokio_handle::GetTokioHandle, - ocall_bridge::{ - bridge_api::{ - GetOCallBridgeComponents, IpfsBridge, MetricsBridge, RemoteAttestationBridge, - WorkerOnChainBridge, - }, - ipfs_ocall::IpfsOCall, - metrics_ocall::MetricsOCall, - remote_attestation_ocall::RemoteAttestationOCall, - worker_on_chain_ocall::WorkerOnChainOCall, - }, - prometheus_metrics::ReceiveEnclaveMetrics, - worker_peers_updater::PeersRegistry, -}; -use itp_enclave_api::{enclave_base::EnclaveBase, remote_attestation::RemoteAttestationCallBacks}; -use itp_node_api::node_api_factory::CreateNodeApi; -use std::sync::Arc; - -/// Concrete implementation, should be moved out of the OCall Bridge, into the worker -/// since the OCall bridge itself should not know any concrete types to ensure -/// our dependency graph is worker -> ocall bridge -pub struct OCallBridgeComponentFactory< - NodeApi, - EnclaveApi, - WorkerPeersRegistry, - TokioHandle, - MetricsReceiver, -> { - integritee_rpc_api_factory: Arc, - target_a_parentchain_rpc_api_factory: Option>, - target_b_parentchain_rpc_api_factory: Option>, - enclave_api: Arc, - peers_registry: Arc, - tokio_handle: Arc, - metrics_receiver: Arc, -} - -impl - OCallBridgeComponentFactory -{ - #[allow(clippy::too_many_arguments)] - pub fn new( - integritee_rpc_api_factory: Arc, - target_a_parentchain_rpc_api_factory: Option>, - target_b_parentchain_rpc_api_factory: Option>, - enclave_api: Arc, - peers_registry: Arc, - tokio_handle: Arc, - metrics_receiver: Arc, - ) -> Self { - OCallBridgeComponentFactory { - integritee_rpc_api_factory, - target_a_parentchain_rpc_api_factory, - target_b_parentchain_rpc_api_factory, - enclave_api, - peers_registry, - tokio_handle, - metrics_receiver, - } - } -} - -impl - GetOCallBridgeComponents - for OCallBridgeComponentFactory< - NodeApi, - EnclaveApi, - WorkerPeersRegistry, - TokioHandle, - MetricsReceiver, - > where - NodeApi: CreateNodeApi + 'static, - EnclaveApi: EnclaveBase + RemoteAttestationCallBacks + 'static, - WorkerPeersRegistry: PeersRegistry + 'static, - TokioHandle: GetTokioHandle + 'static, - MetricsReceiver: ReceiveEnclaveMetrics + 'static, -{ - fn get_ra_api(&self) -> Arc { - Arc::new(RemoteAttestationOCall::new(self.enclave_api.clone())) - } - - fn get_oc_api(&self) -> Arc { - Arc::new(WorkerOnChainOCall::new( - self.enclave_api.clone(), - self.integritee_rpc_api_factory.clone(), - self.target_a_parentchain_rpc_api_factory.clone(), - self.target_b_parentchain_rpc_api_factory.clone(), - )) - } - - fn get_ipfs_api(&self) -> Arc { - Arc::new(IpfsOCall {}) - } - - fn get_metrics_api(&self) -> Arc { - Arc::new(MetricsOCall::new(self.metrics_receiver.clone())) - } -} diff --git a/tee-worker/bitacross/service/src/ocall_bridge/ffi/get_ias_socket.rs b/tee-worker/bitacross/service/src/ocall_bridge/ffi/get_ias_socket.rs deleted file mode 100644 index 4b48d2b1ad..0000000000 --- a/tee-worker/bitacross/service/src/ocall_bridge/ffi/get_ias_socket.rs +++ /dev/null @@ -1,86 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::ocall_bridge::bridge_api::{Bridge, RemoteAttestationBridge}; -use log::*; -use sgx_types::{c_int, sgx_status_t}; -use std::sync::Arc; - -#[no_mangle] -pub extern "C" fn ocall_get_ias_socket(ret_fd: *mut c_int) -> sgx_status_t { - get_ias_socket(ret_fd, Bridge::get_ra_api()) // inject the RA API (global state) -} - -fn get_ias_socket(ret_fd: *mut c_int, ra_api: Arc) -> sgx_status_t { - debug!(" Entering ocall_get_ias_socket"); - let socket_result = ra_api.get_ias_socket(); - - return match socket_result { - Ok(s) => { - unsafe { - *ret_fd = s; - } - sgx_status_t::SGX_SUCCESS - }, - Err(e) => { - error!("[-] Failed to get IAS socket: {:?}", e); - return e.into() - }, - } -} - -#[cfg(test)] -mod tests { - - use super::*; - use crate::ocall_bridge::bridge_api::{MockRemoteAttestationBridge, OCallBridgeError}; - use std::sync::Arc; - - #[test] - fn get_socket_sets_pointer_result() { - let expected_socket = 4321i32; - - let mut ra_ocall_api_mock = MockRemoteAttestationBridge::new(); - ra_ocall_api_mock - .expect_get_ias_socket() - .times(1) - .returning(move || Ok(expected_socket)); - - let mut ias_sock: i32 = 0; - - let ret_status = get_ias_socket(&mut ias_sock as *mut i32, Arc::new(ra_ocall_api_mock)); - - assert_eq!(ret_status, sgx_status_t::SGX_SUCCESS); - assert_eq!(ias_sock, expected_socket); - } - - #[test] - fn given_error_from_ocall_impl_then_return_sgx_error() { - let mut ra_ocall_api_mock = MockRemoteAttestationBridge::new(); - ra_ocall_api_mock - .expect_get_ias_socket() - .times(1) - .returning(|| Err(OCallBridgeError::GetIasSocket("test error".to_string()))); - - let mut ias_sock: i32 = 0; - let ret_status = get_ias_socket(&mut ias_sock as *mut i32, Arc::new(ra_ocall_api_mock)); - - assert_ne!(ret_status, sgx_status_t::SGX_SUCCESS); - assert_eq!(ias_sock, 0); - } -} diff --git a/tee-worker/bitacross/service/src/ocall_bridge/ffi/get_quote.rs b/tee-worker/bitacross/service/src/ocall_bridge/ffi/get_quote.rs deleted file mode 100644 index abf2954170..0000000000 --- a/tee-worker/bitacross/service/src/ocall_bridge/ffi/get_quote.rs +++ /dev/null @@ -1,140 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::ocall_bridge::bridge_api::{Bridge, RemoteAttestationBridge}; -use log::*; -use sgx_types::{sgx_quote_nonce_t, sgx_quote_sign_type_t, sgx_report_t, sgx_spid_t, sgx_status_t}; -use std::{slice, sync::Arc}; - -/// p_quote must be a pre-allocated memory region of size `maxlen` -#[no_mangle] -pub unsafe extern "C" fn ocall_get_quote( - p_sigrl: *const u8, - sigrl_len: u32, - p_report: *const sgx_report_t, - quote_type: sgx_quote_sign_type_t, - p_spid: *const sgx_spid_t, - p_nonce: *const sgx_quote_nonce_t, - p_qe_report: *mut sgx_report_t, - p_quote: *mut u8, - maxlen: u32, - p_quote_len: *mut u32, -) -> sgx_status_t { - get_quote( - p_sigrl, - sigrl_len, - p_report, - quote_type, - p_spid, - p_nonce, - p_qe_report, - p_quote, - maxlen, - p_quote_len, - Bridge::get_ra_api(), // inject the RA API (global state) - ) -} - -#[allow(clippy::too_many_arguments)] -fn get_quote( - p_sigrl: *const u8, - sigrl_len: u32, - p_report: *const sgx_report_t, - quote_type: sgx_quote_sign_type_t, - p_spid: *const sgx_spid_t, - p_nonce: *const sgx_quote_nonce_t, - p_qe_report: *mut sgx_report_t, - p_quote: *mut u8, - maxlen: u32, - p_quote_len: *mut u32, - ra_api: Arc, -) -> sgx_status_t { - debug!(" Entering ocall_get_quote"); - - let revocation_list: Vec = - unsafe { slice::from_raw_parts(p_sigrl, sigrl_len as usize).to_vec() }; - - let report = unsafe { *p_report }; - let spid = unsafe { *p_spid }; - let quote_nonce = unsafe { *p_nonce }; - - let get_quote_result = - match ra_api.get_quote(revocation_list, report, quote_type, spid, quote_nonce) { - Ok(r) => r, - Err(e) => { - error!("[-] Failed to get quote: {:?}", e); - return e.into() - }, - }; - - let quote = get_quote_result.1; - - if quote.len() as u32 > maxlen { - return sgx_status_t::SGX_ERROR_FAAS_BUFFER_TOO_SHORT - } - - let quote_slice = unsafe { slice::from_raw_parts_mut(p_quote, quote.len()) }; - quote_slice.clone_from_slice(quote.as_slice()); - - unsafe { - *p_qe_report = get_quote_result.0; - *p_quote_len = quote.len() as u32; - }; - - sgx_status_t::SGX_SUCCESS -} - -#[no_mangle] -pub unsafe extern "C" fn ocall_get_dcap_quote( - p_report: *const sgx_report_t, - p_quote: *mut u8, - quote_size: u32, -) -> sgx_status_t { - get_dcap_quote( - p_report, - p_quote, - quote_size, - Bridge::get_ra_api(), // inject the RA API (global state) - ) -} - -fn get_dcap_quote( - p_report: *const sgx_report_t, - p_quote: *mut u8, - quote_size: u32, - ra_api: Arc, -) -> sgx_status_t { - let report = unsafe { *p_report }; - - let quote = match ra_api.get_dcap_quote(report, quote_size) { - Ok(r) => r, - Err(e) => { - error!("Failed to get dcap quote: {:?}", e); - return e.into() - }, - }; - - if quote.len() as u32 > quote_size { - return sgx_status_t::SGX_ERROR_FAAS_BUFFER_TOO_SHORT - } - - let quote_slice = unsafe { slice::from_raw_parts_mut(p_quote, quote.len()) }; - quote_slice.clone_from_slice(quote.as_slice()); - - sgx_status_t::SGX_SUCCESS -} diff --git a/tee-worker/bitacross/service/src/ocall_bridge/ffi/get_qve_report_on_quote.rs b/tee-worker/bitacross/service/src/ocall_bridge/ffi/get_qve_report_on_quote.rs deleted file mode 100755 index 2b73894830..0000000000 --- a/tee-worker/bitacross/service/src/ocall_bridge/ffi/get_qve_report_on_quote.rs +++ /dev/null @@ -1,100 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -use crate::ocall_bridge::bridge_api::{Bridge, RemoteAttestationBridge}; -use log::*; -use sgx_types::*; -use std::{slice, sync::Arc}; - -#[no_mangle] -pub unsafe extern "C" fn ocall_get_qve_report_on_quote( - p_quote: *const u8, - quote_len: u32, - current_time: i64, - p_quote_collateral: *const sgx_ql_qve_collateral_t, - p_collateral_expiration_status: *mut u32, - p_quote_verification_result: *mut sgx_ql_qv_result_t, - p_qve_report_info: *mut sgx_ql_qe_report_info_t, - p_supplemental_data: *mut u8, - supplemental_data_size: u32, -) -> sgx_status_t { - get_qve_report_on_quote( - p_quote, - quote_len, - current_time, - p_quote_collateral, - p_collateral_expiration_status, - p_quote_verification_result, - p_qve_report_info, - p_supplemental_data, - supplemental_data_size, - Bridge::get_ra_api(), // inject the RA API (global state) - ) -} - -#[allow(clippy::too_many_arguments)] -fn get_qve_report_on_quote( - p_quote: *const u8, - quote_len: u32, - current_time: i64, - p_quote_collateral: *const sgx_ql_qve_collateral_t, - p_collateral_expiration_status: *mut u32, - p_quote_verification_result: *mut sgx_ql_qv_result_t, - p_qve_report_info: *mut sgx_ql_qe_report_info_t, - p_supplemental_data: *mut u8, - supplemental_data_size: u32, - ra_api: Arc, -) -> sgx_status_t { - debug!("Entering ocall_get_qve_report_on_quote"); - if p_quote.is_null() - || quote_len == 0 - || p_quote_collateral.is_null() - || p_collateral_expiration_status.is_null() - || p_quote_verification_result.is_null() - || p_qve_report_info.is_null() - || p_supplemental_data.is_null() - || supplemental_data_size == 0 - { - return sgx_status_t::SGX_ERROR_INVALID_PARAMETER - } - let quote: Vec = unsafe { slice::from_raw_parts(p_quote, quote_len as usize).to_vec() }; - let quote_collateral = unsafe { &*p_quote_collateral }; - let qve_report_info = unsafe { *p_qve_report_info }; - - let qve_report = match ra_api.get_qve_report_on_quote( - quote, - current_time, - quote_collateral, - qve_report_info, - supplemental_data_size, - ) { - Ok(return_values) => return_values, - Err(e) => { - error!("Failed to get quote: {:?}", e); - return e.into() - }, - }; - - let supplemental_data_slice = - unsafe { slice::from_raw_parts_mut(p_supplemental_data, supplemental_data_size as usize) }; - supplemental_data_slice.clone_from_slice(qve_report.supplemental_data.as_slice()); - - unsafe { - *p_collateral_expiration_status = qve_report.collateral_expiration_status; - *p_quote_verification_result = qve_report.quote_verification_result; - *p_qve_report_info = qve_report.qve_report_info_return_value; - }; - - sgx_status_t::SGX_SUCCESS -} diff --git a/tee-worker/bitacross/service/src/ocall_bridge/ffi/get_update_info.rs b/tee-worker/bitacross/service/src/ocall_bridge/ffi/get_update_info.rs deleted file mode 100644 index 55a9c7bfb4..0000000000 --- a/tee-worker/bitacross/service/src/ocall_bridge/ffi/get_update_info.rs +++ /dev/null @@ -1,61 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::ocall_bridge::bridge_api::{Bridge, RemoteAttestationBridge}; -use log::*; -use sgx_types::{sgx_platform_info_t, sgx_status_t, sgx_update_info_bit_t}; -use std::sync::Arc; - -#[no_mangle] -pub extern "C" fn ocall_get_update_info( - p_platform_blob: *const sgx_platform_info_t, - enclave_trusted: i32, - p_update_info: *mut sgx_update_info_bit_t, -) -> sgx_status_t { - get_update_info( - p_platform_blob, - enclave_trusted, - p_update_info, - Bridge::get_ra_api(), // inject the RA API (global state) - ) -} - -fn get_update_info( - p_platform_blob: *const sgx_platform_info_t, - enclave_trusted: i32, - p_update_info: *mut sgx_update_info_bit_t, - ra_api: Arc, -) -> sgx_status_t { - debug!(" Entering ocall_get_update_info"); - - let platform_blob = unsafe { *p_platform_blob }; - - let update_info_result = match ra_api.get_update_info(platform_blob, enclave_trusted) { - Ok(r) => r, - Err(e) => { - error!("[-] Failed to get update info: {:?}", e); - return e.into() - }, - }; - - unsafe { - *p_update_info = update_info_result; - } - - sgx_status_t::SGX_SUCCESS -} diff --git a/tee-worker/bitacross/service/src/ocall_bridge/ffi/init_quote.rs b/tee-worker/bitacross/service/src/ocall_bridge/ffi/init_quote.rs deleted file mode 100644 index 095e01af6d..0000000000 --- a/tee-worker/bitacross/service/src/ocall_bridge/ffi/init_quote.rs +++ /dev/null @@ -1,85 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::ocall_bridge::bridge_api::{Bridge, RemoteAttestationBridge}; -use log::*; -use sgx_types::{sgx_epid_group_id_t, sgx_status_t, sgx_target_info_t}; -use std::sync::Arc; - -#[no_mangle] -pub unsafe extern "C" fn ocall_sgx_init_quote( - ret_ti: *mut sgx_target_info_t, - ret_gid: *mut sgx_epid_group_id_t, -) -> sgx_status_t { - sgx_init_quote(ret_ti, ret_gid, Bridge::get_ra_api()) // inject the RA API (global state) -} - -fn sgx_init_quote( - ret_ti: *mut sgx_target_info_t, - ret_gid: *mut sgx_epid_group_id_t, - ra_api: Arc, -) -> sgx_status_t { - debug!(" Entering ocall_sgx_init_quote"); - let init_result = match ra_api.init_quote() { - Ok(r) => r, - Err(e) => { - error!("[-] Failed to init quote: {:?}", e); - return e.into() - }, - }; - - unsafe { - *ret_ti = init_result.0; - *ret_gid = init_result.1; - } - - sgx_status_t::SGX_SUCCESS -} - -#[cfg(test)] -mod tests { - - use super::*; - use crate::ocall_bridge::bridge_api::MockRemoteAttestationBridge; - use std::sync::Arc; - - #[test] - fn init_quote_sets_results() { - let mut ra_ocall_api_mock = MockRemoteAttestationBridge::new(); - ra_ocall_api_mock - .expect_init_quote() - .times(1) - .returning(|| Ok((dummy_target_info(), [8u8; 4]))); - - let mut ti: sgx_target_info_t = sgx_target_info_t::default(); - let mut eg: sgx_epid_group_id_t = sgx_epid_group_id_t::default(); - - let ret_status = sgx_init_quote( - &mut ti as *mut sgx_target_info_t, - &mut eg as *mut sgx_epid_group_id_t, - Arc::new(ra_ocall_api_mock), - ); - - assert_eq!(ret_status, sgx_status_t::SGX_SUCCESS); - assert_eq!(eg, [8u8; 4]); - } - - fn dummy_target_info() -> sgx_target_info_t { - sgx_target_info_t::default() - } -} diff --git a/tee-worker/bitacross/service/src/ocall_bridge/ffi/ipfs.rs b/tee-worker/bitacross/service/src/ocall_bridge/ffi/ipfs.rs deleted file mode 100644 index e264b49db2..0000000000 --- a/tee-worker/bitacross/service/src/ocall_bridge/ffi/ipfs.rs +++ /dev/null @@ -1,76 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::ocall_bridge::bridge_api::{Bridge, Cid, IpfsBridge}; -use log::*; -use sgx_types::sgx_status_t; -use std::{slice, sync::Arc}; - -/// C-API exposed for o-call from enclave -#[no_mangle] -pub unsafe extern "C" fn ocall_write_ipfs( - enc_state: *const u8, - enc_state_size: u32, - cid: *mut u8, - cid_size: u32, -) -> sgx_status_t { - write_ipfs(enc_state, enc_state_size, cid, cid_size, Bridge::get_ipfs_api()) -} - -/// C-API exposed for o-call from enclave -#[no_mangle] -pub unsafe extern "C" fn ocall_read_ipfs(cid: *const u8, cid_size: u32) -> sgx_status_t { - read_ipfs(cid, cid_size, Bridge::get_ipfs_api()) -} - -fn write_ipfs( - enc_state: *const u8, - enc_state_size: u32, - cid: *mut u8, - cid_size: u32, - ipfs_api: Arc, -) -> sgx_status_t { - let state = unsafe { slice::from_raw_parts(enc_state, enc_state_size as usize) }; - let cid = unsafe { slice::from_raw_parts_mut(cid, cid_size as usize) }; - - return match ipfs_api.write_to_ipfs(state) { - Ok(r) => { - cid.clone_from_slice(&r); - sgx_status_t::SGX_SUCCESS - }, - Err(e) => { - error!("OCall to write_ipfs failed: {:?}", e); - sgx_status_t::SGX_ERROR_UNEXPECTED - }, - } -} - -fn read_ipfs(cid: *const u8, cid_size: u32, ipfs_api: Arc) -> sgx_status_t { - let _cid = unsafe { slice::from_raw_parts(cid, cid_size as usize) }; - - let mut cid: Cid = [0; 46]; - cid.clone_from_slice(_cid); - - match ipfs_api.read_from_ipfs(cid) { - Ok(_) => sgx_status_t::SGX_SUCCESS, - Err(e) => { - error!("OCall to read_ipfs failed: {:?}", e); - sgx_status_t::SGX_ERROR_UNEXPECTED - }, - } -} diff --git a/tee-worker/bitacross/service/src/ocall_bridge/ffi/mod.rs b/tee-worker/bitacross/service/src/ocall_bridge/ffi/mod.rs deleted file mode 100644 index b18f24bf15..0000000000 --- a/tee-worker/bitacross/service/src/ocall_bridge/ffi/mod.rs +++ /dev/null @@ -1,32 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Foreign Function interface for all the OCalls. -//! Implementations of C-API functions, that can be called from the Enclave. -//! These should just be wrappers that transform the C-API structures and call the -//! actual implementation of the OCalls (using the traits defined in the bridge_api). - -pub mod get_ias_socket; -pub mod get_quote; -pub mod get_qve_report_on_quote; -pub mod get_update_info; -pub mod init_quote; -pub mod ipfs; -pub mod send_to_parentchain; -pub mod update_metric; -pub mod worker_request; diff --git a/tee-worker/bitacross/service/src/ocall_bridge/ffi/send_to_parentchain.rs b/tee-worker/bitacross/service/src/ocall_bridge/ffi/send_to_parentchain.rs deleted file mode 100644 index 2fc6c0511f..0000000000 --- a/tee-worker/bitacross/service/src/ocall_bridge/ffi/send_to_parentchain.rs +++ /dev/null @@ -1,86 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::ocall_bridge::bridge_api::{Bridge, WorkerOnChainBridge}; -use itp_utils::write_slice_and_whitespace_pad; -use log::*; -use sgx_types::{c_int, sgx_status_t}; -use std::{slice, sync::Arc, vec::Vec}; - -/// # Safety -/// -/// FFI are always unsafe -#[no_mangle] -pub unsafe extern "C" fn ocall_send_to_parentchain( - extrinsics_encoded: *const u8, - extrinsics_encoded_size: u32, - parentchain_id: *const u8, - parentchain_id_size: u32, - watch_until: *const u8, - watch_until_size: u32, - response: *mut u8, - resp_size: u32, -) -> sgx_status_t { - send_to_parentchain( - extrinsics_encoded, - extrinsics_encoded_size, - parentchain_id, - parentchain_id_size, - watch_until, - watch_until_size, - response, - resp_size, - Bridge::get_oc_api(), - ) -} - -#[allow(clippy::too_many_arguments)] -fn send_to_parentchain( - extrinsics_encoded: *const u8, - extrinsics_encoded_size: u32, - parentchain_id: *const u8, - parentchain_id_size: u32, - watch_until: *const u8, - watch_until_size: u32, - response: *mut u8, - resp_size: u32, - oc_api: Arc, -) -> sgx_status_t { - let extrinsics_encoded_vec: Vec = unsafe { - Vec::from(slice::from_raw_parts(extrinsics_encoded, extrinsics_encoded_size as usize)) - }; - let parentchain_id: Vec = - unsafe { Vec::from(slice::from_raw_parts(parentchain_id, parentchain_id_size as usize)) }; - let watch_until: Vec = - unsafe { Vec::from(slice::from_raw_parts(watch_until, watch_until_size as usize)) }; - - match oc_api.send_to_parentchain(extrinsics_encoded_vec, parentchain_id, watch_until) { - Ok(r) => { - let resp_slice = unsafe { slice::from_raw_parts_mut(response, resp_size as usize) }; - if let Err(e) = write_slice_and_whitespace_pad(resp_slice, r) { - error!("Failed to transfer send_to_parentchain response to o-call buffer: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - } - sgx_status_t::SGX_SUCCESS - }, - Err(e) => { - error!("send extrinsics_encoded failed: {:?}", e); - sgx_status_t::SGX_ERROR_UNEXPECTED - }, - } -} diff --git a/tee-worker/bitacross/service/src/ocall_bridge/ffi/update_metric.rs b/tee-worker/bitacross/service/src/ocall_bridge/ffi/update_metric.rs deleted file mode 100644 index 0b97de74f9..0000000000 --- a/tee-worker/bitacross/service/src/ocall_bridge/ffi/update_metric.rs +++ /dev/null @@ -1,50 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::ocall_bridge::bridge_api::{Bridge, MetricsBridge}; -use log::*; -use sgx_types::sgx_status_t; -use std::{slice, sync::Arc}; - -/// # Safety -/// -/// FFI are always unsafe -#[no_mangle] -pub unsafe extern "C" fn ocall_update_metric( - metric_ptr: *const u8, - metric_size: u32, -) -> sgx_status_t { - update_metric(metric_ptr, metric_size, Bridge::get_metrics_api()) -} - -fn update_metric( - metric_ptr: *const u8, - metric_size: u32, - oc_api: Arc, -) -> sgx_status_t { - let metric_encoded: Vec = - unsafe { Vec::from(slice::from_raw_parts(metric_ptr, metric_size as usize)) }; - - match oc_api.update_metric(metric_encoded) { - Ok(_) => sgx_status_t::SGX_SUCCESS, - Err(e) => { - error!("update_metric o-call failed: {:?}", e); - sgx_status_t::SGX_ERROR_UNEXPECTED - }, - } -} diff --git a/tee-worker/bitacross/service/src/ocall_bridge/ffi/worker_request.rs b/tee-worker/bitacross/service/src/ocall_bridge/ffi/worker_request.rs deleted file mode 100644 index 7dbd9be957..0000000000 --- a/tee-worker/bitacross/service/src/ocall_bridge/ffi/worker_request.rs +++ /dev/null @@ -1,77 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::ocall_bridge::bridge_api::{Bridge, WorkerOnChainBridge}; -use itp_utils::write_slice_and_whitespace_pad; -use log::*; -use sgx_types::sgx_status_t; -use std::{slice, sync::Arc, vec::Vec}; - -/// # Safety -/// -/// FFI are always unsafe -#[no_mangle] -pub unsafe extern "C" fn ocall_worker_request( - request: *const u8, - req_size: u32, - parentchain_id: *const u8, - parentchain_id_size: u32, - response: *mut u8, - resp_size: u32, -) -> sgx_status_t { - worker_request( - request, - req_size, - parentchain_id, - parentchain_id_size, - response, - resp_size, - Bridge::get_oc_api(), - ) -} - -fn worker_request( - request: *const u8, - req_size: u32, - parentchain_id: *const u8, - parentchain_id_size: u32, - response: *mut u8, - resp_size: u32, - oc_api: Arc, -) -> sgx_status_t { - let request_vec: Vec = - unsafe { Vec::from(slice::from_raw_parts(request, req_size as usize)) }; - - let parentchain_id: Vec = - unsafe { Vec::from(slice::from_raw_parts(parentchain_id, parentchain_id_size as usize)) }; - - match oc_api.worker_request(request_vec, parentchain_id) { - Ok(r) => { - let resp_slice = unsafe { slice::from_raw_parts_mut(response, resp_size as usize) }; - if let Err(e) = write_slice_and_whitespace_pad(resp_slice, r) { - error!("Failed to transfer worker request response to o-call buffer: {:?}", e); - return sgx_status_t::SGX_ERROR_UNEXPECTED - } - sgx_status_t::SGX_SUCCESS - }, - Err(e) => { - error!("Worker request failed: {:?}", e); - sgx_status_t::SGX_ERROR_UNEXPECTED - }, - } -} diff --git a/tee-worker/bitacross/service/src/ocall_bridge/ipfs_ocall.rs b/tee-worker/bitacross/service/src/ocall_bridge/ipfs_ocall.rs deleted file mode 100644 index 1dc1d9beab..0000000000 --- a/tee-worker/bitacross/service/src/ocall_bridge/ipfs_ocall.rs +++ /dev/null @@ -1,112 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::ocall_bridge::bridge_api::{Cid, IpfsBridge, OCallBridgeError, OCallBridgeResult}; -use futures::TryStreamExt; -use ipfs_api::IpfsClient; -use log::*; -use std::{ - fs::File, - io::{Cursor, Write}, - str, - sync::mpsc::channel, -}; - -pub struct IpfsOCall; - -impl IpfsBridge for IpfsOCall { - fn write_to_ipfs(&self, data: &'static [u8]) -> OCallBridgeResult { - debug!(" Entering ocall_write_ipfs"); - write_to_ipfs(data) - } - - fn read_from_ipfs(&self, cid: Cid) -> OCallBridgeResult<()> { - debug!("Entering ocall_read_ipfs"); - - let result = read_from_ipfs(cid); - match result { - Ok(res) => { - let filename = str::from_utf8(&cid).map_err(|_| { - OCallBridgeError::IpfsError("Could not convert cid bytes".to_string()) - })?; - create_file(filename, &res).map_err(OCallBridgeError::IpfsError) - }, - Err(_) => Err(OCallBridgeError::IpfsError("failed to read from IPFS".to_string())), - } - } -} - -fn create_file(filename: &str, result: &[u8]) -> Result<(), String> { - match File::create(filename) { - Ok(mut f) => f - .write_all(result) - .map_or_else(|e| Err(format!("failed writing to file: {}", e)), |_| Ok(())), - Err(e) => Err(format!("failed to create file: {}", e)), - } -} - -#[tokio::main] -async fn write_to_ipfs(data: &'static [u8]) -> OCallBridgeResult { - // Creates an `IpfsClient` connected to the endpoint specified in ~/.ipfs/api. - // If not found, tries to connect to `localhost:5001`. - let client = IpfsClient::default(); - - match client.version().await { - Ok(version) => info!("version: {:?}", version.version), - Err(e) => eprintln!("error getting version: {}", e), - } - - let datac = Cursor::new(data); - let (tx, rx) = channel(); - - match client.add(datac).await { - Ok(res) => { - info!("Result Hash {}", res.hash); - tx.send(res.hash.into_bytes()).map_err(|e| { - OCallBridgeError::IpfsError(format!( - "Could not get result from IPFS, reason: {:?}", - e - )) - })? - }, - Err(e) => eprintln!("error adding file: {}", e), - } - let mut cid: Cid = [0; 46]; - let result = &rx.recv().map_err(|e| { - OCallBridgeError::IpfsError(format!("Could not get result from IPFS, reason: {:?}", e)) - })?; - cid.clone_from_slice(result); - Ok(cid) -} - -#[tokio::main] -pub async fn read_from_ipfs(cid: Cid) -> Result, String> { - // Creates an `IpfsClient` connected to the endpoint specified in ~/.ipfs/api. - // If not found, tries to connect to `localhost:5001`. - let client = IpfsClient::default(); - let h = str::from_utf8(&cid).map_err(|_| "Could not convert cid bytes".to_string())?; - - info!("Fetching content from: {}", h); - - client - .cat(h) - .map_ok(|chunk| chunk.to_vec()) - .map_err(|e| e.to_string()) - .try_concat() - .await -} diff --git a/tee-worker/bitacross/service/src/ocall_bridge/metrics_ocall.rs b/tee-worker/bitacross/service/src/ocall_bridge/metrics_ocall.rs deleted file mode 100644 index a06deff339..0000000000 --- a/tee-worker/bitacross/service/src/ocall_bridge/metrics_ocall.rs +++ /dev/null @@ -1,51 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - ocall_bridge::bridge_api::{MetricsBridge, OCallBridgeError, OCallBridgeResult}, - prometheus_metrics::ReceiveEnclaveMetrics, -}; -use codec::Decode; -use itp_enclave_metrics::EnclaveMetric; -use std::sync::Arc; - -pub struct MetricsOCall { - receiver: Arc, -} - -impl MetricsOCall { - pub fn new(receiver: Arc) -> Self { - MetricsOCall { receiver } - } -} - -impl MetricsBridge for MetricsOCall -where - MetricsReceiver: ReceiveEnclaveMetrics, -{ - fn update_metric(&self, metric_encoded: Vec) -> OCallBridgeResult<()> { - let metric: EnclaveMetric = - Decode::decode(&mut metric_encoded.as_slice()).map_err(|e| { - OCallBridgeError::UpdateMetric(format!("Failed to decode metric: {:?}", e)) - })?; - - self.receiver.receive_enclave_metric(metric).map_err(|e| { - OCallBridgeError::UpdateMetric(format!("Failed to receive enclave metric: {:?}", e)) - }) - } -} diff --git a/tee-worker/bitacross/service/src/ocall_bridge/mod.rs b/tee-worker/bitacross/service/src/ocall_bridge/mod.rs deleted file mode 100644 index db5775cec3..0000000000 --- a/tee-worker/bitacross/service/src/ocall_bridge/mod.rs +++ /dev/null @@ -1,28 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -// TODO This entire module should be extracted to a separate crate and re-named to o-call tunnel, see #288 and #316 - -pub mod bridge_api; -pub mod component_factory; - -mod ffi; -mod ipfs_ocall; -mod metrics_ocall; -mod remote_attestation_ocall; -mod worker_on_chain_ocall; diff --git a/tee-worker/bitacross/service/src/ocall_bridge/remote_attestation_ocall.rs b/tee-worker/bitacross/service/src/ocall_bridge/remote_attestation_ocall.rs deleted file mode 100644 index 0310f7ad18..0000000000 --- a/tee-worker/bitacross/service/src/ocall_bridge/remote_attestation_ocall.rs +++ /dev/null @@ -1,150 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::ocall_bridge::bridge_api::{ - OCallBridgeError, OCallBridgeResult, RemoteAttestationBridge, -}; -use itp_enclave_api::remote_attestation::{QveReport, RemoteAttestationCallBacks}; -use log::debug; -use sgx_types::*; -use std::{ - net::{SocketAddr, TcpStream}, - os::unix::io::IntoRawFd, - sync::Arc, -}; - -pub struct RemoteAttestationOCall { - enclave_api: Arc, -} - -impl RemoteAttestationOCall { - pub fn new(enclave_api: Arc) -> Self { - RemoteAttestationOCall { enclave_api } - } -} - -impl RemoteAttestationBridge for RemoteAttestationOCall -where - E: RemoteAttestationCallBacks, -{ - fn init_quote(&self) -> OCallBridgeResult<(sgx_target_info_t, sgx_epid_group_id_t)> { - debug!("RemoteAttestationBridge: init quote"); - self.enclave_api.init_quote().map_err(|e| match e { - itp_enclave_api::error::Error::Sgx(s) => OCallBridgeError::InitQuote(s), - _ => OCallBridgeError::InitQuote(sgx_status_t::SGX_ERROR_UNEXPECTED), - }) - } - - fn get_ias_socket(&self) -> OCallBridgeResult { - let port = 443; - let hostname = "api.trustedservices.intel.com"; - - let addr = lookup_ipv4(hostname, port).map_err(OCallBridgeError::GetIasSocket)?; - - let stream = TcpStream::connect(addr).map_err(|_| { - OCallBridgeError::GetIasSocket("[-] Connect tls server failed!".to_string()) - })?; - - Ok(stream.into_raw_fd()) - } - - fn get_quote( - &self, - revocation_list: Vec, - report: sgx_report_t, - quote_type: sgx_quote_sign_type_t, - spid: sgx_spid_t, - quote_nonce: sgx_quote_nonce_t, - ) -> OCallBridgeResult<(sgx_report_t, Vec)> { - debug!("RemoteAttestationBridge: get quote type: {:?}", quote_type); - let real_quote_len = - self.enclave_api.calc_quote_size(revocation_list.clone()).map_err(|e| match e { - itp_enclave_api::error::Error::Sgx(s) => OCallBridgeError::GetQuote(s), - _ => OCallBridgeError::GetQuote(sgx_status_t::SGX_ERROR_UNEXPECTED), - })?; - - debug!("RemoteAttestationBridge: real quote length: {}", real_quote_len); - self.enclave_api - .get_quote(revocation_list, report, quote_type, spid, quote_nonce, real_quote_len) - .map_err(|e| match e { - itp_enclave_api::error::Error::Sgx(s) => OCallBridgeError::GetQuote(s), - _ => OCallBridgeError::GetQuote(sgx_status_t::SGX_ERROR_UNEXPECTED), - }) - } - - fn get_dcap_quote(&self, report: sgx_report_t, quote_size: u32) -> OCallBridgeResult> { - debug!("RemoteAttestationBridge: get dcap quote, size: {}", quote_size); - - self.enclave_api.get_dcap_quote(report, quote_size).map_err(|e| match e { - itp_enclave_api::error::Error::Sgx(s) => OCallBridgeError::GetQuote(s), - _ => OCallBridgeError::GetQuote(sgx_status_t::SGX_ERROR_UNEXPECTED), - }) - } - - fn get_qve_report_on_quote( - &self, - quote: Vec, - current_time: i64, - quote_collateral: &sgx_ql_qve_collateral_t, - qve_report_info: sgx_ql_qe_report_info_t, - supplemental_data_size: u32, - ) -> OCallBridgeResult { - debug!("RemoteAttestationBridge: get qve report on quote, length: {}", quote.len()); - - self.enclave_api - .get_qve_report_on_quote( - quote, - current_time, - quote_collateral, - qve_report_info, - supplemental_data_size, - ) - .map_err(|e| match e { - itp_enclave_api::error::Error::Sgx(s) => OCallBridgeError::GetQuote(s), - _ => OCallBridgeError::GetQuote(sgx_status_t::SGX_ERROR_UNEXPECTED), - }) - } - - fn get_update_info( - &self, - platform_blob: sgx_platform_info_t, - enclave_trusted: i32, - ) -> OCallBridgeResult { - debug!("RemoteAttestationBridge: get update into"); - - self.enclave_api - .get_update_info(platform_blob, enclave_trusted) - .map_err(|e| match e { - itp_enclave_api::error::Error::Sgx(s) => OCallBridgeError::GetUpdateInfo(s), - _ => OCallBridgeError::GetUpdateInfo(sgx_status_t::SGX_ERROR_UNEXPECTED), - }) - } -} - -fn lookup_ipv4(host: &str, port: u16) -> Result { - use std::net::ToSocketAddrs; - - let addrs = (host, port).to_socket_addrs().map_err(|e| format!("{:?}", e))?; - for addr in addrs { - if let SocketAddr::V4(_) = addr { - return Ok(addr) - } - } - - Err("Cannot lookup address".to_string()) -} diff --git a/tee-worker/bitacross/service/src/ocall_bridge/worker_on_chain_ocall.rs b/tee-worker/bitacross/service/src/ocall_bridge/worker_on_chain_ocall.rs deleted file mode 100644 index 750de6d147..0000000000 --- a/tee-worker/bitacross/service/src/ocall_bridge/worker_on_chain_ocall.rs +++ /dev/null @@ -1,297 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::ocall_bridge::bridge_api::{OCallBridgeError, OCallBridgeResult, WorkerOnChainBridge}; -use codec::{Decode, Encode}; -use itp_api_client_types::ParentchainApi; -use itp_enclave_api::enclave_base::EnclaveBase; -use itp_node_api::{ - api_client::{AccountApi, ExtrinsicReport, XtStatus}, - node_api_factory::CreateNodeApi, -}; -use itp_types::{ - parentchain::{AccountId, ParentchainId}, - WorkerRequest, WorkerResponse, -}; -use log::*; -use sp_core::H256; -use sp_runtime::OpaqueExtrinsic; -use std::{sync::Arc, thread, vec::Vec}; -use substrate_api_client::{ - ac_primitives::serde_impls::StorageKey, GetAccountInformation, GetChainInfo, GetStorage, - SubmitAndWatch, SubmitExtrinsic, -}; - -#[cfg(feature = "link-binary")] -use crate::main_impl::enclave_account; - -pub struct WorkerOnChainOCall { - enclave_api: Arc, - integritee_api_factory: Arc, - target_a_parentchain_api_factory: Option>, - target_b_parentchain_api_factory: Option>, -} - -impl WorkerOnChainOCall { - pub fn new( - enclave_api: Arc, - integritee_api_factory: Arc, - target_a_parentchain_api_factory: Option>, - target_b_parentchain_api_factory: Option>, - ) -> Self { - WorkerOnChainOCall { - enclave_api, - integritee_api_factory, - target_a_parentchain_api_factory, - target_b_parentchain_api_factory, - } - } -} - -impl WorkerOnChainOCall { - pub fn create_api(&self, parentchain_id: ParentchainId) -> OCallBridgeResult { - Ok(match parentchain_id { - ParentchainId::Litentry => self.integritee_api_factory.create_api()?, - ParentchainId::TargetA => self - .target_a_parentchain_api_factory - .as_ref() - .ok_or(OCallBridgeError::TargetAParentchainNotInitialized) - .and_then(|f| f.create_api().map_err(Into::into))?, - ParentchainId::TargetB => self - .target_b_parentchain_api_factory - .as_ref() - .ok_or(OCallBridgeError::TargetBParentchainNotInitialized) - .and_then(|f| f.create_api().map_err(Into::into))?, - }) - } -} - -impl WorkerOnChainBridge for WorkerOnChainOCall -where - E: EnclaveBase, - F: CreateNodeApi, -{ - fn worker_request( - &self, - request: Vec, - parentchain_id: Vec, - ) -> OCallBridgeResult> { - trace!(" Entering ocall_worker_request"); - - let requests: Vec = Decode::decode(&mut request.as_slice())?; - if requests.is_empty() { - debug!("requests is empty, returning empty vector"); - return Ok(Vec::::new().encode()) - } - - let parentchain_id = ParentchainId::decode(&mut parentchain_id.as_slice())?; - - let api = self.create_api(parentchain_id)?; - - let resp: Vec>> = requests - .into_iter() - .map(|req| match req { - WorkerRequest::ChainStorage(key, hash) => WorkerResponse::ChainStorage( - key.clone(), - api.get_opaque_storage_by_key(StorageKey(key.clone()), hash).unwrap(), - api.get_storage_proof_by_keys(vec![StorageKey(key)], hash).unwrap().map( - |read_proof| read_proof.proof.into_iter().map(|bytes| bytes.0).collect(), - ), - ), - WorkerRequest::ChainStorageKeys(key, hash) => { - let keys: Vec> = match api.get_keys(StorageKey(key), hash) { - Ok(Some(keys)) => keys.iter().map(String::encode).collect(), - _ => Default::default(), - }; - WorkerResponse::ChainStorageKeys(keys) - }, - WorkerRequest::ChainStorageKeysPaged(prefix, count, start_key, hash) => { - let keys: Vec> = match api.get_storage_keys_paged( - Some(StorageKey(prefix)), - count, - start_key.map(StorageKey), - hash, - ) { - Ok(keys) => keys.iter().map(|k| k.0.to_vec()).collect(), - _ => Default::default(), - }; - WorkerResponse::ChainStorageKeys(keys) - }, - WorkerRequest::ChainHeader(block_hash) => { - let header = match api.get_header(block_hash) { - Ok(Some(header)) => Some(header.encode()), - _ => None, - }; - WorkerResponse::ChainHeader(header) - }, - WorkerRequest::ChainAccountNonce(encoded_account_id) => { - let maybe_nonce = match AccountId::decode(&mut encoded_account_id.as_slice()) { - Ok(account_id) => api.get_account_nonce(&account_id).ok(), - _ => { - error!("[ChainAccountNonce] account_id could not be decoded"); - None - }, - }; - WorkerResponse::ChainAccountNonce(maybe_nonce) - }, - }) - .collect(); - - let encoded_response: Vec = resp.encode(); - - Ok(encoded_response) - } - - fn send_to_parentchain( - &self, - extrinsics_encoded: Vec, - parentchain_id: Vec, - watch_until: Vec, - ) -> OCallBridgeResult> { - let maybe_watch_until: Option = Decode::decode(&mut watch_until.as_slice()) - .map_err(|_| { - OCallBridgeError::SendExtrinsicsToParentchain( - "Could not decode watch_until".to_string(), - ) - })?; - let extrinsics: Vec = Decode::decode(&mut extrinsics_encoded.as_slice()) - .map_err(|_| { - OCallBridgeError::SendExtrinsicsToParentchain("Could not decode extrinsics".to_string()) - })?; - let mut extrinsic_reports: Vec> = Vec::new(); - let parentchain_id = ParentchainId::decode(&mut parentchain_id.as_slice())?; - debug!( - "Enclave wants to send {} extrinsics to parentchain: {:?}. watch_until: {:?}", - extrinsics.len(), - parentchain_id, - maybe_watch_until - ); - let api = self.create_api(parentchain_id)?; - let mut send_extrinsic_failed = false; - - for call in extrinsics.into_iter() { - if let Some(xt_status) = maybe_watch_until { - match api.submit_and_watch_opaque_extrinsic_until( - &call.encode().into(), - xt_status.into(), - ) { - Ok(report) => extrinsic_reports.push(report.into()), - Err(e) => { - error!( - "Could not send extrinsic to {:?}: {:?}, error: {:?}", - parentchain_id, - serde_json::to_string(&call), - e - ); - send_extrinsic_failed = true; - }, - } - } else if let Err(e) = api.submit_opaque_extrinsic(&call.encode().into()) { - error!( - "Could not send extrinsic to {:?}: {:?}, error: {:?}", - parentchain_id, - serde_json::to_string(&call), - e - ); - send_extrinsic_failed = true; - } - } - - // Try to reset nonce, see - // - https://github.com/litentry/litentry-parachain/issues/1036 - // - https://github.com/integritee-network/worker/issues/970 - // It has to be done in a separate thread as nested ECALL/OCALL is disallowed - // - // This workaround is likely to cause duplicate nonce or "transaction outdated" error in the parentchain - // tx pool, because the retrieved on-chain nonce doesn't count the pending tx, meanwhile the extrinsic factory - // keeps composing new extrinsics. So the nonce used for composing the new extrinsics can collide with the nonce - // in the already submitted tx. As a result, a few txs could be dropped during the parentchain tx pool processing. - // Not to mention the thread dispatch delay and network delay (query on-chain nonce). - // - // However, we still consider it better than the current situation, where the nonce never gets rectified and - // all following extrinsics will be blocked. Moreover, the txs sent to the parentchain are mostly - // "notification extrinsics" and don't cause chain state change, therefore we deem it less harmful to drop them. - // The worst case is some action is wrongly intepreted as "failed" (because F/E doesn't get the event in time) - // while it actually succeeds. In that case, the user needs to re-do the extrinsic, which is suboptimal, - // but still better than the chain stalling. - // - // To have a better synchronisation handling we probably need a sending queue in extrinsic factory that - // can be paused on demand (or wait for the nonce synchronisation). - // - // Another small thing that can be improved is to use rpc.system.accountNextIndex instead of system.account.nonce - // see https://polkadot.js.org/docs/api/cookbook/tx/#how-do-i-take-the-pending-tx-pool-into-account-in-my-nonce - #[cfg(feature = "link-binary")] - if send_extrinsic_failed { - // drop &self lifetime - let node_api_factory_cloned = self.integritee_api_factory.clone(); - let enclave_cloned = self.enclave_api.clone(); - thread::spawn(move || { - let api = node_api_factory_cloned.create_api().unwrap(); - let enclave_account = enclave_account(enclave_cloned.as_ref()); - warn!("send_extrinsic failed, try to reset nonce ..."); - match api.get_account_next_index(&enclave_account) { - Ok(nonce) => { - warn!("query on-chain nonce OK, reset nonce to: {}", nonce); - if let Err(e) = enclave_cloned.set_nonce(nonce, ParentchainId::Litentry) { - warn!("failed to reset nonce due to: {:?}", e); - } - }, - Err(e) => warn!("query on-chain nonce failed: {:?}", e), - } - }); - } - - Ok(extrinsic_reports.encode()) - } -} - -#[cfg(test)] -mod tests { - - use super::*; - use crate::tests::mocks::enclave_api_mock::EnclaveMock; - use itp_node_api::{ - api_client::ParentchainApi, - node_api_factory::{CreateNodeApi, Result as NodeApiResult}, - }; - use mockall::mock; - - #[test] - fn given_empty_worker_request_when_submitting_then_return_empty_response() { - mock! { - NodeApiFactory {} - impl CreateNodeApi for NodeApiFactory { - fn create_api(&self) -> NodeApiResult; - } - } - - let mock_enclave = Arc::new(EnclaveMock {}); - let mock_node_api_factory = Arc::new(MockNodeApiFactory::new()); - - let on_chain_ocall = - WorkerOnChainOCall::new(mock_enclave, mock_node_api_factory, None, None); - - let response = on_chain_ocall - .worker_request(Vec::::new().encode(), ParentchainId::Litentry.encode()) - .unwrap(); - - assert!(!response.is_empty()); // the encoded empty vector is not empty - let decoded_response: Vec = Decode::decode(&mut response.as_slice()).unwrap(); - assert!(decoded_response.is_empty()); // decode the response, and we get an empty vector again - } -} diff --git a/tee-worker/bitacross/service/src/parentchain_handler.rs b/tee-worker/bitacross/service/src/parentchain_handler.rs deleted file mode 100644 index 7e0bc5242e..0000000000 --- a/tee-worker/bitacross/service/src/parentchain_handler.rs +++ /dev/null @@ -1,350 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::error::{Error, ServiceResult}; -use codec::{Decode, Encode}; -use humantime::format_duration; -use ita_parentchain_interface::integritee::Header; -use itp_api_client_types::ParentchainApi; -use itp_enclave_api::{enclave_base::EnclaveBase, sidechain::Sidechain}; -use itp_node_api::api_client::ChainApi; -use itp_storage::StorageProof; -use itp_time_utils::duration_now; -use itp_types::{ - parentchain::{GrandpaParams, ParentchainId, ParentchainInitParams, SimpleParams}, - ShardIdentifier, -}; -use log::*; -use rayon::prelude::*; -use sp_consensus_grandpa::VersionedAuthorityList; -use sp_runtime::traits::Header as HeaderTrait; -use std::{cmp::min, sync::Arc, time::Duration}; -use substrate_api_client::{ - ac_primitives::{Block, Header as HeaderT}, - GetChainInfo, -}; - -const BLOCK_SYNC_BATCH_SIZE: u32 = 1000; - -pub trait HandleParentchain { - /// Initializes all parentchain specific components on the enclave side. - /// Returns the latest synced block header. - fn init_parentchain_components(&self) -> ServiceResult
    ; - - /// Fetches the parentchain blocks to sync from the parentchain and feeds them to the enclave. - /// Returns the latest synced block header. - /// - /// Litentry: `overriden_start_block` to forcibly start from the given parentchain block number - fn sync_parentchain_until_latest_finalized( - &self, - last_synced_header: Header, - overriden_start_block: u32, - shard: ShardIdentifier, - immediate_import: bool, - ) -> ServiceResult
    ; - - /// Syncs and directly imports parentchain blocks from the latest synced header - /// until at least the specified until_header. - /// - /// Litentry: `overriden_start_block` to forcibly start from the given parentchain block number - fn await_sync_and_import_parentchain_until_at_least( - &self, - last_synced_header: &Header, - until_header: &Header, - overriden_start_block: u32, - shard: ShardIdentifier, - ) -> ServiceResult
    ; -} - -/// Handles the interaction between parentchain and enclave. -pub(crate) struct ParentchainHandler { - parentchain_api: ParentchainApi, - enclave_api: Arc, - pub parentchain_init_params: ParentchainInitParams, -} - -// #TODO: #1451: Reintroduce `ParentchainApi: ChainApi` once there is no trait bound conflict -// any more with the api-clients own trait definitions. -impl ParentchainHandler -where - EnclaveApi: EnclaveBase, -{ - pub fn new( - parentchain_api: ParentchainApi, - enclave_api: Arc, - parentchain_init_params: ParentchainInitParams, - ) -> Self { - Self { parentchain_api, enclave_api, parentchain_init_params } - } - - // FIXME: Necessary in the future? Fix with #1080 - pub fn new_with_automatic_light_client_allocation( - parentchain_api: ParentchainApi, - enclave_api: Arc, - id: ParentchainId, - shard: ShardIdentifier, - ) -> ServiceResult { - let genesis_hash = parentchain_api.get_genesis_hash()?; - let genesis_header = - parentchain_api.header(Some(genesis_hash))?.ok_or(Error::MissingGenesisHeader)?; - - let parentchain_init_params: ParentchainInitParams = if parentchain_api - .is_grandpa_available()? - { - let grandpas = parentchain_api.grandpa_authorities(Some(genesis_hash))?; - let grandpa_proof = parentchain_api.grandpa_authorities_proof(Some(genesis_hash))?; - - debug!("[{:?}] Grandpa Authority List: \n {:?} \n ", id, grandpas); - - let authority_list = VersionedAuthorityList::from(grandpas); - - ( - id, - shard, - GrandpaParams::new( - // #TODO: #1451: clean up type hacks - Header::decode(&mut genesis_header.encode().as_slice())?, - authority_list.into(), - grandpa_proof, - ), - ) - .into() - } else { - ( - id, - shard, - SimpleParams::new( - // #TODO: #1451: clean up type hacks - Header::decode(&mut genesis_header.encode().as_slice())?, - ), - ) - .into() - }; - - Ok(Self::new(parentchain_api, enclave_api, parentchain_init_params)) - } - - pub fn parentchain_api(&self) -> &ParentchainApi { - &self.parentchain_api - } - - pub fn parentchain_id(&self) -> &ParentchainId { - self.parentchain_init_params.id() - } -} - -impl HandleParentchain for ParentchainHandler -where - EnclaveApi: Sidechain + EnclaveBase, -{ - fn init_parentchain_components(&self) -> ServiceResult
    { - Ok(self - .enclave_api - .init_parentchain_components(self.parentchain_init_params.clone())?) - } - - fn sync_parentchain_until_latest_finalized( - &self, - last_synced_header: Header, - overriden_start_block: u32, - shard: ShardIdentifier, - immediate_import: bool, - ) -> ServiceResult
    { - let id = self.parentchain_id(); - trace!("[{:?}] Getting current head", id); - let curr_block = self - .parentchain_api - .last_finalized_block()? - .ok_or(Error::MissingLastFinalizedBlock)?; - let curr_block_number = curr_block.block.header().number(); - let last_synced_header_number = last_synced_header.number; - // verify that the last_synced_header is indeed a block from this chain - self.parentchain_api - .get_block(Some(last_synced_header.hash()))? - .ok_or_else(|| Error::UnknownBlockHeader(last_synced_header.hash()))?; - - info!( - "[{:?}] Syncing blocks from {} to {}", - id, last_synced_header_number, curr_block_number - ); - let creation_info = self.enclave_api.get_shard_creation_info(&shard)?; - let maybe_creation_block = if let Some(creation_block) = creation_info.for_parentchain(*id) - { - trace!("[{:?}] shard creation block: {:?}", id, creation_block); - Some(creation_block) - } else { - None - }; - - let start_time = duration_now(); - let mut until_synced_header = last_synced_header; - let mut start_block = until_synced_header.number + 1; - if overriden_start_block > start_block { - start_block = overriden_start_block; - // ask the enclave to ignore the parentchain block import validation until `overriden_start_block` - // TODO: maybe ignoring the next block import is enough, since the given `overriden_start_block` - // should be the very first parentchain block to be imported - self.enclave_api - .ignore_parentchain_block_import_validation_until(overriden_start_block)?; - } - - loop { - let chunk_range = - start_block..min(start_block + BLOCK_SYNC_BATCH_SIZE, curr_block_number); - - let start_fetch_time = duration_now(); - - let block_chunk_to_sync = chunk_range - .into_par_iter() - .filter_map(|block_number| { - self.parentchain_api - .get_block_by_number(block_number) - .expect("failed to get block") - }) - .collect::>(); - - debug!( - "[{:?}] Fetched {} blocks in {}", - id, - block_chunk_to_sync.len(), - format_duration(duration_now().saturating_sub(start_fetch_time)) - ); - - if block_chunk_to_sync.len() == BLOCK_SYNC_BATCH_SIZE as usize { - let now = duration_now(); - let total_blocks = curr_block_number.saturating_sub(last_synced_header_number); - let remaining_blocks = curr_block_number.saturating_sub(until_synced_header.number); - let remaining_time_estimate: Duration = (now.saturating_sub(start_time)) - .saturating_mul(remaining_blocks) - / (total_blocks.saturating_sub(remaining_blocks) + 1); - info!( - "[{:?}] syncing parentchain to {}. already synced until block {}. immediate import={}. est. remaining: {}", - id, curr_block_number, until_synced_header.number, immediate_import, format_duration(remaining_time_estimate) - ); - } - debug!( - "[{:?}] Found {} block(s) to sync in this chunk. immediate import={} ", - id, - block_chunk_to_sync.len(), - immediate_import - ); - if block_chunk_to_sync.is_empty() { - return Ok(until_synced_header) - } - - let skip_invocations = if let Some(creation_block) = maybe_creation_block { - let max_blocknumber_in_chunk = - block_chunk_to_sync.last().map_or_else(|| 0, |b| b.block.header.number()); - if max_blocknumber_in_chunk < creation_block.number { - trace!("skipping invocations for fast-sync for blocks older than shard creation: {} < {}", max_blocknumber_in_chunk, creation_block.number); - true - } else { - false - } - } else { - false - }; - - let events_chunk_to_sync: Vec> = if skip_invocations { - vec![] - } else { - let evs = block_chunk_to_sync - .par_iter() - .map(|block| { - self.parentchain_api.get_events_for_block(Some(block.block.header.hash())) - }) - .collect::, _>>()?; - debug!("[{:?}] Found {} event vector(s) to sync in this chunk", id, evs.len()); - evs - }; - - let events_proofs_chunk_to_sync: Vec = if skip_invocations { - vec![] - } else { - block_chunk_to_sync - .par_iter() - .map(|block| { - self.parentchain_api.get_events_value_proof(Some(block.block.header.hash())) - }) - .collect::, _>>()? - }; - - let sync_start_time = duration_now(); - - self.enclave_api.sync_parentchain( - block_chunk_to_sync.as_slice(), - events_chunk_to_sync.as_slice(), - events_proofs_chunk_to_sync.as_slice(), - self.parentchain_id(), - immediate_import, - )?; - - info!( - "[{:?}] Synced parentchain batch in {}", - id, - format_duration(duration_now().saturating_sub(sync_start_time)) - ); - - let api_client_until_synced_header = block_chunk_to_sync - .last() - .map(|b| b.block.header.clone()) - .ok_or(Error::EmptyChunk)?; - - // #TODO: #1451: fix api/client types - until_synced_header = - Header::decode(&mut api_client_until_synced_header.encode().as_slice()) - .expect("Can decode previously encoded header; qed"); - - start_block = until_synced_header.number + 1; - info!( - "[{:?}] Synced {} out of {} finalized parentchain blocks", - id, until_synced_header.number, curr_block_number, - ); - } - } - - fn await_sync_and_import_parentchain_until_at_least( - &self, - last_synced_header: &Header, - until_header: &Header, - overriden_start_block: u32, - shard: ShardIdentifier, - ) -> ServiceResult
    { - let id = self.parentchain_id(); - - trace!( - "[{:?}] last synced block number: {}. synching until {}", - id, - last_synced_header.number, - until_header.number - ); - let mut last_synced_header = last_synced_header.clone(); - - while last_synced_header.number() < until_header.number() { - last_synced_header = self.sync_parentchain_until_latest_finalized( - last_synced_header, - overriden_start_block, - shard, - true, - )?; - info!("[{:?}] synced block number: #{}", id, last_synced_header.number); - std::thread::sleep(std::time::Duration::from_secs(1)); - } - Ok(last_synced_header) - } -} diff --git a/tee-worker/bitacross/service/src/prometheus_metrics.rs b/tee-worker/bitacross/service/src/prometheus_metrics.rs deleted file mode 100644 index 6f0444bfe6..0000000000 --- a/tee-worker/bitacross/service/src/prometheus_metrics.rs +++ /dev/null @@ -1,210 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -//! Service for prometheus metrics, hosted on a http server. - -use crate::{ - account_funding::EnclaveAccountInfo, - error::{Error, ServiceResult}, -}; -use async_trait::async_trait; -use codec::{Decode, Encode}; -#[cfg(feature = "attesteer")] -use core::time::Duration; -use frame_support::scale_info::TypeInfo; -#[cfg(feature = "dcap")] -use itc_rest_client::{ - http_client::{DefaultSend, HttpClient}, - rest_client::{RestClient, Url as URL}, - RestGet, RestPath, -}; -use itp_enclave_metrics::EnclaveMetric; -use lazy_static::lazy_static; -use log::*; -use prometheus::{ - proto::MetricFamily, register_counter, register_histogram, register_histogram_vec, - register_int_gauge, register_int_gauge_vec, Counter, Histogram, HistogramVec, IntGauge, - IntGaugeVec, -}; -use serde::{Deserialize, Serialize}; -use std::{net::SocketAddr, sync::Arc}; -use warp::{Filter, Rejection, Reply}; - -lazy_static! { - /// Register all the prometheus metrics we want to monitor (aside from the default process ones). - static ref ENCLAVE_PARENTCHAIN_BLOCK_IMPORT_TIME: Histogram = - register_histogram!("bitacross_worker_enclave_parentchain_block_import_time", "Time taken to import parentchain block") - .unwrap(); - static ref MUSIG2_CEREMONIES_STARTED: Counter = - register_counter!("bitacross_worker_ceremonies_started", "Musig2 ceremonies started") - .unwrap(); - static ref MUSIG2_CEREMONIES_FAILED: Counter = - register_counter!("bitacross_worker_ceremonies_failed", "Musig2 ceremonies failed") - .unwrap(); - static ref MUSIG2_CEREMONIES_TIMED_OUT: Counter = - register_counter!("bitacross_worker_ceremonies_timed_out", "Musig2 ceremonies timed out") - .unwrap(); - static ref MUSIG2_CEREMONY_DURATION: Histogram = - register_histogram!("bitacross_worker_ceremony_duration", "Time taken to perform musig2 ceremony", vec![0.0005, 0.005, 0.01, 0.025, 0.05, 0.1]) - .unwrap(); - - -} - -pub async fn start_metrics_server( - metrics_handler: Arc, - port: u16, -) -> ServiceResult<()> -where - MetricsHandler: HandleMetrics + Send + Sync + 'static, -{ - let metrics_route = warp::path!("metrics").and_then(move || { - let handler_clone = metrics_handler.clone(); - async move { handler_clone.handle_metrics().await } - }); - let socket_addr: SocketAddr = ([0, 0, 0, 0], port).into(); - - info!("Running prometheus metrics server on: {:?}", socket_addr); - warp::serve(metrics_route).run(socket_addr).await; - - info!("Prometheus metrics server shut down"); - Ok(()) -} - -#[async_trait] -pub trait HandleMetrics { - type ReplyType: Reply; - - async fn handle_metrics(&self) -> Result; -} - -/// Metrics handler implementation. -pub struct MetricsHandler {} - -#[async_trait] -impl HandleMetrics for MetricsHandler { - type ReplyType = String; - - async fn handle_metrics(&self) -> Result { - let default_metrics = match gather_metrics_into_reply(&prometheus::gather()) { - Ok(r) => r, - Err(e) => { - error!("Failed to gather prometheus metrics: {:?}", e); - String::default() - }, - }; - - Ok(default_metrics) - } -} - -fn gather_metrics_into_reply(metrics: &[MetricFamily]) -> ServiceResult { - use prometheus::Encoder; - let encoder = prometheus::TextEncoder::new(); - - let mut buffer = Vec::new(); - encoder.encode(metrics, &mut buffer).map_err(|e| { - Error::Custom(format!("Failed to encode prometheus metrics: {:?}", e).into()) - })?; - - let result_string = String::from_utf8(buffer).map_err(|e| { - Error::Custom( - format!("Failed to convert Prometheus encoded metrics to UTF8: {:?}", e).into(), - ) - })?; - - Ok(result_string) -} - -/// Trait to receive metric updates from inside the enclave. -pub trait ReceiveEnclaveMetrics { - fn receive_enclave_metric(&self, metric: EnclaveMetric) -> ServiceResult<()>; -} - -pub struct EnclaveMetricsReceiver; - -impl ReceiveEnclaveMetrics for EnclaveMetricsReceiver { - fn receive_enclave_metric(&self, metric: EnclaveMetric) -> ServiceResult<()> { - match metric { - EnclaveMetric::ParentchainBlockImportTime(time) => - ENCLAVE_PARENTCHAIN_BLOCK_IMPORT_TIME.observe(time.as_secs_f64()), - EnclaveMetric::Musig2CeremonyStarted => MUSIG2_CEREMONIES_STARTED.inc(), - EnclaveMetric::Musig2CeremonyFailed => MUSIG2_CEREMONIES_FAILED.inc(), - EnclaveMetric::Musig2CeremonyTimedout(count) => - for i in 0..count { - MUSIG2_CEREMONIES_TIMED_OUT.inc() - }, - EnclaveMetric::Musig2CeremonyDuration(time) => - MUSIG2_CEREMONY_DURATION.observe(time.as_secs_f64()), - _ => warn!("Not supported metric: {:?}", metric), - } - Ok(()) - } -} - -#[derive(Serialize, Deserialize, Debug)] -struct PrometheusMarblerunEvents(pub Vec); - -#[cfg(feature = "attesteer")] -impl RestPath<&str> for PrometheusMarblerunEvents { - fn get_path(path: &str) -> Result { - Ok(format!("{}", path)) - } -} - -#[cfg(feature = "attesteer")] -pub fn fetch_marblerun_events(base_url: &str) -> Result, Error> { - let base_url = URL::parse(&base_url).map_err(|e| { - Error::Custom( - format!("Failed to parse marblerun prometheus endpoint base URL: {:?}", e).into(), - ) - })?; - let timeout = 3u64; - let http_client = - HttpClient::new(DefaultSend {}, true, Some(Duration::from_secs(timeout)), None, None); - - let mut rest_client = RestClient::new(http_client, base_url.clone()); - let events: PrometheusMarblerunEvents = rest_client.get("events").map_err(|e| { - Error::Custom( - format!("Failed to fetch marblerun prometheus events from: {}, error: {}", base_url, e) - .into(), - ) - })?; - - Ok(events.0) -} - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Encode, Decode, TypeInfo)] -pub struct PrometheusMarblerunEvent { - pub time: String, - pub activation: PrometheusMarblerunEventActivation, -} - -#[cfg(feature = "attesteer")] -impl PrometheusMarblerunEvent { - pub fn get_quote_without_prepended_bytes(&self) -> &[u8] { - let marblerun_magic_prepended_header_size = 16usize; - &self.activation.quote.as_bytes()[marblerun_magic_prepended_header_size..] - } -} -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Encode, Decode, TypeInfo)] -#[serde(rename_all = "camelCase")] -pub struct PrometheusMarblerunEventActivation { - pub marble_type: String, - pub uuid: String, - pub quote: String, -} diff --git a/tee-worker/bitacross/service/src/setup.rs b/tee-worker/bitacross/service/src/setup.rs deleted file mode 100644 index 94548b7bd8..0000000000 --- a/tee-worker/bitacross/service/src/setup.rs +++ /dev/null @@ -1,276 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::error::{Error, ServiceResult}; -use itp_settings::files::{ - ENCLAVE_REGISTRY_FILE, LITENTRY_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, RELAYER_REGISTRY_FILE, - SHARDS_PATH, SIGNER_REGISTRY_FILE, TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, - TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, -}; -use std::{fs, path::Path}; - -#[cfg(feature = "link-binary")] -pub(crate) use needs_enclave::{ - generate_shielding_key_file, generate_signing_key_file, init_shard, initialize_shard_and_keys, - migrate_shard, -}; - -#[cfg(feature = "link-binary")] -mod needs_enclave { - use crate::error::{Error, ServiceResult}; - use codec::Encode; - use itp_enclave_api::{enclave_base::EnclaveBase, Enclave}; - use itp_settings::files::{ - LITENTRY_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, SHARDS_PATH, SHIELDING_KEY_FILE, - SIGNING_KEY_FILE, TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, - TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, - }; - use itp_types::ShardIdentifier; - use log::*; - use std::{fs, fs::File, path::Path}; - - /// Initializes the shard and generates the key files. - pub(crate) fn initialize_shard_and_keys( - enclave: &Enclave, - shard_identifier: &ShardIdentifier, - ) -> ServiceResult<()> { - println!("[+] Initialize the shard"); - init_shard(enclave, shard_identifier); - - let pubkey = enclave.get_ecc_signing_pubkey().unwrap(); - debug!("Enclave signing key (public) raw: {:?}", pubkey); - let pubkey = enclave.get_rsa_shielding_pubkey().unwrap(); - debug!("Enclave shielding key (public) raw (may be overwritten later): {:?}", pubkey); - Ok(()) - } - - pub(crate) fn init_shard(enclave: &Enclave, shard_identifier: &ShardIdentifier) { - use base58::ToBase58; - - match enclave.init_shard(shard_identifier.encode()) { - Err(e) => { - println!( - "Failed to initialize shard {:?}: {:?}", - shard_identifier.0.to_base58(), - e - ); - }, - Ok(_) => { - println!("Successfully initialized shard {:?}", shard_identifier.0.to_base58()); - }, - } - } - - pub(crate) fn migrate_shard(enclave: &Enclave, &new_shard: &ShardIdentifier) { - match enclave.migrate_shard(new_shard.encode()) { - Err(e) => { - panic!("Failed to migrate shard {:?}. {:?}", new_shard, e); - }, - Ok(_) => { - println!("Shard {:?} migrated Successfully", new_shard); - }, - } - } - - pub(crate) fn generate_signing_key_file(enclave: &Enclave) { - info!("*** Get the signing key from the TEE\n"); - let pubkey = enclave.get_ecc_signing_pubkey().unwrap(); - debug!("[+] Signing key raw: {:?}", pubkey); - match fs::write(SIGNING_KEY_FILE, pubkey) { - Err(x) => { - error!("[-] Failed to write '{}'. {}", SIGNING_KEY_FILE, x); - }, - _ => { - println!("[+] File '{}' written successfully", SIGNING_KEY_FILE); - }, - } - } - - pub(crate) fn generate_shielding_key_file(enclave: &Enclave) { - info!("*** Get the public key from the TEE\n"); - let pubkey = enclave.get_rsa_shielding_pubkey().unwrap(); - let file = File::create(SHIELDING_KEY_FILE).unwrap(); - match serde_json::to_writer(file, &pubkey) { - Err(x) => { - error!("[-] Failed to write '{}'. {}", SHIELDING_KEY_FILE, x); - }, - _ => { - println!("[+] File '{}' written successfully", SHIELDING_KEY_FILE); - }, - } - } -} - -/// backs up shard directory and restores it after cleaning shards directory -pub(crate) fn remove_old_shards(root_dir: &Path, new_shard_name: &str) { - let shard_backup = root_dir.join("shard_backup"); - let shard_dir = root_dir.join(SHARDS_PATH).join(new_shard_name); - - fs::rename(shard_dir.clone(), shard_backup.clone()).expect("Failed to backup shard"); - remove_dir_if_it_exists(root_dir, SHARDS_PATH).expect("Failed to remove shards directory"); - fs::create_dir_all(root_dir.join(SHARDS_PATH)).expect("Failed to create shards directory"); - fs::rename(shard_backup, shard_dir).expect("Failed to restore shard"); -} - -/// Purge all worker files from `dir`. -pub(crate) fn purge_files_from_dir(dir: &Path) -> ServiceResult<()> { - println!("[+] Performing a clean reset of the worker"); - - println!("[+] Purge all files from previous runs"); - purge_files(dir)?; - - Ok(()) -} - -/// Purge all worker files in a given path. -fn purge_files(root_directory: &Path) -> ServiceResult<()> { - remove_dir_if_it_exists(root_directory, SHARDS_PATH)?; - - remove_dir_if_it_exists(root_directory, LITENTRY_PARENTCHAIN_LIGHT_CLIENT_DB_PATH)?; - remove_dir_if_it_exists(root_directory, TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH)?; - remove_dir_if_it_exists(root_directory, TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH)?; - - remove_file_if_it_exists(root_directory, RELAYER_REGISTRY_FILE)?; - remove_file_if_it_exists(root_directory, ENCLAVE_REGISTRY_FILE)?; - remove_file_if_it_exists(root_directory, SIGNER_REGISTRY_FILE)?; - Ok(()) -} - -fn remove_dir_if_it_exists(root_directory: &Path, dir_name: &str) -> ServiceResult<()> { - let directory_path = root_directory.join(dir_name); - if directory_path.exists() { - fs::remove_dir_all(directory_path).map_err(|e| Error::Custom(e.into()))?; - } - Ok(()) -} - -fn remove_file_if_it_exists(root_directory: &Path, file_name: &str) -> ServiceResult<()> { - let file = root_directory.join(file_name); - if file.exists() { - fs::remove_file(file).map_err(|e| Error::Custom(e.into()))?; - } - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use itp_settings::files::{ - SHARDS_PATH, SIGNER_REGISTRY_FILE, TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH, - }; - use std::{fs, path::PathBuf}; - - #[test] - fn purge_files_deletes_all_relevant_files() { - let test_directory_handle = - TestDirectoryHandle::new(PathBuf::from("test_purge_files_deletes_all_relevant_files")); - let root_directory = test_directory_handle.path(); - - let shards_path = root_directory.join(SHARDS_PATH); - fs::create_dir_all(&shards_path).unwrap(); - fs::File::create(&shards_path.join("state_1.bin")).unwrap(); - fs::File::create(&shards_path.join("state_2.bin")).unwrap(); - - fs::File::create(&root_directory.join(RELAYER_REGISTRY_FILE)).unwrap(); - fs::File::create(&root_directory.join(ENCLAVE_REGISTRY_FILE)).unwrap(); - fs::File::create(&root_directory.join(SIGNER_REGISTRY_FILE)).unwrap(); - - fs::create_dir_all(&root_directory.join(LITENTRY_PARENTCHAIN_LIGHT_CLIENT_DB_PATH)) - .unwrap(); - fs::create_dir_all(&root_directory.join(TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH)) - .unwrap(); - fs::create_dir_all(&root_directory.join(TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH)) - .unwrap(); - - purge_files(&root_directory).unwrap(); - - assert!(!shards_path.exists()); - assert!(!root_directory.join(LITENTRY_PARENTCHAIN_LIGHT_CLIENT_DB_PATH).exists()); - assert!(!root_directory.join(TARGET_A_PARENTCHAIN_LIGHT_CLIENT_DB_PATH).exists()); - assert!(!root_directory.join(TARGET_B_PARENTCHAIN_LIGHT_CLIENT_DB_PATH).exists()); - assert!(!root_directory.join(RELAYER_REGISTRY_FILE).exists()); - assert!(!root_directory.join(ENCLAVE_REGISTRY_FILE).exists()); - assert!(!root_directory.join(SIGNER_REGISTRY_FILE).exists()); - } - - #[test] - fn purge_files_succeeds_when_no_files_exist() { - let test_directory_handle = TestDirectoryHandle::new(PathBuf::from( - "test_purge_files_succeeds_when_no_files_exist", - )); - let root_directory = test_directory_handle.path(); - - assert!(purge_files(&root_directory).is_ok()); - } - - #[test] - fn test_remove_old_shards() { - let test_directory_handle = TestDirectoryHandle::new(PathBuf::from("test_backup_shard")); - let root_directory = test_directory_handle.path(); - let shard_1_name = "test_shard_1"; - let shard_2_name = "test_shard_2"; - - let shard_1_dir = root_directory.join(SHARDS_PATH).join(shard_1_name); - fs::create_dir_all(&shard_1_dir).unwrap(); - fs::File::create(shard_1_dir.join("test_state.bin")).unwrap(); - fs::File::create(shard_1_dir.join("test_state_2.bin")).unwrap(); - - let shard_2_dir = root_directory.join(SHARDS_PATH).join(shard_2_name); - fs::create_dir_all(&shard_2_dir).unwrap(); - fs::File::create(shard_2_dir.join("test_state.bin")).unwrap(); - - assert!(root_directory.join(SHARDS_PATH).join(shard_2_name).exists()); - - remove_old_shards(root_directory, shard_1_name); - - assert!(root_directory.join(SHARDS_PATH).join(shard_1_name).exists()); - assert_eq!( - fs::read_dir(root_directory.join(SHARDS_PATH).join(shard_1_name)) - .expect("Failed to read shard directory") - .count(), - 2 - ); - assert!(!root_directory.join(SHARDS_PATH).join(shard_2_name).exists()); - } - - /// Directory handle to automatically initialize a directory - /// and upon dropping the reference, removing it again. - struct TestDirectoryHandle { - path: PathBuf, - } - - impl TestDirectoryHandle { - pub fn new(path: PathBuf) -> Self { - let test_path = std::env::current_dir().unwrap().join(&path); - fs::create_dir_all(&test_path).unwrap(); - TestDirectoryHandle { path: test_path } - } - - pub fn path(&self) -> &PathBuf { - &self.path - } - } - - impl Drop for TestDirectoryHandle { - fn drop(&mut self) { - if self.path.exists() { - fs::remove_dir_all(&self.path).unwrap(); - } - } - } -} diff --git a/tee-worker/bitacross/service/src/sync_state.rs b/tee-worker/bitacross/service/src/sync_state.rs deleted file mode 100644 index e3bb1bcf99..0000000000 --- a/tee-worker/bitacross/service/src/sync_state.rs +++ /dev/null @@ -1,92 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -//! Request state keys from a fellow validateer. - -use crate::{ - enclave::tls_ra::enclave_request_state_provisioning, - error::{Error, ServiceResult as Result}, -}; -use futures::executor; -use itc_rpc_client::direct_client::{DirectApi, DirectClient as DirectWorkerApi}; -use itp_enclave_api::{ - enclave_base::EnclaveBase, - remote_attestation::{RemoteAttestation, TlsRemoteAttestation}, -}; -use itp_node_api::api_client::PalletTeebagApi; -use itp_types::{ShardIdentifier, WorkerType}; -use log::info; -use sgx_types::sgx_quote_sign_type_t; -use sp_runtime::MultiSigner; -use std::string::String; - -pub(crate) fn sync_state< - E: TlsRemoteAttestation + EnclaveBase + RemoteAttestation, - NodeApi: PalletTeebagApi, ->( - node_api: &NodeApi, - shard: &ShardIdentifier, - enclave_api: &E, - skip_ra: bool, -) { - let provider_url = - // TODO(Litentry P-629): maybe implement `get_enclave_url_of_last_active` - executor::block_on(get_enclave_url_of_primary_worker_for_shard(node_api, shard)) - .expect("Author of primary worker for shard could not be found"); - - println!("Requesting state provisioning from worker at {}", &provider_url); - - enclave_request_state_provisioning( - enclave_api, - sgx_quote_sign_type_t::SGX_UNLINKABLE_SIGNATURE, - &provider_url, - shard, - skip_ra, - ) - .unwrap(); - println!("[+] State provisioning successfully performed."); -} - -/// Returns the url of the primary worker for the given shard -async fn get_enclave_url_of_primary_worker_for_shard( - node_api: &NodeApi, - shard: &ShardIdentifier, -) -> Result { - let enclave = node_api - .primary_enclave_for_shard(WorkerType::BitAcross, shard, None)? - .ok_or_else(|| Error::NoWorkerForShardFound(*shard))?; - let worker_api_direct = - DirectWorkerApi::new(String::from_utf8_lossy(enclave.url.as_slice()).to_string()); - Ok(worker_api_direct.get_mu_ra_url()?) -} - -/// Returns the url of the first Enclave that matches our own MRENCLAVE. -/// -/// This should be run before we register ourselves as enclave, to ensure we don't get our own url. -async fn get_enclave_url_of_first_registered( - node_api: &NodeApi, - enclave_api: &EnclaveApi, -) -> Result { - let self_mrenclave = enclave_api.get_fingerprint()?; - let first_enclave = node_api - .all_enclaves(WorkerType::BitAcross, None)? - .into_iter() - .find(|e| e.mrenclave == self_mrenclave.to_fixed_bytes()) - .ok_or(Error::NoPeerWorkerFound)?; - let worker_api_direct = - DirectWorkerApi::new(String::from_utf8_lossy(first_enclave.url.as_slice()).to_string()); - Ok(worker_api_direct.get_mu_ra_url()?) -} diff --git a/tee-worker/bitacross/service/src/tests/commons.rs b/tee-worker/bitacross/service/src/tests/commons.rs deleted file mode 100644 index 9f9ec63271..0000000000 --- a/tee-worker/bitacross/service/src/tests/commons.rs +++ /dev/null @@ -1,63 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use serde_derive::{Deserialize, Serialize}; -use sgx_types::*; -use std::str; - -#[cfg(test)] -use crate::config::Config; - -#[derive(Debug, Serialize, Deserialize)] -pub struct Message { - pub account: String, - pub amount: u32, - pub sha256: sgx_sha256_hash_t, -} - -#[cfg(test)] -pub fn local_worker_config( - worker_url: String, - untrusted_worker_port: String, - mu_ra_port: String, -) -> Config { - let mut url = worker_url.split(':'); - - Config::new( - Default::default(), - Default::default(), - Default::default(), - Default::default(), - Default::default(), - Default::default(), - url.next().unwrap().into(), - None, - url.next().unwrap().into(), - None, - untrusted_worker_port, - None, - mu_ra_port, - false, - "8787".to_string(), - "4545".to_string(), - crate::config::pwd(), - None, - "0".to_string(), - 5, - 10, - ) -} diff --git a/tee-worker/bitacross/service/src/tests/mock.rs b/tee-worker/bitacross/service/src/tests/mock.rs deleted file mode 100644 index 0f96dc3f1e..0000000000 --- a/tee-worker/bitacross/service/src/tests/mock.rs +++ /dev/null @@ -1,69 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use itp_node_api::api_client::{ApiResult, PalletTeebagApi}; -use itp_types::{AccountId, Enclave, MrEnclave, ShardIdentifier, WorkerType, H256 as Hash}; -use std::collections::HashSet; - -pub struct TestNodeApi; - -pub const W1_URL: &str = "127.0.0.1:22222"; -pub const W2_URL: &str = "127.0.0.1:33333"; - -pub fn enclaves() -> Vec { - vec![ - Enclave::new(WorkerType::BitAcross).with_url(W1_URL.into()), - Enclave::new(WorkerType::BitAcross).with_url(W2_URL.into()), - ] -} - -impl PalletTeebagApi for TestNodeApi { - type Hash = Hash; - - fn enclave(&self, _account: &AccountId, _at_block: Option) -> ApiResult> { - unreachable!() - } - fn enclave_count(&self, _worker_type: WorkerType, _at_block: Option) -> ApiResult { - unreachable!() - } - - fn all_enclaves( - &self, - _worker_type: WorkerType, - _at_block: Option, - ) -> ApiResult> { - Ok(enclaves()) - } - - fn primary_enclave_identifier_for_shard( - &self, - worker_type: WorkerType, - shard: &ShardIdentifier, - at_block: Option, - ) -> ApiResult> { - unreachable!() - } - - fn primary_enclave_for_shard( - &self, - worker_type: WorkerType, - shard: &ShardIdentifier, - at_block: Option, - ) -> ApiResult> { - unreachable!() - } -} diff --git a/tee-worker/bitacross/service/src/tests/mocks/enclave_api_mock.rs b/tee-worker/bitacross/service/src/tests/mocks/enclave_api_mock.rs deleted file mode 100644 index 8e47e6cada..0000000000 --- a/tee-worker/bitacross/service/src/tests/mocks/enclave_api_mock.rs +++ /dev/null @@ -1,147 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use codec::{Decode, Encode}; -use core::fmt::Debug; -use itp_enclave_api::{enclave_base::EnclaveBase, sidechain::Sidechain, EnclaveResult}; -use itp_settings::worker::MR_ENCLAVE_SIZE; -use itp_sgx_crypto::{ecdsa, schnorr}; -use itp_stf_interface::ShardCreationInfo; -use itp_storage::StorageProof; -use itp_types::{ - parentchain::{ - Balance, Header, ParentchainId, ParentchainInitParams, - ParentchainInitParams::{Parachain, Solochain}, - }, - EnclaveFingerprint, ShardIdentifier, -}; -use sgx_crypto_helper::rsa3072::Rsa3072PubKey; -use sp_core::ed25519; - -/// mock for EnclaveBase - use in tests -pub struct EnclaveMock; - -impl EnclaveBase for EnclaveMock { - fn init( - &self, - _mu_ra_url: &str, - _untrusted_url: &str, - _base_dir: &str, - _ceremony_commands_thread_count: u8, - _ceremony_events_thread_count: u8, - ) -> EnclaveResult<()> { - Ok(()) - } - - fn init_direct_invocation_server(&self, _rpc_server_addr: String) -> EnclaveResult<()> { - unreachable!() - } - - fn init_parentchain_components( - &self, - params: ParentchainInitParams, - ) -> EnclaveResult
    { - let genesis_header_encoded = match params { - Solochain { params, .. } => params.genesis_header.encode(), - Parachain { params, .. } => params.genesis_header.encode(), - }; - let header = Header::decode(&mut genesis_header_encoded.as_slice())?; - Ok(header) - } - - fn init_shard(&self, _shard: Vec) -> EnclaveResult<()> { - unimplemented!() - } - - fn init_shard_creation_parentchain_header( - &self, - shard: &ShardIdentifier, - parentchain_id: &ParentchainId, - header: &Header, - ) -> EnclaveResult<()> { - unimplemented!() - } - - fn get_shard_creation_info(&self, shard: &ShardIdentifier) -> EnclaveResult { - unimplemented!() - } - - fn set_nonce(&self, _: u32, _: ParentchainId) -> EnclaveResult<()> { - unimplemented!() - } - - fn set_node_metadata(&self, _metadata: Vec, _: ParentchainId) -> EnclaveResult<()> { - todo!() - } - - fn get_rsa_shielding_pubkey(&self) -> EnclaveResult { - unreachable!() - } - - fn get_ecc_signing_pubkey(&self) -> EnclaveResult { - unreachable!() - } - - fn get_bitcoin_wallet_pair(&self) -> EnclaveResult { - unreachable!() - } - - fn get_ethereum_wallet_pair(&self) -> EnclaveResult { - unreachable!() - } - - fn get_ton_wallet_pair(&self) -> EnclaveResult { - unreachable!() - } - - fn get_fingerprint(&self) -> EnclaveResult { - Ok([1u8; MR_ENCLAVE_SIZE].into()) - } - - fn publish_wallets(&self) -> EnclaveResult<()> { - unimplemented!() - } - - fn finish_enclave_init(&self) -> EnclaveResult<()> { - unimplemented!() - } - - fn init_wallets(&self, _base_dir: &str) -> EnclaveResult<()> { - unimplemented!() - } - - fn migrate_shard(&self, new_shard: Vec) -> EnclaveResult<()> { - unimplemented!() - } -} - -impl Sidechain for EnclaveMock { - fn sync_parentchain( - &self, - _blocks: &[sp_runtime::generic::SignedBlock], - _events: &[Vec], - _events_proofs: &[StorageProof], - _: &ParentchainId, - _: bool, - ) -> EnclaveResult<()> { - Ok(()) - } - - fn ignore_parentchain_block_import_validation_until(&self, _until: u32) -> EnclaveResult<()> { - todo!() - } -} diff --git a/tee-worker/bitacross/service/src/tests/mocks/initialization_handler_mock.rs b/tee-worker/bitacross/service/src/tests/mocks/initialization_handler_mock.rs deleted file mode 100644 index 79b2797d7b..0000000000 --- a/tee-worker/bitacross/service/src/tests/mocks/initialization_handler_mock.rs +++ /dev/null @@ -1,34 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::initialized_service::{IsInitialized, TrackInitialization}; - -pub struct TrackInitializationMock; - -impl TrackInitialization for TrackInitializationMock { - fn registered_on_parentchain(&self) {} - - fn worker_for_shard_registered(&self) {} -} - -pub struct IsInitializedMock; - -impl IsInitialized for IsInitializedMock { - fn is_initialized(&self) -> bool { - true - } -} diff --git a/tee-worker/bitacross/service/src/tests/mocks/mod.rs b/tee-worker/bitacross/service/src/tests/mocks/mod.rs deleted file mode 100644 index 406392ef62..0000000000 --- a/tee-worker/bitacross/service/src/tests/mocks/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pub mod enclave_api_mock; -pub mod initialization_handler_mock; -pub mod parentchain_api_mock; -pub mod update_worker_peers_mock; diff --git a/tee-worker/bitacross/service/src/tests/mocks/parentchain_api_mock.rs b/tee-worker/bitacross/service/src/tests/mocks/parentchain_api_mock.rs deleted file mode 100644 index b383c2412c..0000000000 --- a/tee-worker/bitacross/service/src/tests/mocks/parentchain_api_mock.rs +++ /dev/null @@ -1,111 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use itc_parentchain_test::{ParentchainBlockBuilder, ParentchainHeaderBuilder}; -use itp_node_api::api_client::{ApiResult, ChainApi}; -use itp_types::{ - parentchain::{Hash, Header, StorageProof}, - Block, SignedBlock, H256, -}; -use sp_consensus_grandpa::AuthorityList; - -pub struct ParentchainApiMock { - parentchain: Vec, -} - -impl ParentchainApiMock { - // Todo: Remove when #1451 is resolved - #[allow(unused)] - pub(crate) fn new() -> Self { - ParentchainApiMock { parentchain: Vec::new() } - } - - /// Initializes parentchain with a default block chain of a given length. - // Todo: Remove when #1451 is resolved - #[allow(unused)] - pub fn with_default_blocks(mut self, number_of_blocks: u32) -> Self { - self.parentchain = (1..=number_of_blocks) - .map(|n| { - let header = ParentchainHeaderBuilder::default().with_number(n).build(); - ParentchainBlockBuilder::default().with_header(header).build_signed() - }) - .collect(); - self - } -} - -impl ChainApi for ParentchainApiMock { - type Hash = Hash; - type Block = Block; - type Header = Header; - type BlockNumber = u32; - - fn last_finalized_block(&self) -> ApiResult> { - Ok(self.parentchain.last().cloned()) - } - - fn signed_block(&self, _hash: Option) -> ApiResult> { - todo!() - } - - fn get_genesis_hash(&self) -> ApiResult { - todo!() - } - - fn header(&self, _header_hash: Option) -> ApiResult> { - todo!() - } - - fn get_blocks(&self, from: u32, to: u32) -> ApiResult> { - let num_elements = to.checked_sub(from).map(|n| n + 1).unwrap_or(0); - let blocks = self - .parentchain - .iter() - .skip(from as usize) - .take(num_elements as usize) - .cloned() - .collect(); - ApiResult::Ok(blocks) - } - - fn is_grandpa_available(&self) -> ApiResult { - todo!() - } - - fn grandpa_authorities(&self, _hash: Option) -> ApiResult { - todo!() - } - - fn grandpa_authorities_proof(&self, _hash: Option) -> ApiResult { - todo!() - } - - fn get_events_value_proof(&self, _block_hash: Option) -> ApiResult { - Ok(Default::default()) - } - - fn get_events_for_block(&self, _block_hash: Option) -> ApiResult> { - Ok(Default::default()) - } - - fn get_block_by_number( - &self, - block: Self::BlockNumber, - ) -> ApiResult>> { - Ok(self.parentchain.get(block as usize).cloned()) - } -} diff --git a/tee-worker/bitacross/service/src/tests/mocks/update_worker_peers_mock.rs b/tee-worker/bitacross/service/src/tests/mocks/update_worker_peers_mock.rs deleted file mode 100644 index aeff6ff377..0000000000 --- a/tee-worker/bitacross/service/src/tests/mocks/update_worker_peers_mock.rs +++ /dev/null @@ -1,34 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - worker::{Url, WorkerResult}, - worker_peers_updater::PeersRegistry, -}; -use itp_types::ShardIdentifier; - -pub struct WorkerPeersRegistryMock; - -impl PeersRegistry for WorkerPeersRegistryMock { - fn update_peers(&self, _shard: ShardIdentifier) -> WorkerResult<()> { - Ok(()) - } - - fn read_trusted_peers(&self) -> WorkerResult> { - Ok(Vec::new()) - } -} diff --git a/tee-worker/bitacross/service/src/tests/mod.rs b/tee-worker/bitacross/service/src/tests/mod.rs deleted file mode 100644 index 0ef2c4f253..0000000000 --- a/tee-worker/bitacross/service/src/tests/mod.rs +++ /dev/null @@ -1,48 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -pub mod commons; -pub mod mock; - -#[cfg(test)] -pub mod mocks; - -// Todo: Revive when #1451 is resolved -// #[cfg(test)] -// pub mod parentchain_handler_test; - -#[cfg(feature = "link-binary")] -use clap::ArgMatches; - -#[cfg(feature = "link-binary")] -pub fn run_enclave_tests(matches: &ArgMatches) { - use crate::{config::Config, enclave::api::*, setup}; - use itp_enclave_api::enclave_test::EnclaveTest; - - println!("*** Starting Test enclave"); - let config = Config::from(matches); - setup::purge_files_from_dir(config.data_dir()).unwrap(); - let enclave = enclave_init(&config).unwrap(); - - if matches.is_present("all") || matches.is_present("unit") { - println!("Running unit Tests"); - enclave.test_main_entrance().unwrap(); - println!("[+] unit_test ended!"); - } - - println!("[+] All tests ended!"); -} diff --git a/tee-worker/bitacross/service/src/tests/parentchain_handler_test.rs b/tee-worker/bitacross/service/src/tests/parentchain_handler_test.rs deleted file mode 100644 index 30339e92bb..0000000000 --- a/tee-worker/bitacross/service/src/tests/parentchain_handler_test.rs +++ /dev/null @@ -1,51 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use crate::{ - parentchain_handler::{HandleParentchain, ParentchainHandler}, - tests::mocks::{enclave_api_mock::EnclaveMock, parentchain_api_mock::ParentchainApiMock}, -}; -use itc_parentchain::{ - light_client::light_client_init_params::SimpleParams, - primitives::{ParentchainId, ParentchainInitParams}, -}; -use itc_parentchain_test::ParentchainHeaderBuilder; -use itp_node_api::api_client::ChainApi; -use std::sync::Arc; - -#[test] -fn test_number_of_synced_blocks() { - let number_of_blocks = 42; - - let parentchain_api_mock = ParentchainApiMock::new().with_default_blocks(number_of_blocks); - let last_synced_block = - parentchain_api_mock.get_blocks(2, 2).unwrap().first().cloned().unwrap(); - - let enclave_api_mock = EnclaveMock; - let parentchain_params: ParentchainInitParams = - (ParentchainId::Litentry, SimpleParams::new(ParentchainHeaderBuilder::default().build())) - .into(); - - let parentchain_handler = ParentchainHandler::new( - parentchain_api_mock, - Arc::new(enclave_api_mock), - parentchain_params, - ); - - let header = parentchain_handler.sync_parentchain(last_synced_block.block.header).unwrap(); - assert_eq!(header.number, number_of_blocks); -} diff --git a/tee-worker/bitacross/service/src/utils.rs b/tee-worker/bitacross/service/src/utils.rs deleted file mode 100644 index fd0b60fe82..0000000000 --- a/tee-worker/bitacross/service/src/utils.rs +++ /dev/null @@ -1,53 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -use base58::{FromBase58, ToBase58}; -use itp_enclave_api::enclave_base::EnclaveBase; -use itp_types::ShardIdentifier; -use log::info; - -pub fn extract_shard( - maybe_shard_str: Option<&str>, - enclave_api: &E, -) -> ShardIdentifier { - match maybe_shard_str { - Some(value) => { - let shard_vec = value.from_base58().expect("shard must be hex encoded"); - let mut shard = [0u8; 32]; - shard.copy_from_slice(&shard_vec[..]); - shard.into() - }, - _ => { - let mrenclave = enclave_api.get_fingerprint().unwrap(); - info!("no shard specified. using mrenclave as id: {}", mrenclave.0.to_base58()); - ShardIdentifier::from_slice(&mrenclave[..]) - }, - } -} - -#[cfg(not(feature = "dcap"))] -pub fn check_files() { - use itp_settings::files::{ENCLAVE_FILE, RA_API_KEY_FILE, RA_SPID_FILE}; - use log::debug; - use std::path::Path; - debug!("*** Check files"); - let files = [ENCLAVE_FILE, RA_SPID_FILE, RA_API_KEY_FILE]; - for f in files.iter() { - assert!(Path::new(f).exists(), "File doesn't exist: {}", f); - } -} diff --git a/tee-worker/bitacross/service/src/wasm.rs b/tee-worker/bitacross/service/src/wasm.rs deleted file mode 100644 index fe99445759..0000000000 --- a/tee-worker/bitacross/service/src/wasm.rs +++ /dev/null @@ -1,62 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -use sgx_types::*; - -extern "C" { - fn sgxwasm_init(eid: sgx_enclave_id_t, retval: *mut sgx_status_t) -> sgx_status_t; -} - -#[derive(Debug, Serialize, Deserialize)] -pub enum SgxWasmAction { - #[codec(index = 0)] - Call { module: Option>, function: String }, -} - -#[derive(Debug, Serialize, Deserialize)] -pub enum BoundaryValue { - #[codec(index = 0)] - I32(i32), - #[codec(index = 1)] - I64(i64), - #[codec(index = 2)] - F32(u32), - #[codec(index = 3)] - F64(u64), -} - -pub fn sgx_enclave_wasm_init(eid: sgx_enclave_id_t) -> Result<(), String> { - let mut retval: sgx_status_t = sgx_status_t::SGX_SUCCESS; - let result = unsafe { sgxwasm_init(eid, &mut retval) }; - - match result { - sgx_status_t::SGX_SUCCESS => {}, - _ => { - println!("[-] ECALL Enclave Failed {}!", result.as_str()); - panic!("sgx_enclave_wasm_init's ECALL returned unknown error!"); - }, - } - - match retval { - sgx_status_t::SGX_SUCCESS => {}, - _ => { - println!("[-] ECALL Enclave Function return fail: {}!", retval.as_str()); - return Err(format!("ECALL func return error: {}", retval.as_str())) - }, - } - - Ok(()) -} diff --git a/tee-worker/bitacross/service/src/worker.rs b/tee-worker/bitacross/service/src/worker.rs deleted file mode 100644 index f33976001d..0000000000 --- a/tee-worker/bitacross/service/src/worker.rs +++ /dev/null @@ -1,160 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -///! Integritee worker. Inspiration for this design came from parity's substrate Client. -/// -/// This should serve as a proof of concept for a potential refactoring design. Ultimately, everything -/// from the main.rs should be covered by the worker struct here - hidden and split across -/// multiple traits. -use crate::{config::Config, error::Error, initialized_service::TrackInitialization}; -use async_trait::async_trait; -use codec::{Decode, Encode}; -use itc_rpc_client::direct_client::{DirectApi, DirectClient as DirectWorkerApi}; -use itp_enclave_api::enclave_base::EnclaveBase; -use itp_node_api::{api_client::PalletTeebagApi, node_api_factory::CreateNodeApi}; -use itp_types::ShardIdentifier; -use jsonrpsee::{ - types::{to_json_value, traits::Client}, - ws_client::WsClientBuilder, -}; -use litentry_primitives::WorkerType; -use log::*; -use std::{ - collections::HashSet, - sync::{Arc, RwLock}, -}; - -pub type WorkerResult = Result; -pub type Url = String; - -#[derive(Clone, Hash, Eq, PartialEq, Encode, Decode, Debug)] -pub struct PeerUrls { - pub trusted: Url, - pub untrusted: Url, - pub me: bool, -} - -impl PeerUrls { - pub fn new(trusted: Url, untrusted: Url, me: bool) -> Self { - PeerUrls { trusted, untrusted, me } - } -} - -pub struct Worker { - _config: Config, - // unused yet, but will be used when more methods are migrated to the worker - _enclave_api: Arc, - node_api_factory: Arc, - initialization_handler: Arc, - peer_urls: RwLock>, -} - -impl - Worker -{ - pub fn new( - config: Config, - enclave_api: Arc, - node_api_factory: Arc, - initialization_handler: Arc, - peer_urls: HashSet, - ) -> Self { - Self { - _config: config, - _enclave_api: enclave_api, - node_api_factory, - initialization_handler, - peer_urls: RwLock::new(peer_urls), - } - } -} - -/// Looks for new peers and updates them. -pub trait UpdatePeers { - fn search_peers(&self, shard: ShardIdentifier) -> WorkerResult>; - - fn set_peers_urls(&self, peers: HashSet) -> WorkerResult<()>; - - fn update_peers(&self, shard: ShardIdentifier) -> WorkerResult<()> { - let peers = self.search_peers(shard)?; - self.set_peers_urls(peers) - } -} - -pub trait GetPeers { - fn read_peers_urls(&self) -> WorkerResult>; -} - -impl GetPeers - for Worker -where - NodeApiFactory: CreateNodeApi + Send + Sync, - Enclave: EnclaveBase + itp_enclave_api::remote_attestation::TlsRemoteAttestation, -{ - fn read_peers_urls(&self) -> WorkerResult> { - if let Ok(peer_urls) = self.peer_urls.read() { - Ok(peer_urls.clone()) - } else { - Err(Error::Custom("Encountered poisoned lock for peers".into())) - } - } -} - -impl UpdatePeers - for Worker -where - NodeApiFactory: CreateNodeApi + Send + Sync, - Enclave: EnclaveBase + itp_enclave_api::remote_attestation::TlsRemoteAttestation, -{ - fn search_peers(&self, shard: ShardIdentifier) -> WorkerResult> { - let worker_url_external = self._config.trusted_worker_url_external(); - let node_api = self - .node_api_factory - .create_api() - .map_err(|e| Error::Custom(format!("Failed to create NodeApi: {:?}", e).into()))?; - let enclaves = node_api.all_enclaves(WorkerType::BitAcross, None)?; - let mut peer_urls = HashSet::::new(); - for enclave in enclaves { - // FIXME: This is temporary only, as block broadcasting should be moved to trusted ws server. - let enclave_url = String::from_utf8_lossy(enclave.url.as_slice()).to_string(); - trace!("found peer rpc url: {}", enclave_url); - let worker_api_direct = DirectWorkerApi::new(enclave_url.clone()); - match worker_api_direct.get_untrusted_worker_url() { - Ok(untrusted_worker_url) => { - let is_me = enclave_url == worker_url_external; - peer_urls.insert(PeerUrls::new(enclave_url, untrusted_worker_url, is_me)); - }, - Err(e) => { - warn!("Failed to get untrusted worker url (enclave: {}): {:?}", enclave_url, e); - }, - } - } - debug!("found {} peers in shard state for {:?}", peer_urls.len(), shard); - Ok(peer_urls) - } - - fn set_peers_urls(&self, peers: HashSet) -> WorkerResult<()> { - let peers_vec: Vec = peers.clone().into_iter().collect(); - info!("Setting peers urls: {:?}", peers_vec); - - let mut peer_urls = self.peer_urls.write().map_err(|e| { - Error::Custom(format!("Encountered poisoned lock for peers urls: {:?}", e).into()) - })?; - *peer_urls = peers; - Ok(()) - } -} diff --git a/tee-worker/bitacross/service/src/worker_peers_updater.rs b/tee-worker/bitacross/service/src/worker_peers_updater.rs deleted file mode 100644 index fb3671aaab..0000000000 --- a/tee-worker/bitacross/service/src/worker_peers_updater.rs +++ /dev/null @@ -1,57 +0,0 @@ -/* - Copyright 2021 Integritee AG and Supercomputing Systems AG - Copyright (C) 2017-2019 Baidu, Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#[cfg(test)] -use mockall::predicate::*; -#[cfg(test)] -use mockall::*; - -use crate::worker::{GetPeers, UpdatePeers, Url, WorkerResult}; -use itp_types::ShardIdentifier; -use std::sync::Arc; - -/// Updates the peers of the global worker. -#[cfg_attr(test, automock)] -pub trait PeersRegistry { - fn update_peers(&self, shard: ShardIdentifier) -> WorkerResult<()>; - fn read_trusted_peers(&self) -> WorkerResult>; -} - -pub struct WorkerPeersRegistry { - worker: Arc, -} - -impl WorkerPeersRegistry { - pub fn new(worker: Arc) -> Self { - WorkerPeersRegistry { worker } - } -} - -impl PeersRegistry for WorkerPeersRegistry -where - WorkerType: UpdatePeers + GetPeers, -{ - fn update_peers(&self, shard: ShardIdentifier) -> WorkerResult<()> { - self.worker.update_peers(shard) - } - - fn read_trusted_peers(&self) -> WorkerResult> { - let peer_urls = self.worker.read_peers_urls()?; - Ok(peer_urls.into_iter().filter(|urls| !urls.me).map(|urls| urls.trusted).collect()) - } -} diff --git a/tee-worker/bitacross/ts-tests/.editorconfig b/tee-worker/bitacross/ts-tests/.editorconfig deleted file mode 100644 index 347fc689b2..0000000000 --- a/tee-worker/bitacross/ts-tests/.editorconfig +++ /dev/null @@ -1,6 +0,0 @@ -# Editor configuration, see http://editorconfig.org - -[*] -indent_style = space -indent_size = 4 - diff --git a/tee-worker/bitacross/ts-tests/.gitignore b/tee-worker/bitacross/ts-tests/.gitignore deleted file mode 100644 index 3a8fe5ede8..0000000000 --- a/tee-worker/bitacross/ts-tests/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.env.local \ No newline at end of file diff --git a/tee-worker/bitacross/ts-tests/.prettierrc b/tee-worker/bitacross/ts-tests/.prettierrc deleted file mode 100644 index b65f49a91b..0000000000 --- a/tee-worker/bitacross/ts-tests/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "trailingComma": "es5", - "singleQuote": true, - "printWidth": 120, - "tabWidth": 4, - "semi": true -} diff --git a/tee-worker/bitacross/ts-tests/README.md b/tee-worker/bitacross/ts-tests/README.md deleted file mode 100644 index c26dd8f0c2..0000000000 --- a/tee-worker/bitacross/ts-tests/README.md +++ /dev/null @@ -1,23 +0,0 @@ -## Description - -ts-tests of bitacross-worker - -## Environment setup - -- Install [nvm](https://github.com/nvm-sh/nvm) -- Inside the repository, run `nvm use` to set the correct Node version. - - If the version is not installed, run `nvm install`. - -## Prerequisite - -Before running the ts-tests, the client-api types generation needs to be completed. - -See client-api [README.md](https://github.com/litentry/litentry-parachain/blob/dev/tee-worker/identity/client-api/README.md) - -## Installation - -``` -nvm use -corepack enable pnpm -pnpm install -``` \ No newline at end of file diff --git a/tee-worker/bitacross/ts-tests/integration-tests/.env.local.example b/tee-worker/bitacross/ts-tests/integration-tests/.env.local.example deleted file mode 100644 index 87c0179b9f..0000000000 --- a/tee-worker/bitacross/ts-tests/integration-tests/.env.local.example +++ /dev/null @@ -1,5 +0,0 @@ -NODE_ENV = local -ENCLAVE_ENDPOINT = ws://localhost:2000 -PARACHAIN_ENDPOINT = ws://localhost:9944 -BINARY_DIR=../../bin -LITENTRY_CLI_DIR=../../bin/bitacross-cli diff --git a/tee-worker/bitacross/ts-tests/integration-tests/.env.staging b/tee-worker/bitacross/ts-tests/integration-tests/.env.staging deleted file mode 100644 index 53ae73ef81..0000000000 --- a/tee-worker/bitacross/ts-tests/integration-tests/.env.staging +++ /dev/null @@ -1,5 +0,0 @@ -NODE_ENV = staging -ENCLAVE_ENDPOINT = ws://bitacross-worker-1:2011 -PARACHAIN_ENDPOINT = "ws://litentry-node:9912" -BINARY_DIR=/usr/local/bin -LITENTRY_CLI_DIR=/usr/local/bin/bitacross-cli \ No newline at end of file diff --git a/tee-worker/bitacross/ts-tests/integration-tests/.eslintrc.json b/tee-worker/bitacross/ts-tests/integration-tests/.eslintrc.json deleted file mode 100644 index 0e93a1188c..0000000000 --- a/tee-worker/bitacross/ts-tests/integration-tests/.eslintrc.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], - "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint"], - "root": true, - "rules": { - /** - It's a temporary solution, folks. We had no choice but to shut it off, - because there's just a liiittle bit too much "any" lurking around in the code. - But fear not, my friends, for this is not the end of the story. - We shall return, armed with determination and resolve, - to tackle those "any" types head-on in the near future. - **/ - "@typescript-eslint/no-explicit-any": ["off"], - "@typescript-eslint/no-non-null-assertion": ["off"], - "@typescript-eslint/no-var-requires": ["off"], - - // explanation: https://typescript-eslint.io/rules/naming-convention/ - "@typescript-eslint/naming-convention": [ - "error", - { - "selector": "typeLike", - "format": ["StrictPascalCase"] - }, - { - "selector": "variable", - "modifiers": ["const"], - "format": ["strictCamelCase", "UPPER_CASE"] - }, - { - "selector": "function", - "format": ["strictCamelCase", "StrictPascalCase"] - }, - { - "selector": "parameter", - "format": ["strictCamelCase"] - } - ] - } -} diff --git a/tee-worker/bitacross/ts-tests/integration-tests/package.json b/tee-worker/bitacross/ts-tests/integration-tests/package.json deleted file mode 100644 index e7f39d9541..0000000000 --- a/tee-worker/bitacross/ts-tests/integration-tests/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "integration-tests", - "license": "ISC", - "type": "module", - "scripts": { - "check-format": "prettier --check .", - "format": "prettier --write .", - "pretest": "eslint .", - "test": "mocha --exit --sort -r ts-node/register --loader=ts-node/esm", - "check-types": "tsc --noEmit" - }, - "dependencies": { - "chai": "^5.1.1", - "dotenv": "^16.4.5", - "mocha": "^10.6.0", - "ws": "^8.18.0" - }, - "devDependencies": { - "prettier": "2.8.1", - "ts-node": "^10.9.1", - "typescript": "5.0.4" - }, - "packageManager": "pnpm@8.7.6" -} diff --git a/tee-worker/bitacross/ts-tests/integration-tests/sign_bitcoin.test.ts b/tee-worker/bitacross/ts-tests/integration-tests/sign_bitcoin.test.ts deleted file mode 100644 index cb041f73db..0000000000 --- a/tee-worker/bitacross/ts-tests/integration-tests/sign_bitcoin.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -import WebSocket from 'ws'; -import { assert } from 'chai'; -import dotenv from 'dotenv'; - -function getWorkerUrls(env: String): String[] { - if (env == 'local') { - let enclaveEndpoint = process.env.ENCLAVE_ENDPOINT!; - const enclaveEndpointParts = enclaveEndpoint.split(':'); - let url = enclaveEndpointParts[0] + ':' + enclaveEndpointParts[1]; - let port = parseInt(enclaveEndpointParts[2]); - return [url + ':' + port, url + ':' + (port + 10), url + ':' + (port + 20)]; - } else { - return ['wss://bitacross-worker-1:2011', 'wss://bitacross-worker-2:2011', 'wss://bitacross-worker-3:2011']; - } -} - -function sleep(time) { - return new Promise((resolve) => setTimeout(resolve, time)); -} - -describe('test-bitcoin', async () => { - // eslint-disable-next-line @typescript-eslint/no-var-requires, no-undef - dotenv.config({ path: `.env.${process.env.NODE_ENV || 'local'}` }); - - const workerUrls = getWorkerUrls(process.env.NODE_ENV as string); - console.log('Using worker urls: ' + workerUrls); - console.log('Start: ' + Date.now()); - // it needs to wait for workers to be ready, todo: use is_initialized - await sleep(60 * 1000); - console.log('Run: ' + Date.now()); - - it('should pass on all workers', async () => { - const worker1 = new WebSocket(workerUrls[0], { - perMessageDeflate: false, - rejectUnauthorized: false, - }); - const worker2 = new WebSocket(workerUrls[1], { - perMessageDeflate: false, - rejectUnauthorized: false, - }); - const worker3 = new WebSocket(workerUrls[2], { - perMessageDeflate: false, - rejectUnauthorized: false, - }); - - let worker1Resolve: any; - let worker1Result = new Promise((resolve, reject) => { - worker1Resolve = resolve; - }); - let worker2Resolve: any; - let worker2Result = new Promise((resolve, reject) => { - worker2Resolve = resolve; - }); - let worker3Resolve: any; - let worker3Result = new Promise((resolve, reject) => { - worker3Resolve = resolve; - }); - - worker1.on('message', (message: any) => { - worker1Resolve(message == '{"jsonrpc":"2.0","result":"0x04010000","id":1}'); - }); - worker2.on('message', (message: any) => { - worker2Resolve(message == '{"jsonrpc":"2.0","result":"0x04010000","id":1}'); - }); - worker3.on('message', (message: any) => { - worker3Resolve(message == '{"jsonrpc":"2.0","result":"0x04010000","id":1}'); - }); - - worker1.on('open', () => { - worker1.send('{"id":1,"jsonrpc":"2.0","method":"bitacross_checkSignBitcoin","params":[]}'); - }); - worker2.on('open', () => { - worker2.send('{"id":1,"jsonrpc":"2.0","method":"bitacross_checkSignBitcoin","params":[]}'); - }); - worker3.on('open', () => { - worker3.send('{"id":1,"jsonrpc":"2.0","method":"bitacross_checkSignBitcoin","params":[]}'); - }); - - await Promise.all([worker1Result, worker2Result, worker3Result]).then(([w1, w2, w3]) => { - assert(w1 && w2 && w3); - }); - }); -}); diff --git a/tee-worker/bitacross/ts-tests/integration-tests/tsconfig.json b/tee-worker/bitacross/ts-tests/integration-tests/tsconfig.json deleted file mode 100644 index 077ab9bb03..0000000000 --- a/tee-worker/bitacross/ts-tests/integration-tests/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "moduleResolution": "Node", - "declaration": true, - "strict": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "resolveJsonModule": true, - "baseUrl": "." - }, - "ts-node": { - "esm": true, - "experimentalResolver": true, - "experimentalSpecifierResolution": "node", - "transpileOnly": true - } -} diff --git a/tee-worker/bitacross/ts-tests/package.json b/tee-worker/bitacross/ts-tests/package.json deleted file mode 100644 index 346af2afda..0000000000 --- a/tee-worker/bitacross/ts-tests/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "type": "module", - "license": "ISC", - "scripts": { - "format": "pnpm run --recursive format", - "check-format": "pnpm run --recursive check-format" - }, - "packageManager": "pnpm@8.7.6" -} diff --git a/tee-worker/bitacross/ts-tests/pnpm-lock.yaml b/tee-worker/bitacross/ts-tests/pnpm-lock.yaml deleted file mode 100644 index befebb7204..0000000000 --- a/tee-worker/bitacross/ts-tests/pnpm-lock.yaml +++ /dev/null @@ -1,706 +0,0 @@ -lockfileVersion: '6.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: {} - - integration-tests: - dependencies: - chai: - specifier: ^5.1.1 - version: 5.1.1 - dotenv: - specifier: ^16.4.5 - version: 16.4.5 - mocha: - specifier: ^10.6.0 - version: 10.6.0 - ws: - specifier: ^8.18.0 - version: 8.18.0 - devDependencies: - prettier: - specifier: 2.8.1 - version: 2.8.1 - ts-node: - specifier: ^10.9.1 - version: 10.9.2(@types/node@20.14.10)(typescript@5.0.4) - typescript: - specifier: 5.0.4 - version: 5.0.4 - -packages: - - /@cspotcode/source-map-support@0.8.1: - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - dev: true - - /@jridgewell/resolve-uri@3.1.2: - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - dev: true - - /@jridgewell/sourcemap-codec@1.4.15: - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - dev: true - - /@jridgewell/trace-mapping@0.3.9: - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - - /@tsconfig/node10@1.0.11: - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} - dev: true - - /@tsconfig/node12@1.0.11: - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - dev: true - - /@tsconfig/node14@1.0.3: - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - dev: true - - /@tsconfig/node16@1.0.4: - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - dev: true - - /@types/node@20.14.10: - resolution: {integrity: sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==} - dependencies: - undici-types: 5.26.5 - dev: true - - /acorn-walk@8.3.3: - resolution: {integrity: sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==} - engines: {node: '>=0.4.0'} - dependencies: - acorn: 8.12.1 - dev: true - - /acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} - engines: {node: '>=0.4.0'} - hasBin: true - dev: true - - /ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - dev: false - - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - dev: false - - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - dependencies: - color-convert: 2.0.1 - dev: false - - /anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - dev: false - - /arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - dev: true - - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: false - - /assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} - engines: {node: '>=12'} - dev: false - - /balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: false - - /binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - dev: false - - /brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - dependencies: - balanced-match: 1.0.2 - dev: false - - /braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - dependencies: - fill-range: 7.1.1 - dev: false - - /browser-stdout@1.3.1: - resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} - dev: false - - /camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - dev: false - - /chai@5.1.1: - resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} - engines: {node: '>=12'} - dependencies: - assertion-error: 2.0.1 - check-error: 2.1.1 - deep-eql: 5.0.2 - loupe: 3.1.1 - pathval: 2.0.0 - dev: false - - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - dev: false - - /check-error@2.1.1: - resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} - engines: {node: '>= 16'} - dev: false - - /chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - dev: false - - /cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - dev: false - - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - dependencies: - color-name: 1.1.4 - dev: false - - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - dev: false - - /create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - dev: true - - /debug@4.3.5(supports-color@8.1.1): - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - supports-color: 8.1.1 - dev: false - - /decamelize@4.0.0: - resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} - engines: {node: '>=10'} - dev: false - - /deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} - dev: false - - /diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - dev: true - - /diff@5.2.0: - resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} - engines: {node: '>=0.3.1'} - dev: false - - /dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} - engines: {node: '>=12'} - dev: false - - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: false - - /escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} - engines: {node: '>=6'} - dev: false - - /escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - dev: false - - /fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - dependencies: - to-regex-range: 5.0.1 - dev: false - - /find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - dev: false - - /flat@5.0.2: - resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} - hasBin: true - dev: false - - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: false - - /fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true - dev: false - optional: true - - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - dev: false - - /get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - dev: false - - /glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - dependencies: - is-glob: 4.0.3 - dev: false - - /glob@8.1.0: - resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} - engines: {node: '>=12'} - deprecated: Glob versions prior to v9 are no longer supported - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 5.1.6 - once: 1.4.0 - dev: false - - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - dev: false - - /he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - dev: false - - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - dev: false - - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - dev: false - - /is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - dependencies: - binary-extensions: 2.3.0 - dev: false - - /is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - dev: false - - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - dev: false - - /is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - dependencies: - is-extglob: 2.1.1 - dev: false - - /is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - dev: false - - /is-plain-obj@2.1.0: - resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} - engines: {node: '>=8'} - dev: false - - /is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - dev: false - - /js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - dependencies: - argparse: 2.0.1 - dev: false - - /locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - dependencies: - p-locate: 5.0.0 - dev: false - - /log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 - dev: false - - /loupe@3.1.1: - resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} - dependencies: - get-func-name: 2.0.2 - dev: false - - /make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - dev: true - - /minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} - dependencies: - brace-expansion: 2.0.1 - dev: false - - /mocha@10.6.0: - resolution: {integrity: sha512-hxjt4+EEB0SA0ZDygSS015t65lJw/I2yRCS3Ae+SJ5FrbzrXgfYwJr96f0OvIXdj7h4lv/vLCrH3rkiuizFSvw==} - engines: {node: '>= 14.0.0'} - hasBin: true - dependencies: - ansi-colors: 4.1.3 - browser-stdout: 1.3.1 - chokidar: 3.6.0 - debug: 4.3.5(supports-color@8.1.1) - diff: 5.2.0 - escape-string-regexp: 4.0.0 - find-up: 5.0.0 - glob: 8.1.0 - he: 1.2.0 - js-yaml: 4.1.0 - log-symbols: 4.1.0 - minimatch: 5.1.6 - ms: 2.1.3 - serialize-javascript: 6.0.2 - strip-json-comments: 3.1.1 - supports-color: 8.1.1 - workerpool: 6.5.1 - yargs: 16.2.0 - yargs-parser: 20.2.9 - yargs-unparser: 2.0.0 - dev: false - - /ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: false - - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - dev: false - - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - dev: false - - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - dependencies: - wrappy: 1.0.2 - dev: false - - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - dependencies: - yocto-queue: 0.1.0 - dev: false - - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - dependencies: - p-limit: 3.1.0 - dev: false - - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - dev: false - - /pathval@2.0.0: - resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} - engines: {node: '>= 14.16'} - dev: false - - /picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - dev: false - - /prettier@2.8.1: - resolution: {integrity: sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==} - engines: {node: '>=10.13.0'} - hasBin: true - dev: true - - /randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - dependencies: - safe-buffer: 5.2.1 - dev: false - - /readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - dependencies: - picomatch: 2.3.1 - dev: false - - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - dev: false - - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: false - - /serialize-javascript@6.0.2: - resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} - dependencies: - randombytes: 2.1.0 - dev: false - - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - dev: false - - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - dependencies: - ansi-regex: 5.0.1 - dev: false - - /strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - dev: false - - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - dependencies: - has-flag: 4.0.0 - dev: false - - /supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - dependencies: - has-flag: 4.0.0 - dev: false - - /to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - dependencies: - is-number: 7.0.0 - dev: false - - /ts-node@10.9.2(@types/node@20.14.10)(typescript@5.0.4): - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.14.10 - acorn: 8.12.1 - acorn-walk: 8.3.3 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.0.4 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - dev: true - - /typescript@5.0.4: - resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==} - engines: {node: '>=12.20'} - hasBin: true - dev: true - - /undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - dev: true - - /v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - dev: true - - /workerpool@6.5.1: - resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==} - dev: false - - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - dev: false - - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: false - - /ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - dev: false - - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - dev: false - - /yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - dev: false - - /yargs-unparser@2.0.0: - resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} - engines: {node: '>=10'} - dependencies: - camelcase: 6.3.0 - decamelize: 4.0.0 - flat: 5.0.2 - is-plain-obj: 2.1.0 - dev: false - - /yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} - dependencies: - cliui: 7.0.4 - escalade: 3.1.2 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 20.2.9 - dev: false - - /yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - dev: true - - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - dev: false diff --git a/tee-worker/bitacross/ts-tests/pnpm-workspace.yaml b/tee-worker/bitacross/ts-tests/pnpm-workspace.yaml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tee-worker/bitacross/upstream_commit b/tee-worker/bitacross/upstream_commit deleted file mode 100644 index 195f70c5e6..0000000000 --- a/tee-worker/bitacross/upstream_commit +++ /dev/null @@ -1 +0,0 @@ -9a3b032 diff --git a/tee-worker/common/core-primitives/enclave-metrics/src/lib.rs b/tee-worker/common/core-primitives/enclave-metrics/src/lib.rs index 68f55825dc..477eb6c5a9 100644 --- a/tee-worker/common/core-primitives/enclave-metrics/src/lib.rs +++ b/tee-worker/common/core-primitives/enclave-metrics/src/lib.rs @@ -51,9 +51,4 @@ pub enum EnclaveMetric { ParentchainEventProcessed(String), DynamicAssertionSaveTime(Duration), DynamicAssertionGetTime(Duration), - // bitacross - Musig2CeremonyStarted, - Musig2CeremonyFailed, - Musig2CeremonyTimedout(u8), - Musig2CeremonyDuration(Duration), } diff --git a/tee-worker/common/core-primitives/node-api/metadata/src/lib.rs b/tee-worker/common/core-primitives/node-api/metadata/src/lib.rs index 0362f47b5b..a5a0c369a2 100644 --- a/tee-worker/common/core-primitives/node-api/metadata/src/lib.rs +++ b/tee-worker/common/core-primitives/node-api/metadata/src/lib.rs @@ -20,7 +20,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use crate::{ - error::Result, pallet_balances::BalancesCallIndexes, pallet_bitacross::BitAcrossCallIndexes, + error::Result, pallet_balances::BalancesCallIndexes, pallet_evm_assertion::EvmAssertionsCallIndexes, pallet_imp::IMPCallIndexes, pallet_omni_account::OmniAccountCallIndexes, pallet_proxy::ProxyCallIndexes, pallet_system::SystemConstants, pallet_teebag::TeebagCallIndexes, @@ -35,7 +35,6 @@ pub use itp_api_client_types::{Metadata, MetadataError}; pub mod error; pub mod pallet_balances; -pub mod pallet_bitacross; pub mod pallet_evm_assertion; pub mod pallet_imp; pub mod pallet_omni_account; @@ -61,7 +60,6 @@ pub trait NodeMetadataTrait: + BalancesCallIndexes + TimestampCallIndexes + EvmAssertionsCallIndexes - + BitAcrossCallIndexes + OmniAccountCallIndexes { } @@ -76,7 +74,6 @@ impl< + BalancesCallIndexes + TimestampCallIndexes + EvmAssertionsCallIndexes - + BitAcrossCallIndexes + OmniAccountCallIndexes, > NodeMetadataTrait for T { diff --git a/tee-worker/common/core-primitives/node-api/metadata/src/metadata_mocks.rs b/tee-worker/common/core-primitives/node-api/metadata/src/metadata_mocks.rs index 0189d1309d..e15812a093 100644 --- a/tee-worker/common/core-primitives/node-api/metadata/src/metadata_mocks.rs +++ b/tee-worker/common/core-primitives/node-api/metadata/src/metadata_mocks.rs @@ -16,7 +16,7 @@ */ use crate::{ - error::Result, pallet_balances::BalancesCallIndexes, pallet_bitacross::BitAcrossCallIndexes, + error::Result, pallet_balances::BalancesCallIndexes, pallet_evm_assertion::EvmAssertionsCallIndexes, pallet_imp::IMPCallIndexes, pallet_omni_account::OmniAccountCallIndexes, pallet_proxy::ProxyCallIndexes, pallet_system::SystemConstants, pallet_teebag::TeebagCallIndexes, @@ -90,13 +90,6 @@ pub struct NodeMetadataMock { runtime_spec_version: u32, runtime_transaction_version: u32, - bitacross_module: u8, - bitacross_add_relayer: u8, - bitacross_remove_relayer: u8, - btc_wallet_generated: u8, - eth_wallet_generated: u8, - ton_wallet_generated: u8, - omni_account_module: u8, dispatch_as_omni_account: u8, dispatch_as_signed: u8, @@ -158,13 +151,6 @@ impl NodeMetadataMock { runtime_spec_version: 25, runtime_transaction_version: 4, - bitacross_module: 69u8, - bitacross_add_relayer: 0u8, - bitacross_remove_relayer: 1u8, - btc_wallet_generated: 2u8, - eth_wallet_generated: 3u8, - ton_wallet_generated: 4u8, - omni_account_module: 70u8, dispatch_as_omni_account: 0u8, dispatch_as_signed: 1u8, @@ -322,28 +308,6 @@ impl BalancesCallIndexes for NodeMetadataMock { } } -impl BitAcrossCallIndexes for NodeMetadataMock { - fn add_relayer_call_indexes(&self) -> Result<[u8; 2]> { - Ok([self.bitacross_module, self.bitacross_add_relayer]) - } - - fn remove_relayer_call_indexes(&self) -> Result<[u8; 2]> { - Ok([self.bitacross_module, self.bitacross_remove_relayer]) - } - - fn btc_wallet_generated_indexes(&self) -> Result<[u8; 2]> { - Ok([self.bitacross_module, self.btc_wallet_generated]) - } - - fn eth_wallet_generated_indexes(&self) -> Result<[u8; 2]> { - Ok([self.bitacross_module, self.eth_wallet_generated]) - } - - fn ton_wallet_generated_indexes(&self) -> Result<[u8; 2]> { - Ok([self.bitacross_module, self.ton_wallet_generated]) - } -} - impl TimestampCallIndexes for NodeMetadataMock { fn timestamp_set_call_indexes(&self) -> Result<[u8; 2]> { Ok([self.timestamp_module, self.timestamp_set]) diff --git a/tee-worker/common/core-primitives/node-api/metadata/src/pallet_bitacross.rs b/tee-worker/common/core-primitives/node-api/metadata/src/pallet_bitacross.rs deleted file mode 100644 index 0c45043f28..0000000000 --- a/tee-worker/common/core-primitives/node-api/metadata/src/pallet_bitacross.rs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2020-2024 Trust Computing GmbH. -// This file is part of Litentry. -// -// Litentry is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Litentry is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Litentry. If not, see . - -// TODO: maybe use macros to simplify this -use crate::{error::Result, NodeMetadata}; - -const BITACROSS: &str = "Bitacross"; - -pub trait BitAcrossCallIndexes { - fn add_relayer_call_indexes(&self) -> Result<[u8; 2]>; - fn remove_relayer_call_indexes(&self) -> Result<[u8; 2]>; - fn btc_wallet_generated_indexes(&self) -> Result<[u8; 2]>; - fn eth_wallet_generated_indexes(&self) -> Result<[u8; 2]>; - fn ton_wallet_generated_indexes(&self) -> Result<[u8; 2]>; -} - -impl BitAcrossCallIndexes for NodeMetadata { - fn add_relayer_call_indexes(&self) -> Result<[u8; 2]> { - self.call_indexes(BITACROSS, "add_relayer") - } - - fn remove_relayer_call_indexes(&self) -> Result<[u8; 2]> { - self.call_indexes(BITACROSS, "remove_relayer") - } - - fn btc_wallet_generated_indexes(&self) -> Result<[u8; 2]> { - self.call_indexes(BITACROSS, "btc_wallet_generated") - } - - fn eth_wallet_generated_indexes(&self) -> Result<[u8; 2]> { - self.call_indexes(BITACROSS, "eth_wallet_generated") - } - - fn ton_wallet_generated_indexes(&self) -> Result<[u8; 2]> { - self.call_indexes(BITACROSS, "ton_wallet_generated") - } -} diff --git a/tee-worker/common/core-primitives/settings/src/lib.rs b/tee-worker/common/core-primitives/settings/src/lib.rs index 55dfb81caf..44300ae631 100644 --- a/tee-worker/common/core-primitives/settings/src/lib.rs +++ b/tee-worker/common/core-primitives/settings/src/lib.rs @@ -50,13 +50,6 @@ pub mod files { pub const RA_DUMP_CERT_DER_FILE: &str = "ra_dump_cert.der"; - // bitacross - pub const RELAYER_REGISTRY_FILE: &str = "relayer_registry_sealed.bin"; - - pub const ENCLAVE_REGISTRY_FILE: &str = "enclave_registry_sealed.bin"; - - pub const SIGNER_REGISTRY_FILE: &str = "signer_registry_sealed.bin"; - // used by worker and enclave pub const SHARDS_PATH: &str = "shards"; diff --git a/tee-worker/common/core-primitives/types/src/parentchain/events.rs b/tee-worker/common/core-primitives/types/src/parentchain/events.rs index 57205832ed..8c379e6359 100644 --- a/tee-worker/common/core-primitives/types/src/parentchain/events.rs +++ b/tee-worker/common/core-primitives/types/src/parentchain/events.rs @@ -6,7 +6,7 @@ use alloc::{format, vec::Vec}; use codec::{Decode, Encode}; use core::fmt::Debug; use itp_utils::{hex::ToHexPrefixed, stringify::account_id_to_string}; -use litentry_primitives::{Address32, Identity, MemberAccount}; +use litentry_primitives::{Address32, MemberAccount}; use sp_core::H160; use substrate_api_client::ac_node_api::StaticEvent; @@ -306,66 +306,3 @@ impl StaticEvent for AssertionCreated { const PALLET: &'static str = "EvmAssertions"; const EVENT: &'static str = "AssertionCreated"; } - -// Bitacross pallet events - -#[derive(Encode, Decode, Debug)] -pub struct RelayerAdded { - pub who: Identity, -} - -impl core::fmt::Display for RelayerAdded { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - if let Some(account_id) = self.who.to_native_account() { - let message = format!("RelayerAdded :: account_id: {:?}", account_id); - write!(f, "{}", message) - } else { - write!(f, "RelayerAdded :: account_id: None") - } - } -} - -impl StaticEvent for RelayerAdded { - const PALLET: &'static str = "Bitacross"; - const EVENT: &'static str = "RelayerAdded"; -} - -#[derive(Encode, Decode, Debug)] -pub struct RelayerRemoved { - pub who: Identity, -} - -impl core::fmt::Display for RelayerRemoved { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - if let Some(account_id) = self.who.to_native_account() { - let message = format!("RelayerRemoved :: account_id: {:?}", account_id); - write!(f, "{}", message) - } else { - write!(f, "RelayerRemoved :: account_id: None") - } - } -} - -impl StaticEvent for RelayerRemoved { - const PALLET: &'static str = "Bitacross"; - const EVENT: &'static str = "RelayerRemoved"; -} - -#[derive(Encode, Decode, Debug)] -pub struct BtcWalletGenerated { - pub pub_key: [u8; 33], - pub account_id: AccountId, -} - -impl core::fmt::Display for BtcWalletGenerated { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - let account_id = account_id_to_string::(&self.account_id); - let message = format!("BtcWalletGenerated :: account_id: {:?}", account_id); - write!(f, "{}", message) - } -} - -impl StaticEvent for BtcWalletGenerated { - const PALLET: &'static str = "Bitacross"; - const EVENT: &'static str = "BtcWalletGenerated"; -} diff --git a/tee-worker/common/core-primitives/types/src/parentchain/mod.rs b/tee-worker/common/core-primitives/types/src/parentchain/mod.rs index 05172aabd5..64a5eddfc4 100644 --- a/tee-worker/common/core-primitives/types/src/parentchain/mod.rs +++ b/tee-worker/common/core-primitives/types/src/parentchain/mod.rs @@ -118,16 +118,10 @@ pub trait FilterEvents { &self, ) -> Result, Self::Error>; - fn get_relayer_added_events(&self) -> Result, Self::Error>; - - fn get_relayers_removed_events(&self) -> Result, Self::Error>; - fn get_enclave_added_events(&self) -> Result, Self::Error>; fn get_enclave_removed_events(&self) -> Result, Self::Error>; - fn get_btc_wallet_generated_events(&self) -> Result, Self::Error>; - fn get_account_store_updated_events(&self) -> Result, Self::Error>; } diff --git a/tee-worker/identity/app-libs/parentchain-interface/src/integritee/event_filter.rs b/tee-worker/identity/app-libs/parentchain-interface/src/integritee/event_filter.rs index f0ae6078dc..c6fcf4d94a 100644 --- a/tee-worker/identity/app-libs/parentchain-interface/src/integritee/event_filter.rs +++ b/tee-worker/identity/app-libs/parentchain-interface/src/integritee/event_filter.rs @@ -98,14 +98,6 @@ impl FilterEvents for FilterableEvents { self.filter() } - fn get_relayer_added_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_relayers_removed_events(&self) -> Result, Self::Error> { - self.filter() - } - fn get_enclave_added_events(&self) -> Result, Self::Error> { self.filter() } @@ -114,10 +106,6 @@ impl FilterEvents for FilterableEvents { self.filter() } - fn get_btc_wallet_generated_events(&self) -> Result, Self::Error> { - self.filter() - } - fn get_account_store_updated_events(&self) -> Result, Self::Error> { self.filter() } diff --git a/tee-worker/identity/app-libs/parentchain-interface/src/target_a/event_filter.rs b/tee-worker/identity/app-libs/parentchain-interface/src/target_a/event_filter.rs index 4b86a285e8..95659a3a37 100644 --- a/tee-worker/identity/app-libs/parentchain-interface/src/target_a/event_filter.rs +++ b/tee-worker/identity/app-libs/parentchain-interface/src/target_a/event_filter.rs @@ -96,14 +96,6 @@ impl FilterEvents for FilterableEvents { Ok(Vec::new()) } - fn get_relayer_added_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_relayers_removed_events(&self) -> Result, Self::Error> { - self.filter() - } - fn get_enclave_added_events(&self) -> Result, Self::Error> { self.filter() } @@ -112,10 +104,6 @@ impl FilterEvents for FilterableEvents { self.filter() } - fn get_btc_wallet_generated_events(&self) -> Result, Self::Error> { - self.filter() - } - fn get_account_store_updated_events(&self) -> Result, Self::Error> { self.filter() } diff --git a/tee-worker/identity/app-libs/parentchain-interface/src/target_b/event_filter.rs b/tee-worker/identity/app-libs/parentchain-interface/src/target_b/event_filter.rs index 4b86a285e8..95659a3a37 100644 --- a/tee-worker/identity/app-libs/parentchain-interface/src/target_b/event_filter.rs +++ b/tee-worker/identity/app-libs/parentchain-interface/src/target_b/event_filter.rs @@ -96,14 +96,6 @@ impl FilterEvents for FilterableEvents { Ok(Vec::new()) } - fn get_relayer_added_events(&self) -> Result, Self::Error> { - self.filter() - } - - fn get_relayers_removed_events(&self) -> Result, Self::Error> { - self.filter() - } - fn get_enclave_added_events(&self) -> Result, Self::Error> { self.filter() } @@ -112,10 +104,6 @@ impl FilterEvents for FilterableEvents { self.filter() } - fn get_btc_wallet_generated_events(&self) -> Result, Self::Error> { - self.filter() - } - fn get_account_store_updated_events(&self) -> Result, Self::Error> { self.filter() } diff --git a/tee-worker/identity/core/parentchain/indirect-calls-executor/src/mock.rs b/tee-worker/identity/core/parentchain/indirect-calls-executor/src/mock.rs index 25cab5b0e6..16a95d30ce 100644 --- a/tee-worker/identity/core/parentchain/indirect-calls-executor/src/mock.rs +++ b/tee-worker/identity/core/parentchain/indirect-calls-executor/src/mock.rs @@ -66,10 +66,6 @@ impl FilterEvents for MockEvents { Ok(Vec::new()) } - fn get_btc_wallet_generated_events(&self) -> Result, Self::Error> { - Ok(Vec::new()) - } - fn get_enclave_added_events(&self) -> Result, Self::Error> { Ok(Vec::new()) } @@ -78,14 +74,6 @@ impl FilterEvents for MockEvents { Ok(Vec::new()) } - fn get_relayer_added_events(&self) -> Result, Self::Error> { - Ok(Vec::new()) - } - - fn get_relayers_removed_events(&self) -> Result, Self::Error> { - Ok(Vec::new()) - } - fn get_account_store_updated_events(&self) -> Result, Self::Error> { Ok(Vec::new()) } From 91778e5fdc5c201bfe201a8a4c9f1c4b321b3d15 Mon Sep 17 00:00:00 2001 From: Kailai Wang Date: Fri, 13 Dec 2024 16:23:27 +0000 Subject: [PATCH 2/2] remove macros --- common/primitives/core/src/teebag/types.rs | 7 ------- parachain/Cargo.lock | 18 ------------------ 2 files changed, 25 deletions(-) diff --git a/common/primitives/core/src/teebag/types.rs b/common/primitives/core/src/teebag/types.rs index 91c947f7a5..55190d981d 100644 --- a/common/primitives/core/src/teebag/types.rs +++ b/common/primitives/core/src/teebag/types.rs @@ -42,11 +42,8 @@ pub type EnclaveFingerprint = H256; )] pub enum OperationalMode { #[default] - #[codec(index = 0)] Production, - #[codec(index = 1)] Development, - #[codec(index = 2)] Maintenance, } @@ -70,9 +67,7 @@ pub enum AttestationType { #[derive(Encode, Decode, Clone, Copy, Default, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub enum WorkerType { #[default] - #[codec(index = 0)] Identity, - #[codec(index = 2)] OmniExecutor, } @@ -86,9 +81,7 @@ pub enum WorkerMode { #[derive(Encode, Decode, Copy, Clone, Default, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub enum SgxBuildMode { #[default] - #[codec(index = 0)] Production, - #[codec(index = 1)] Debug, } diff --git a/parachain/Cargo.lock b/parachain/Cargo.lock index d3b87aa8b8..c64f355a4a 100644 --- a/parachain/Cargo.lock +++ b/parachain/Cargo.lock @@ -5936,7 +5936,6 @@ dependencies = [ "pallet-aura", "pallet-authorship", "pallet-balances", - "pallet-bitacross", "pallet-bounties", "pallet-bridge-transfer", "pallet-chain-bridge", @@ -7630,22 +7629,6 @@ dependencies = [ "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.11.0)", ] -[[package]] -name = "pallet-bitacross" -version = "0.1.0" -dependencies = [ - "core-primitives", - "frame-benchmarking", - "frame-support", - "frame-system", - "pallet-balances", - "pallet-timestamp", - "parity-scale-codec", - "scale-info", - "sp-io", - "sp-runtime", -] - [[package]] name = "pallet-bounties" version = "27.0.0" @@ -9460,7 +9443,6 @@ dependencies = [ "pallet-aura", "pallet-authorship", "pallet-balances", - "pallet-bitacross", "pallet-bounties", "pallet-bridge-transfer", "pallet-chain-bridge",

    0!QfkZ{$rnf@G8(VU9V?c|7s?i{ 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�#!$ys^60y!9+@vl7D+d5C1K`NwN!`VGsax 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�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 z%0A?KB|?uXF>w(_f1C(|41oWB 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