From b7b507993437ba517e2133fb6b05a151b069a23d Mon Sep 17 00:00:00 2001 From: Stanislav Bogatyrev Date: Fri, 24 Jul 2020 16:54:03 +0300 Subject: [PATCH] Add Inner Ring code --- .github/logo.svg | 129 +++ .gitignore | 1 + CHANGELOG.md | 14 + CONTRIBUTING.md | 60 +- CREDITS.md | 26 + Dockerfile | 7 +- Dockerfile.ir | 20 + Makefile | 26 +- README.md | 57 ++ cmd/neofs-ir/defaults.go | 74 ++ cmd/neofs-ir/main.go | 82 ++ cmd/neofs-node/defaults.go | 34 +- cmd/neofs-node/main.go | 22 +- .../neofs-node/modules}/bootstrap/healthy.go | 26 +- .../neofs-node/modules}/bootstrap/module.go | 4 +- {lib => cmd/neofs-node/modules}/fix/catch.go | 0 .../neofs-node/modules}/fix/config/config.go | 0 {lib => cmd/neofs-node/modules}/fix/fix.go | 9 +- .../neofs-node/modules}/fix/module/module.go | 0 .../neofs-node/modules}/fix/services.go | 0 .../neofs-node/modules}/fix/worker/worker.go | 0 .../neofs-node/modules}/grpc/billing.go | 0 .../neofs-node/modules}/grpc/module.go | 4 +- .../neofs-node/modules}/grpc/routing.go | 7 +- .../neofs-node/modules}/morph/balance.go | 38 +- .../neofs-node/modules}/morph/common.go | 18 +- .../neofs-node/modules}/morph/container.go | 77 +- .../neofs-node/modules}/morph/event.go | 4 +- cmd/neofs-node/modules/morph/goclient.go | 31 + .../neofs-node/modules}/morph/listener.go | 4 +- .../neofs-node/modules}/morph/module.go | 5 +- .../neofs-node/modules}/morph/netmap.go | 75 +- .../neofs-node/modules}/network/http.go | 2 +- .../neofs-node/modules}/network/module.go | 9 +- .../neofs-node/modules}/network/muxer.go | 6 +- .../neofs-node/modules}/network/peers.go | 21 +- .../neofs-node/modules}/network/placement.go | 24 +- .../neofs-node/modules}/node/audit.go | 18 +- .../neofs-node/modules}/node/container.go | 12 +- cmd/neofs-node/modules/node/core.go | 35 + .../neofs-node/modules}/node/localstore.go | 29 +- .../neofs-node/modules}/node/metrics.go | 24 +- .../neofs-node/modules}/node/module.go | 36 +- .../neofs-node/modules}/node/objectmanager.go | 88 +-- .../neofs-node/modules}/node/peerstore.go | 2 +- cmd/neofs-node/modules/node/placement.go | 28 + .../neofs-node/modules}/node/replication.go | 51 +- cmd/neofs-node/modules/node/services.go | 36 + .../neofs-node/modules}/node/session.go | 6 +- .../neofs-node/modules}/settings/address.go | 9 +- .../neofs-node/modules}/settings/module.go | 4 +- .../neofs-node/modules}/settings/node.go | 17 +- .../neofs-node/modules}/workers/module.go | 4 +- .../neofs-node/modules}/workers/prepare.go | 2 +- go.mod | 8 +- go.sum | Bin 66535 -> 65607 bytes internal/error.go | 7 - lib/.gitkeep | 0 lib/acl/action.go | 94 --- lib/acl/action_test.go | 163 ---- lib/acl/basic.go | 179 ----- lib/acl/basic_test.go | 116 --- lib/acl/binary.go | 129 --- lib/acl/binary_test.go | 27 - lib/acl/extended.go | 29 - lib/acl/header.go | 234 ------ lib/acl/headers_test.go | 60 -- lib/acl/match.go | 94 --- lib/acl/match_test.go | 192 ----- lib/boot/bootstrap_test.go | 24 - lib/boot/bootstrapper.go | 31 - lib/boot/storage.go | 46 -- lib/buckets/boltdb/methods_test.go | 95 --- lib/buckets/boltdb/plugin/main.go | 25 - lib/buckets/init.go | 64 -- lib/buckets/inmemory/bucket.go | 60 -- lib/buckets/inmemory/methods.go | 107 --- lib/container/alias.go | 15 - lib/container/storage.go | 134 ---- lib/container/storage_test.go | 83 -- lib/core/storage.go | 94 --- lib/core/storage_test.go | 65 -- lib/implementations/acl.go | 392 --------- lib/implementations/acl_test.go | 19 - lib/implementations/balance.go | 141 ---- lib/implementations/balance_test.go | 35 - lib/implementations/bootstrap.go | 311 -------- lib/implementations/bootstrap_test.go | 30 - lib/implementations/epoch.go | 7 - lib/implementations/reputation.go | 41 - lib/ir/info.go | 17 - lib/ir/info_test.go | 25 - lib/ir/node.go | 17 - lib/ir/node_test.go | 16 - lib/ir/storage.go | 94 --- lib/ir/storage_test.go | 101 --- lib/netmap/netmap.go | 392 --------- lib/netmap/netmap_test.go | 261 ------ lib/netmap/storage.go | 27 - lib/netmap/storage_test.go | 23 - lib/peers/peers.go | 455 ----------- lib/peers/peers_test.go | 484 ------------ lib/storage/storage.go | 122 --- lib/transport/connection.go | 39 - lib/transport/transport.go | 76 -- lib/transport/transport_test.go | 61 -- misc/build.go | 13 +- modules/morph/goclient.go | 32 - modules/morph/reputation.go | 59 -- modules/node/core.go | 29 - modules/node/placement.go | 33 - modules/node/services.go | 36 - pkg/core/container/acl/basic/enum.go | 24 + pkg/core/container/acl/basic/types.go | 159 ++++ pkg/core/container/acl/basic/types_test.go | 189 +++++ pkg/core/container/acl/basic/util.go | 64 ++ pkg/core/container/acl/basic/util_test.go | 36 + .../container/acl/extended/storage/storage.go | 53 ++ .../acl/extended/storage/test/storage.go | 48 ++ pkg/core/container/acl/extended/types.go | 102 +++ pkg/core/container/container.go | 82 ++ pkg/core/container/container_test.go | 30 + pkg/core/container/id.go | 49 ++ pkg/core/container/id_test.go | 38 + pkg/core/container/marshal.go | 75 ++ pkg/core/container/marshal_test.go | 26 + pkg/core/container/storage/storage.go | 79 ++ pkg/core/container/storage/test/storage.go | 127 +++ .../container/storage/test/storage_test.go | 11 + pkg/core/netmap/epoch/marshal.go | 37 + pkg/core/netmap/epoch/marshal_test.go | 20 + pkg/core/netmap/epoch/math.go | 12 + pkg/core/netmap/epoch/math_test.go | 28 + pkg/core/netmap/epoch/relation.go | 55 ++ pkg/core/netmap/epoch/relation_test.go | 40 + pkg/core/netmap/epoch/type.go | 23 + pkg/core/netmap/netmap.go | 119 +++ pkg/core/netmap/netmap_test.go | 39 + pkg/core/netmap/node/info.go | 154 ++++ pkg/core/netmap/node/info_test.go | 133 ++++ pkg/core/netmap/node/options.go | 46 ++ pkg/core/netmap/node/options_test.go | 24 + pkg/core/netmap/node/status.go | 63 ++ pkg/core/netmap/node/status_test.go | 17 + pkg/core/object/extended.go | 94 +++ pkg/core/object/extended_test.go | 25 + pkg/core/object/header.go | 73 ++ pkg/core/object/header_test.go | 98 +++ pkg/core/object/headers/enum.go | 63 ++ pkg/core/object/headers/enum_test.go | 34 + pkg/core/object/headers/user.go | 45 ++ pkg/core/object/headers/user_test.go | 45 ++ pkg/core/object/id.go | 61 ++ pkg/core/object/id_test.go | 42 + pkg/core/object/object.go | 76 ++ pkg/core/object/object_test.go | 58 ++ pkg/core/object/storage/storage.go | 61 ++ pkg/core/object/storage/test/storage.go | 88 +++ pkg/core/object/storage/test/storage_test.go | 11 + pkg/core/object/sys.go | 107 +++ pkg/core/object/sys_test.go | 62 ++ pkg/innerring/bindings.go | 54 ++ pkg/innerring/innerring.go | 302 +++++++ pkg/innerring/invoke/balance.go | 59 ++ pkg/innerring/invoke/neofs.go | 68 ++ pkg/innerring/invoke/netmap.go | 39 + pkg/innerring/processors/balance/handlers.go | 25 + .../processors/balance/process_assets.go | 27 + pkg/innerring/processors/balance/processor.go | 103 +++ pkg/innerring/processors/neofs/handlers.go | 57 ++ .../processors/neofs/process_assets.go | 104 +++ pkg/innerring/processors/neofs/processor.go | 143 ++++ pkg/innerring/processors/netmap/handlers.go | 40 + .../processors/netmap/process_epoch.go | 29 + pkg/innerring/processors/netmap/processor.go | 131 ++++ pkg/innerring/state.go | 17 + pkg/innerring/timers/epoch.go | 12 + pkg/innerring/timers/timers.go | 86 ++ .../bucket}/boltdb/boltdb.go | 40 +- .../bucket}/boltdb/methods.go | 26 +- pkg/local_object_storage/bucket/bucket.go | 41 + .../bucket}/fsbucket/bucket.go | 30 +- .../bucket}/fsbucket/methods.go | 24 +- .../bucket}/fsbucket/queue.go | 0 .../bucket}/fsbucket/treemethods.go | 12 +- .../bucket}/fsbucket/treemethods_test.go | 7 +- .../bucket}/test/bucket.go | 20 +- .../local_object_storage}/localstore/alias.go | 0 .../local_object_storage}/localstore/del.go | 4 +- .../localstore/filter.go | 3 +- .../localstore/filter_funcs.go | 0 .../localstore/filter_test.go | 4 +- .../local_object_storage}/localstore/get.go | 0 .../local_object_storage}/localstore/has.go | 0 .../localstore/interface.go | 20 +- .../local_object_storage}/localstore/list.go | 0 .../localstore/localstore.pb.go | Bin 11883 -> 11986 bytes .../localstore/localstore.proto | 2 +- .../localstore/localstore_test.go | 105 +-- .../local_object_storage}/localstore/meta.go | 0 .../local_object_storage}/localstore/put.go | 4 +- .../local_object_storage}/localstore/range.go | 0 .../local_object_storage}/meta/iterator.go | 0 pkg/morph/client/balance/balanceOf.go | 52 ++ pkg/morph/client/balance/client.go | 102 +++ pkg/morph/client/balance/decimals.go | 44 ++ pkg/morph/client/balance/wrapper/balanceOf.go | 35 + pkg/morph/client/balance/wrapper/decimals.go | 22 + pkg/morph/client/balance/wrapper/wrapper.go | 43 + .../goclient => pkg/morph/client}/client.go | 112 +-- .../morph/client}/client_test.go | 2 +- pkg/morph/client/constructor.go | 162 ++++ pkg/morph/client/container/client.go | 174 ++++ pkg/morph/client/container/delete.go | 42 + pkg/morph/client/container/eacl.go | 53 ++ pkg/morph/client/container/eacl_set.go | 42 + pkg/morph/client/container/get.go | 53 ++ pkg/morph/client/container/list.go | 70 ++ pkg/morph/client/container/put.go | 44 ++ .../client/container/wrapper/container.go | 148 ++++ pkg/morph/client/container/wrapper/eacl.go | 51 ++ pkg/morph/client/container/wrapper/wrapper.go | 43 + pkg/morph/client/netmap/add_peer.go | 104 +++ pkg/morph/client/netmap/client.go | 156 ++++ pkg/morph/client/netmap/ir_list.go | 61 ++ pkg/morph/client/netmap/netmap.go | 99 +++ pkg/morph/client/netmap/new_epoch.go | 23 + pkg/morph/client/netmap/update_state.go | 34 + pkg/morph/client/netmap/wrapper/add_peer.go | 37 + pkg/morph/client/netmap/wrapper/irlist.go | 21 + pkg/morph/client/netmap/wrapper/netmap.go | 60 ++ pkg/morph/client/netmap/wrapper/new_epoch.go | 23 + .../client/netmap/wrapper/update_state.go | 32 + pkg/morph/client/netmap/wrapper/wrapper.go | 43 + pkg/morph/client/static.go | 62 ++ .../goclient => pkg/morph/client}/util.go | 13 +- .../morph/client}/util_test.go | 2 +- pkg/morph/event/balance/lock.go | 90 +++ pkg/morph/event/balance/lock_test.go | 155 ++++ {lib/blockchain => pkg/morph}/event/event.go | 0 .../blockchain => pkg/morph}/event/handler.go | 3 +- .../morph}/event/listener.go | 43 +- pkg/morph/event/neofs/cheque.go | 80 ++ pkg/morph/event/neofs/cheque_test.go | 123 +++ pkg/morph/event/neofs/deposit.go | 77 ++ pkg/morph/event/neofs/deposit_test.go | 123 +++ pkg/morph/event/neofs/withdraw.go | 62 ++ pkg/morph/event/neofs/withdraw_test.go | 95 +++ .../morph}/event/netmap/epoch.go | 6 +- .../morph}/event/netmap/epoch_test.go | 2 +- {lib/blockchain => pkg/morph}/event/parser.go | 0 {lib/blockchain => pkg/morph}/event/utils.go | 6 +- .../morph}/subscriber/subscriber.go | 5 + pkg/network/address.go | 12 + pkg/network/bootstrap/bootstrap.go | 60 ++ pkg/network/dial.go | 26 + pkg/network/listen.go | 17 + {lib => pkg/network}/muxer/listener.go | 0 {lib => pkg/network}/muxer/muxer.go | 16 +- {lib => pkg/network}/muxer/muxer_test.go | 31 +- {lib => pkg/network}/muxer/muxer_test.pb.go | Bin 15514 -> 15587 bytes {lib => pkg/network}/muxer/muxer_test.proto | 2 +- {lib => pkg/network}/peers/metrics.go | 0 pkg/network/peers/peers.go | 234 ++++++ pkg/network/peers/peers_test.go | 211 +++++ {lib => pkg/network}/peers/peers_test.pb.go | Bin 15514 -> 15587 bytes {lib => pkg/network}/peers/peers_test.proto | 2 +- {lib => pkg/network}/peers/peerstore.go | 2 +- {lib => pkg/network}/peers/peerstore_test.go | 30 +- {lib => pkg/network}/peers/storage.go | 15 +- {lib => pkg/network}/peers/worker.go | 0 .../transport/accounting/grpc}/service.go | 43 +- .../accounting/grpc}/service_test.go | 0 .../network/transport/container/grpc}/acl.go | 42 +- .../transport/container/grpc}/acl_test.go | 59 +- pkg/network/transport/container/grpc/alias.go | 30 + .../transport/container/grpc}/common_test.go | 0 .../transport/container/grpc}/delete.go | 11 +- .../transport/container/grpc}/delete_test.go | 60 -- pkg/network/transport/container/grpc/get.go | 56 ++ .../transport/container/grpc}/get_test.go | 65 -- .../network/transport/container/grpc}/list.go | 17 +- .../transport/container/grpc/list_test.go | 58 ++ .../network/transport/container/grpc}/put.go | 37 +- .../transport/container/grpc/put_test.go | 58 ++ .../transport/container/grpc}/service.go | 32 +- pkg/network/transport/grpc/service.go | 13 + .../network/transport/grpc/validate.go | 7 +- .../transport/metrics/grpc}/service.go | 12 +- .../transport/metrics/grpc}/service.pb.go | Bin 13467 -> 13673 bytes .../transport/metrics/grpc}/service.proto | 2 +- pkg/network/transport/object/grpc/acl.go | 742 ++++++++++++++++++ .../transport/object/grpc}/acl_test.go | 365 +++++---- .../network/transport/object/grpc}/bearer.go | 6 +- .../transport/object/grpc}/capacity.go | 0 .../transport/object/grpc}/capacity_test.go | 2 +- .../network/transport/object/grpc}/delete.go | 14 +- .../transport/object/grpc}/delete_test.go | 17 +- .../transport/object/grpc/eacl/validator.go | 244 ++++++ .../transport/object/grpc}/execution.go | 27 +- .../transport/object/grpc}/execution_test.go | 90 ++- .../network/transport/object/grpc}/filter.go | 19 +- .../transport/object/grpc}/filter_test.go | 17 +- .../network/transport/object/grpc}/get.go | 0 .../transport/object/grpc}/get_test.go | 9 +- .../network/transport/object/grpc}/handler.go | 0 .../transport/object/grpc}/handler_test.go | 18 +- .../network/transport/object/grpc}/head.go | 41 +- .../transport/object/grpc}/head_test.go | 20 +- .../transport/object/grpc}/implementations.go | 4 +- .../network/transport/object/grpc}/listing.go | 24 +- .../transport/object/grpc}/listing_test.go | 37 +- .../transport/object/grpc}/postprocessor.go | 0 .../object/grpc}/postprocessor_test.go | 4 +- .../transport/object/grpc}/preprocessor.go | 5 +- .../object/grpc}/preprocessor_test.go | 14 +- .../network/transport/object/grpc}/put.go | 47 +- .../transport/object/grpc}/put_test.go | 37 +- .../network/transport/object/grpc}/query.go | 14 +- .../transport/object/grpc}/query_test.go | 14 +- .../transport/object/grpc/range}/range.go | 4 +- .../object/grpc/range}/range_test.go | 2 +- .../network/transport/object/grpc}/ranges.go | 34 +- .../transport/object/grpc}/ranges_test.go | 38 +- .../transport/object/grpc}/response.go | 10 +- .../transport/object/grpc}/response_test.go | 0 .../network/transport/object/grpc}/search.go | 2 +- .../transport/object/grpc}/search_test.go | 10 +- .../network/transport/object/grpc}/service.go | 128 ++- .../network/transport/object/grpc}/status.go | 215 ++--- .../transport/object/grpc}/status_test.go | 36 +- .../network/transport/object/grpc}/token.go | 14 +- .../transport/object/grpc}/token_test.go | 44 +- .../object/grpc}/transport_implementations.go | 8 +- .../transport/object/grpc}/transport_test.go | 0 .../transport/object/grpc}/traverse.go | 4 +- .../transport/object/grpc}/traverse_test.go | 6 +- .../network/transport/object/grpc}/ttl.go | 6 +- .../transport/object/grpc}/ttl_test.go | 18 +- .../network/transport/object/grpc}/verb.go | 0 .../transport/object/grpc}/verb_test.go | 0 .../transport/object/grpc}/verification.go | 0 .../object/grpc}/verification_test.go | 4 +- .../network/transport/session/grpc}/create.go | 0 .../transport/session/grpc}/service.go | 5 +- .../transport/session/grpc}/service_test.go | 0 .../network/transport/state/grpc}/service.go | 36 +- .../transport/state/grpc}/service_test.go | 11 +- .../verify.go => pkg/services/id/owner_key.go | 32 +- {lib => pkg/services}/metrics/meta.go | 8 +- {lib => pkg/services}/metrics/metrics.go | 26 +- {lib => pkg/services}/metrics/metrics_test.go | 10 +- {lib => pkg/services}/metrics/prometeus.go | 0 {lib => pkg/services}/metrics/store.go | 6 +- {lib => pkg/services}/metrics/store_test.go | 6 +- .../object_manager}/placement/graph.go | 22 +- .../object_manager}/placement/interface.go | 29 +- .../object_manager}/placement/neighbours.go | 10 +- .../placement/neighbours_test.go | 42 +- .../object_manager}/placement/placement.go | 83 +- .../placement/placement_test.go | 106 +-- .../object_manager}/placement/store.go | 2 +- .../object_manager/placement/wrapper.go | 63 +- .../object_manager}/replication/common.go | 0 .../object_manager}/replication/garbage.go | 0 .../replication/implementations.go | 24 +- .../replication/location_detector.go | 0 .../object_manager}/replication/manager.go | 0 .../replication/object_replicator.go | 0 .../replication/object_restorer.go | 2 +- .../replication/placement_honorer.go | 0 .../replication/storage}/locator.go | 22 +- .../replication/storage}/locator_test.go | 8 +- .../replication/storage}/object.go | 32 +- .../replication/storage}/peerstore.go | 17 +- .../replication/storage}/validation.go | 105 ++- .../replication/storage}/validation_test.go | 62 +- .../replication/storage_validator.go | 0 .../object_manager}/transformer/alias.go | 0 .../object_manager}/transformer/put_test.go | 42 +- .../object_manager}/transformer/restore.go | 0 .../transformer/transformer.go | 41 +- .../object_manager}/transport/object.go | 0 .../transport/storagegroup}/sg.go | 30 +- .../object_manager/transport}/transport.go | 63 +- .../object_manager/verifier}/verifier.go | 2 +- {lib/fix => pkg/util/grace}/grace.go | 5 +- {lib/fix => pkg/util}/logger/logger.go | 46 +- {lib => pkg/util/logger}/test/logger.go | 7 +- {lib/fix/web => pkg/util/profiler}/http.go | 2 +- {lib/fix/web => pkg/util/profiler}/metrics.go | 2 +- {lib/fix/web => pkg/util/profiler}/pprof.go | 2 +- {lib/fix/web => pkg/util/profiler}/server.go | 2 +- {lib => pkg/util}/rand/rand.go | 0 {lib => pkg/util}/test/keys.go | 0 services/public/container/alias.go | 15 - services/public/container/get.go | 38 - services/public/container/list_test.go | 124 --- services/public/container/put_test.go | 132 ---- services/public/object/acl.go | 428 ---------- 400 files changed, 11326 insertions(+), 8595 deletions(-) create mode 100644 .github/logo.svg create mode 100644 CREDITS.md create mode 100644 Dockerfile.ir create mode 100644 README.md create mode 100644 cmd/neofs-ir/defaults.go create mode 100644 cmd/neofs-ir/main.go rename {modules => cmd/neofs-node/modules}/bootstrap/healthy.go (69%) rename {modules => cmd/neofs-node/modules}/bootstrap/module.go (62%) rename {lib => cmd/neofs-node/modules}/fix/catch.go (100%) rename {lib => cmd/neofs-node/modules}/fix/config/config.go (100%) rename {lib => cmd/neofs-node/modules}/fix/fix.go (85%) rename {lib => cmd/neofs-node/modules}/fix/module/module.go (100%) rename {lib => cmd/neofs-node/modules}/fix/services.go (100%) rename {lib => cmd/neofs-node/modules}/fix/worker/worker.go (100%) rename {modules => cmd/neofs-node/modules}/grpc/billing.go (100%) rename {modules => cmd/neofs-node/modules}/grpc/module.go (58%) rename {modules => cmd/neofs-node/modules}/grpc/routing.go (96%) rename {modules => cmd/neofs-node/modules}/morph/balance.go (61%) rename {modules => cmd/neofs-node/modules}/morph/common.go (83%) rename {modules => cmd/neofs-node/modules}/morph/container.go (61%) rename {modules => cmd/neofs-node/modules}/morph/event.go (82%) create mode 100644 cmd/neofs-node/modules/morph/goclient.go rename {modules => cmd/neofs-node/modules}/morph/listener.go (90%) rename {modules => cmd/neofs-node/modules}/morph/module.go (74%) rename {modules => cmd/neofs-node/modules}/morph/netmap.go (58%) rename {modules => cmd/neofs-node/modules}/network/http.go (92%) rename {modules => cmd/neofs-node/modules}/network/module.go (58%) rename {modules => cmd/neofs-node/modules}/network/muxer.go (89%) rename {modules => cmd/neofs-node/modules}/network/peers.go (52%) rename {modules => cmd/neofs-node/modules}/network/placement.go (67%) rename {modules => cmd/neofs-node/modules}/node/audit.go (64%) rename {modules => cmd/neofs-node/modules}/node/container.go (52%) create mode 100644 cmd/neofs-node/modules/node/core.go rename {modules => cmd/neofs-node/modules}/node/localstore.go (55%) rename {modules => cmd/neofs-node/modules}/node/metrics.go (54%) rename {modules => cmd/neofs-node/modules}/node/module.go (59%) rename {modules => cmd/neofs-node/modules}/node/objectmanager.go (64%) rename {modules => cmd/neofs-node/modules}/node/peerstore.go (90%) create mode 100644 cmd/neofs-node/modules/node/placement.go rename {modules => cmd/neofs-node/modules}/node/replication.go (85%) create mode 100644 cmd/neofs-node/modules/node/services.go rename {modules => cmd/neofs-node/modules}/node/session.go (65%) rename {modules => cmd/neofs-node/modules}/settings/address.go (85%) rename {modules => cmd/neofs-node/modules}/settings/module.go (61%) rename {modules => cmd/neofs-node/modules}/settings/node.go (90%) rename {modules => cmd/neofs-node/modules}/workers/module.go (58%) rename {modules => cmd/neofs-node/modules}/workers/prepare.go (96%) delete mode 100644 internal/error.go delete mode 100644 lib/.gitkeep delete mode 100644 lib/acl/action.go delete mode 100644 lib/acl/action_test.go delete mode 100644 lib/acl/basic.go delete mode 100644 lib/acl/basic_test.go delete mode 100644 lib/acl/binary.go delete mode 100644 lib/acl/binary_test.go delete mode 100644 lib/acl/extended.go delete mode 100644 lib/acl/header.go delete mode 100644 lib/acl/headers_test.go delete mode 100644 lib/acl/match.go delete mode 100644 lib/acl/match_test.go delete mode 100644 lib/boot/bootstrap_test.go delete mode 100644 lib/boot/bootstrapper.go delete mode 100644 lib/boot/storage.go delete mode 100644 lib/buckets/boltdb/methods_test.go delete mode 100644 lib/buckets/boltdb/plugin/main.go delete mode 100644 lib/buckets/init.go delete mode 100644 lib/buckets/inmemory/bucket.go delete mode 100644 lib/buckets/inmemory/methods.go delete mode 100644 lib/container/alias.go delete mode 100644 lib/container/storage.go delete mode 100644 lib/container/storage_test.go delete mode 100644 lib/core/storage.go delete mode 100644 lib/core/storage_test.go delete mode 100644 lib/implementations/acl.go delete mode 100644 lib/implementations/acl_test.go delete mode 100644 lib/implementations/balance.go delete mode 100644 lib/implementations/balance_test.go delete mode 100644 lib/implementations/bootstrap.go delete mode 100644 lib/implementations/bootstrap_test.go delete mode 100644 lib/implementations/epoch.go delete mode 100644 lib/implementations/reputation.go delete mode 100644 lib/ir/info.go delete mode 100644 lib/ir/info_test.go delete mode 100644 lib/ir/node.go delete mode 100644 lib/ir/node_test.go delete mode 100644 lib/ir/storage.go delete mode 100644 lib/ir/storage_test.go delete mode 100644 lib/netmap/netmap.go delete mode 100644 lib/netmap/netmap_test.go delete mode 100644 lib/netmap/storage.go delete mode 100644 lib/netmap/storage_test.go delete mode 100644 lib/peers/peers.go delete mode 100644 lib/peers/peers_test.go delete mode 100644 lib/storage/storage.go delete mode 100644 lib/transport/connection.go delete mode 100644 lib/transport/transport.go delete mode 100644 lib/transport/transport_test.go delete mode 100644 modules/morph/goclient.go delete mode 100644 modules/morph/reputation.go delete mode 100644 modules/node/core.go delete mode 100644 modules/node/placement.go delete mode 100644 modules/node/services.go create mode 100644 pkg/core/container/acl/basic/enum.go create mode 100644 pkg/core/container/acl/basic/types.go create mode 100644 pkg/core/container/acl/basic/types_test.go create mode 100644 pkg/core/container/acl/basic/util.go create mode 100644 pkg/core/container/acl/basic/util_test.go create mode 100644 pkg/core/container/acl/extended/storage/storage.go create mode 100644 pkg/core/container/acl/extended/storage/test/storage.go create mode 100644 pkg/core/container/acl/extended/types.go create mode 100644 pkg/core/container/container.go create mode 100644 pkg/core/container/container_test.go create mode 100644 pkg/core/container/id.go create mode 100644 pkg/core/container/id_test.go create mode 100644 pkg/core/container/marshal.go create mode 100644 pkg/core/container/marshal_test.go create mode 100644 pkg/core/container/storage/storage.go create mode 100644 pkg/core/container/storage/test/storage.go create mode 100644 pkg/core/container/storage/test/storage_test.go create mode 100644 pkg/core/netmap/epoch/marshal.go create mode 100644 pkg/core/netmap/epoch/marshal_test.go create mode 100644 pkg/core/netmap/epoch/math.go create mode 100644 pkg/core/netmap/epoch/math_test.go create mode 100644 pkg/core/netmap/epoch/relation.go create mode 100644 pkg/core/netmap/epoch/relation_test.go create mode 100644 pkg/core/netmap/epoch/type.go create mode 100644 pkg/core/netmap/netmap.go create mode 100644 pkg/core/netmap/netmap_test.go create mode 100644 pkg/core/netmap/node/info.go create mode 100644 pkg/core/netmap/node/info_test.go create mode 100644 pkg/core/netmap/node/options.go create mode 100644 pkg/core/netmap/node/options_test.go create mode 100644 pkg/core/netmap/node/status.go create mode 100644 pkg/core/netmap/node/status_test.go create mode 100644 pkg/core/object/extended.go create mode 100644 pkg/core/object/extended_test.go create mode 100644 pkg/core/object/header.go create mode 100644 pkg/core/object/header_test.go create mode 100644 pkg/core/object/headers/enum.go create mode 100644 pkg/core/object/headers/enum_test.go create mode 100644 pkg/core/object/headers/user.go create mode 100644 pkg/core/object/headers/user_test.go create mode 100644 pkg/core/object/id.go create mode 100644 pkg/core/object/id_test.go create mode 100644 pkg/core/object/object.go create mode 100644 pkg/core/object/object_test.go create mode 100644 pkg/core/object/storage/storage.go create mode 100644 pkg/core/object/storage/test/storage.go create mode 100644 pkg/core/object/storage/test/storage_test.go create mode 100644 pkg/core/object/sys.go create mode 100644 pkg/core/object/sys_test.go create mode 100644 pkg/innerring/bindings.go create mode 100644 pkg/innerring/innerring.go create mode 100644 pkg/innerring/invoke/balance.go create mode 100644 pkg/innerring/invoke/neofs.go create mode 100644 pkg/innerring/invoke/netmap.go create mode 100644 pkg/innerring/processors/balance/handlers.go create mode 100644 pkg/innerring/processors/balance/process_assets.go create mode 100644 pkg/innerring/processors/balance/processor.go create mode 100644 pkg/innerring/processors/neofs/handlers.go create mode 100644 pkg/innerring/processors/neofs/process_assets.go create mode 100644 pkg/innerring/processors/neofs/processor.go create mode 100644 pkg/innerring/processors/netmap/handlers.go create mode 100644 pkg/innerring/processors/netmap/process_epoch.go create mode 100644 pkg/innerring/processors/netmap/processor.go create mode 100644 pkg/innerring/state.go create mode 100644 pkg/innerring/timers/epoch.go create mode 100644 pkg/innerring/timers/timers.go rename {lib/buckets => pkg/local_object_storage/bucket}/boltdb/boltdb.go (58%) rename {lib/buckets => pkg/local_object_storage/bucket}/boltdb/methods.go (67%) create mode 100644 pkg/local_object_storage/bucket/bucket.go rename {lib/buckets => pkg/local_object_storage/bucket}/fsbucket/bucket.go (60%) rename {lib/buckets => pkg/local_object_storage/bucket}/fsbucket/methods.go (74%) rename {lib/buckets => pkg/local_object_storage/bucket}/fsbucket/queue.go (100%) rename {lib/buckets => pkg/local_object_storage/bucket}/fsbucket/treemethods.go (95%) rename {lib/buckets => pkg/local_object_storage/bucket}/fsbucket/treemethods_test.go (97%) rename {lib => pkg/local_object_storage/bucket}/test/bucket.go (82%) rename {lib => pkg/local_object_storage}/localstore/alias.go (100%) rename {lib => pkg/local_object_storage}/localstore/del.go (89%) rename {lib => pkg/local_object_storage}/localstore/filter.go (98%) rename {lib => pkg/local_object_storage}/localstore/filter_funcs.go (100%) rename {lib => pkg/local_object_storage}/localstore/filter_test.go (89%) rename {lib => pkg/local_object_storage}/localstore/get.go (100%) rename {lib => pkg/local_object_storage}/localstore/has.go (100%) rename {lib => pkg/local_object_storage}/localstore/interface.go (84%) rename {lib => pkg/local_object_storage}/localstore/list.go (100%) rename {lib => pkg/local_object_storage}/localstore/localstore.pb.go (84%) rename {lib => pkg/local_object_storage}/localstore/localstore.proto (78%) rename {lib => pkg/local_object_storage}/localstore/localstore_test.go (80%) rename {lib => pkg/local_object_storage}/localstore/meta.go (100%) rename {lib => pkg/local_object_storage}/localstore/put.go (91%) rename {lib => pkg/local_object_storage}/localstore/range.go (100%) rename {lib => pkg/local_object_storage}/meta/iterator.go (100%) create mode 100644 pkg/morph/client/balance/balanceOf.go create mode 100644 pkg/morph/client/balance/client.go create mode 100644 pkg/morph/client/balance/decimals.go create mode 100644 pkg/morph/client/balance/wrapper/balanceOf.go create mode 100644 pkg/morph/client/balance/wrapper/decimals.go create mode 100644 pkg/morph/client/balance/wrapper/wrapper.go rename {lib/blockchain/goclient => pkg/morph/client}/client.go (51%) rename {lib/blockchain/goclient => pkg/morph/client}/client_test.go (97%) create mode 100644 pkg/morph/client/constructor.go create mode 100644 pkg/morph/client/container/client.go create mode 100644 pkg/morph/client/container/delete.go create mode 100644 pkg/morph/client/container/eacl.go create mode 100644 pkg/morph/client/container/eacl_set.go create mode 100644 pkg/morph/client/container/get.go create mode 100644 pkg/morph/client/container/list.go create mode 100644 pkg/morph/client/container/put.go create mode 100644 pkg/morph/client/container/wrapper/container.go create mode 100644 pkg/morph/client/container/wrapper/eacl.go create mode 100644 pkg/morph/client/container/wrapper/wrapper.go create mode 100644 pkg/morph/client/netmap/add_peer.go create mode 100644 pkg/morph/client/netmap/client.go create mode 100644 pkg/morph/client/netmap/ir_list.go create mode 100644 pkg/morph/client/netmap/netmap.go create mode 100644 pkg/morph/client/netmap/new_epoch.go create mode 100644 pkg/morph/client/netmap/update_state.go create mode 100644 pkg/morph/client/netmap/wrapper/add_peer.go create mode 100644 pkg/morph/client/netmap/wrapper/irlist.go create mode 100644 pkg/morph/client/netmap/wrapper/netmap.go create mode 100644 pkg/morph/client/netmap/wrapper/new_epoch.go create mode 100644 pkg/morph/client/netmap/wrapper/update_state.go create mode 100644 pkg/morph/client/netmap/wrapper/wrapper.go create mode 100644 pkg/morph/client/static.go rename {lib/blockchain/goclient => pkg/morph/client}/util.go (91%) rename {lib/blockchain/goclient => pkg/morph/client}/util_test.go (99%) create mode 100644 pkg/morph/event/balance/lock.go create mode 100644 pkg/morph/event/balance/lock_test.go rename {lib/blockchain => pkg/morph}/event/event.go (100%) rename {lib/blockchain => pkg/morph}/event/handler.go (82%) rename {lib/blockchain => pkg/morph}/event/listener.go (86%) create mode 100644 pkg/morph/event/neofs/cheque.go create mode 100644 pkg/morph/event/neofs/cheque_test.go create mode 100644 pkg/morph/event/neofs/deposit.go create mode 100644 pkg/morph/event/neofs/deposit_test.go create mode 100644 pkg/morph/event/neofs/withdraw.go create mode 100644 pkg/morph/event/neofs/withdraw_test.go rename {lib/blockchain => pkg/morph}/event/netmap/epoch.go (81%) rename {lib/blockchain => pkg/morph}/event/netmap/epoch_test.go (94%) rename {lib/blockchain => pkg/morph}/event/parser.go (100%) rename {lib/blockchain => pkg/morph}/event/utils.go (73%) rename {lib/blockchain => pkg/morph}/subscriber/subscriber.go (98%) create mode 100644 pkg/network/address.go create mode 100644 pkg/network/bootstrap/bootstrap.go create mode 100644 pkg/network/dial.go create mode 100644 pkg/network/listen.go rename {lib => pkg/network}/muxer/listener.go (100%) rename {lib => pkg/network}/muxer/muxer.go (92%) rename {lib => pkg/network}/muxer/muxer_test.go (91%) rename {lib => pkg/network}/muxer/muxer_test.pb.go (89%) rename {lib => pkg/network}/muxer/muxer_test.proto (78%) rename {lib => pkg/network}/peers/metrics.go (100%) create mode 100644 pkg/network/peers/peers.go create mode 100644 pkg/network/peers/peers_test.go rename {lib => pkg/network}/peers/peers_test.pb.go (89%) rename {lib => pkg/network}/peers/peers_test.proto (78%) rename {lib => pkg/network}/peers/peerstore.go (99%) rename {lib => pkg/network}/peers/peerstore_test.go (89%) rename {lib => pkg/network}/peers/storage.go (93%) rename {lib => pkg/network}/peers/worker.go (100%) rename {services/public/accounting => pkg/network/transport/accounting/grpc}/service.go (64%) rename {services/public/accounting => pkg/network/transport/accounting/grpc}/service_test.go (100%) rename {services/public/container => pkg/network/transport/container/grpc}/acl.go (58%) rename {services/public/container => pkg/network/transport/container/grpc}/acl_test.go (77%) create mode 100644 pkg/network/transport/container/grpc/alias.go rename {services/public/container => pkg/network/transport/container/grpc}/common_test.go (100%) rename {services/public/container => pkg/network/transport/container/grpc}/delete.go (69%) rename {services/public/container => pkg/network/transport/container/grpc}/delete_test.go (51%) create mode 100644 pkg/network/transport/container/grpc/get.go rename {services/public/container => pkg/network/transport/container/grpc}/get_test.go (50%) rename {services/public/container => pkg/network/transport/container/grpc}/list.go (69%) create mode 100644 pkg/network/transport/container/grpc/list_test.go rename {services/public/container => pkg/network/transport/container/grpc}/put.go (53%) create mode 100644 pkg/network/transport/container/grpc/put_test.go rename {services/public/container => pkg/network/transport/container/grpc}/service.go (61%) create mode 100644 pkg/network/transport/grpc/service.go rename lib/core/validator.go => pkg/network/transport/grpc/validate.go (81%) rename {services/metrics => pkg/network/transport/metrics/grpc}/service.go (79%) rename {services/metrics => pkg/network/transport/metrics/grpc}/service.pb.go (87%) rename {services/metrics => pkg/network/transport/metrics/grpc}/service.proto (67%) create mode 100644 pkg/network/transport/object/grpc/acl.go rename {services/public/object => pkg/network/transport/object/grpc}/acl_test.go (58%) rename {services/public/object => pkg/network/transport/object/grpc}/bearer.go (89%) rename {services/public/object => pkg/network/transport/object/grpc}/capacity.go (100%) rename {services/public/object => pkg/network/transport/object/grpc}/capacity_test.go (96%) rename {services/public/object => pkg/network/transport/object/grpc}/delete.go (93%) rename {services/public/object => pkg/network/transport/object/grpc}/delete_test.go (95%) create mode 100644 pkg/network/transport/object/grpc/eacl/validator.go rename {services/public/object => pkg/network/transport/object/grpc}/execution.go (94%) rename {services/public/object => pkg/network/transport/object/grpc}/execution_test.go (90%) rename {services/public/object => pkg/network/transport/object/grpc}/filter.go (93%) rename {services/public/object => pkg/network/transport/object/grpc}/filter_test.go (95%) rename {services/public/object => pkg/network/transport/object/grpc}/get.go (100%) rename {services/public/object => pkg/network/transport/object/grpc}/get_test.go (95%) rename {services/public/object => pkg/network/transport/object/grpc}/handler.go (100%) rename {services/public/object => pkg/network/transport/object/grpc}/handler_test.go (95%) rename {services/public/object => pkg/network/transport/object/grpc}/head.go (91%) rename {services/public/object => pkg/network/transport/object/grpc}/head_test.go (95%) rename {services/public/object => pkg/network/transport/object/grpc}/implementations.go (86%) rename {services/public/object => pkg/network/transport/object/grpc}/listing.go (89%) rename {services/public/object => pkg/network/transport/object/grpc}/listing_test.go (91%) rename {services/public/object => pkg/network/transport/object/grpc}/postprocessor.go (100%) rename {services/public/object => pkg/network/transport/object/grpc}/postprocessor_test.go (95%) rename {services/public/object => pkg/network/transport/object/grpc}/preprocessor.go (97%) rename {services/public/object => pkg/network/transport/object/grpc}/preprocessor_test.go (90%) rename {services/public/object => pkg/network/transport/object/grpc}/put.go (86%) rename {services/public/object => pkg/network/transport/object/grpc}/put_test.go (95%) rename {services/public/object => pkg/network/transport/object/grpc}/query.go (92%) rename {services/public/object => pkg/network/transport/object/grpc}/query_test.go (98%) rename {lib/objio => pkg/network/transport/object/grpc/range}/range.go (99%) rename {lib/objio => pkg/network/transport/object/grpc/range}/range_test.go (99%) rename {services/public/object => pkg/network/transport/object/grpc}/ranges.go (89%) rename {services/public/object => pkg/network/transport/object/grpc}/ranges_test.go (94%) rename {services/public/object => pkg/network/transport/object/grpc}/response.go (92%) rename {services/public/object => pkg/network/transport/object/grpc}/response_test.go (100%) rename {services/public/object => pkg/network/transport/object/grpc}/search.go (97%) rename {services/public/object => pkg/network/transport/object/grpc}/search_test.go (95%) rename {services/public/object => pkg/network/transport/object/grpc}/service.go (79%) rename {services/public/object => pkg/network/transport/object/grpc}/status.go (79%) rename {services/public/object => pkg/network/transport/object/grpc}/status_test.go (96%) rename {services/public/object => pkg/network/transport/object/grpc}/token.go (83%) rename {services/public/object => pkg/network/transport/object/grpc}/token_test.go (82%) rename {services/public/object => pkg/network/transport/object/grpc}/transport_implementations.go (98%) rename {services/public/object => pkg/network/transport/object/grpc}/transport_test.go (100%) rename {services/public/object => pkg/network/transport/object/grpc}/traverse.go (97%) rename {services/public/object => pkg/network/transport/object/grpc}/traverse_test.go (98%) rename {services/public/object => pkg/network/transport/object/grpc}/ttl.go (97%) rename {services/public/object => pkg/network/transport/object/grpc}/ttl_test.go (95%) rename {services/public/object => pkg/network/transport/object/grpc}/verb.go (100%) rename {services/public/object => pkg/network/transport/object/grpc}/verb_test.go (100%) rename {services/public/object => pkg/network/transport/object/grpc}/verification.go (100%) rename {services/public/object => pkg/network/transport/object/grpc}/verification_test.go (94%) rename {services/public/session => pkg/network/transport/session/grpc}/create.go (100%) rename {services/public/session => pkg/network/transport/session/grpc}/service.go (92%) rename {services/public/session => pkg/network/transport/session/grpc}/service_test.go (100%) rename {services/public/state => pkg/network/transport/state/grpc}/service.go (91%) rename {services/public/state => pkg/network/transport/state/grpc}/service_test.go (95%) rename lib/core/verify.go => pkg/services/id/owner_key.go (58%) rename {lib => pkg/services}/metrics/meta.go (58%) rename {lib => pkg/services}/metrics/metrics.go (84%) rename {lib => pkg/services}/metrics/metrics_test.go (95%) rename {lib => pkg/services}/metrics/prometeus.go (100%) rename {lib => pkg/services}/metrics/store.go (93%) rename {lib => pkg/services}/metrics/store_test.go (94%) rename {lib => pkg/services/object_manager}/placement/graph.go (83%) rename {lib => pkg/services/object_manager}/placement/interface.go (79%) rename {lib => pkg/services/object_manager}/placement/neighbours.go (77%) rename {lib => pkg/services/object_manager}/placement/neighbours_test.go (79%) rename {lib => pkg/services/object_manager}/placement/placement.go (73%) rename {lib => pkg/services/object_manager}/placement/placement_test.go (78%) rename {lib => pkg/services/object_manager}/placement/store.go (94%) rename lib/implementations/placement.go => pkg/services/object_manager/placement/wrapper.go (51%) rename {lib => pkg/services/object_manager}/replication/common.go (100%) rename {lib => pkg/services/object_manager}/replication/garbage.go (100%) rename {lib => pkg/services/object_manager}/replication/implementations.go (92%) rename {lib => pkg/services/object_manager}/replication/location_detector.go (100%) rename {lib => pkg/services/object_manager}/replication/manager.go (100%) rename {lib => pkg/services/object_manager}/replication/object_replicator.go (100%) rename {lib => pkg/services/object_manager}/replication/object_restorer.go (98%) rename {lib => pkg/services/object_manager}/replication/placement_honorer.go (100%) rename {lib/implementations => pkg/services/object_manager/replication/storage}/locator.go (69%) rename {lib/implementations => pkg/services/object_manager/replication/storage}/locator_test.go (74%) rename {lib/implementations => pkg/services/object_manager/replication/storage}/object.go (73%) rename {lib/implementations => pkg/services/object_manager/replication/storage}/peerstore.go (79%) rename {lib/implementations => pkg/services/object_manager/replication/storage}/validation.go (78%) rename {lib/implementations => pkg/services/object_manager/replication/storage}/validation_test.go (80%) rename {lib => pkg/services/object_manager}/replication/storage_validator.go (100%) rename {lib => pkg/services/object_manager}/transformer/alias.go (100%) rename {lib => pkg/services/object_manager}/transformer/put_test.go (94%) rename {lib => pkg/services/object_manager}/transformer/restore.go (100%) rename {lib => pkg/services/object_manager}/transformer/transformer.go (92%) rename {lib => pkg/services/object_manager}/transport/object.go (100%) rename {lib/implementations => pkg/services/object_manager/transport/storagegroup}/sg.go (76%) rename {lib/implementations => pkg/services/object_manager/transport}/transport.go (90%) rename {lib/objutil => pkg/services/object_manager/verifier}/verifier.go (98%) rename {lib/fix => pkg/util/grace}/grace.go (79%) rename {lib/fix => pkg/util}/logger/logger.go (84%) rename {lib => pkg/util/logger}/test/logger.go (71%) rename {lib/fix/web => pkg/util/profiler}/http.go (99%) rename {lib/fix/web => pkg/util/profiler}/metrics.go (97%) rename {lib/fix/web => pkg/util/profiler}/pprof.go (98%) rename {lib/fix/web => pkg/util/profiler}/server.go (98%) rename {lib => pkg/util}/rand/rand.go (100%) rename {lib => pkg/util}/test/keys.go (100%) delete mode 100644 services/public/container/alias.go delete mode 100644 services/public/container/get.go delete mode 100644 services/public/container/list_test.go delete mode 100644 services/public/container/put_test.go delete mode 100644 services/public/object/acl.go diff --git a/.github/logo.svg b/.github/logo.svg new file mode 100644 index 00000000..b4da0765 --- /dev/null +++ b/.github/logo.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore index 690f5d9e..0dade22e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea bin temp cmd/test diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e4a00da..4c96a8aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,20 @@ # Changelog Changelog for NeoFS Node +## [0.11.0] - 2020-07-23 + +### Added + +- Inner ring application to repository. +- Inner ring epoch processor. +- Inner ring asset processor for GAS deposit and withdraw. + +### Changed + +- The structure of source code tree. + ## [0.10.0] - 2020-07-10 First public review release. + +[0.11.0]: https://github.com/nspcc-dev/neofs-api-go/compare/v0.10.0...v0.11.0 \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b11fe2a4..a3ae9c02 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,61 @@ # Contributing -We do not accept any contributions. As yet. +## DCO Sign off + +All authors to the project retain copyright to their work. However, to ensure +that they are only submitting work that they have rights to, we are requiring +everyone to acknowledge this by signing their work. + +Any copyright notices in this repository should specify the authors as "the +contributors". + +To sign your work, just add a line like this at the end of your commit message: + +``` +Signed-off-by: Samii Sakisaka +``` + +This can easily be done with the `--signoff` option to `git commit`. + +By doing this you state that you can certify the following (from [The Developer +Certificate of Origin](https://developercertificate.org/): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +1 Letterman Drive +Suite D4700 +San Francisco, CA, 94129 + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` diff --git a/CREDITS.md b/CREDITS.md new file mode 100644 index 00000000..06dcb307 --- /dev/null +++ b/CREDITS.md @@ -0,0 +1,26 @@ +# Credits + +Initial NeoFS research and development (2018-2020) was done by +[NeoSPCC](https://nspcc.ru) team. + +In alphabetical order: + +- Alexey Vanin +- Anastasia Prasolova +- Anatoly Bogatyrev +- Evgeny Kulikov +- Evgeny Stratonikov +- Leonard Liubich +- Sergei Liubich +- Stanislav Bogatyrev + +# Contributors + +In chronological order: + +# Special Thanks + +For product development support: + +- Fabian Wahle +- Neo Global Development diff --git a/Dockerfile b/Dockerfile index 82e56813..60cb95a2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,13 @@ -FROM golang:1.14-alpine as builder +FROM golang:1.14-alpine as basebuilder +RUN apk add --update make bash +FROM basebuilder as builder ARG BUILD=now ARG VERSION=dev ARG REPO=repository - WORKDIR /src - COPY . /src -RUN apk add --update make bash RUN make bin/neofs-node # Executable image diff --git a/Dockerfile.ir b/Dockerfile.ir new file mode 100644 index 00000000..98860dbf --- /dev/null +++ b/Dockerfile.ir @@ -0,0 +1,20 @@ +FROM golang:1.14-alpine as basebuilder +RUN apk add --update make bash + +FROM basebuilder as builder +ARG BUILD=now +ARG VERSION=dev +ARG REPO=repository +WORKDIR /src +COPY . /src + +RUN make bin/neofs-ir + +# Executable image +FROM scratch AS neofs-ir + +WORKDIR / + +COPY --from=builder /src/bin/neofs-ir /bin/neofs-ir + +CMD ["neofs-ir"] diff --git a/Makefile b/Makefile index 48dcf385..c44df3d1 100644 --- a/Makefile +++ b/Makefile @@ -11,16 +11,15 @@ BIN = bin DIRS= $(BIN) # List of binaries to build. May be automated. -CMDS = neofs-node +CMDS = neofs-node neofs-ir CMS = $(addprefix $(BIN)/, $(CMDS)) BINS = $(addprefix $(BIN)/, $(CMDS)) .PHONY: help dep clean fmt -# To build a specific binary, use it's name prfixed with bin/ as a target -# For example `make bin/neofs-node` will buils only Storage node binary -# Just `make` will -# Build all possible binaries +# To build a specific binary, use it's name prefix with bin/ as a target +# For example `make bin/neofs-node` will build only storage node binary +# Just `make` will build all possible binaries all: $(DIRS) $(BINS) $(BINS): $(DIRS) dep @@ -41,7 +40,7 @@ dep: @go mod tidy -v && echo OK || (echo fail && exit 2) @printf "⇒ Download requirements: " @go mod download && echo OK || (echo fail && exit 2) - @printf "⇒ Store vendor localy: " + @printf "⇒ Store vendor locally: " @go mod vendor && echo OK || (echo fail && exit 2) # Regenerate proto files: @@ -60,17 +59,26 @@ protoc: --gofast_out=plugins=grpc,paths=source_relative:. $$f; \ done -# Build NeoFS Sorage Node docker image +# Build NeoFS Storage Node docker image image-storage: - @echo "⇒ Build NeoFS Sorage Node docker image " + @echo "⇒ Build NeoFS Storage Node docker image " @docker build \ --build-arg REPO=$(REPO) \ --build-arg VERSION=$(VERSION) \ -f Dockerfile \ -t $(HUB_IMAGE)-storage:$(HUB_TAG) . +# Build NeoFS Storage Node docker image +image-ir: + @echo "⇒ Build NeoFS Inner Ring docker image " + @docker build \ + --build-arg REPO=$(REPO) \ + --build-arg VERSION=$(VERSION) \ + -f Dockerfile.ir \ + -t $(HUB_IMAGE)-ir:$(HUB_TAG) . + # Build all Docker images -images: image-storage +images: image-storage image-ir # Reformat code fmt: diff --git a/README.md b/README.md new file mode 100644 index 00000000..52e8d84a --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +

+NeoFS +

+

+ NeoFS is a decentralized distributed object storage integrated with the NEO Blockchain. +

+ +--- +[![Report](https://goreportcard.com/badge/github.com/nspcc-dev/neo-go)](https://goreportcard.com/report/github.com/nspcc-dev/neofs-node) +![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/nspcc-dev/neofs-node?sort=semver) +![License](https://img.shields.io/github/license/nspcc-dev/neofs-node.svg?style=popout) + +# Overview + +NeoFS Nodes are organized in peer-to-peer network that takes care of storing and +distributing user's data. Any Neo user may participate in the network and get +paid for providing storage resources to other users or store his data in NeoFS +and pay a competitive price for it. + +Users can reliably store object data in the NeoFS network and have a transparent +data placement process due to decentralized architecture and flexible storage +policies. Each node is responsible for executing the storage policies that the +users select for geographical location, reliability level, number of nodes, type +of disks, capacity, etc. Thus, NeoFS gives full control over data to users. + +Deep [Neo Blockchain](https://neo.org) integration allows NeoFS to be used by +dApp directly from +[NeoVM](https://docs.neo.org/docs/en-us/basic/technology/neovm.html) on the +[Smart Contract](https://docs.neo.org/docs/en-us/basic/technology/neocontract.html) +code level. This way dApps are not limited to on-chain storage and can +manipulate large amounts of data without paying a prohibitive price. + +NeoFS has native [gRPC](https://grpc.io) API and popular protocol gates such as +[AWS S3](https://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html), +[HTTP](https://wikipedia.org/wiki/Hypertext_Transfer_Protocol), +[FUSE](https://wikipedia.org/wiki/Filesystem_in_Userspace) and +[sFTP](https://en.wikipedia.org/wiki/SSH_File_Transfer_Protocol) allowing +developers to easily integrate applications without rewriting their code. + +# Contributing + +Feel free to contribute to this project after reading the [contributing +guidelines](CONTRIBUTING.md). + +Before starting to work on a certain topic, create an new issue first, +describing the feature/topic you are going to implement. + +# Credits + +NeoFS is maintained by [NeoSPCC](https://nspcc.ru) with the help and +contributions from community members. + +Please see [CREDITS](CREDITS.md) for details. + +# License + +- [GNU General Public License v3.0](LICENSE) diff --git a/cmd/neofs-ir/defaults.go b/cmd/neofs-ir/defaults.go new file mode 100644 index 00000000..508c003a --- /dev/null +++ b/cmd/neofs-ir/defaults.go @@ -0,0 +1,74 @@ +package main + +import ( + "strings" + + "github.com/nspcc-dev/neo-go/pkg/config/netmode" + "github.com/nspcc-dev/neofs-node/misc" + "github.com/spf13/viper" +) + +func newConfig(path string) (*viper.Viper, error) { + var ( + err error + v = viper.New() + ) + + v.SetEnvPrefix(misc.InnerRingPrefix) + v.AutomaticEnv() + v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + + v.SetDefault("app.name", misc.InnerRingName) + v.SetDefault("app.version", misc.Version) + + defaultConfiguration(v) + + if path != "" { + v.SetConfigFile(path) + v.SetConfigType("yml") // fixme: for now + err = v.ReadInConfig() + } + + return v, err +} + +func defaultConfiguration(cfg *viper.Viper) { + cfg.SetDefault("logger.level", "info") + cfg.SetDefault("logger.format", "console") + cfg.SetDefault("logger.trace_level", "fatal") + cfg.SetDefault("logger.no_disclaimer", false) + cfg.SetDefault("logger.sampling.initial", 1000) + cfg.SetDefault("logger.sampling.thereafter", 1000) + + cfg.SetDefault("pprof.enabled", false) + cfg.SetDefault("pprof.address", ":6060") + cfg.SetDefault("pprof.shutdown_ttl", "30s") + + cfg.SetDefault("metrics.enabled", false) + cfg.SetDefault("metrics.address", ":9090") + cfg.SetDefault("metrics.shutdown_ttl", "30s") + + cfg.SetDefault("morph.endpoint.client", "") + cfg.SetDefault("morph.endpoint.notification", "") + cfg.SetDefault("morph.dial_timeout", "10s") + cfg.SetDefault("morph.magic_number", uint32(netmode.PrivNet)) + + cfg.SetDefault("mainnet.endpoint.client", "") + cfg.SetDefault("mainnet.endpoint.notification", "") + cfg.SetDefault("mainnet.dial_timeout", "10s") + cfg.SetDefault("mainnet.magic_number", uint32(netmode.PrivNet)) + + cfg.SetDefault("key", "") // inner ring node key + + cfg.SetDefault("contracts.netmap", "") + cfg.SetDefault("contracts.neofs", "") + cfg.SetDefault("contracts.balance", "") + // gas native contract + cfg.SetDefault("contracts.gas", "8c23f196d8a1bfd103a9dcb1f9ccf0c611377d3b") + + cfg.SetDefault("timers.epoch", "5s") + + cfg.SetDefault("workers.netmap", "10") + cfg.SetDefault("workers.balance", "10") + cfg.SetDefault("workers.neofs", "10") +} diff --git a/cmd/neofs-ir/main.go b/cmd/neofs-ir/main.go new file mode 100644 index 00000000..2f634f5b --- /dev/null +++ b/cmd/neofs-ir/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/nspcc-dev/neofs-node/misc" + "github.com/nspcc-dev/neofs-node/pkg/innerring" + "github.com/nspcc-dev/neofs-node/pkg/util/grace" + "github.com/nspcc-dev/neofs-node/pkg/util/logger" + "github.com/nspcc-dev/neofs-node/pkg/util/profiler" +) + +const ( + // ErrorReturnCode returns when application crashed at initialization stage + ErrorReturnCode = 1 + + // SuccessReturnCode returns when application closed without panic + SuccessReturnCode = 0 +) + +func exitErr(err error) { + if err != nil { + fmt.Println(err) + os.Exit(ErrorReturnCode) + } +} + +func main() { + configFile := flag.String("config", "", "path to config") + versionFlag := flag.Bool("version", false, "neofs-ir node version") + flag.Parse() + + if *versionFlag { + fmt.Println("version:", misc.Version) + os.Exit(SuccessReturnCode) + } + + cfg, err := newConfig(*configFile) + exitErr(err) + + log, err := logger.NewLogger(cfg) + exitErr(err) + + ctx := grace.NewGracefulContext(log) + + pprof := profiler.NewProfiler(log, cfg) + prometheus := profiler.NewMetrics(log, cfg) + + innerRing, err := innerring.New(ctx, log, cfg) + if err != nil { + exitErr(err) + } + + // start pprof if enabled + if pprof != nil { + pprof.Start(ctx) + defer pprof.Stop() + } + + // start prometheus if enabled + if prometheus != nil { + prometheus.Start(ctx) + defer prometheus.Stop() + } + + // start inner ring + err = innerRing.Start(ctx) + if err != nil { + exitErr(err) + } + + log.Info("application started") + + // todo: select ctx.Done or exported error channel + <-ctx.Done() + + innerRing.Stop() + + log.Info("application stopped") +} diff --git a/cmd/neofs-node/defaults.go b/cmd/neofs-node/defaults.go index 4fe24b50..dfe26eb5 100644 --- a/cmd/neofs-node/defaults.go +++ b/cmd/neofs-node/defaults.go @@ -4,8 +4,7 @@ import ( "time" "github.com/nspcc-dev/neo-go/pkg/config/netmode" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/modules/morph" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/morph" "github.com/spf13/viper" ) @@ -65,23 +64,6 @@ func setDefaults(v *viper.Viper) { }) } - // Storage section - { - storageTypes := []string{ - core.BlobStore.String(), - core.MetaStore.String(), - core.SpaceMetricsStore.String(), - } - - for i := range storageTypes { - v.SetDefault("storage."+storageTypes[i]+".bucket", "boltdb") - v.SetDefault("storage."+storageTypes[i]+".path", "./temp/storage/"+storageTypes[i]) - v.SetDefault("storage."+storageTypes[i]+".perm", 0777) - // v.SetDefault("storage."+storageTypes[i]+".no_grow_sync", false) - // v.SetDefault("storage."+storageTypes[i]+".lock_timeout", "30s") - } - } - // Object section { v.SetDefault("object.max_processing_size", 100) // size in MB, use 0 to remove restriction @@ -274,20 +256,6 @@ func setDefaults(v *viper.Viper) { ) } - { // Reputation - // Put method name - v.SetDefault( - morph.ReputationContractPutOptPath(), - "Put", - ) - - // List method name - v.SetDefault( - morph.ReputationContractListOptPath(), - "List", - ) - } - { // Netmap // AddPeer method name v.SetDefault( diff --git a/cmd/neofs-node/main.go b/cmd/neofs-node/main.go index 851f800c..4e00169a 100644 --- a/cmd/neofs-node/main.go +++ b/cmd/neofs-node/main.go @@ -12,14 +12,14 @@ import ( "github.com/nspcc-dev/neofs-api-go/service" state2 "github.com/nspcc-dev/neofs-api-go/state" crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-node/lib/fix" - "github.com/nspcc-dev/neofs-node/lib/fix/config" - "github.com/nspcc-dev/neofs-node/lib/fix/web" - "github.com/nspcc-dev/neofs-node/lib/fix/worker" - "github.com/nspcc-dev/neofs-node/lib/muxer" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/fix" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/fix/config" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/fix/worker" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/node" "github.com/nspcc-dev/neofs-node/misc" - "github.com/nspcc-dev/neofs-node/modules/node" - "github.com/nspcc-dev/neofs-node/services/public/state" + "github.com/nspcc-dev/neofs-node/pkg/network/muxer" + statesrv "github.com/nspcc-dev/neofs-node/pkg/network/transport/state/grpc" + "github.com/nspcc-dev/neofs-node/pkg/util/profiler" "github.com/pkg/errors" "github.com/spf13/viper" "go.uber.org/dig" @@ -30,9 +30,9 @@ import ( type params struct { dig.In - Debug web.Profiler `optional:"true"` - Metric web.Metrics `optional:"true"` - Worker worker.Workers `optional:"true"` + Debug profiler.Profiler `optional:"true"` + Metric profiler.Metrics `optional:"true"` + Worker worker.Workers `optional:"true"` Muxer muxer.Mux Logger *zap.Logger } @@ -105,7 +105,7 @@ func runHealthCheck() { grpc.WithInsecure()) check(err) - req := new(state.HealthRequest) + req := new(statesrv.HealthRequest) req.SetTTL(service.NonForwardingTTL) if err := service.SignRequestData(key, req); err != nil { check(err) diff --git a/modules/bootstrap/healthy.go b/cmd/neofs-node/modules/bootstrap/healthy.go similarity index 69% rename from modules/bootstrap/healthy.go rename to cmd/neofs-node/modules/bootstrap/healthy.go index bd93fd0f..116b424e 100644 --- a/modules/bootstrap/healthy.go +++ b/cmd/neofs-node/modules/bootstrap/healthy.go @@ -2,12 +2,12 @@ package bootstrap import ( "crypto/ecdsa" + "errors" "sync" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/placement" - "github.com/nspcc-dev/neofs-node/services/public/state" + contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap/wrapper" + state "github.com/nspcc-dev/neofs-node/pkg/network/transport/state/grpc" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement" "github.com/spf13/viper" "go.uber.org/dig" "go.uber.org/zap" @@ -25,7 +25,7 @@ type ( // for ChangeState PrivateKey *ecdsa.PrivateKey - MorphNetmapContract *implementations.MorphNetmapContract + Client *contract.Wrapper } healthyResult struct { @@ -47,9 +47,7 @@ type ( } ) -const ( - errUnhealthy = internal.Error("unhealthy") -) +var errUnhealthy = errors.New("unhealthy") func (h *healthyClient) setHandler(handler func() error) { if handler == nil { @@ -71,12 +69,12 @@ func (h *healthyClient) Healthy() error { func newHealthy(p healthyParams) (res healthyResult, err error) { sp := state.Params{ - Stater: p.Place, - Logger: p.Logger, - Viper: p.Viper, - Checkers: p.Checkers, - PrivateKey: p.PrivateKey, - MorphNetmapContract: p.MorphNetmapContract, + Stater: p.Place, + Logger: p.Logger, + Viper: p.Viper, + Checkers: p.Checkers, + PrivateKey: p.PrivateKey, + Client: p.Client, } if res.StateService, err = state.New(sp); err != nil { diff --git a/modules/bootstrap/module.go b/cmd/neofs-node/modules/bootstrap/module.go similarity index 62% rename from modules/bootstrap/module.go rename to cmd/neofs-node/modules/bootstrap/module.go index 8b31ed2e..e6655254 100644 --- a/modules/bootstrap/module.go +++ b/cmd/neofs-node/modules/bootstrap/module.go @@ -1,8 +1,6 @@ package bootstrap -import ( - "github.com/nspcc-dev/neofs-node/lib/fix/module" -) +import "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/fix/module" // Module is a module of bootstrap component. var Module = module.Module{ diff --git a/lib/fix/catch.go b/cmd/neofs-node/modules/fix/catch.go similarity index 100% rename from lib/fix/catch.go rename to cmd/neofs-node/modules/fix/catch.go diff --git a/lib/fix/config/config.go b/cmd/neofs-node/modules/fix/config/config.go similarity index 100% rename from lib/fix/config/config.go rename to cmd/neofs-node/modules/fix/config/config.go diff --git a/lib/fix/fix.go b/cmd/neofs-node/modules/fix/fix.go similarity index 85% rename from lib/fix/fix.go rename to cmd/neofs-node/modules/fix/fix.go index 7fd4e9df..d3990a26 100644 --- a/lib/fix/fix.go +++ b/cmd/neofs-node/modules/fix/fix.go @@ -6,10 +6,11 @@ import ( "strconv" "strings" - "github.com/nspcc-dev/neofs-node/lib/fix/config" - "github.com/nspcc-dev/neofs-node/lib/fix/logger" - "github.com/nspcc-dev/neofs-node/lib/fix/module" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/fix/config" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/fix/module" "github.com/nspcc-dev/neofs-node/misc" + "github.com/nspcc-dev/neofs-node/pkg/util/grace" + "github.com/nspcc-dev/neofs-node/pkg/util/logger" "github.com/pkg/errors" "github.com/spf13/viper" "go.uber.org/dig" @@ -90,7 +91,7 @@ func New(s *Settings, mod module.Module) App { mod = mod.Append( module.Module{ {Constructor: logger.NewLogger}, - {Constructor: NewGracefulContext}, + {Constructor: grace.NewGracefulContext}, {Constructor: func() (*viper.Viper, error) { return config.NewConfig(config.Params{ File: s.File, diff --git a/lib/fix/module/module.go b/cmd/neofs-node/modules/fix/module/module.go similarity index 100% rename from lib/fix/module/module.go rename to cmd/neofs-node/modules/fix/module/module.go diff --git a/lib/fix/services.go b/cmd/neofs-node/modules/fix/services.go similarity index 100% rename from lib/fix/services.go rename to cmd/neofs-node/modules/fix/services.go diff --git a/lib/fix/worker/worker.go b/cmd/neofs-node/modules/fix/worker/worker.go similarity index 100% rename from lib/fix/worker/worker.go rename to cmd/neofs-node/modules/fix/worker/worker.go diff --git a/modules/grpc/billing.go b/cmd/neofs-node/modules/grpc/billing.go similarity index 100% rename from modules/grpc/billing.go rename to cmd/neofs-node/modules/grpc/billing.go diff --git a/modules/grpc/module.go b/cmd/neofs-node/modules/grpc/module.go similarity index 58% rename from modules/grpc/module.go rename to cmd/neofs-node/modules/grpc/module.go index 7e266039..861084ad 100644 --- a/modules/grpc/module.go +++ b/cmd/neofs-node/modules/grpc/module.go @@ -1,8 +1,6 @@ package grpc -import ( - "github.com/nspcc-dev/neofs-node/lib/fix/module" -) +import "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/fix/module" // Module is a gRPC layer module. var Module = module.Module{ diff --git a/modules/grpc/routing.go b/cmd/neofs-node/modules/grpc/routing.go similarity index 96% rename from modules/grpc/routing.go rename to cmd/neofs-node/modules/grpc/routing.go index d0fc6fca..bd97e42c 100644 --- a/modules/grpc/routing.go +++ b/cmd/neofs-node/modules/grpc/routing.go @@ -8,6 +8,7 @@ import ( middleware "github.com/grpc-ecosystem/go-grpc-middleware" gZap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap" prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" + libgrpc "github.com/nspcc-dev/neofs-node/pkg/network/transport/grpc" "github.com/spf13/viper" "go.uber.org/dig" "go.uber.org/zap" @@ -15,11 +16,7 @@ import ( ) type ( - // Service interface - Service interface { - Name() string - Register(*grpc.Server) - } + Service = libgrpc.Service // ServerParams to create gRPC-server // and provide service-handlers diff --git a/modules/morph/balance.go b/cmd/neofs-node/modules/morph/balance.go similarity index 61% rename from modules/morph/balance.go rename to cmd/neofs-node/modules/morph/balance.go index df396442..e2220002 100644 --- a/modules/morph/balance.go +++ b/cmd/neofs-node/modules/morph/balance.go @@ -1,8 +1,9 @@ package morph import ( - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/services/public/accounting" + contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/balance" + clientWrapper "github.com/nspcc-dev/neofs-node/pkg/morph/client/balance/wrapper" + accounting "github.com/nspcc-dev/neofs-node/pkg/network/transport/accounting/grpc" "github.com/pkg/errors" "go.uber.org/dig" ) @@ -10,7 +11,7 @@ import ( type balanceContractResult struct { dig.Out - BalanceContract implementations.MorphBalanceContract + Client *clientWrapper.Wrapper AccountingService accounting.Service } @@ -41,27 +42,28 @@ func newBalanceContract(p contractParams) (res balanceContractResult, err error) return } - morphClient := implementations.MorphBalanceContract{} - morphClient.SetBalanceContractClient(client) + var ( + balanceOfMethod = p.Viper.GetString(BalanceContractBalanceOfOptPath()) + decimalsMethod = p.Viper.GetString(BalanceContractDecimalsOfOptPath()) + ) - morphClient.SetBalanceOfMethodName( - p.Viper.GetString( - BalanceContractBalanceOfOptPath(), - ), - ) - morphClient.SetDecimalsMethodName( - p.Viper.GetString( - BalanceContractDecimalsOfOptPath(), - ), - ) + var c *contract.Client + if c, err = contract.New(client, + contract.WithBalanceOfMethod(balanceOfMethod), + contract.WithDecimalsMethod(decimalsMethod), + ); err != nil { + return + } + + if res.Client, err = clientWrapper.New(c); err != nil { + return + } if res.AccountingService, err = accounting.New(accounting.Params{ - MorphBalanceContract: morphClient, + ContractClient: res.Client, }); err != nil { return } - res.BalanceContract = morphClient - return } diff --git a/modules/morph/common.go b/cmd/neofs-node/modules/morph/common.go similarity index 83% rename from modules/morph/common.go rename to cmd/neofs-node/modules/morph/common.go index 6584f7ae..55fa64c4 100644 --- a/modules/morph/common.go +++ b/cmd/neofs-node/modules/morph/common.go @@ -2,17 +2,16 @@ package morph import ( "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neofs-api-go/bootstrap" - "github.com/nspcc-dev/neofs-node/lib/blockchain/event" - "github.com/nspcc-dev/neofs-node/lib/blockchain/goclient" - "github.com/nspcc-dev/neofs-node/lib/implementations" + "github.com/nspcc-dev/neofs-node/pkg/core/netmap" + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" "github.com/spf13/viper" "go.uber.org/dig" "go.uber.org/zap" ) // SmartContracts maps smart contract name to contract client. -type SmartContracts map[string]implementations.StaticContractClient +type SmartContracts map[string]*client.StaticClient // EventHandlers maps notification event name to handler information. type EventHandlers map[string]event.HandlerInfo @@ -22,7 +21,7 @@ type morphContractsParams struct { Viper *viper.Viper - GoClient *goclient.Client + Client *client.Client Listener event.Listener } @@ -36,11 +35,11 @@ type contractParams struct { MorphContracts SmartContracts - NodeInfo bootstrap.NodeInfo + NodeInfo netmap.Info } func newMorphContracts(p morphContractsParams) (SmartContracts, EventHandlers, error) { - mContracts := make(map[string]implementations.StaticContractClient, len(ContractNames)) + mContracts := make(map[string]*client.StaticClient, len(ContractNames)) mHandlers := make(map[string]event.HandlerInfo) for _, contractName := range ContractNames { @@ -59,7 +58,7 @@ func newMorphContracts(p morphContractsParams) (SmartContracts, EventHandlers, e ), ) - mContracts[contractName], err = implementations.NewStaticContractClient(p.GoClient, scHash, fee) + mContracts[contractName], err = client.NewStatic(p.Client, scHash, fee) if err != nil { return nil, nil, err } @@ -109,7 +108,6 @@ const ( // ContractNames is a list of smart contract names. var ContractNames = []string{ containerContractName, - reputationContractName, NetmapContractName, BalanceContractName, } diff --git a/modules/morph/container.go b/cmd/neofs-node/modules/morph/container.go similarity index 61% rename from modules/morph/container.go rename to cmd/neofs-node/modules/morph/container.go index 770bf4b7..550d35c8 100644 --- a/modules/morph/container.go +++ b/cmd/neofs-node/modules/morph/container.go @@ -1,9 +1,10 @@ package morph import ( - "github.com/nspcc-dev/neofs-node/lib/acl" - "github.com/nspcc-dev/neofs-node/lib/container" - "github.com/nspcc-dev/neofs-node/lib/implementations" + eacl "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage" + "github.com/nspcc-dev/neofs-node/pkg/core/container/storage" + contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/container" + clientWrapper "github.com/nspcc-dev/neofs-node/pkg/morph/client/container/wrapper" "github.com/pkg/errors" "go.uber.org/dig" ) @@ -11,13 +12,9 @@ import ( type containerContractResult struct { dig.Out - ContainerContract *implementations.MorphContainerContract + ExtendedACLStore eacl.Storage - BinaryExtendedACLStore acl.BinaryExtendedACLStore - - ExtendedACLSource acl.ExtendedACLSource - - ContainerStorage container.Storage + ContainerStorage storage.Storage } const ( @@ -73,50 +70,34 @@ func newContainerContract(p contractParams) (res containerContractResult, err er return } - morphClient := new(implementations.MorphContainerContract) - morphClient.SetContainerContractClient(client) - - morphClient.SetEACLSetMethodName( - p.Viper.GetString( - ContainerContractSetEACLOptPath(), - ), - ) - morphClient.SetEACLGetMethodName( - p.Viper.GetString( - ContainerContractEACLOptPath(), - ), - ) - morphClient.SetContainerGetMethodName( - p.Viper.GetString( - ContainerContractGetOptPath(), - ), - ) - morphClient.SetContainerPutMethodName( - p.Viper.GetString( - ContainerContractPutOptPath(), - ), - ) - morphClient.SetContainerDeleteMethodName( - p.Viper.GetString( - ContainerContractDelOptPath(), - ), - ) - morphClient.SetContainerListMethodName( - p.Viper.GetString( - ContainerContractListOptPath(), - ), + var ( + setEACLMethod = p.Viper.GetString(ContainerContractSetEACLOptPath()) + eaclMethod = p.Viper.GetString(ContainerContractEACLOptPath()) + getMethod = p.Viper.GetString(ContainerContractGetOptPath()) + putMethod = p.Viper.GetString(ContainerContractPutOptPath()) + deleteMethod = p.Viper.GetString(ContainerContractDelOptPath()) + listMethod = p.Viper.GetString(ContainerContractListOptPath()) ) - res.ContainerContract = morphClient - - res.BinaryExtendedACLStore = morphClient - - res.ExtendedACLSource, err = implementations.ExtendedACLSourceFromBinary(res.BinaryExtendedACLStore) - if err != nil { + var containerClient *contract.Client + if containerClient, err = contract.New(client, + contract.WithSetEACLMethod(setEACLMethod), + contract.WithEACLMethod(eaclMethod), + contract.WithGetMethod(getMethod), + contract.WithPutMethod(putMethod), + contract.WithDeleteMethod(deleteMethod), + contract.WithListMethod(listMethod), + ); err != nil { return } - res.ContainerStorage = morphClient + var wrapClient *clientWrapper.Wrapper + if wrapClient, err = clientWrapper.New(containerClient); err != nil { + return + } + + res.ContainerStorage = wrapClient + res.ExtendedACLStore = wrapClient return res, nil } diff --git a/modules/morph/event.go b/cmd/neofs-node/modules/morph/event.go similarity index 82% rename from modules/morph/event.go rename to cmd/neofs-node/modules/morph/event.go index 4df3f486..b6d6a631 100644 --- a/modules/morph/event.go +++ b/cmd/neofs-node/modules/morph/event.go @@ -1,8 +1,8 @@ package morph import ( - "github.com/nspcc-dev/neofs-node/lib/blockchain/event" - "github.com/nspcc-dev/neofs-node/lib/blockchain/event/netmap" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + "github.com/nspcc-dev/neofs-node/pkg/morph/event/netmap" ) const eventOpt = "event" diff --git a/cmd/neofs-node/modules/morph/goclient.go b/cmd/neofs-node/modules/morph/goclient.go new file mode 100644 index 00000000..77b52149 --- /dev/null +++ b/cmd/neofs-node/modules/morph/goclient.go @@ -0,0 +1,31 @@ +package morph + +import ( + "crypto/ecdsa" + + "github.com/nspcc-dev/neo-go/pkg/config/netmode" + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/spf13/viper" + "go.uber.org/dig" + "go.uber.org/zap" +) + +type morphClientParams struct { + dig.In + + Viper *viper.Viper + + Logger *zap.Logger + + Key *ecdsa.PrivateKey +} + +func newClient(p morphClientParams) (*client.Client, error) { + return client.New( + p.Key, + p.Viper.GetString(optPath(prefix, endpointOpt)), + client.WithLogger(p.Logger), + client.WithDialTimeout(p.Viper.GetDuration(optPath(prefix, dialTimeoutOpt))), + client.WithMagic(netmode.Magic(p.Viper.GetUint32(optPath(prefix, magicNumberOpt)))), + ) +} diff --git a/modules/morph/listener.go b/cmd/neofs-node/modules/morph/listener.go similarity index 90% rename from modules/morph/listener.go rename to cmd/neofs-node/modules/morph/listener.go index 4c334ced..d7015443 100644 --- a/modules/morph/listener.go +++ b/cmd/neofs-node/modules/morph/listener.go @@ -3,8 +3,8 @@ package morph import ( "context" - "github.com/nspcc-dev/neofs-node/lib/blockchain/event" - "github.com/nspcc-dev/neofs-node/lib/blockchain/subscriber" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + "github.com/nspcc-dev/neofs-node/pkg/morph/subscriber" "github.com/spf13/viper" "go.uber.org/dig" "go.uber.org/zap" diff --git a/modules/morph/module.go b/cmd/neofs-node/modules/morph/module.go similarity index 74% rename from modules/morph/module.go rename to cmd/neofs-node/modules/morph/module.go index c2ae2637..b61ce2c7 100644 --- a/modules/morph/module.go +++ b/cmd/neofs-node/modules/morph/module.go @@ -3,15 +3,14 @@ package morph import ( "strings" - "github.com/nspcc-dev/neofs-node/lib/fix/module" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/fix/module" ) // Module is a Neo:Morph module. var Module = module.Module{ - {Constructor: newMorphClient}, + {Constructor: newClient}, {Constructor: newMorphContracts}, {Constructor: newContainerContract}, - {Constructor: newReputationContract}, {Constructor: newNetmapContract}, {Constructor: newEventListener}, {Constructor: newBalanceContract}, diff --git a/modules/morph/netmap.go b/cmd/neofs-node/modules/morph/netmap.go similarity index 58% rename from modules/morph/netmap.go rename to cmd/neofs-node/modules/morph/netmap.go index 3c5e4f66..1fb8452f 100644 --- a/modules/morph/netmap.go +++ b/cmd/neofs-node/modules/morph/netmap.go @@ -1,10 +1,9 @@ package morph import ( - "github.com/nspcc-dev/neofs-node/lib/boot" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/ir" - "github.com/nspcc-dev/neofs-node/lib/netmap" + contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap" + clientWrapper "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap/wrapper" + "github.com/nspcc-dev/neofs-node/pkg/network/bootstrap" "github.com/pkg/errors" "go.uber.org/dig" ) @@ -12,13 +11,9 @@ import ( type netmapContractResult struct { dig.Out - NetmapContract *implementations.MorphNetmapContract + Client *clientWrapper.Wrapper - NetMapStorage netmap.Storage - - IRStorage ir.Storage - - StorageBootController boot.StorageBootController + NodeRegisterer *bootstrap.Registerer } const ( @@ -68,48 +63,32 @@ func newNetmapContract(p contractParams) (res netmapContractResult, err error) { return } - morphClient := new(implementations.MorphNetmapContract) - morphClient.SetNetmapContractClient(client) - - morphClient.SetAddPeerMethodName( - p.Viper.GetString( - NetmapContractAddPeerOptPath(), - ), - ) - morphClient.SetNewEpochMethodName( - p.Viper.GetString( - NetmapContractNewEpochOptPath(), - ), - ) - morphClient.SetNetMapMethodName( - p.Viper.GetString( - NetmapContractNetmapOptPath(), - ), - ) - morphClient.SetUpdateStateMethodName( - p.Viper.GetString( - NetmapContractUpdateStateOptPath(), - ), - ) - morphClient.SetIRListMethodName( - p.Viper.GetString( - NetmapContractIRListOptPath(), - ), + var ( + addPeerMethod = p.Viper.GetString(NetmapContractAddPeerOptPath()) + newEpochMethod = p.Viper.GetString(NetmapContractNewEpochOptPath()) + netmapMethod = p.Viper.GetString(NetmapContractNetmapOptPath()) + updStateMethod = p.Viper.GetString(NetmapContractUpdateStateOptPath()) + irListMethod = p.Viper.GetString(NetmapContractIRListOptPath()) ) - bootCtrl := boot.StorageBootController{} - bootCtrl.SetPeerBootstrapper(morphClient) - bootCtrl.SetLogger(p.Logger) + var c *contract.Client + if c, err = contract.New(client, + contract.WithAddPeerMethod(addPeerMethod), + contract.WithNewEpochMethod(newEpochMethod), + contract.WithNetMapMethod(netmapMethod), + contract.WithUpdateStateMethod(updStateMethod), + contract.WithInnerRingListMethod(irListMethod), + ); err != nil { + return + } - bootPrm := boot.StorageBootParams{} - bootPrm.SetNodeInfo(&p.NodeInfo) + if res.Client, err = clientWrapper.New(c); err != nil { + return + } - bootCtrl.SetBootParams(bootPrm) - - res.StorageBootController = bootCtrl - res.NetmapContract = morphClient - res.NetMapStorage = morphClient - res.IRStorage = morphClient + if res.NodeRegisterer, err = bootstrap.New(res.Client, p.NodeInfo); err != nil { + return + } return res, nil } diff --git a/modules/network/http.go b/cmd/neofs-node/modules/network/http.go similarity index 92% rename from modules/network/http.go rename to cmd/neofs-node/modules/network/http.go index 21fbd722..001411c4 100644 --- a/modules/network/http.go +++ b/cmd/neofs-node/modules/network/http.go @@ -2,7 +2,7 @@ package network import ( "github.com/fasthttp/router" - svc "github.com/nspcc-dev/neofs-node/modules/bootstrap" + svc "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/bootstrap" "github.com/valyala/fasthttp" "go.uber.org/dig" ) diff --git a/modules/network/module.go b/cmd/neofs-node/modules/network/module.go similarity index 58% rename from modules/network/module.go rename to cmd/neofs-node/modules/network/module.go index 95c6041f..8e0e1f25 100644 --- a/modules/network/module.go +++ b/cmd/neofs-node/modules/network/module.go @@ -1,8 +1,8 @@ package network import ( - "github.com/nspcc-dev/neofs-node/lib/fix/module" - "github.com/nspcc-dev/neofs-node/lib/fix/web" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/fix/module" + "github.com/nspcc-dev/neofs-node/pkg/util/profiler" ) // Module is a network layer module. @@ -10,11 +10,10 @@ var Module = module.Module{ {Constructor: newMuxer}, {Constructor: newPeers}, {Constructor: newPlacement}, - {Constructor: newTransport}, // Metrics is prometheus handler - {Constructor: web.NewMetrics}, + {Constructor: profiler.NewMetrics}, // Profiler is pprof handler - {Constructor: web.NewProfiler}, + {Constructor: profiler.NewProfiler}, {Constructor: newHTTPHandler}, } diff --git a/modules/network/muxer.go b/cmd/neofs-node/modules/network/muxer.go similarity index 89% rename from modules/network/muxer.go rename to cmd/neofs-node/modules/network/muxer.go index 63ad8fc5..2de81f25 100644 --- a/modules/network/muxer.go +++ b/cmd/neofs-node/modules/network/muxer.go @@ -4,8 +4,7 @@ import ( "time" "github.com/multiformats/go-multiaddr" - "github.com/nspcc-dev/neofs-node/lib/muxer" - "github.com/nspcc-dev/neofs-node/lib/peers" + "github.com/nspcc-dev/neofs-node/pkg/network/muxer" "github.com/spf13/viper" "github.com/valyala/fasthttp" "go.uber.org/dig" @@ -19,8 +18,6 @@ type muxerParams struct { Logger *zap.Logger P2P *grpc.Server - Peers peers.Interface - Address multiaddr.Multiaddr ShutdownTTL time.Duration `name:"shutdown_ttl"` API fasthttp.RequestHandler @@ -48,7 +45,6 @@ func newFastHTTPServer(p muxerParams) *fasthttp.Server { func newMuxer(p muxerParams) muxer.Mux { return muxer.New(muxer.Params{ P2P: p.P2P, - Peers: p.Peers, Logger: p.Logger, Address: p.Address, ShutdownTTL: p.ShutdownTTL, diff --git a/modules/network/peers.go b/cmd/neofs-node/modules/network/peers.go similarity index 52% rename from modules/network/peers.go rename to cmd/neofs-node/modules/network/peers.go index f9af19c0..3fe60b77 100644 --- a/modules/network/peers.go +++ b/cmd/neofs-node/modules/network/peers.go @@ -2,8 +2,7 @@ package network import ( "github.com/multiformats/go-multiaddr" - "github.com/nspcc-dev/neofs-node/lib/peers" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/network/peers" "github.com/spf13/viper" "go.uber.org/dig" "go.uber.org/zap" @@ -12,26 +11,14 @@ import ( type peersParams struct { dig.In - Viper *viper.Viper - Logger *zap.Logger - Address multiaddr.Multiaddr - Transport transport.Transport -} - -func newTransport(v *viper.Viper) transport.Transport { - return transport.New( - v.GetInt64("transport.attempts_count"), - v.GetDuration("transport.attempts_ttl"), - ) + Viper *viper.Viper + Logger *zap.Logger + Address multiaddr.Multiaddr } func newPeers(p peersParams) (peers.Interface, error) { return peers.New(peers.Params{ Logger: p.Logger, - Address: p.Address, - Transport: p.Transport, - Attempts: p.Viper.GetInt64("peers.attempts_count"), - AttemptsTTL: p.Viper.GetDuration("peers.attempts_ttl"), ConnectionTTL: p.Viper.GetDuration("peers.connections_ttl"), ConnectionIDLE: p.Viper.GetDuration("peers.connections_idle"), MetricsTimeout: p.Viper.GetDuration("peers.metrics_timeout"), diff --git a/modules/network/placement.go b/cmd/neofs-node/modules/network/placement.go similarity index 67% rename from modules/network/placement.go rename to cmd/neofs-node/modules/network/placement.go index 36959efd..4b1d8bd8 100644 --- a/modules/network/placement.go +++ b/cmd/neofs-node/modules/network/placement.go @@ -1,14 +1,14 @@ package network import ( - "github.com/nspcc-dev/neofs-node/lib/blockchain/event" - netmapevent "github.com/nspcc-dev/neofs-node/lib/blockchain/event/netmap" - libcnr "github.com/nspcc-dev/neofs-node/lib/container" - "github.com/nspcc-dev/neofs-node/lib/netmap" - "github.com/nspcc-dev/neofs-node/lib/peers" - "github.com/nspcc-dev/neofs-node/lib/placement" - "github.com/nspcc-dev/neofs-node/modules/morph" - "github.com/nspcc-dev/neofs-node/services/public/state" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/morph" + "github.com/nspcc-dev/neofs-node/pkg/core/container/storage" + contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap/wrapper" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + netmapevent "github.com/nspcc-dev/neofs-node/pkg/morph/event/netmap" + "github.com/nspcc-dev/neofs-node/pkg/network/peers" + state "github.com/nspcc-dev/neofs-node/pkg/network/transport/state/grpc" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement" "go.uber.org/dig" "go.uber.org/zap" ) @@ -19,11 +19,11 @@ type ( Log *zap.Logger Peers peers.Store - Fetcher libcnr.Storage + Fetcher storage.Storage MorphEventListener event.Listener - NetMapStorage netmap.Storage + NetMapClient *contract.Wrapper MorphEventHandlers morph.EventHandlers } @@ -51,7 +51,7 @@ func newPlacement(p placementParams) placementOutput { morph.NewEpochEventType, )]; ok { handlerInfo.SetHandler(func(ev event.Event) { - nmRes, err := p.NetMapStorage.GetNetMap(netmap.GetParams{}) + nm, err := p.NetMapClient.GetNetMap() if err != nil { p.Log.Error("could not get network map", zap.String("error", err.Error()), @@ -61,7 +61,7 @@ func newPlacement(p placementParams) placementOutput { if err := place.Update( ev.(netmapevent.NewEpoch).EpochNumber(), - nmRes.NetMap(), + nm, ); err != nil { p.Log.Error("could not update network map in placement component", zap.String("error", err.Error()), diff --git a/modules/node/audit.go b/cmd/neofs-node/modules/node/audit.go similarity index 64% rename from modules/node/audit.go rename to cmd/neofs-node/modules/node/audit.go index a2c02b28..77f22420 100644 --- a/modules/node/audit.go +++ b/cmd/neofs-node/modules/node/audit.go @@ -4,9 +4,11 @@ import ( "crypto/ecdsa" "github.com/nspcc-dev/neofs-api-go/session" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/peers" - "github.com/nspcc-dev/neofs-node/services/public/object" + "github.com/nspcc-dev/neofs-node/pkg/network/peers" + object "github.com/nspcc-dev/neofs-node/pkg/network/transport/object/grpc" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/replication/storage" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" "github.com/spf13/viper" "go.uber.org/zap" ) @@ -15,7 +17,7 @@ type ( cnrHandlerParams struct { *viper.Viper *zap.Logger - Placer implementations.ObjectPlacer + Placer *placement.PlacementWrapper PeerStore peers.Store Peers peers.Interface TimeoutsPrefix string @@ -25,8 +27,8 @@ type ( } ) -func newObjectsContainerHandler(p cnrHandlerParams) (implementations.SelectiveContainerExecutor, error) { - as, err := implementations.NewAddressStore(p.PeerStore, p.Logger) +func newObjectsContainerHandler(p cnrHandlerParams) (transport.SelectiveContainerExecutor, error) { + as, err := storage.NewAddressStore(p.PeerStore, p.Logger) if err != nil { return nil, err } @@ -50,12 +52,12 @@ func newObjectsContainerHandler(p cnrHandlerParams) (implementations.SelectiveCo return nil, err } - exec, err := implementations.NewContainerTraverseExecutor(multiTransport) + exec, err := transport.NewContainerTraverseExecutor(multiTransport) if err != nil { return nil, err } - return implementations.NewObjectContainerHandler(implementations.ObjectContainerHandlerParams{ + return transport.NewObjectContainerHandler(transport.ObjectContainerHandlerParams{ NodeLister: p.Placer, Executor: exec, Logger: p.Logger, diff --git a/modules/node/container.go b/cmd/neofs-node/modules/node/container.go similarity index 52% rename from modules/node/container.go rename to cmd/neofs-node/modules/node/container.go index af081cb4..a0cac8ab 100644 --- a/modules/node/container.go +++ b/cmd/neofs-node/modules/node/container.go @@ -1,10 +1,10 @@ package node import ( - "github.com/nspcc-dev/neofs-node/lib/acl" - libcnr "github.com/nspcc-dev/neofs-node/lib/container" - svc "github.com/nspcc-dev/neofs-node/modules/bootstrap" - "github.com/nspcc-dev/neofs-node/services/public/container" + svc "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/bootstrap" + eacl "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage" + "github.com/nspcc-dev/neofs-node/pkg/core/container/storage" + container "github.com/nspcc-dev/neofs-node/pkg/network/transport/container/grpc" "go.uber.org/dig" "go.uber.org/zap" ) @@ -16,9 +16,9 @@ type cnrParams struct { Healthy svc.HealthyClient - ExtendedACLStore acl.BinaryExtendedACLStore + ExtendedACLStore eacl.Storage - ContainerStorage libcnr.Storage + ContainerStorage storage.Storage } func newContainerService(p cnrParams) (container.Service, error) { diff --git a/cmd/neofs-node/modules/node/core.go b/cmd/neofs-node/modules/node/core.go new file mode 100644 index 00000000..cde0172b --- /dev/null +++ b/cmd/neofs-node/modules/node/core.go @@ -0,0 +1,35 @@ +package node + +import ( + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket/boltdb" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket/fsbucket" + "github.com/spf13/viper" +) + +type Buckets map[string]bucket.Bucket + +const ( + fsBucket = "fsbucket" + boltBucket = "bolt" +) + +func newBuckets(v *viper.Viper) (Buckets, error) { + var ( + err error + mBuckets = make(Buckets) + ) + + if mBuckets[fsBucket], err = fsbucket.NewBucket(v); err != nil { + return nil, err + } + + boltOpts, err := boltdb.NewOptions(v) + if err != nil { + return nil, err + } else if mBuckets[boltBucket], err = boltdb.NewBucket(&boltOpts); err != nil { + return nil, err + } + + return mBuckets, nil +} diff --git a/modules/node/localstore.go b/cmd/neofs-node/modules/node/localstore.go similarity index 55% rename from modules/node/localstore.go rename to cmd/neofs-node/modules/node/localstore.go index 7be10bed..c96eb1f4 100644 --- a/modules/node/localstore.go +++ b/cmd/neofs-node/modules/node/localstore.go @@ -1,10 +1,9 @@ package node import ( - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/meta" - "github.com/nspcc-dev/neofs-node/lib/metrics" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + meta2 "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/meta" + metrics2 "github.com/nspcc-dev/neofs-node/pkg/services/metrics" "go.uber.org/atomic" "go.uber.org/dig" "go.uber.org/zap" @@ -15,9 +14,9 @@ type ( dig.In Logger *zap.Logger - Storage core.Storage + Buckets Buckets Counter *atomic.Float64 - Collector metrics.Collector + Collector metrics2.Collector } metaIterator struct { @@ -25,30 +24,20 @@ type ( } ) -func newMetaIterator(iter localstore.Iterator) meta.Iterator { +func newMetaIterator(iter localstore.Iterator) meta2.Iterator { return &metaIterator{iter: iter} } -func (m *metaIterator) Iterate(handler meta.IterateFunc) error { +func (m *metaIterator) Iterate(handler meta2.IterateFunc) error { return m.iter.Iterate(nil, func(objMeta *localstore.ObjectMeta) bool { return handler == nil || handler(objMeta.Object) != nil }) } func newLocalstore(p localstoreParams) (localstore.Localstore, error) { - metaBucket, err := p.Storage.GetBucket(core.MetaStore) - if err != nil { - return nil, err - } - - blobBucket, err := p.Storage.GetBucket(core.BlobStore) - if err != nil { - return nil, err - } - local, err := localstore.New(localstore.Params{ - BlobBucket: blobBucket, - MetaBucket: metaBucket, + BlobBucket: p.Buckets[fsBucket], + MetaBucket: p.Buckets[boltBucket], Logger: p.Logger, Collector: p.Collector, }) diff --git a/modules/node/metrics.go b/cmd/neofs-node/modules/node/metrics.go similarity index 54% rename from modules/node/metrics.go rename to cmd/neofs-node/modules/node/metrics.go index 0faad5d1..1eba1d69 100644 --- a/modules/node/metrics.go +++ b/cmd/neofs-node/modules/node/metrics.go @@ -1,9 +1,8 @@ package node import ( - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/lib/metrics" - mService "github.com/nspcc-dev/neofs-node/services/metrics" + metrics "github.com/nspcc-dev/neofs-node/pkg/network/transport/metrics/grpc" + metrics2 "github.com/nspcc-dev/neofs-node/pkg/services/metrics" "github.com/spf13/viper" "go.uber.org/atomic" "go.uber.org/dig" @@ -17,36 +16,31 @@ type ( Logger *zap.Logger Options []string `name:"node_options"` Viper *viper.Viper - Store core.Storage + Buckets Buckets } metricsServiceParams struct { dig.In Logger *zap.Logger - Collector metrics.Collector + Collector metrics2.Collector } ) func newObjectCounter() *atomic.Float64 { return atomic.NewFloat64(0) } -func newMetricsService(p metricsServiceParams) (mService.Service, error) { - return mService.New(mService.Params{ +func newMetricsService(p metricsServiceParams) (metrics.Service, error) { + return metrics.New(metrics.Params{ Logger: p.Logger, Collector: p.Collector, }) } -func newMetricsCollector(p metricsParams) (metrics.Collector, error) { - store, err := p.Store.GetBucket(core.SpaceMetricsStore) - if err != nil { - return nil, err - } - - return metrics.New(metrics.Params{ +func newMetricsCollector(p metricsParams) (metrics2.Collector, error) { + return metrics2.New(metrics2.Params{ Options: p.Options, Logger: p.Logger, Interval: p.Viper.GetDuration("metrics_collector.interval"), - MetricsStore: store, + MetricsStore: p.Buckets[fsBucket], }) } diff --git a/modules/node/module.go b/cmd/neofs-node/modules/node/module.go similarity index 59% rename from modules/node/module.go rename to cmd/neofs-node/modules/node/module.go index 83a81b48..6edc42d7 100644 --- a/modules/node/module.go +++ b/cmd/neofs-node/modules/node/module.go @@ -2,20 +2,19 @@ package node import ( "github.com/nspcc-dev/neofs-api-go/session" - "github.com/nspcc-dev/neofs-node/lib/blockchain/event" - "github.com/nspcc-dev/neofs-node/lib/boot" - "github.com/nspcc-dev/neofs-node/lib/fix/module" - "github.com/nspcc-dev/neofs-node/lib/fix/worker" - "github.com/nspcc-dev/neofs-node/lib/metrics" - "github.com/nspcc-dev/neofs-node/lib/netmap" - "github.com/nspcc-dev/neofs-node/lib/peers" - "github.com/nspcc-dev/neofs-node/lib/replication" - "github.com/nspcc-dev/neofs-node/modules/bootstrap" - "github.com/nspcc-dev/neofs-node/modules/grpc" - "github.com/nspcc-dev/neofs-node/modules/morph" - "github.com/nspcc-dev/neofs-node/modules/network" - "github.com/nspcc-dev/neofs-node/modules/settings" - "github.com/nspcc-dev/neofs-node/modules/workers" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/bootstrap" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/fix/module" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/fix/worker" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/grpc" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/morph" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/network" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/settings" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/workers" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + libboot "github.com/nspcc-dev/neofs-node/pkg/network/bootstrap" + "github.com/nspcc-dev/neofs-node/pkg/network/peers" + metrics2 "github.com/nspcc-dev/neofs-node/pkg/services/metrics" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/replication" "github.com/spf13/viper" "go.uber.org/dig" "go.uber.org/zap" @@ -30,11 +29,11 @@ type jobParams struct { Replicator replication.Manager PeersInterface peers.Interface - Metrics metrics.Collector + Metrics metrics2.Collector MorphEventListener event.Listener - StorageBootController boot.StorageBootController + NodeRegisterer *libboot.Registerer } // Module is a NeoFS node module. @@ -42,8 +41,7 @@ var Module = module.Module{ {Constructor: attachJobs}, {Constructor: newPeerstore}, {Constructor: attachServices}, - {Constructor: netmap.NewNetmap}, - {Constructor: newStorage}, + {Constructor: newBuckets}, {Constructor: newMetricsCollector}, {Constructor: newObjectCounter}, @@ -86,6 +84,6 @@ func attachJobs(p jobParams) worker.Jobs { "metrics": p.Metrics.Start, "event_listener": p.MorphEventListener.Listen, "replicator": p.Replicator.Process, - "boot": p.StorageBootController.Bootstrap, + "boot": p.NodeRegisterer.Bootstrap, } } diff --git a/modules/node/objectmanager.go b/cmd/neofs-node/modules/node/objectmanager.go similarity index 64% rename from modules/node/objectmanager.go rename to cmd/neofs-node/modules/node/objectmanager.go index 6d96f5c7..f54dd4fe 100644 --- a/modules/node/objectmanager.go +++ b/cmd/neofs-node/modules/node/objectmanager.go @@ -7,16 +7,17 @@ import ( "github.com/nspcc-dev/neofs-api-go/hash" apiobj "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/session" - libacl "github.com/nspcc-dev/neofs-node/lib/acl" - "github.com/nspcc-dev/neofs-node/lib/container" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/ir" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/peers" - "github.com/nspcc-dev/neofs-node/lib/placement" - "github.com/nspcc-dev/neofs-node/lib/transformer" - "github.com/nspcc-dev/neofs-node/services/public/object" + eacl "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage" + "github.com/nspcc-dev/neofs-node/pkg/core/container/storage" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap/wrapper" + "github.com/nspcc-dev/neofs-node/pkg/network/peers" + object "github.com/nspcc-dev/neofs-node/pkg/network/transport/object/grpc" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement" + storage2 "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/replication/storage" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transformer" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport/storagegroup" "github.com/spf13/viper" "go.uber.org/dig" "go.uber.org/zap" @@ -38,22 +39,18 @@ type ( Options []string `name:"node_options"` Key *ecdsa.PrivateKey - IRStorage ir.Storage + NetMapClient *contract.Wrapper - EpochReceiver implementations.EpochReceiver + Placer *placement.PlacementWrapper - Placer implementations.ObjectPlacer + ExtendedACLStore eacl.Storage - ExtendedACLStore libacl.ExtendedACLSource - - ContainerStorage container.Storage + ContainerStorage storage.Storage } ) const ( transformersSectionPath = "object.transformers." - - aclMandatorySetBits = 0x04040444 ) const xorSalitor = "xor" @@ -65,7 +62,7 @@ func newObjectManager(p objectManagerParams) (object.Service, error) { sltr = hash.SaltXOR } - as, err := implementations.NewAddressStore(p.Peers, p.Logger) + as, err := storage2.NewAddressStore(p.Peers, p.Logger) if err != nil { return nil, err } @@ -81,7 +78,7 @@ func newObjectManager(p objectManagerParams) (object.Service, error) { tr, err := object.NewMultiTransport(object.MultiTransportParams{ AddressStore: as, - EpochReceiver: p.EpochReceiver, + EpochReceiver: p.Placer, RemoteService: rs, Logger: p.Logger, Key: p.Key, @@ -98,12 +95,12 @@ func newObjectManager(p objectManagerParams) (object.Service, error) { return nil, err } - exec, err := implementations.NewContainerTraverseExecutor(tr) + exec, err := transport.NewContainerTraverseExecutor(tr) if err != nil { return nil, err } - selectiveExec, err := implementations.NewObjectContainerHandler(implementations.ObjectContainerHandlerParams{ + selectiveExec, err := transport.NewObjectContainerHandler(transport.ObjectContainerHandlerParams{ NodeLister: p.Placer, Executor: exec, Logger: p.Logger, @@ -112,7 +109,7 @@ func newObjectManager(p objectManagerParams) (object.Service, error) { return nil, err } - sgInfoRecv, err := implementations.NewStorageGroupInfoReceiver(implementations.StorageGroupInfoReceiverParams{ + sgInfoRecv, err := storagegroup.NewStorageGroupInfoReceiver(storagegroup.StorageGroupInfoReceiverParams{ SelectiveContainerExecutor: selectiveExec, Logger: p.Logger, }) @@ -120,16 +117,14 @@ func newObjectManager(p objectManagerParams) (object.Service, error) { return nil, err } - verifier, err := implementations.NewLocalIntegrityVerifier( - core.NewNeoKeyVerifier(), - ) + verifier, err := storage2.NewLocalIntegrityVerifier() if err != nil { return nil, err } trans, err := transformer.NewTransformer(transformer.Params{ SGInfoReceiver: sgInfoRecv, - EpochReceiver: p.EpochReceiver, + EpochReceiver: p.Placer, SizeLimit: uint64(p.Viper.GetInt64(transformersSectionPath+"payload_limiter.max_payload_size") * apiobj.UnitsKB), Verifier: verifier, }) @@ -137,16 +132,7 @@ func newObjectManager(p objectManagerParams) (object.Service, error) { return nil, err } - aclChecker := libacl.NewMaskedBasicACLChecker(aclMandatorySetBits, libacl.DefaultAndFilter) - - aclHelper, err := implementations.NewACLHelper(p.ContainerStorage) - if err != nil { - return nil, err - } - - verifier, err = implementations.NewLocalHeadIntegrityVerifier( - core.NewNeoKeyVerifier(), - ) + verifier, err = storage2.NewLocalHeadIntegrityVerifier() if err != nil { return nil, err } @@ -163,16 +149,16 @@ func newObjectManager(p objectManagerParams) (object.Service, error) { ObjectRestorer: transformer.NewRestorePipeline( transformer.SplitRestorer(), ), - RemoteService: rs, - AddressStore: as, - Logger: p.Logger, - TokenStore: p.TokenStore, - EpochReceiver: p.EpochReceiver, - ContainerNodesLister: p.Placer, - Key: p.Key, - CheckACL: p.Viper.GetBool("object.check_acl"), - DialTimeout: p.Viper.GetDuration("object.dial_timeout"), - MaxPayloadSize: p.Viper.GetUint64("object.transformers.payload_limiter.max_payload_size") * uint64(apiobj.UnitsKB), + RemoteService: rs, + AddressStore: as, + Logger: p.Logger, + TokenStore: p.TokenStore, + EpochReceiver: p.Placer, + PlacementWrapper: p.Placer, + Key: p.Key, + CheckACL: p.Viper.GetBool("object.check_acl"), + DialTimeout: p.Viper.GetDuration("object.dial_timeout"), + MaxPayloadSize: p.Viper.GetUint64("object.transformers.payload_limiter.max_payload_size") * uint64(apiobj.UnitsKB), PutParams: object.OperationParams{ Timeout: pto, LogErrors: p.Viper.GetBool("object.put.log_errs"), @@ -205,15 +191,11 @@ func newObjectManager(p objectManagerParams) (object.Service, error) { WindowSize: p.Viper.GetInt("object.window_size"), - ACLHelper: aclHelper, - BasicACLChecker: aclChecker, - IRStorage: p.IRStorage, - ContainerLister: p.Placer, + ContainerStorage: p.ContainerStorage, + NetmapClient: p.NetMapClient, SGInfoReceiver: sgInfoRecv, - OwnerKeyVerifier: core.NewNeoKeyVerifier(), - ExtendedACLSource: p.ExtendedACLStore, }) } diff --git a/modules/node/peerstore.go b/cmd/neofs-node/modules/node/peerstore.go similarity index 90% rename from modules/node/peerstore.go rename to cmd/neofs-node/modules/node/peerstore.go index 1ccd1f1d..05e68fc9 100644 --- a/modules/node/peerstore.go +++ b/cmd/neofs-node/modules/node/peerstore.go @@ -4,7 +4,7 @@ import ( "crypto/ecdsa" "github.com/multiformats/go-multiaddr" - "github.com/nspcc-dev/neofs-node/lib/peers" + "github.com/nspcc-dev/neofs-node/pkg/network/peers" "go.uber.org/dig" "go.uber.org/zap" ) diff --git a/cmd/neofs-node/modules/node/placement.go b/cmd/neofs-node/modules/node/placement.go new file mode 100644 index 00000000..015b6402 --- /dev/null +++ b/cmd/neofs-node/modules/node/placement.go @@ -0,0 +1,28 @@ +package node + +import ( + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement" + "go.uber.org/dig" +) + +type ( + placementToolParams struct { + dig.In + + Placement placement.Component + } + + placementToolResult struct { + dig.Out + + Placer *placement.PlacementWrapper + } +) + +func newPlacementTool(p placementToolParams) (res placementToolResult, err error) { + if res.Placer, err = placement.NewObjectPlacer(p.Placement); err != nil { + return + } + + return +} diff --git a/modules/node/replication.go b/cmd/neofs-node/modules/node/replication.go similarity index 85% rename from modules/node/replication.go rename to cmd/neofs-node/modules/node/replication.go index 546fdda9..0c053812 100644 --- a/modules/node/replication.go +++ b/cmd/neofs-node/modules/node/replication.go @@ -6,16 +6,14 @@ import ( "github.com/nspcc-dev/neofs-api-go/hash" "github.com/nspcc-dev/neofs-api-go/session" - "github.com/nspcc-dev/neofs-node/lib/blockchain/event" - "github.com/nspcc-dev/neofs-node/lib/blockchain/event/netmap" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/ir" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/peers" - "github.com/nspcc-dev/neofs-node/lib/placement" - "github.com/nspcc-dev/neofs-node/lib/replication" - "github.com/nspcc-dev/neofs-node/modules/morph" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/morph" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + "github.com/nspcc-dev/neofs-node/pkg/morph/event/netmap" + "github.com/nspcc-dev/neofs-node/pkg/network/peers" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/replication" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/replication/storage" "github.com/pkg/errors" "github.com/spf13/viper" "go.uber.org/dig" @@ -34,10 +32,9 @@ type ( Peers peers.Store Placement placement.Component Logger *zap.Logger - Lister ir.Storage Key *ecdsa.PrivateKey - Placer implementations.ObjectPlacer + Placer *placement.PlacementWrapper TokenStore session.PrivateTokenStore @@ -57,7 +54,7 @@ const ( ) func newReplicationManager(p replicationManagerParams) (replication.Manager, error) { - as, err := implementations.NewAddressStore(p.Peers, p.Logger) + as, err := storage.NewAddressStore(p.Peers, p.Logger) if err != nil { return nil, err } @@ -80,14 +77,12 @@ func newReplicationManager(p replicationManagerParams) (replication.Manager, err return nil, err } - integrityVerifier, err := implementations.NewLocalIntegrityVerifier( - core.NewNeoKeyVerifier(), - ) + integrityVerifier, err := storage.NewLocalIntegrityVerifier() if err != nil { return nil, err } - verifier, err := implementations.NewObjectValidator(&implementations.ObjectValidatorParams{ + verifier, err := storage.NewObjectValidator(&storage.ObjectValidatorParams{ AddressStore: ms, Localstore: p.LocalStore, Logger: p.Logger, @@ -183,7 +178,7 @@ func newPlacementHonorer(p replicationManagerParams, rss replication.RemoteStora return nil, err } - storage, err := implementations.NewObjectStorage(implementations.ObjectStorageParams{ + storage, err := storage.NewObjectStorage(storage.ObjectStorageParams{ Localstore: p.LocalStore, SelectiveContainerExecutor: och, Logger: p.Logger, @@ -221,7 +216,7 @@ func newLocationDetector(p replicationManagerParams, ms replication.MultiSolver) return nil, err } - locator, err := implementations.NewObjectLocator(implementations.LocatorParams{ + locator, err := storage.NewObjectLocator(storage.LocatorParams{ SelectiveContainerExecutor: och, Logger: p.Logger, }) @@ -243,7 +238,7 @@ func newLocationDetector(p replicationManagerParams, ms replication.MultiSolver) func newStorageValidator(p replicationManagerParams, as replication.AddressStore) (replication.StorageValidator, error) { prefix := mainReplicationPrefix + "." + storageValidatorPrefix - var sltr implementations.Salitor + var sltr storage.Salitor switch v := p.Viper.GetString(prefix + ".salitor"); v { case xorSalitor: @@ -267,14 +262,12 @@ func newStorageValidator(p replicationManagerParams, as replication.AddressStore return nil, err } - headVerifier, err := implementations.NewLocalHeadIntegrityVerifier( - core.NewNeoKeyVerifier(), - ) + headVerifier, err := storage.NewLocalHeadIntegrityVerifier() if err != nil { return nil, err } - verifier, err := implementations.NewObjectValidator(&implementations.ObjectValidatorParams{ + verifier, err := storage.NewObjectValidator(&storage.ObjectValidatorParams{ AddressStore: as, Localstore: p.LocalStore, SelectiveContainerExecutor: och, @@ -317,7 +310,7 @@ func newObjectReplicator(p replicationManagerParams, rss replication.RemoteStora return nil, err } - storage, err := implementations.NewObjectStorage(implementations.ObjectStorageParams{ + storage, err := storage.NewObjectStorage(storage.ObjectStorageParams{ Localstore: p.LocalStore, SelectiveContainerExecutor: och, Logger: p.Logger, @@ -355,14 +348,12 @@ func newRestorer(p replicationManagerParams, ms replication.MultiSolver) (replic return nil, err } - integrityVerifier, err := implementations.NewLocalIntegrityVerifier( - core.NewNeoKeyVerifier(), - ) + integrityVerifier, err := storage.NewLocalIntegrityVerifier() if err != nil { return nil, err } - verifier, err := implementations.NewObjectValidator(&implementations.ObjectValidatorParams{ + verifier, err := storage.NewObjectValidator(&storage.ObjectValidatorParams{ AddressStore: ms, Localstore: p.LocalStore, SelectiveContainerExecutor: och, @@ -373,7 +364,7 @@ func newRestorer(p replicationManagerParams, ms replication.MultiSolver) (replic return nil, err } - storage, err := implementations.NewObjectStorage(implementations.ObjectStorageParams{ + storage, err := storage.NewObjectStorage(storage.ObjectStorageParams{ Localstore: p.LocalStore, Logger: p.Logger, }) diff --git a/cmd/neofs-node/modules/node/services.go b/cmd/neofs-node/modules/node/services.go new file mode 100644 index 00000000..3aec901e --- /dev/null +++ b/cmd/neofs-node/modules/node/services.go @@ -0,0 +1,36 @@ +package node + +import ( + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/grpc" + accounting "github.com/nspcc-dev/neofs-node/pkg/network/transport/accounting/grpc" + container "github.com/nspcc-dev/neofs-node/pkg/network/transport/container/grpc" + metrics "github.com/nspcc-dev/neofs-node/pkg/network/transport/metrics/grpc" + object "github.com/nspcc-dev/neofs-node/pkg/network/transport/object/grpc" + session "github.com/nspcc-dev/neofs-node/pkg/network/transport/session/grpc" + state "github.com/nspcc-dev/neofs-node/pkg/network/transport/state/grpc" + "go.uber.org/dig" +) + +type servicesParams struct { + dig.In + + Status state.Service + Container container.Service + Object object.Service + Session session.Service + Accounting accounting.Service + Metrics metrics.Service +} + +func attachServices(p servicesParams) grpc.ServicesResult { + return grpc.ServicesResult{ + Services: []grpc.Service{ + p.Status, + p.Container, + p.Accounting, + p.Metrics, + p.Session, + p.Object, + }, + } +} diff --git a/modules/node/session.go b/cmd/neofs-node/modules/node/session.go similarity index 65% rename from modules/node/session.go rename to cmd/neofs-node/modules/node/session.go index aaa25277..46aaed8b 100644 --- a/modules/node/session.go +++ b/cmd/neofs-node/modules/node/session.go @@ -1,8 +1,8 @@ package node import ( - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/services/public/session" + session "github.com/nspcc-dev/neofs-node/pkg/network/transport/session/grpc" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement" "go.uber.org/dig" "go.uber.org/zap" ) @@ -14,7 +14,7 @@ type sessionParams struct { TokenStore session.TokenStore - EpochReceiver implementations.EpochReceiver + EpochReceiver *placement.PlacementWrapper } func newSessionService(p sessionParams) (session.Service, error) { diff --git a/modules/settings/address.go b/cmd/neofs-node/modules/settings/address.go similarity index 85% rename from modules/settings/address.go rename to cmd/neofs-node/modules/settings/address.go index c1c9722c..53cc8034 100644 --- a/modules/settings/address.go +++ b/cmd/neofs-node/modules/settings/address.go @@ -6,7 +6,6 @@ import ( "strings" "github.com/multiformats/go-multiaddr" - "github.com/nspcc-dev/neofs-node/internal" "github.com/pkg/errors" ) @@ -21,10 +20,10 @@ const emptyAddr = "0.0.0.0" const ip4ColonCount = 1 var ( - errEmptyAddress = internal.Error("`node.address` could not be empty") - errEmptyProtocol = internal.Error("`node.protocol` could not be empty") - errUnknownProtocol = internal.Error("`node.protocol` unknown protocol") - errEmptyShutdownTTL = internal.Error("`node.shutdown_ttl` could not be empty") + errEmptyAddress = errors.New("`node.address` could not be empty") + errEmptyProtocol = errors.New("`node.protocol` could not be empty") + errUnknownProtocol = errors.New("`node.protocol` unknown protocol") + errEmptyShutdownTTL = errors.New("`node.shutdown_ttl` could not be empty") ) func ipVersion(address string) string { diff --git a/modules/settings/module.go b/cmd/neofs-node/modules/settings/module.go similarity index 61% rename from modules/settings/module.go rename to cmd/neofs-node/modules/settings/module.go index 1e075103..6980967e 100644 --- a/modules/settings/module.go +++ b/cmd/neofs-node/modules/settings/module.go @@ -1,8 +1,6 @@ package settings -import ( - "github.com/nspcc-dev/neofs-node/lib/fix/module" -) +import "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/fix/module" // Module is a node settings module. var Module = module.Module{ diff --git a/modules/settings/node.go b/cmd/neofs-node/modules/settings/node.go similarity index 90% rename from modules/settings/node.go rename to cmd/neofs-node/modules/settings/node.go index 47b940e6..1cdf2a59 100644 --- a/modules/settings/node.go +++ b/cmd/neofs-node/modules/settings/node.go @@ -10,9 +10,9 @@ import ( "time" "github.com/multiformats/go-multiaddr" - "github.com/nspcc-dev/neofs-api-go/bootstrap" crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-node/lib/peers" + "github.com/nspcc-dev/neofs-node/pkg/core/netmap" + "github.com/nspcc-dev/neofs-node/pkg/network/peers" "github.com/pkg/errors" "github.com/spf13/viper" "go.uber.org/dig" @@ -28,7 +28,7 @@ type ( NodeOpts []string `name:"node_options"` ShutdownTTL time.Duration `name:"shutdown_ttl"` - NodeInfo bootstrap.NodeInfo + NodeInfo netmap.Info } ) @@ -136,11 +136,12 @@ loop: cfg.NodeOpts = append(cfg.NodeOpts, val) } - cfg.NodeInfo = bootstrap.NodeInfo{ - Address: cfg.Address.String(), - PubKey: crypto.MarshalPublicKey(&cfg.PrivateKey.PublicKey), - Options: cfg.NodeOpts, - } + nodeInfo := netmap.Info{} + nodeInfo.SetAddress(cfg.Address.String()) + nodeInfo.SetPublicKey(crypto.MarshalPublicKey(&cfg.PrivateKey.PublicKey)) + nodeInfo.SetOptions(cfg.NodeOpts) + + cfg.NodeInfo = nodeInfo l.Debug("loaded node options", zap.Strings("options", cfg.NodeOpts)) diff --git a/modules/workers/module.go b/cmd/neofs-node/modules/workers/module.go similarity index 58% rename from modules/workers/module.go rename to cmd/neofs-node/modules/workers/module.go index 275a5faf..a95da32b 100644 --- a/modules/workers/module.go +++ b/cmd/neofs-node/modules/workers/module.go @@ -1,8 +1,6 @@ package workers -import ( - "github.com/nspcc-dev/neofs-node/lib/fix/module" -) +import "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/fix/module" // Module is a workers module. var Module = module.Module{ diff --git a/modules/workers/prepare.go b/cmd/neofs-node/modules/workers/prepare.go similarity index 96% rename from modules/workers/prepare.go rename to cmd/neofs-node/modules/workers/prepare.go index ea5411fb..45930b10 100644 --- a/modules/workers/prepare.go +++ b/cmd/neofs-node/modules/workers/prepare.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/nspcc-dev/neofs-node/lib/fix/worker" + "github.com/nspcc-dev/neofs-node/cmd/neofs-node/modules/fix/worker" "github.com/spf13/viper" "go.uber.org/dig" "go.uber.org/zap" diff --git a/go.mod b/go.mod index be64ab5e..1c1628ae 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,6 @@ go 1.14 require ( bou.ke/monkey v1.0.2 - github.com/cenk/backoff v2.2.1+incompatible // indirect - github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect github.com/fasthttp/router v1.0.2 github.com/gogo/protobuf v1.3.1 github.com/golang/protobuf v1.4.2 @@ -17,15 +15,13 @@ require ( github.com/multiformats/go-multiaddr-net v0.1.2 // v0.1.1 => v0.1.2 github.com/multiformats/go-multihash v0.0.13 github.com/nspcc-dev/hrw v1.0.9 - github.com/nspcc-dev/neo-go v0.90.0-pre.0.20200708064050-cf1e5243b90b - github.com/nspcc-dev/neofs-api-go v1.2.0 + github.com/nspcc-dev/neo-go v0.90.0 + github.com/nspcc-dev/neofs-api-go v1.3.0 github.com/nspcc-dev/neofs-crypto v0.3.0 github.com/nspcc-dev/netmap v1.7.0 github.com/panjf2000/ants/v2 v2.3.0 - github.com/peterbourgon/g2s v0.0.0-20170223122336-d4e7ad98afea // indirect github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.6.0 - github.com/rubyist/circuitbreaker v2.2.1+incompatible github.com/soheilhy/cmux v0.1.4 github.com/spaolacci/murmur3 v1.1.0 github.com/spf13/pflag v1.0.5 // indirect diff --git a/go.sum b/go.sum index a397a2427848d7243d89b49144dee2b296f7e784..4b62e23e163012b09f0b8b1f221649f0d0ef1166 100644 GIT binary patch delta 383 zcmZvXyH3Jj07esoiHjPe0d;aRB*tFojk+kkaA~3R5=iL=Ew+?i{#HwoIJh}#(ibu5 zmJw!D|GmoMr(g`y~qBN%>a5*5-k zP4beUkRt~@xaPqL|mFmyPP8OI$Y2A-WqrR>GuDgJNE z--6>cXyvRi5*G_eon}!zX)6g5(VVFDJDyFCl>oNMSg#7B7(+*yGTYDI!4?VIA>@bF ziUxAs3Ti+|dzxAcfHjdB!=qZ1J439Hvx#6hHi0IK_=&PCI?UjBvw}fCXHp~`wDg56 zw0JLdqM;KgDU3?uq&at0LlhjY*U?6N)3>I-D7{4gj7uKtMm`K1s?JE>{5qa^?GD3*)2wi;x&jNmU delta 1224 zcmai!ON%2_6vuI#g^Vsng;rcOjPxcXb?eq!7@0DuN0O>My3$Ek!7S==>yfI|t<)>2 zY}$q3N*D-!4<+Ee zNr%D3hcEA3d!gjVTR6 zo^PTM4n$XDwStJ}u_5t_EOuwgJk=+8wRzCBZ$HK@-peBb+*x7ogyj^Q1}L~`R@LHm zD0n2iann=bIWt8ReTN)nF6P-txzdVlK&`w!AD_V1EUFaBTc@b&8I*`%H2 z=r}zHAOIZT0ZRi0v|XP>45TSX0M5yT-k0>XV}``CPhXo`Uy)1MSeDXCS?AdX_kHX=4-ho>l+W{5SLdD^vyL~CeoR5$ab zv#Y=GO9(sQ)EZyo+T+7I#S4R8$A-=3VWZ2MTrah9G_-T3Hm|Ygw;KB2QsG#74-QM8 zA_F)o2n(W`@p#Q51fQI%ePlA4n)RGDEfu}c)WKQd?fSFBZ`t1;-JufY4zBV5r$hjY zdpYC)JZ|v#DYos=h_k&@5Yah@#c;E2NL66c%&s;SUzf7L)Cy5Fxdt-cH!MutCcS_O z;s-g*9^-ER%l%WldoIbjt4bb~V|uD%)sIFAvE&AEk_xTiw8D7?CjC;?2j(a^{M`KT z`JK~5MM=0(?p9IhK<|wHx#{zK=*4v)@@WKHSbDHj#&|U||ll5iK5G{Peo` E2R*ljKmY&$ diff --git a/internal/error.go b/internal/error.go deleted file mode 100644 index 7df16033..00000000 --- a/internal/error.go +++ /dev/null @@ -1,7 +0,0 @@ -package internal - -// Error is a custom error. -type Error string - -// Error is an implementation of error interface. -func (e Error) Error() string { return string(e) } diff --git a/lib/.gitkeep b/lib/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/acl/action.go b/lib/acl/action.go deleted file mode 100644 index a173d92e..00000000 --- a/lib/acl/action.go +++ /dev/null @@ -1,94 +0,0 @@ -package acl - -import ( - "bytes" - - "github.com/nspcc-dev/neofs-api-go/acl" -) - -// RequestInfo is an interface of request information needed for extended ACL check. -type RequestInfo interface { - TypedHeaderSource - - // Must return the binary representation of request initiator's key. - Key() []byte - - // Must return true if request corresponds to operation type. - TypeOf(acl.OperationType) bool - - // Must return true if request has passed target. - TargetOf(acl.Target) bool -} - -// ExtendedACLChecker is an interface of extended ACL checking tool. -type ExtendedACLChecker interface { - // Must return an action according to the results of applying the ACL table rules to request. - // - // Must return ActionUndefined if it is unable to explicitly calculate the action. - Action(acl.ExtendedACLTable, RequestInfo) acl.ExtendedACLAction -} - -type extendedACLChecker struct{} - -// NewExtendedACLChecker creates a new extended ACL checking tool and returns ExtendedACLChecker interface. -func NewExtendedACLChecker() ExtendedACLChecker { - return new(extendedACLChecker) -} - -// Action returns an action for passed request based on information about it and ACL table. -// -// Returns action of the first suitable table record, or ActionUndefined in the absence thereof. -// -// If passed ExtendedACLTable is nil, ActionUndefined returns. -// If passed RequestInfo is nil, ActionUndefined returns. -func (s extendedACLChecker) Action(table acl.ExtendedACLTable, req RequestInfo) acl.ExtendedACLAction { - if table == nil { - return acl.ActionUndefined - } else if req == nil { - return acl.ActionUndefined - } - - for _, record := range table.Records() { - // check type of operation - if !req.TypeOf(record.OperationType()) { - continue - } - - // check target - if !targetMatches(req, record.TargetList()) { - continue - } - - // check headers - switch MatchFilters(req, record.HeaderFilters()) { - case mResUndefined: - // headers of some type could not be composed => allow - return acl.ActionAllow - case mResMatch: - return record.Action() - } - } - - return acl.ActionAllow -} - -// returns true if one of ExtendedACLTarget has suitable target OR suitable public key. -func targetMatches(req RequestInfo, list []acl.ExtendedACLTarget) bool { - rKey := req.Key() - - for _, target := range list { - // check public key match - for _, key := range target.KeyList() { - if bytes.Equal(key, rKey) { - return true - } - } - - // check target group match - if req.TargetOf(target.Target()) { - return true - } - } - - return false -} diff --git a/lib/acl/action_test.go b/lib/acl/action_test.go deleted file mode 100644 index 49e30eea..00000000 --- a/lib/acl/action_test.go +++ /dev/null @@ -1,163 +0,0 @@ -package acl - -import ( - "testing" - - "github.com/nspcc-dev/neofs-api-go/acl" - "github.com/stretchr/testify/require" -) - -type testExtendedACLTable struct { - records []acl.ExtendedACLRecord -} - -type testRequestInfo struct { - headers []acl.TypedHeader - key []byte - opType acl.OperationType - target acl.Target -} - -type testEACLRecord struct { - opType acl.OperationType - filters []acl.HeaderFilter - targets []acl.ExtendedACLTarget - action acl.ExtendedACLAction -} - -type testEACLTarget struct { - target acl.Target - keys [][]byte -} - -func (s testEACLTarget) Target() acl.Target { - return s.target -} - -func (s testEACLTarget) KeyList() [][]byte { - return s.keys -} - -func (s testEACLRecord) OperationType() acl.OperationType { - return s.opType -} - -func (s testEACLRecord) HeaderFilters() []acl.HeaderFilter { - return s.filters -} - -func (s testEACLRecord) TargetList() []acl.ExtendedACLTarget { - return s.targets -} - -func (s testEACLRecord) Action() acl.ExtendedACLAction { - return s.action -} - -func (s testRequestInfo) HeadersOfType(typ acl.HeaderType) ([]acl.Header, bool) { - res := make([]acl.Header, 0, len(s.headers)) - - for i := range s.headers { - if s.headers[i].HeaderType() == typ { - res = append(res, s.headers[i]) - } - } - - return res, true -} - -func (s testRequestInfo) Key() []byte { - return s.key -} - -func (s testRequestInfo) TypeOf(t acl.OperationType) bool { - return s.opType == t -} - -func (s testRequestInfo) TargetOf(t acl.Target) bool { - return s.target == t -} - -func (s testExtendedACLTable) Records() []acl.ExtendedACLRecord { - return s.records -} - -func TestExtendedACLChecker_Action(t *testing.T) { - s := NewExtendedACLChecker() - - // nil ExtendedACLTable - require.Equal(t, acl.ActionUndefined, s.Action(nil, nil)) - - // create test ExtendedACLTable - table := new(testExtendedACLTable) - - // nil RequestInfo - require.Equal(t, acl.ActionUndefined, s.Action(table, nil)) - - // create test RequestInfo - req := new(testRequestInfo) - - // create test ExtendedACLRecord - record := new(testEACLRecord) - table.records = append(table.records, record) - - // set different OperationType - record.opType = acl.OperationType(3) - req.opType = record.opType + 1 - - require.Equal(t, acl.ActionAllow, s.Action(table, req)) - - // set equal OperationType - req.opType = record.opType - - // create test ExtendedACLTarget through group - target := new(testEACLTarget) - record.targets = append(record.targets, target) - - // set not matching ExtendedACLTarget - target.target = acl.Target(5) - req.target = target.target + 1 - - require.Equal(t, acl.ActionAllow, s.Action(table, req)) - - // set matching ExtendedACLTarget - req.target = target.target - - // create test HeaderFilter - fHeader := new(testTypedHeader) - hFilter := &testHeaderFilter{ - TypedHeader: fHeader, - } - record.filters = append(record.filters, hFilter) - - // create test TypedHeader - header := new(testTypedHeader) - req.headers = append(req.headers, header) - - // set not matching values - header.t = hFilter.HeaderType() + 1 - - require.Equal(t, acl.ActionAllow, s.Action(table, req)) - - // set matching values - header.k = "key" - header.v = "value" - - fHeader.t = header.HeaderType() - fHeader.k = header.Name() - fHeader.v = header.Value() - - hFilter.t = acl.StringEqual - - // set ExtendedACLAction - record.action = acl.ExtendedACLAction(7) - - require.Equal(t, record.action, s.Action(table, req)) - - // set matching ExtendedACLTarget through key - target.target = req.target + 1 - req.key = []byte{1, 2, 3} - target.keys = append(target.keys, req.key) - - require.Equal(t, record.action, s.Action(table, req)) -} diff --git a/lib/acl/basic.go b/lib/acl/basic.go deleted file mode 100644 index eae2d7fa..00000000 --- a/lib/acl/basic.go +++ /dev/null @@ -1,179 +0,0 @@ -package acl - -import ( - "github.com/nspcc-dev/neofs-api-go/acl" - "github.com/nspcc-dev/neofs-api-go/object" - "github.com/nspcc-dev/neofs-node/internal" -) - -type ( - // BasicChecker is an interface of the basic ACL control tool. - BasicChecker interface { - // Action returns true if request is allowed for this target. - Action(uint32, object.RequestType, acl.Target) (bool, error) - - // Bearer returns true if bearer token is allowed for this request. - Bearer(uint32, object.RequestType) (bool, error) - - // Extended returns true if extended ACL is allowed for this. - Extended(uint32) bool - - // Sticky returns true if sticky bit is set. - Sticky(uint32) bool - } - - // BasicACLChecker performs basic ACL check. - BasicACLChecker struct{} - - // MaskedBasicACLChecker performs all basic ACL checks, but applying - // mask on ACL first. It is useful, when some bits must be always - // set or unset. - MaskedBasicACLChecker struct { - BasicACLChecker - - andMask uint32 - orMask uint32 - } - - nibble struct { - value uint32 - } -) - -const ( - errUnknownRequest = internal.Error("unknown request type") - errUnknownTarget = internal.Error("unknown target type") -) - -const ( - aclFinalBit = 0x10000000 // 29th bit - aclStickyBit = 0x20000000 // 30th bit - - nibbleBBit = 0x1 - nibbleOBit = 0x2 - nibbleSBit = 0x4 - nibbleUBit = 0x8 - - // DefaultAndFilter is a default AND mask of basic ACL value of container. - DefaultAndFilter = 0xFFFFFFFF -) - -var ( - nibbleOffset = map[object.RequestType]uint32{ - object.RequestGet: 0, - object.RequestHead: 1 * 4, - object.RequestPut: 2 * 4, - object.RequestDelete: 3 * 4, - object.RequestSearch: 4 * 4, - object.RequestRange: 5 * 4, - object.RequestRangeHash: 6 * 4, - } -) - -// Action returns true if request is allowed for target. -func (c *BasicACLChecker) Action(rule uint32, req object.RequestType, t acl.Target) (bool, error) { - n, err := fetchNibble(rule, req) - if err != nil { - return false, err - } - - switch t { - case acl.Target_User: - return n.U(), nil - case acl.Target_System: - return n.S(), nil - case acl.Target_Others: - return n.O(), nil - default: - return false, errUnknownTarget - } -} - -// Bearer returns true if bearer token is allowed to use for this request -// as source of extended ACL. -func (c *BasicACLChecker) Bearer(rule uint32, req object.RequestType) (bool, error) { - n, err := fetchNibble(rule, req) - if err != nil { - return false, err - } - - return n.B(), nil -} - -// Extended returns true if extended ACL stored in the container are allowed -// to use. -func (c *BasicACLChecker) Extended(rule uint32) bool { - return rule&aclFinalBit != aclFinalBit -} - -// Sticky returns true if container is not allowed to store objects with -// owners different from request owner. -func (c *BasicACLChecker) Sticky(rule uint32) bool { - return rule&aclStickyBit == aclStickyBit -} - -func fetchNibble(rule uint32, req object.RequestType) (*nibble, error) { - offset, ok := nibbleOffset[req] - if !ok { - return nil, errUnknownRequest - } - - return &nibble{value: (rule >> offset) & 0xf}, nil -} - -// B returns true if `Bearer` bit set in the nibble. -func (n *nibble) B() bool { return n.value&nibbleBBit == nibbleBBit } - -// O returns true if `Others` bit set in the nibble. -func (n *nibble) O() bool { return n.value&nibbleOBit == nibbleOBit } - -// S returns true if `System` bit set in the nibble. -func (n *nibble) S() bool { return n.value&nibbleSBit == nibbleSBit } - -// U returns true if `User` bit set in the nibble. -func (n *nibble) U() bool { return n.value&nibbleUBit == nibbleUBit } - -// NewMaskedBasicACLChecker returns BasicChecker that applies predefined -// bit mask on basic ACL value. -func NewMaskedBasicACLChecker(or, and uint32) BasicChecker { - return MaskedBasicACLChecker{ - BasicACLChecker: BasicACLChecker{}, - andMask: and, - orMask: or, - } -} - -// Action returns true if request is allowed for target. -func (c MaskedBasicACLChecker) Action(rule uint32, req object.RequestType, t acl.Target) (bool, error) { - rule |= c.orMask - rule &= c.andMask - - return c.BasicACLChecker.Action(rule, req, t) -} - -// Bearer returns true if bearer token is allowed to use for this request -// as source of extended ACL. -func (c MaskedBasicACLChecker) Bearer(rule uint32, req object.RequestType) (bool, error) { - rule |= c.orMask - rule &= c.andMask - - return c.BasicACLChecker.Bearer(rule, req) -} - -// Extended returns true if extended ACL stored in the container are allowed -// to use. -func (c MaskedBasicACLChecker) Extended(rule uint32) bool { - rule |= c.orMask - rule &= c.andMask - - return c.BasicACLChecker.Extended(rule) -} - -// Sticky returns true if container is not allowed to store objects with -// owners different from request owner. -func (c MaskedBasicACLChecker) Sticky(rule uint32) bool { - rule |= c.orMask - rule &= c.andMask - - return c.BasicACLChecker.Sticky(rule) -} diff --git a/lib/acl/basic_test.go b/lib/acl/basic_test.go deleted file mode 100644 index b379751f..00000000 --- a/lib/acl/basic_test.go +++ /dev/null @@ -1,116 +0,0 @@ -package acl - -import ( - "math/bits" - "testing" - - "github.com/nspcc-dev/neofs-api-go/acl" - "github.com/nspcc-dev/neofs-api-go/object" - "github.com/stretchr/testify/require" -) - -func TestBasicACLChecker(t *testing.T) { - reqs := []object.RequestType{ - object.RequestGet, - object.RequestHead, - object.RequestPut, - object.RequestDelete, - object.RequestSearch, - object.RequestRange, - object.RequestRangeHash, - } - - targets := []acl.Target{ - acl.Target_Others, - acl.Target_System, - acl.Target_User, - } - - checker := new(BasicACLChecker) - - t.Run("verb permissions", func(t *testing.T) { - mask := uint32(1) - - for i := range reqs { - res, err := checker.Bearer(mask, reqs[i]) - require.NoError(t, err) - require.True(t, res) - - mask = bits.Reverse32(mask) - res, err = checker.Bearer(mask, reqs[i]) - require.NoError(t, err) - require.False(t, res) - - mask = bits.Reverse32(mask) - - for j := range targets { - mask <<= 1 - res, err = checker.Action(mask, reqs[i], targets[j]) - require.NoError(t, err) - require.True(t, res) - - mask = bits.Reverse32(mask) - res, err = checker.Action(mask, reqs[i], targets[j]) - require.NoError(t, err) - require.False(t, res) - - mask = bits.Reverse32(mask) - } - mask <<= 1 - } - }) - - t.Run("unknown verb", func(t *testing.T) { - mask := uint32(1) - _, err := checker.Bearer(mask, -1) - require.Error(t, err) - - mask = 2 - _, err = checker.Action(mask, -1, acl.Target_Others) - require.Error(t, err) - }) - - t.Run("unknown action", func(t *testing.T) { - mask := uint32(2) - _, err := checker.Action(mask, object.RequestGet, -1) - require.Error(t, err) - }) - - t.Run("extended acl permission", func(t *testing.T) { - // set F-bit - mask := uint32(0) | aclFinalBit - require.False(t, checker.Extended(mask)) - - // unset F-bit - mask = bits.Reverse32(mask) - require.True(t, checker.Extended(mask)) - }) - - t.Run("sticky bit permission", func(t *testing.T) { - mask := uint32(0x20000000) - require.True(t, checker.Sticky(mask)) - - mask = bits.Reverse32(mask) - require.False(t, checker.Sticky(mask)) - }) -} - -// todo: add tests like in basic acl checker -func TestNeoFSMaskedBasicACLChecker(t *testing.T) { - const orFilter = 0x04040444 // this OR filter will be used in neofs-node - checker := NewMaskedBasicACLChecker(orFilter, DefaultAndFilter) - - reqs := []object.RequestType{ - object.RequestGet, - object.RequestHead, - object.RequestPut, - object.RequestSearch, - object.RequestRangeHash, - } - - for i := range reqs { - res, err := checker.Action(0, reqs[i], acl.Target_System) - require.NoError(t, err) - require.True(t, res) - } -} diff --git a/lib/acl/binary.go b/lib/acl/binary.go deleted file mode 100644 index a1cf6e50..00000000 --- a/lib/acl/binary.go +++ /dev/null @@ -1,129 +0,0 @@ -package acl - -import ( - "context" - "encoding/binary" - "io" - - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-node/internal" -) - -// BinaryEACLKey is a binary EACL storage key. -type BinaryEACLKey struct { - cid refs.CID -} - -// BinaryEACLValue is a binary EACL storage value. -type BinaryEACLValue struct { - eacl []byte - - sig []byte -} - -// BinaryExtendedACLSource is an interface of storage of binary extended ACL tables with read access. -type BinaryExtendedACLSource interface { - // Must return binary extended ACL table by key. - GetBinaryEACL(context.Context, BinaryEACLKey) (BinaryEACLValue, error) -} - -// BinaryExtendedACLStore is an interface of storage of binary extended ACL tables. -type BinaryExtendedACLStore interface { - BinaryExtendedACLSource - - // Must store binary extended ACL table for key. - PutBinaryEACL(context.Context, BinaryEACLKey, BinaryEACLValue) error -} - -// ErrNilBinaryExtendedACLStore is returned by function that expect a non-nil -// BinaryExtendedACLStore, but received nil. -const ErrNilBinaryExtendedACLStore = internal.Error("binary extended ACL store is nil") - -const sliceLenSize = 4 - -var eaclEndianness = binary.BigEndian - -// CID is a container ID getter. -func (s BinaryEACLKey) CID() refs.CID { - return s.cid -} - -// SetCID is a container ID setter. -func (s *BinaryEACLKey) SetCID(v refs.CID) { - s.cid = v -} - -// EACL is a binary extended ACL table getter. -func (s BinaryEACLValue) EACL() []byte { - return s.eacl -} - -// SetEACL is a binary extended ACL table setter. -func (s *BinaryEACLValue) SetEACL(v []byte) { - s.eacl = v -} - -// Signature is an EACL signature getter. -func (s BinaryEACLValue) Signature() []byte { - return s.sig -} - -// SetSignature is an EACL signature setter. -func (s *BinaryEACLValue) SetSignature(v []byte) { - s.sig = v -} - -// MarshalBinary returns a binary representation of BinaryEACLValue. -func (s BinaryEACLValue) MarshalBinary() ([]byte, error) { - data := make([]byte, sliceLenSize+len(s.eacl)+sliceLenSize+len(s.sig)) - - off := 0 - - eaclEndianness.PutUint32(data[off:], uint32(len(s.eacl))) - off += sliceLenSize - - off += copy(data[off:], s.eacl) - - eaclEndianness.PutUint32(data[off:], uint32(len(s.sig))) - off += sliceLenSize - - copy(data[off:], s.sig) - - return data, nil -} - -// UnmarshalBinary unmarshals BinaryEACLValue from bytes. -func (s *BinaryEACLValue) UnmarshalBinary(data []byte) (err error) { - err = io.ErrUnexpectedEOF - off := 0 - - if len(data[off:]) < sliceLenSize { - return - } - - aclLn := eaclEndianness.Uint32(data[off:]) - off += 4 - - if uint32(len(data[off:])) < aclLn { - return - } - - s.eacl = make([]byte, aclLn) - off += copy(s.eacl, data[off:]) - - if len(data[off:]) < sliceLenSize { - return - } - - sigLn := eaclEndianness.Uint32(data[off:]) - off += 4 - - if uint32(len(data[off:])) < sigLn { - return - } - - s.sig = make([]byte, sigLn) - copy(s.sig, data[off:]) - - return nil -} diff --git a/lib/acl/binary_test.go b/lib/acl/binary_test.go deleted file mode 100644 index eefb59ab..00000000 --- a/lib/acl/binary_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package acl - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestBinaryEACLValue(t *testing.T) { - s := BinaryEACLValue{} - - eacl := []byte{1, 2, 3} - s.SetEACL(eacl) - require.Equal(t, eacl, s.EACL()) - - sig := []byte{4, 5, 6} - s.SetSignature(sig) - require.Equal(t, sig, s.Signature()) - - data, err := s.MarshalBinary() - require.NoError(t, err) - - s2 := BinaryEACLValue{} - require.NoError(t, s2.UnmarshalBinary(data)) - - require.Equal(t, s, s2) -} diff --git a/lib/acl/extended.go b/lib/acl/extended.go deleted file mode 100644 index 20695bc6..00000000 --- a/lib/acl/extended.go +++ /dev/null @@ -1,29 +0,0 @@ -package acl - -import ( - "context" - - "github.com/nspcc-dev/neofs-api-go/acl" - "github.com/nspcc-dev/neofs-api-go/refs" -) - -// TypedHeaderSource is a various types of header set interface. -type TypedHeaderSource interface { - // Must return list of Header of particular type. - // Must return false if there is no ability to compose header list. - HeadersOfType(acl.HeaderType) ([]acl.Header, bool) -} - -// ExtendedACLSource is an interface of storage of extended ACL tables with read access. -type ExtendedACLSource interface { - // Must return extended ACL table by container ID key. - GetExtendedACLTable(context.Context, refs.CID) (acl.ExtendedACLTable, error) -} - -// ExtendedACLStore is an interface of storage of extended ACL tables. -type ExtendedACLStore interface { - ExtendedACLSource - - // Must store extended ACL table for container ID key. - PutExtendedACLTable(context.Context, refs.CID, acl.ExtendedACLTable) error -} diff --git a/lib/acl/header.go b/lib/acl/header.go deleted file mode 100644 index 8c779b3b..00000000 --- a/lib/acl/header.go +++ /dev/null @@ -1,234 +0,0 @@ -package acl - -import ( - "strconv" - - "github.com/nspcc-dev/neofs-api-go/acl" - "github.com/nspcc-dev/neofs-api-go/object" - "github.com/nspcc-dev/neofs-api-go/service" -) - -type objectHeaderSource struct { - obj *object.Object -} - -type typedHeader struct { - n string - v string - t acl.HeaderType -} - -type extendedHeadersWrapper struct { - hdrSrc service.ExtendedHeadersSource -} - -type typedExtendedHeader struct { - hdr service.ExtendedHeader -} - -func newTypedObjSysHdr(name, value string) acl.TypedHeader { - return &typedHeader{ - n: name, - v: value, - t: acl.HdrTypeObjSys, - } -} - -// Name is a name field getter. -func (s typedHeader) Name() string { - return s.n -} - -// Value is a value field getter. -func (s typedHeader) Value() string { - return s.v -} - -// HeaderType is a type field getter. -func (s typedHeader) HeaderType() acl.HeaderType { - return s.t -} - -// TypedHeaderSourceFromObject wraps passed object and returns TypedHeaderSource interface. -func TypedHeaderSourceFromObject(obj *object.Object) TypedHeaderSource { - return &objectHeaderSource{ - obj: obj, - } -} - -// HeaderOfType gathers object headers of passed type and returns Header list. -// -// If value of some header can not be calculated (e.g. nil extended header), it does not appear in list. -// -// Always returns true. -func (s objectHeaderSource) HeadersOfType(typ acl.HeaderType) ([]acl.Header, bool) { - if s.obj == nil { - return nil, true - } - - var res []acl.Header - - switch typ { - case acl.HdrTypeObjUsr: - objHeaders := s.obj.GetHeaders() - - res = make([]acl.Header, 0, len(objHeaders)) // 7 system header fields - - for i := range objHeaders { - if h := newTypedObjectExtendedHeader(objHeaders[i]); h != nil { - res = append(res, h) - } - } - case acl.HdrTypeObjSys: - res = make([]acl.Header, 0, 7) - - sysHdr := s.obj.GetSystemHeader() - - created := sysHdr.GetCreatedAt() - - res = append(res, - // ID - newTypedObjSysHdr( - acl.HdrObjSysNameID, - sysHdr.ID.String(), - ), - - // CID - newTypedObjSysHdr( - acl.HdrObjSysNameCID, - sysHdr.CID.String(), - ), - - // OwnerID - newTypedObjSysHdr( - acl.HdrObjSysNameOwnerID, - sysHdr.OwnerID.String(), - ), - - // Version - newTypedObjSysHdr( - acl.HdrObjSysNameVersion, - strconv.FormatUint(sysHdr.GetVersion(), 10), - ), - - // PayloadLength - newTypedObjSysHdr( - acl.HdrObjSysNamePayloadLength, - strconv.FormatUint(sysHdr.GetPayloadLength(), 10), - ), - - // CreatedAt.UnitTime - newTypedObjSysHdr( - acl.HdrObjSysNameCreatedUnix, - strconv.FormatUint(uint64(created.GetUnixTime()), 10), - ), - - // CreatedAt.Epoch - newTypedObjSysHdr( - acl.HdrObjSysNameCreatedEpoch, - strconv.FormatUint(created.GetEpoch(), 10), - ), - ) - } - - return res, true -} - -func newTypedObjectExtendedHeader(h object.Header) acl.TypedHeader { - val := h.GetValue() - if val == nil { - return nil - } - - res := new(typedHeader) - res.t = acl.HdrTypeObjSys - - switch hdr := val.(type) { - case *object.Header_UserHeader: - if hdr.UserHeader == nil { - return nil - } - - res.t = acl.HdrTypeObjUsr - res.n = hdr.UserHeader.GetKey() - res.v = hdr.UserHeader.GetValue() - case *object.Header_Link: - if hdr.Link == nil { - return nil - } - - switch hdr.Link.GetType() { - case object.Link_Previous: - res.n = acl.HdrObjSysLinkPrev - case object.Link_Next: - res.n = acl.HdrObjSysLinkNext - case object.Link_Child: - res.n = acl.HdrObjSysLinkChild - case object.Link_Parent: - res.n = acl.HdrObjSysLinkPar - case object.Link_StorageGroup: - res.n = acl.HdrObjSysLinkSG - default: - return nil - } - - res.v = hdr.Link.ID.String() - default: - return nil - } - - return res -} - -// TypedHeaderSourceFromExtendedHeaders wraps passed ExtendedHeadersSource and returns TypedHeaderSource interface. -func TypedHeaderSourceFromExtendedHeaders(hdrSrc service.ExtendedHeadersSource) TypedHeaderSource { - return &extendedHeadersWrapper{ - hdrSrc: hdrSrc, - } -} - -// Name returns the result of Key method. -func (s typedExtendedHeader) Name() string { - return s.hdr.Key() -} - -// Value returns the result of Value method. -func (s typedExtendedHeader) Value() string { - return s.hdr.Value() -} - -// HeaderType always returns HdrTypeRequest. -func (s typedExtendedHeader) HeaderType() acl.HeaderType { - return acl.HdrTypeRequest -} - -// TypedHeaders gathers extended request headers and returns TypedHeader list. -// -// Nil headers are ignored. -// -// Always returns true. -func (s extendedHeadersWrapper) HeadersOfType(typ acl.HeaderType) ([]acl.Header, bool) { - if s.hdrSrc == nil { - return nil, true - } - - var res []acl.Header - - if typ == acl.HdrTypeRequest { - hs := s.hdrSrc.ExtendedHeaders() - - res = make([]acl.Header, 0, len(hs)) - - for i := range hs { - if hs[i] == nil { - continue - } - - res = append(res, &typedExtendedHeader{ - hdr: hs[i], - }) - } - } - - return res, true -} diff --git a/lib/acl/headers_test.go b/lib/acl/headers_test.go deleted file mode 100644 index 236e084d..00000000 --- a/lib/acl/headers_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package acl - -import ( - "testing" - - "github.com/nspcc-dev/neofs-api-go/acl" - "github.com/nspcc-dev/neofs-api-go/object" - "github.com/stretchr/testify/require" -) - -func TestNewTypedObjectExtendedHeader(t *testing.T) { - var res acl.TypedHeader - - hdr := object.Header{} - - // nil value - require.Nil(t, newTypedObjectExtendedHeader(hdr)) - - // UserHeader - { - key := "key" - val := "val" - hdr.Value = &object.Header_UserHeader{ - UserHeader: &object.UserHeader{ - Key: key, - Value: val, - }, - } - - res = newTypedObjectExtendedHeader(hdr) - require.Equal(t, acl.HdrTypeObjUsr, res.HeaderType()) - require.Equal(t, key, res.Name()) - require.Equal(t, val, res.Value()) - } - - { // Link - link := new(object.Link) - link.ID = object.ID{1, 2, 3} - - hdr.Value = &object.Header_Link{ - Link: link, - } - - check := func(lt object.Link_Type, name string) { - link.Type = lt - - res = newTypedObjectExtendedHeader(hdr) - - require.Equal(t, acl.HdrTypeObjSys, res.HeaderType()) - require.Equal(t, name, res.Name()) - require.Equal(t, link.ID.String(), res.Value()) - } - - check(object.Link_Previous, acl.HdrObjSysLinkPrev) - check(object.Link_Next, acl.HdrObjSysLinkNext) - check(object.Link_Parent, acl.HdrObjSysLinkPar) - check(object.Link_Child, acl.HdrObjSysLinkChild) - check(object.Link_StorageGroup, acl.HdrObjSysLinkSG) - } -} diff --git a/lib/acl/match.go b/lib/acl/match.go deleted file mode 100644 index 7d4289cb..00000000 --- a/lib/acl/match.go +++ /dev/null @@ -1,94 +0,0 @@ -package acl - -import ( - "github.com/nspcc-dev/neofs-api-go/acl" -) - -// Maps MatchType to corresponding function. -// 1st argument of function - header value, 2nd - header filter. -var mMatchFns = map[acl.MatchType]func(acl.Header, acl.Header) bool{ - acl.StringEqual: stringEqual, - - acl.StringNotEqual: stringNotEqual, -} - -const ( - mResUndefined = iota - mResMatch - mResMismatch -) - -// MatchFilters checks if passed source carry at least one header that satisfies passed filters. -// -// Nil header does not satisfy any filter. Any header does not satisfy nil filter. -// -// Returns mResMismatch if passed TypedHeaderSource is nil. -// Returns mResMatch if passed filters are empty. -// -// If headers for some of the HeaderType could not be composed, mResUndefined returns. -func MatchFilters(src TypedHeaderSource, filters []acl.HeaderFilter) int { - if src == nil { - return mResMismatch - } else if len(filters) == 0 { - return mResMatch - } - - matched := 0 - - for _, filter := range filters { - // prevent NPE - if filter == nil { - continue - } - - headers, ok := src.HeadersOfType(filter.HeaderType()) - if !ok { - return mResUndefined - } - - // get headers of filtering type - for _, header := range headers { - // prevent NPE - if header == nil { - continue - } - - // check header name - if header.Name() != filter.Name() { - continue - } - - // get match function - matchFn, ok := mMatchFns[filter.MatchType()] - if !ok { - continue - } - - // check match - if !matchFn(header, filter) { - continue - } - - // increment match counter - matched++ - - break - } - } - - res := mResMismatch - - if matched >= len(filters) { - res = mResMatch - } - - return res -} - -func stringEqual(header, filter acl.Header) bool { - return header.Value() == filter.Value() -} - -func stringNotEqual(header, filter acl.Header) bool { - return header.Value() != filter.Value() -} diff --git a/lib/acl/match_test.go b/lib/acl/match_test.go deleted file mode 100644 index 123f852b..00000000 --- a/lib/acl/match_test.go +++ /dev/null @@ -1,192 +0,0 @@ -package acl - -import ( - "testing" - - "github.com/nspcc-dev/neofs-api-go/acl" - "github.com/stretchr/testify/require" -) - -type testTypedHeader struct { - t acl.HeaderType - k string - v string -} - -type testHeaderSrc struct { - hs []acl.TypedHeader -} - -type testHeaderFilter struct { - acl.TypedHeader - t acl.MatchType -} - -func (s testHeaderFilter) MatchType() acl.MatchType { - return s.t -} - -func (s testHeaderSrc) HeadersOfType(typ acl.HeaderType) ([]acl.Header, bool) { - res := make([]acl.Header, 0, len(s.hs)) - - for i := range s.hs { - if s.hs[i].HeaderType() == typ { - res = append(res, s.hs[i]) - } - } - - return res, true -} - -func (s testTypedHeader) Name() string { - return s.k -} - -func (s testTypedHeader) Value() string { - return s.v -} - -func (s testTypedHeader) HeaderType() acl.HeaderType { - return s.t -} - -func TestMatchFilters(t *testing.T) { - // nil TypedHeaderSource - require.Equal(t, mResMismatch, MatchFilters(nil, nil)) - - // empty HeaderFilter list - require.Equal(t, mResMatch, MatchFilters(new(testHeaderSrc), nil)) - - k := "key" - v := "value" - ht := acl.HeaderType(1) - - items := []struct { - // list of Key-Value-HeaderType for headers construction - hs []interface{} - // list of Key-Value-HeaderType-MatchType for filters construction - fs []interface{} - exp int - }{ - { // different HeaderType - hs: []interface{}{ - k, v, ht, - }, - fs: []interface{}{ - k, v, ht + 1, acl.StringEqual, - }, - exp: mResMismatch, - }, - { // different keys - hs: []interface{}{ - k, v, ht, - }, - fs: []interface{}{ - k + "1", v, ht, acl.StringEqual, - }, - exp: mResMismatch, - }, - { // equal values, StringEqual - hs: []interface{}{ - k, v, ht, - }, - fs: []interface{}{ - k, v, ht, acl.StringEqual, - }, - exp: mResMatch, - }, - { // equal values, StringNotEqual - hs: []interface{}{ - k, v, ht, - }, - fs: []interface{}{ - k, v, ht, acl.StringNotEqual, - }, - exp: mResMismatch, - }, - { // not equal values, StringEqual - hs: []interface{}{ - k, v, ht, - }, - fs: []interface{}{ - k, v + "1", ht, acl.StringEqual, - }, - exp: mResMismatch, - }, - { // not equal values, StringNotEqual - hs: []interface{}{ - k, v, ht, - }, - fs: []interface{}{ - k, v + "1", ht, acl.StringNotEqual, - }, - exp: mResMatch, - }, - { // one header, two filters - hs: []interface{}{ - k, v, ht, - }, - fs: []interface{}{ - k, v + "1", ht, acl.StringNotEqual, - k, v, ht, acl.StringEqual, - }, - exp: mResMatch, - }, - { // two headers, one filter - hs: []interface{}{ - k, v + "1", ht, - k, v, ht, - }, - fs: []interface{}{ - k, v, ht, acl.StringEqual, - }, - exp: mResMatch, - }, - { - hs: []interface{}{ - k, v + "1", acl.HdrTypeRequest, - k, v, acl.HdrTypeObjUsr, - }, - fs: []interface{}{ - k, v, acl.HdrTypeRequest, acl.StringNotEqual, - k, v, acl.HdrTypeObjUsr, acl.StringEqual, - }, - exp: mResMatch, - }, - } - - for _, item := range items { - headers := make([]acl.TypedHeader, 0) - - for i := 0; i < len(item.hs); i += 3 { - headers = append(headers, &testTypedHeader{ - t: item.hs[i+2].(acl.HeaderType), - k: item.hs[i].(string), - v: item.hs[i+1].(string), - }) - } - - filters := make([]acl.HeaderFilter, 0) - - for i := 0; i < len(item.fs); i += 4 { - filters = append(filters, &testHeaderFilter{ - TypedHeader: &testTypedHeader{ - t: item.fs[i+2].(acl.HeaderType), - k: item.fs[i].(string), - v: item.fs[i+1].(string), - }, - t: item.fs[i+3].(acl.MatchType), - }) - } - - require.Equal(t, - item.exp, - MatchFilters( - &testHeaderSrc{ - hs: headers, - }, - filters, - ), - ) - } -} diff --git a/lib/boot/bootstrap_test.go b/lib/boot/bootstrap_test.go deleted file mode 100644 index 206e2562..00000000 --- a/lib/boot/bootstrap_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package boot - -import ( - "testing" - - "github.com/nspcc-dev/neofs-api-go/bootstrap" - "github.com/stretchr/testify/require" -) - -func TestBootstrapPeerParams(t *testing.T) { - s := BootstrapPeerParams{} - - nodeInfo := &bootstrap.NodeInfo{ - Address: "address", - PubKey: []byte{1, 2, 3}, - Options: []string{ - "opt1", - "opt2", - }, - } - s.SetNodeInfo(nodeInfo) - - require.Equal(t, nodeInfo, s.NodeInfo()) -} diff --git a/lib/boot/bootstrapper.go b/lib/boot/bootstrapper.go deleted file mode 100644 index f97e6a78..00000000 --- a/lib/boot/bootstrapper.go +++ /dev/null @@ -1,31 +0,0 @@ -package boot - -import ( - "github.com/nspcc-dev/neofs-api-go/bootstrap" - "github.com/nspcc-dev/neofs-node/internal" -) - -// BootstrapPeerParams is a group of parameters -// for storage node bootstrap. -type BootstrapPeerParams struct { - info *bootstrap.NodeInfo -} - -// PeerBootstrapper is an interface of the NeoFS node bootstrap tool. -type PeerBootstrapper interface { - AddPeer(BootstrapPeerParams) error -} - -// ErrNilPeerBootstrapper is returned by functions that expect -// a non-nil PeerBootstrapper, but received nil. -const ErrNilPeerBootstrapper = internal.Error("peer bootstrapper is nil") - -// SetNodeInfo is a node info setter. -func (s *BootstrapPeerParams) SetNodeInfo(v *bootstrap.NodeInfo) { - s.info = v -} - -// NodeInfo is a node info getter. -func (s BootstrapPeerParams) NodeInfo() *bootstrap.NodeInfo { - return s.info -} diff --git a/lib/boot/storage.go b/lib/boot/storage.go deleted file mode 100644 index 9043576c..00000000 --- a/lib/boot/storage.go +++ /dev/null @@ -1,46 +0,0 @@ -package boot - -import ( - "context" - - "go.uber.org/zap" -) - -// StorageBootParams is a group of parameters -// for storage node bootstrap operation. -type StorageBootParams struct { - BootstrapPeerParams -} - -// StorageBootController is an entity that performs -// registration of a storage node in NeoFS network. -type StorageBootController struct { - peerBoot PeerBootstrapper - - bootPrm StorageBootParams - - log *zap.Logger -} - -// SetPeerBootstrapper is a PeerBootstrapper setter. -func (s *StorageBootController) SetPeerBootstrapper(v PeerBootstrapper) { - s.peerBoot = v -} - -// SetBootParams is a storage node bootstrap parameters setter. -func (s *StorageBootController) SetBootParams(v StorageBootParams) { - s.bootPrm = v -} - -// SetLogger is a logging component setter. -func (s *StorageBootController) SetLogger(v *zap.Logger) { - s.log = v -} - -// Bootstrap registers storage node in NeoFS system. -func (s StorageBootController) Bootstrap(context.Context) { - // register peer in NeoFS network - if err := s.peerBoot.AddPeer(s.bootPrm.BootstrapPeerParams); err != nil && s.log != nil { - s.log.Error("could not register storage node in network") - } -} diff --git a/lib/buckets/boltdb/methods_test.go b/lib/buckets/boltdb/methods_test.go deleted file mode 100644 index dc9517d7..00000000 --- a/lib/buckets/boltdb/methods_test.go +++ /dev/null @@ -1,95 +0,0 @@ -package boltdb - -import ( - "encoding/binary" - "io/ioutil" - "os" - "strings" - "testing" - "time" - - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/pkg/errors" - "github.com/spf13/viper" - "github.com/stretchr/testify/require" -) - -var config = strings.NewReader(` -storage: - test_bucket: - bucket: boltdb - path: ./temp/storage/test_bucket - perm: 0777 -`) - -func TestBucket(t *testing.T) { - file, err := ioutil.TempFile("", "test_bolt_db") - require.NoError(t, err) - require.NoError(t, file.Close()) - - v := viper.New() - require.NoError(t, v.ReadConfig(config)) - - // -- // - _, err = NewOptions("storage.test_bucket", v) - require.EqualError(t, err, errEmptyPath.Error()) - - v.SetDefault("storage.test_bucket.path", file.Name()) - v.SetDefault("storage.test_bucket.timeout", time.Millisecond*100) - // -- // - - opts, err := NewOptions("storage.test_bucket", v) - require.NoError(t, err) - - db, err := NewBucket(&opts) - require.NoError(t, err) - - require.NotPanics(t, func() { db.Size() }) - - var ( - count = uint64(10) - expected = []byte("test") - ) - - for i := uint64(0); i < count; i++ { - key := make([]byte, 8) - binary.BigEndian.PutUint64(key, i) - - require.False(t, db.Has(key)) - - val, err := db.Get(key) - require.EqualError(t, errors.Cause(err), core.ErrNotFound.Error()) - require.Empty(t, val) - - require.NoError(t, db.Set(key, expected)) - - require.True(t, db.Has(key)) - - val, err = db.Get(key) - require.NoError(t, err) - require.Equal(t, expected, val) - - keys, err := db.List() - require.NoError(t, err) - require.Len(t, keys, 1) - require.Equal(t, key, keys[0]) - - require.EqualError(t, db.Iterate(nil), core.ErrNilFilterHandler.Error()) - - items, err := core.ListBucketItems(db, func(_, _ []byte) bool { return true }) - require.NoError(t, err) - require.Len(t, items, 1) - require.Equal(t, key, items[0].Key) - require.Equal(t, val, items[0].Val) - - require.NoError(t, db.Del(key)) - require.False(t, db.Has(key)) - - val, err = db.Get(key) - require.EqualError(t, errors.Cause(err), core.ErrNotFound.Error()) - require.Empty(t, val) - } - - require.NoError(t, db.Close()) - require.NoError(t, os.RemoveAll(file.Name())) -} diff --git a/lib/buckets/boltdb/plugin/main.go b/lib/buckets/boltdb/plugin/main.go deleted file mode 100644 index 04a8f9f2..00000000 --- a/lib/buckets/boltdb/plugin/main.go +++ /dev/null @@ -1,25 +0,0 @@ -package main - -import ( - "github.com/nspcc-dev/neofs-node/lib/buckets/boltdb" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/pkg/errors" - "github.com/spf13/viper" -) - -var _ = PrepareBucket - -// PrepareBucket is interface method for bucket. -func PrepareBucket(name core.BucketType, v *viper.Viper) (db core.Bucket, err error) { - var opts boltdb.Options - - if opts, err = boltdb.NewOptions("storage."+name, v); err != nil { - err = errors.Wrapf(err, "%q: could not prepare options", name) - return - } else if db, err = boltdb.NewBucket(&opts); err != nil { - err = errors.Wrapf(err, "%q: could not prepare bucket", name) - return - } - - return -} diff --git a/lib/buckets/init.go b/lib/buckets/init.go deleted file mode 100644 index ea4c5756..00000000 --- a/lib/buckets/init.go +++ /dev/null @@ -1,64 +0,0 @@ -package buckets - -import ( - "plugin" - "strings" - - "github.com/nspcc-dev/neofs-node/lib/buckets/boltdb" - "github.com/nspcc-dev/neofs-node/lib/buckets/fsbucket" - "github.com/nspcc-dev/neofs-node/lib/buckets/inmemory" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/pkg/errors" - "github.com/spf13/viper" - "go.uber.org/zap" -) - -const ( - // BoltDBBucket is a name of BoltDB bucket. - BoltDBBucket = "boltdb" - - // InMemoryBucket is a name RAM bucket. - InMemoryBucket = "in-memory" - - // FileSystemBucket is a name of file system bucket. - FileSystemBucket = "fsbucket" - - bucketSymbol = "PrepareBucket" -) - -// NewBucket is a bucket's constructor. -func NewBucket(name core.BucketType, l *zap.Logger, v *viper.Viper) (core.Bucket, error) { - bucket := v.GetString("storage." + string(name) + ".bucket") - - l.Info("initialize bucket", - zap.String("name", string(name)), - zap.String("bucket", bucket)) - - switch strings.ToLower(bucket) { - case FileSystemBucket: - return fsbucket.NewBucket(name, v) - - case InMemoryBucket: - return inmemory.NewBucket(name, v), nil - - case BoltDBBucket: - opts, err := boltdb.NewOptions("storage."+name, v) - if err != nil { - return nil, err - } - - return boltdb.NewBucket(&opts) - default: - instance, err := plugin.Open(bucket) - if err != nil { - return nil, errors.Wrapf(err, "could not load bucket: `%s`", bucket) - } - - sym, err := instance.Lookup(bucketSymbol) - if err != nil { - return nil, errors.Wrapf(err, "could not find bucket signature: `%s`", bucket) - } - - return sym.(func(core.BucketType, *viper.Viper) (core.Bucket, error))(name, v) - } -} diff --git a/lib/buckets/inmemory/bucket.go b/lib/buckets/inmemory/bucket.go deleted file mode 100644 index b5f48316..00000000 --- a/lib/buckets/inmemory/bucket.go +++ /dev/null @@ -1,60 +0,0 @@ -package inmemory - -import ( - "sync" - - "github.com/mr-tron/base58" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/spf13/viper" -) - -type ( - bucket struct { - *sync.RWMutex - items map[string][]byte - } -) - -const ( - defaultCapacity = 100 -) - -var ( - _ core.Bucket = (*bucket)(nil) - - // for in usage - _ = NewBucket -) - -func stringifyKey(key []byte) string { - return base58.Encode(key) -} - -func decodeKey(key string) []byte { - k, err := base58.Decode(key) - if err != nil { - panic(err) // it can fail only for not base58 strings - } - - return k -} - -func makeCopy(val []byte) []byte { - tmp := make([]byte, len(val)) - copy(tmp, val) - - return tmp -} - -// NewBucket creates new in-memory bucket instance. -func NewBucket(name core.BucketType, v *viper.Viper) core.Bucket { - var capacity int - if capacity = v.GetInt("storage." + string(name) + ".capacity"); capacity <= 0 { - capacity = defaultCapacity - } - - return &bucket{ - RWMutex: new(sync.RWMutex), - items: make(map[string][]byte, capacity), - } -} diff --git a/lib/buckets/inmemory/methods.go b/lib/buckets/inmemory/methods.go deleted file mode 100644 index 7e1685c7..00000000 --- a/lib/buckets/inmemory/methods.go +++ /dev/null @@ -1,107 +0,0 @@ -package inmemory - -import ( - "unsafe" - - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/pkg/errors" -) - -// Get value by key. -func (b *bucket) Get(key []byte) ([]byte, error) { - k := stringifyKey(key) - - b.RLock() - val, ok := b.items[k] - result := makeCopy(val) - b.RUnlock() - - if !ok { - return nil, errors.Wrapf(core.ErrNotFound, "key=`%s`", k) - } - - return result, nil -} - -// Set value by key. -func (b *bucket) Set(key, value []byte) error { - k := stringifyKey(key) - - b.Lock() - b.items[k] = makeCopy(value) - b.Unlock() - - return nil -} - -// Del value by key. -func (b *bucket) Del(key []byte) error { - k := stringifyKey(key) - - b.Lock() - delete(b.items, k) - b.Unlock() - - return nil -} - -// Has checks key exists. -func (b *bucket) Has(key []byte) bool { - k := stringifyKey(key) - - b.RLock() - _, ok := b.items[k] - b.RUnlock() - - return ok -} - -// Size size of bucket. -func (b *bucket) Size() int64 { - b.RLock() - // TODO we must replace in future - size := unsafe.Sizeof(b.items) - b.RUnlock() - - return int64(size) -} - -func (b *bucket) List() ([][]byte, error) { - var result = make([][]byte, 0) - - b.RLock() - for key := range b.items { - result = append(result, decodeKey(key)) - } - b.RUnlock() - - return result, nil -} - -// Filter items by closure. -func (b *bucket) Iterate(handler core.FilterHandler) error { - if handler == nil { - return core.ErrNilFilterHandler - } - - b.RLock() - for key, val := range b.items { - k, v := decodeKey(key), makeCopy(val) - - if !handler(k, v) { - return core.ErrIteratingAborted - } - } - b.RUnlock() - - return nil -} - -// Close bucket (just empty). -func (b *bucket) Close() error { - b.Lock() - b.items = make(map[string][]byte) - b.Unlock() - - return nil -} diff --git a/lib/container/alias.go b/lib/container/alias.go deleted file mode 100644 index cb2cdf3c..00000000 --- a/lib/container/alias.go +++ /dev/null @@ -1,15 +0,0 @@ -package container - -import ( - "github.com/nspcc-dev/neofs-api-go/container" - "github.com/nspcc-dev/neofs-api-go/refs" -) - -// Container is a type alias of Container. -type Container = container.Container - -// CID is a type alias of CID. -type CID = refs.CID - -// OwnerID is a type alias of OwnerID. -type OwnerID = refs.OwnerID diff --git a/lib/container/storage.go b/lib/container/storage.go deleted file mode 100644 index 5192a3b2..00000000 --- a/lib/container/storage.go +++ /dev/null @@ -1,134 +0,0 @@ -package container - -import ( - "context" -) - -// GetParams is a group of parameters for container receiving operation. -type GetParams struct { - ctxValue - - cidValue -} - -// GetResult is a group of values returned by container receiving operation. -type GetResult struct { - cnrValue -} - -// PutParams is a group of parameters for container storing operation. -type PutParams struct { - ctxValue - - cnrValue -} - -// PutResult is a group of values returned by container storing operation. -type PutResult struct { - cidValue -} - -// DeleteParams is a group of parameters for container removal operation. -type DeleteParams struct { - ctxValue - - cidValue - - ownerID OwnerID -} - -// DeleteResult is a group of values returned by container removal operation. -type DeleteResult struct{} - -// ListParams is a group of parameters for container listing operation. -type ListParams struct { - ctxValue - - ownerIDList []OwnerID -} - -// ListResult is a group of values returned by container listing operation. -type ListResult struct { - cidList []CID -} - -type cnrValue struct { - cnr *Container -} - -type cidValue struct { - cid CID -} - -type ctxValue struct { - ctx context.Context -} - -// Storage is an interface of the storage of NeoFS containers. -type Storage interface { - GetContainer(GetParams) (*GetResult, error) - PutContainer(PutParams) (*PutResult, error) - DeleteContainer(DeleteParams) (*DeleteResult, error) - ListContainers(ListParams) (*ListResult, error) - // TODO: add EACL methods -} - -// Context is a context getter. -func (s ctxValue) Context() context.Context { - return s.ctx -} - -// SetContext is a context setter. -func (s *ctxValue) SetContext(v context.Context) { - s.ctx = v -} - -// CID is a container ID getter. -func (s cidValue) CID() CID { - return s.cid -} - -// SetCID is a container ID getter. -func (s *cidValue) SetCID(v CID) { - s.cid = v -} - -// Container is a container getter. -func (s cnrValue) Container() *Container { - return s.cnr -} - -// SetContainer is a container setter. -func (s *cnrValue) SetContainer(v *Container) { - s.cnr = v -} - -// OwnerID is an owner ID getter. -func (s DeleteParams) OwnerID() OwnerID { - return s.ownerID -} - -// SetOwnerID is an owner ID setter. -func (s *DeleteParams) SetOwnerID(v OwnerID) { - s.ownerID = v -} - -// OwnerIDList is an owner ID list getter. -func (s ListParams) OwnerIDList() []OwnerID { - return s.ownerIDList -} - -// SetOwnerIDList is an owner ID list setter. -func (s *ListParams) SetOwnerIDList(v ...OwnerID) { - s.ownerIDList = v -} - -// CIDList is a container ID list getter. -func (s ListResult) CIDList() []CID { - return s.cidList -} - -// SetCIDList is a container ID list setter. -func (s *ListResult) SetCIDList(v []CID) { - s.cidList = v -} diff --git a/lib/container/storage_test.go b/lib/container/storage_test.go deleted file mode 100644 index 77f38651..00000000 --- a/lib/container/storage_test.go +++ /dev/null @@ -1,83 +0,0 @@ -package container - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestGetParams(t *testing.T) { - p := new(GetParams) - - cid := CID{1, 2, 3} - p.SetCID(cid) - - require.Equal(t, cid, p.CID()) -} - -func TestGetResult(t *testing.T) { - r := new(GetResult) - - cnr := &Container{ - OwnerID: OwnerID{1, 2, 3}, - } - r.SetContainer(cnr) - - require.Equal(t, cnr, r.Container()) -} - -func TestPutParams(t *testing.T) { - p := new(PutParams) - - cnr := &Container{ - OwnerID: OwnerID{1, 2, 3}, - } - p.SetContainer(cnr) - - require.Equal(t, cnr, p.Container()) -} - -func TestPutResult(t *testing.T) { - r := new(PutResult) - - cid := CID{1, 2, 3} - r.SetCID(cid) - - require.Equal(t, cid, r.CID()) -} - -func TestDeleteParams(t *testing.T) { - p := new(DeleteParams) - - ownerID := OwnerID{1, 2, 3} - p.SetOwnerID(ownerID) - require.Equal(t, ownerID, p.OwnerID()) - - cid := CID{4, 5, 6} - p.SetCID(cid) - require.Equal(t, cid, p.CID()) -} - -func TestListParams(t *testing.T) { - p := new(ListParams) - - ownerIDList := []OwnerID{ - {1, 2, 3}, - {4, 5, 6}, - } - p.SetOwnerIDList(ownerIDList...) - - require.Equal(t, ownerIDList, p.OwnerIDList()) -} - -func TestListResult(t *testing.T) { - r := new(ListResult) - - cidList := []CID{ - {1, 2, 3}, - {4, 5, 6}, - } - r.SetCIDList(cidList) - - require.Equal(t, cidList, r.CIDList()) -} diff --git a/lib/core/storage.go b/lib/core/storage.go deleted file mode 100644 index 27e22f6d..00000000 --- a/lib/core/storage.go +++ /dev/null @@ -1,94 +0,0 @@ -package core - -import ( - "github.com/nspcc-dev/neofs-node/internal" - "github.com/pkg/errors" -) - -type ( - // BucketType is name of bucket - BucketType string - - // FilterHandler where you receive key/val in your closure - FilterHandler func(key, val []byte) bool - - // BucketItem used in filter - BucketItem struct { - Key []byte - Val []byte - } - - // Bucket is sub-store interface - Bucket interface { - Get(key []byte) ([]byte, error) - Set(key, value []byte) error - Del(key []byte) error - Has(key []byte) bool - Size() int64 - List() ([][]byte, error) - Iterate(FilterHandler) error - // Steam can be implemented by badger.Stream, but not for now - // Stream(ctx context.Context, key []byte, cb func(io.ReadWriter) error) error - Close() error - } - - // Storage component interface - Storage interface { - GetBucket(name BucketType) (Bucket, error) - Size() int64 - Close() error - } -) - -const ( - // BlobStore is a blob bucket name. - BlobStore BucketType = "blob" - - // MetaStore is a meta bucket name. - MetaStore BucketType = "meta" - - // SpaceMetricsStore is a space metrics bucket name. - SpaceMetricsStore BucketType = "space-metrics" -) - -var ( - // ErrNilFilterHandler when FilterHandler is empty - ErrNilFilterHandler = errors.New("handler can't be nil") - - // ErrNotFound is returned by key-value storage methods - // that could not find element by key. - ErrNotFound = internal.Error("key not found") -) - -// ErrIteratingAborted is returned by storage iterator -// after iteration has been interrupted. -var ErrIteratingAborted = errors.New("iteration aborted") - -var errEmptyBucket = errors.New("empty bucket") - -func (t BucketType) String() string { return string(t) } - -// ListBucketItems performs iteration over Bucket and returns the full list of its items. -func ListBucketItems(b Bucket, h FilterHandler) ([]BucketItem, error) { - if b == nil { - return nil, errEmptyBucket - } else if h == nil { - return nil, ErrNilFilterHandler - } - - items := make([]BucketItem, 0) - - if err := b.Iterate(func(key, val []byte) bool { - if h(key, val) { - items = append(items, BucketItem{ - Key: key, - Val: val, - }) - } - return true - }); err != nil { - return nil, err - } - - return items, nil -} diff --git a/lib/core/storage_test.go b/lib/core/storage_test.go deleted file mode 100644 index a4b45111..00000000 --- a/lib/core/storage_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package core - -import ( - "crypto/rand" - "testing" - - "github.com/stretchr/testify/require" -) - -type testBucket struct { - Bucket - - items []BucketItem -} - -func (s *testBucket) Iterate(f FilterHandler) error { - for i := range s.items { - if !f(s.items[i].Key, s.items[i].Val) { - return ErrIteratingAborted - } - } - - return nil -} - -func TestListBucketItems(t *testing.T) { - _, err := ListBucketItems(nil, nil) - require.EqualError(t, err, errEmptyBucket.Error()) - - b := new(testBucket) - - _, err = ListBucketItems(b, nil) - require.EqualError(t, err, ErrNilFilterHandler.Error()) - - var ( - count = 10 - ln = 10 - items = make([]BucketItem, 0, count) - ) - - for i := 0; i < count; i++ { - items = append(items, BucketItem{ - Key: testData(t, ln), - Val: testData(t, ln), - }) - } - - b.items = items - - res, err := ListBucketItems(b, func(key, val []byte) bool { return true }) - require.NoError(t, err) - require.Equal(t, items, res) - - res, err = ListBucketItems(b, func(key, val []byte) bool { return false }) - require.NoError(t, err) - require.Empty(t, res) -} - -func testData(t *testing.T, sz int) []byte { - d := make([]byte, sz) - _, err := rand.Read(d) - require.NoError(t, err) - - return d -} diff --git a/lib/implementations/acl.go b/lib/implementations/acl.go deleted file mode 100644 index ce3fd58a..00000000 --- a/lib/implementations/acl.go +++ /dev/null @@ -1,392 +0,0 @@ -package implementations - -import ( - "context" - - sc "github.com/nspcc-dev/neo-go/pkg/smartcontract" - libacl "github.com/nspcc-dev/neofs-api-go/acl" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/acl" - "github.com/nspcc-dev/neofs-node/lib/blockchain/goclient" - "github.com/nspcc-dev/neofs-node/lib/container" - - "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/pkg/errors" -) - -// Consider moving ACLHelper implementation to the ACL library. - -type ( - // ACLHelper is an interface, that provides useful functions - // for ACL object pre-processor. - ACLHelper interface { - BasicACLGetter - ContainerOwnerChecker - } - - // BasicACLGetter helper provides function to return basic ACL value. - BasicACLGetter interface { - GetBasicACL(context.Context, CID) (uint32, error) - } - - // ContainerOwnerChecker checks owner of the container. - ContainerOwnerChecker interface { - IsContainerOwner(context.Context, CID, refs.OwnerID) (bool, error) - } - - aclHelper struct { - cnr container.Storage - } -) - -type binaryEACLSource struct { - binaryStore acl.BinaryExtendedACLSource -} - -// StaticContractClient is a wrapper over Neo:Morph client -// that invokes single smart contract methods with fixed fee. -type StaticContractClient struct { - // neo-go client instance - client *goclient.Client - - // contract script-hash - scScriptHash util.Uint160 - - // invocation fee - fee util.Fixed8 -} - -// MorphContainerContract is a wrapper over StaticContractClient -// for Container contract calls. -type MorphContainerContract struct { - // NeoFS Container smart-contract - containerContract StaticContractClient - - // set EACL method name of container contract - eaclSetMethodName string - - // get EACL method name of container contract - eaclGetMethodName string - - // get container method name of container contract - cnrGetMethodName string - - // put container method name of container contract - cnrPutMethodName string - - // delete container method name of container contract - cnrDelMethodName string - - // list containers method name of container contract - cnrListMethodName string -} - -const ( - errNewACLHelper = internal.Error("cannot create ACLHelper instance") -) - -// GetBasicACL returns basic ACL of the container. -func (h aclHelper) GetBasicACL(ctx context.Context, cid CID) (uint32, error) { - gp := container.GetParams{} - gp.SetContext(ctx) - gp.SetCID(cid) - - gResp, err := h.cnr.GetContainer(gp) - if err != nil { - return 0, err - } - - return gResp.Container().BasicACL, nil -} - -// IsContainerOwner returns true if provided id is an owner container. -func (h aclHelper) IsContainerOwner(ctx context.Context, cid CID, id refs.OwnerID) (bool, error) { - gp := container.GetParams{} - gp.SetContext(ctx) - gp.SetCID(cid) - - gResp, err := h.cnr.GetContainer(gp) - if err != nil { - return false, err - } - - return gResp.Container().OwnerID.Equal(id), nil -} - -// NewACLHelper returns implementation of the ACLHelper interface. -func NewACLHelper(cnr container.Storage) (ACLHelper, error) { - if cnr == nil { - return nil, errNewACLHelper - } - - return aclHelper{cnr}, nil -} - -// ExtendedACLSourceFromBinary wraps BinaryExtendedACLSource and returns ExtendedACLSource. -// -// If passed BinaryExtendedACLSource is nil, acl.ErrNilBinaryExtendedACLStore returns. -func ExtendedACLSourceFromBinary(v acl.BinaryExtendedACLSource) (acl.ExtendedACLSource, error) { - if v == nil { - return nil, acl.ErrNilBinaryExtendedACLStore - } - - return &binaryEACLSource{ - binaryStore: v, - }, nil -} - -// GetExtendedACLTable receives eACL table in a binary representation from storage, -// unmarshals it and returns ExtendedACLTable interface. -func (s binaryEACLSource) GetExtendedACLTable(ctx context.Context, cid refs.CID) (libacl.ExtendedACLTable, error) { - key := acl.BinaryEACLKey{} - key.SetCID(cid) - - val, err := s.binaryStore.GetBinaryEACL(ctx, key) - if err != nil { - return nil, err - } - - eacl := val.EACL() - - // TODO: verify signature - - res := libacl.WrapEACLTable(nil) - - return res, res.UnmarshalBinary(eacl) -} - -// NewStaticContractClient initializes a new StaticContractClient. -// -// If passed Client is nil, goclient.ErrNilClient returns. -func NewStaticContractClient(client *goclient.Client, scHash util.Uint160, fee util.Fixed8) (StaticContractClient, error) { - res := StaticContractClient{ - client: client, - scScriptHash: scHash, - fee: fee, - } - - var err error - if client == nil { - err = goclient.ErrNilClient - } - - return res, err -} - -// Invoke calls Invoke method of goclient with predefined script hash and fee. -// Supported args types are the same as in goclient. -// -// If Client is not initialized, goclient.ErrNilClient returns. -func (s StaticContractClient) Invoke(method string, args ...interface{}) error { - if s.client == nil { - return goclient.ErrNilClient - } - - return s.client.Invoke( - s.scScriptHash, - s.fee, - method, - args..., - ) -} - -// TestInvoke calls TestInvoke method of goclient with predefined script hash. -// -// If Client is not initialized, goclient.ErrNilClient returns. -func (s StaticContractClient) TestInvoke(method string, args ...interface{}) ([]sc.Parameter, error) { - if s.client == nil { - return nil, goclient.ErrNilClient - } - - return s.client.TestInvoke( - s.scScriptHash, - method, - args..., - ) -} - -// SetContainerContractClient is a container contract client setter. -func (s *MorphContainerContract) SetContainerContractClient(v StaticContractClient) { - s.containerContract = v -} - -// SetEACLGetMethodName is a container contract Get EACL method name setter. -func (s *MorphContainerContract) SetEACLGetMethodName(v string) { - s.eaclGetMethodName = v -} - -// SetEACLSetMethodName is a container contract Set EACL method name setter. -func (s *MorphContainerContract) SetEACLSetMethodName(v string) { - s.eaclSetMethodName = v -} - -// SetContainerGetMethodName is a container contract Get method name setter. -func (s *MorphContainerContract) SetContainerGetMethodName(v string) { - s.cnrGetMethodName = v -} - -// SetContainerPutMethodName is a container contract Put method name setter. -func (s *MorphContainerContract) SetContainerPutMethodName(v string) { - s.cnrPutMethodName = v -} - -// SetContainerDeleteMethodName is a container contract Delete method name setter. -func (s *MorphContainerContract) SetContainerDeleteMethodName(v string) { - s.cnrDelMethodName = v -} - -// SetContainerListMethodName is a container contract List method name setter. -func (s *MorphContainerContract) SetContainerListMethodName(v string) { - s.cnrListMethodName = v -} - -// GetBinaryEACL performs the test invocation call of GetEACL method of NeoFS Container contract. -func (s *MorphContainerContract) GetBinaryEACL(_ context.Context, key acl.BinaryEACLKey) (acl.BinaryEACLValue, error) { - res := acl.BinaryEACLValue{} - - prms, err := s.containerContract.TestInvoke( - s.eaclGetMethodName, - key.CID().Bytes(), - ) - if err != nil { - return res, err - } else if ln := len(prms); ln != 1 { - return res, errors.Errorf("unexpected stack parameter count: %d", ln) - } - - eacl, err := goclient.BytesFromStackParameter(prms[0]) - if err == nil { - res.SetEACL(eacl) - } - - return res, err -} - -// PutBinaryEACL invokes the call of SetEACL method of NeoFS Container contract. -func (s *MorphContainerContract) PutBinaryEACL(_ context.Context, key acl.BinaryEACLKey, val acl.BinaryEACLValue) error { - return s.containerContract.Invoke( - s.eaclSetMethodName, - key.CID().Bytes(), - val.EACL(), - val.Signature(), - ) -} - -// GetContainer performs the test invocation call of Get method of NeoFS Container contract. -func (s *MorphContainerContract) GetContainer(p container.GetParams) (*container.GetResult, error) { - prms, err := s.containerContract.TestInvoke( - s.cnrGetMethodName, - p.CID().Bytes(), - ) - if err != nil { - return nil, errors.Wrap(err, "could not perform test invocation") - } else if ln := len(prms); ln != 1 { - return nil, errors.Errorf("unexpected stack item count: %d", ln) - } - - cnrBytes, err := goclient.BytesFromStackParameter(prms[0]) - if err != nil { - return nil, errors.Wrap(err, "could not get byte array from stack item") - } - - cnr := new(container.Container) - if err := cnr.Unmarshal(cnrBytes); err != nil { - return nil, errors.Wrap(err, "could not unmarshal container from bytes") - } - - res := new(container.GetResult) - res.SetContainer(cnr) - - return res, nil -} - -// PutContainer invokes the call of Put method of NeoFS Container contract. -func (s *MorphContainerContract) PutContainer(p container.PutParams) (*container.PutResult, error) { - cnr := p.Container() - - cid, err := cnr.ID() - if err != nil { - return nil, errors.Wrap(err, "could not calculate container ID") - } - - cnrBytes, err := cnr.Marshal() - if err != nil { - return nil, errors.Wrap(err, "could not marshal container") - } - - if err := s.containerContract.Invoke( - s.cnrPutMethodName, - cnr.OwnerID.Bytes(), - cnrBytes, - []byte{}, - ); err != nil { - return nil, errors.Wrap(err, "could not invoke contract method") - } - - res := new(container.PutResult) - res.SetCID(cid) - - return res, nil -} - -// DeleteContainer invokes the call of Delete method of NeoFS Container contract. -func (s *MorphContainerContract) DeleteContainer(p container.DeleteParams) (*container.DeleteResult, error) { - if err := s.containerContract.Invoke( - s.cnrDelMethodName, - p.CID().Bytes(), - p.OwnerID().Bytes(), - []byte{}, - ); err != nil { - return nil, errors.Wrap(err, "could not invoke contract method") - } - - return new(container.DeleteResult), nil -} - -// ListContainers performs the test invocation call of Get method of NeoFS Container contract. -// -// If owner ID list in parameters is non-empty, bytes of first owner are attached to call. -func (s *MorphContainerContract) ListContainers(p container.ListParams) (*container.ListResult, error) { - args := make([]interface{}, 0, 1) - - if ownerIDList := p.OwnerIDList(); len(ownerIDList) > 0 { - args = append(args, ownerIDList[0].Bytes()) - } - - prms, err := s.containerContract.TestInvoke( - s.cnrListMethodName, - args..., - ) - if err != nil { - return nil, errors.Wrap(err, "could not perform test invocation") - } else if ln := len(prms); ln != 1 { - return nil, errors.Errorf("unexpected stack item count: %d", ln) - } - - prms, err = goclient.ArrayFromStackParameter(prms[0]) - if err != nil { - return nil, errors.Wrap(err, "could not get stack item array from stack item") - } - - cidList := make([]CID, 0, len(prms)) - - for i := range prms { - cidBytes, err := goclient.BytesFromStackParameter(prms[i]) - if err != nil { - return nil, errors.Wrap(err, "could not get byte array from stack item") - } - - cid, err := refs.CIDFromBytes(cidBytes) - if err != nil { - return nil, errors.Wrap(err, "could not get container ID from bytes") - } - - cidList = append(cidList, cid) - } - - res := new(container.ListResult) - res.SetCIDList(cidList) - - return res, nil -} diff --git a/lib/implementations/acl_test.go b/lib/implementations/acl_test.go deleted file mode 100644 index cb462de7..00000000 --- a/lib/implementations/acl_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package implementations - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestStaticContractClient(t *testing.T) { - s := new(StaticContractClient) - - require.NotPanics(t, func() { - _, _ = s.TestInvoke("") - }) - - require.NotPanics(t, func() { - _ = s.Invoke("") - }) -} diff --git a/lib/implementations/balance.go b/lib/implementations/balance.go deleted file mode 100644 index d535c0ea..00000000 --- a/lib/implementations/balance.go +++ /dev/null @@ -1,141 +0,0 @@ -package implementations - -import ( - "github.com/nspcc-dev/neo-go/pkg/encoding/address" - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-node/lib/blockchain/goclient" - "github.com/pkg/errors" -) - -// MorphBalanceContract is a wrapper over NeoFS Balance contract client -// that provides an interface of manipulations with user funds. -type MorphBalanceContract struct { - // NeoFS Balance smart-contract - balanceContract StaticContractClient - - // "balance of" method name of balance contract - balanceOfMethodName string - - // decimals method name of balance contract - decimalsMethodName string -} - -// BalanceOfParams is a structure that groups the parameters -// for NeoFS user balance receiving operation. -type BalanceOfParams struct { - owner refs.OwnerID -} - -// BalanceOfResult is a structure that groups the values -// of the result of NeoFS user balance receiving operation. -type BalanceOfResult struct { - amount int64 -} - -// DecimalsParams is a structure that groups the parameters -// for NeoFS token decimals receiving operation. -type DecimalsParams struct { -} - -// DecimalsResult is a structure that groups the values -// of the result of NeoFS token decimals receiving operation. -type DecimalsResult struct { - dec int64 -} - -// SetBalanceContractClient is a Balance contract client setter. -func (s *MorphBalanceContract) SetBalanceContractClient(v StaticContractClient) { - s.balanceContract = v -} - -// SetBalanceOfMethodName is a Balance contract balanceOf method name setter. -func (s *MorphBalanceContract) SetBalanceOfMethodName(v string) { - s.balanceOfMethodName = v -} - -// SetDecimalsMethodName is a Balance contract decimals method name setter. -func (s *MorphBalanceContract) SetDecimalsMethodName(v string) { - s.decimalsMethodName = v -} - -// BalanceOf performs the test invocation call of balanceOf method of NeoFS Balance contract. -func (s MorphBalanceContract) BalanceOf(p BalanceOfParams) (*BalanceOfResult, error) { - owner := p.OwnerID() - - u160, err := address.StringToUint160(owner.String()) - if err != nil { - return nil, errors.Wrap(err, "could not convert wallet address to Uint160") - } - - prms, err := s.balanceContract.TestInvoke( - s.balanceOfMethodName, - u160.BytesBE(), - ) - if err != nil { - return nil, errors.Wrap(err, "could not perform test invocation") - } else if ln := len(prms); ln != 1 { - return nil, errors.Errorf("unexpected stack item count (balanceOf): %d", ln) - } - - amount, err := goclient.IntFromStackParameter(prms[0]) - if err != nil { - return nil, errors.Wrap(err, "could not get integer stack item from stack item (amount)") - } - - res := new(BalanceOfResult) - res.SetAmount(amount) - - return res, nil -} - -// Decimals performs the test invocation call of decimals method of NeoFS Balance contract. -func (s MorphBalanceContract) Decimals(DecimalsParams) (*DecimalsResult, error) { - prms, err := s.balanceContract.TestInvoke( - s.decimalsMethodName, - ) - if err != nil { - return nil, errors.Wrap(err, "could not perform test invocation") - } else if ln := len(prms); ln != 1 { - return nil, errors.Errorf("unexpected stack item count (decimals): %d", ln) - } - - dec, err := goclient.IntFromStackParameter(prms[0]) - if err != nil { - return nil, errors.Wrap(err, "could not get integer stack item from stack item (decimal)") - } - - res := new(DecimalsResult) - res.SetDecimals(dec) - - return res, nil -} - -// SetOwnerID is an owner ID setter. -func (s *BalanceOfParams) SetOwnerID(v refs.OwnerID) { - s.owner = v -} - -// OwnerID is an owner ID getter. -func (s BalanceOfParams) OwnerID() refs.OwnerID { - return s.owner -} - -// SetAmount is an funds amount setter. -func (s *BalanceOfResult) SetAmount(v int64) { - s.amount = v -} - -// Amount is an funds amount getter. -func (s BalanceOfResult) Amount() int64 { - return s.amount -} - -// SetDecimals is a decimals setter. -func (s *DecimalsResult) SetDecimals(v int64) { - s.dec = v -} - -// Decimals is a decimals getter. -func (s DecimalsResult) Decimals() int64 { - return s.dec -} diff --git a/lib/implementations/balance_test.go b/lib/implementations/balance_test.go deleted file mode 100644 index c9b571c8..00000000 --- a/lib/implementations/balance_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package implementations - -import ( - "testing" - - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/stretchr/testify/require" -) - -func TestBalanceOfParams(t *testing.T) { - s := BalanceOfParams{} - - owner := refs.OwnerID{1, 2, 3} - s.SetOwnerID(owner) - - require.Equal(t, owner, s.OwnerID()) -} - -func TestBalanceOfResult(t *testing.T) { - s := BalanceOfResult{} - - amount := int64(100) - s.SetAmount(amount) - - require.Equal(t, amount, s.Amount()) -} - -func TestDecimalsResult(t *testing.T) { - s := DecimalsResult{} - - dec := int64(100) - s.SetDecimals(dec) - - require.Equal(t, dec, s.Decimals()) -} diff --git a/lib/implementations/bootstrap.go b/lib/implementations/bootstrap.go deleted file mode 100644 index 45896752..00000000 --- a/lib/implementations/bootstrap.go +++ /dev/null @@ -1,311 +0,0 @@ -package implementations - -import ( - "github.com/nspcc-dev/neo-go/pkg/smartcontract" - "github.com/nspcc-dev/neofs-api-go/bootstrap" - "github.com/nspcc-dev/neofs-node/lib/blockchain/goclient" - "github.com/nspcc-dev/neofs-node/lib/boot" - "github.com/nspcc-dev/neofs-node/lib/ir" - "github.com/nspcc-dev/neofs-node/lib/netmap" - "github.com/pkg/errors" -) - -// MorphNetmapContract is a wrapper over NeoFS Netmap contract client -// that provides an interface of network map manipulations. -type MorphNetmapContract struct { - // NeoFS Netmap smart-contract - netmapContract StaticContractClient - - // add peer method name of netmap contract - addPeerMethodName string - - // new epoch method name of netmap contract - newEpochMethodName string - - // get netmap method name of netmap contract - getNetMapMethodName string - - // update state method name of netmap contract - updStateMethodName string - - // IR list method name of netmap contract - irListMethodName string -} - -// UpdateEpochParams is a structure that groups the parameters -// for NeoFS epoch number updating. -type UpdateEpochParams struct { - epoch uint64 -} - -// UpdateStateParams is a structure that groups the parameters -// for NeoFS node state updating. -type UpdateStateParams struct { - st NodeState - - key []byte -} - -// NodeState is a type of node states enumeration. -type NodeState int64 - -const ( - _ NodeState = iota - - // StateOffline is an offline node state value. - StateOffline -) - -const addPeerFixedArgNumber = 2 - -const nodeInfoFixedPrmNumber = 3 - -// SetNetmapContractClient is a Netmap contract client setter. -func (s *MorphNetmapContract) SetNetmapContractClient(v StaticContractClient) { - s.netmapContract = v -} - -// SetAddPeerMethodName is a Netmap contract AddPeer method name setter. -func (s *MorphNetmapContract) SetAddPeerMethodName(v string) { - s.addPeerMethodName = v -} - -// SetNewEpochMethodName is a Netmap contract NewEpoch method name setter. -func (s *MorphNetmapContract) SetNewEpochMethodName(v string) { - s.newEpochMethodName = v -} - -// SetNetMapMethodName is a Netmap contract Netmap method name setter. -func (s *MorphNetmapContract) SetNetMapMethodName(v string) { - s.getNetMapMethodName = v -} - -// SetUpdateStateMethodName is a Netmap contract UpdateState method name setter. -func (s *MorphNetmapContract) SetUpdateStateMethodName(v string) { - s.updStateMethodName = v -} - -// SetIRListMethodName is a Netmap contract InnerRingList method name setter. -func (s *MorphNetmapContract) SetIRListMethodName(v string) { - s.irListMethodName = v -} - -// AddPeer invokes the call of AddPeer method of NeoFS Netmap contract. -func (s *MorphNetmapContract) AddPeer(p boot.BootstrapPeerParams) error { - info := p.NodeInfo() - opts := info.GetOptions() - - args := make([]interface{}, 0, addPeerFixedArgNumber+len(opts)) - - args = append(args, - // Address - []byte(info.GetAddress()), - - // Public key - info.GetPubKey(), - ) - - // Options - for i := range opts { - args = append(args, []byte(opts[i])) - } - - return s.netmapContract.Invoke( - s.addPeerMethodName, - args..., - ) -} - -// UpdateEpoch invokes the call of NewEpoch method of NeoFS Netmap contract. -func (s *MorphNetmapContract) UpdateEpoch(p UpdateEpochParams) error { - return s.netmapContract.Invoke( - s.newEpochMethodName, - int64(p.Number()), // TODO: do not cast after uint64 type will become supported in client - ) -} - -// GetNetMap performs the test invocation call of Netmap method of NeoFS Netmap contract. -func (s *MorphNetmapContract) GetNetMap(p netmap.GetParams) (*netmap.GetResult, error) { - prms, err := s.netmapContract.TestInvoke( - s.getNetMapMethodName, - ) - if err != nil { - return nil, errors.Wrap(err, "could not perform test invocation") - } else if ln := len(prms); ln != 1 { - return nil, errors.Errorf("unexpected stack item count (Nodes): %d", ln) - } - - prms, err = goclient.ArrayFromStackParameter(prms[0]) - if err != nil { - return nil, errors.Wrap(err, "could not get stack item array from stack item (Nodes)") - } - - nm := netmap.NewNetmap() - - for i := range prms { - nodeInfo, err := nodeInfoFromStackItem(prms[i]) - if err != nil { - return nil, errors.Wrapf(err, "could not parse stack item (Node #%d)", i) - } - - if err := nm.AddNode(nodeInfo); err != nil { - return nil, errors.Wrapf(err, "could not add node #%d to network map", i) - } - } - - res := new(netmap.GetResult) - res.SetNetMap(nm) - - return res, nil -} - -func nodeInfoFromStackItem(prm smartcontract.Parameter) (*bootstrap.NodeInfo, error) { - prms, err := goclient.ArrayFromStackParameter(prm) - if err != nil { - return nil, errors.Wrapf(err, "could not get stack item array (NodeInfo)") - } else if ln := len(prms); ln != nodeInfoFixedPrmNumber { - return nil, errors.Errorf("unexpected stack item count (NodeInfo): expected %d, has %d", 3, ln) - } - - res := new(bootstrap.NodeInfo) - - // Address - addrBytes, err := goclient.BytesFromStackParameter(prms[0]) - if err != nil { - return nil, errors.Wrap(err, "could not get byte array from stack item (Address)") - } - - res.Address = string(addrBytes) - - // Public key - res.PubKey, err = goclient.BytesFromStackParameter(prms[1]) - if err != nil { - return nil, errors.Wrap(err, "could not get byte array from stack item (Public key)") - } - - // Options - prms, err = goclient.ArrayFromStackParameter(prms[2]) - if err != nil { - return nil, errors.Wrapf(err, "could not get stack item array (Options)") - } - - res.Options = make([]string, 0, len(prms)) - - for i := range prms { - optBytes, err := goclient.BytesFromStackParameter(prms[i]) - if err != nil { - return nil, errors.Wrapf(err, "could not get byte array from stack item (Option #%d)", i) - } - - res.Options = append(res.Options, string(optBytes)) - } - - return res, nil -} - -// UpdateState invokes the call of UpdateState method of NeoFS Netmap contract. -func (s *MorphNetmapContract) UpdateState(p UpdateStateParams) error { - return s.netmapContract.Invoke( - s.updStateMethodName, - p.State().Int64(), - p.Key(), - ) -} - -// GetIRInfo performs the test invocation call of InnerRingList method of NeoFS Netmap contract. -func (s *MorphNetmapContract) GetIRInfo(ir.GetInfoParams) (*ir.GetInfoResult, error) { - prms, err := s.netmapContract.TestInvoke( - s.irListMethodName, - ) - if err != nil { - return nil, errors.Wrap(err, "could not perform test invocation") - } else if ln := len(prms); ln != 1 { - return nil, errors.Errorf("unexpected stack item count (Nodes): %d", ln) - } - - irInfo, err := irInfoFromStackItem(prms[0]) - if err != nil { - return nil, errors.Wrap(err, "could not get IR info from stack item") - } - - res := new(ir.GetInfoResult) - res.SetInfo(*irInfo) - - return res, nil -} - -func irInfoFromStackItem(prm smartcontract.Parameter) (*ir.Info, error) { - prms, err := goclient.ArrayFromStackParameter(prm) - if err != nil { - return nil, errors.Wrap(err, "could not get stack item array") - } - - nodes := make([]ir.Node, 0, len(prms)) - - for i := range prms { - node, err := irNodeFromStackItem(prms[i]) - if err != nil { - return nil, errors.Wrapf(err, "could not get node info from stack item (IRNode #%d)", i) - } - - nodes = append(nodes, *node) - } - - info := new(ir.Info) - info.SetNodes(nodes) - - return info, nil -} - -func irNodeFromStackItem(prm smartcontract.Parameter) (*ir.Node, error) { - prms, err := goclient.ArrayFromStackParameter(prm) - if err != nil { - return nil, errors.Wrap(err, "could not get stack item array (IRNode)") - } - - // Public key - keyBytes, err := goclient.BytesFromStackParameter(prms[0]) - if err != nil { - return nil, errors.Wrap(err, "could not get byte array from stack item (Key)") - } - - node := new(ir.Node) - node.SetKey(keyBytes) - - return node, nil -} - -// SetNumber is an epoch number setter. -func (s *UpdateEpochParams) SetNumber(v uint64) { - s.epoch = v -} - -// Number is an epoch number getter. -func (s UpdateEpochParams) Number() uint64 { - return s.epoch -} - -// SetState is a state setter. -func (s *UpdateStateParams) SetState(v NodeState) { - s.st = v -} - -// State is a state getter. -func (s UpdateStateParams) State() NodeState { - return s.st -} - -// SetKey is a public key setter. -func (s *UpdateStateParams) SetKey(v []byte) { - s.key = v -} - -// Key is a public key getter. -func (s UpdateStateParams) Key() []byte { - return s.key -} - -// Int64 converts NodeState to int64. -func (s NodeState) Int64() int64 { - return int64(s) -} diff --git a/lib/implementations/bootstrap_test.go b/lib/implementations/bootstrap_test.go deleted file mode 100644 index a9968ae9..00000000 --- a/lib/implementations/bootstrap_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package implementations - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestUpdateEpochParams(t *testing.T) { - s := UpdateEpochParams{} - - e := uint64(100) - s.SetNumber(e) - - require.Equal(t, e, s.Number()) -} - -func TestUpdateStateParams(t *testing.T) { - s := UpdateStateParams{} - - st := NodeState(1) - s.SetState(st) - - require.Equal(t, st, s.State()) - - key := []byte{1, 2, 3} - s.SetKey(key) - - require.Equal(t, key, s.Key()) -} diff --git a/lib/implementations/epoch.go b/lib/implementations/epoch.go deleted file mode 100644 index 16d9a5c3..00000000 --- a/lib/implementations/epoch.go +++ /dev/null @@ -1,7 +0,0 @@ -package implementations - -// EpochReceiver is an interface of the container -// of NeoFS epoch number with read access. -type EpochReceiver interface { - Epoch() uint64 -} diff --git a/lib/implementations/reputation.go b/lib/implementations/reputation.go deleted file mode 100644 index 2fb4865e..00000000 --- a/lib/implementations/reputation.go +++ /dev/null @@ -1,41 +0,0 @@ -package implementations - -import ( - "github.com/nspcc-dev/neofs-node/lib/peers" -) - -// MorphReputationContract is a wrapper over NeoFS Reputation contract client -// that provides an interface of the storage of global trust values. -type MorphReputationContract struct { - // NeoFS Reputation smart-contract - repContract StaticContractClient - - // put method name of reputation contract - putMethodName string - - // list method name of reputation contract - listMethodName string - - // public key storage - pkStore peers.PublicKeyStore -} - -// SetReputationContractClient is a Reputation contract client setter. -func (s *MorphReputationContract) SetReputationContractClient(v StaticContractClient) { - s.repContract = v -} - -// SetPublicKeyStore is a public key store setter. -func (s *MorphReputationContract) SetPublicKeyStore(v peers.PublicKeyStore) { - s.pkStore = v -} - -// SetPutMethodName is a Reputation contract Put method name setter. -func (s *MorphReputationContract) SetPutMethodName(v string) { - s.putMethodName = v -} - -// SetListMethodName is a Reputation contract List method name setter. -func (s *MorphReputationContract) SetListMethodName(v string) { - s.listMethodName = v -} diff --git a/lib/ir/info.go b/lib/ir/info.go deleted file mode 100644 index 991a1efa..00000000 --- a/lib/ir/info.go +++ /dev/null @@ -1,17 +0,0 @@ -package ir - -// Info is a structure that groups the information -// about inner ring. -type Info struct { - nodes []Node -} - -// SetNodes is an IR node list setter. -func (s *Info) SetNodes(v []Node) { - s.nodes = v -} - -// Nodes is an IR node list getter. -func (s Info) Nodes() []Node { - return s.nodes -} diff --git a/lib/ir/info_test.go b/lib/ir/info_test.go deleted file mode 100644 index 6b1f3df4..00000000 --- a/lib/ir/info_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package ir - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestInfo(t *testing.T) { - s := Info{} - - n1 := Node{} - n1.SetKey([]byte{1, 2, 3}) - - n2 := Node{} - n2.SetKey([]byte{4, 5, 6}) - - nodes := []Node{ - n1, - n2, - } - s.SetNodes(nodes) - - require.Equal(t, nodes, s.Nodes()) -} diff --git a/lib/ir/node.go b/lib/ir/node.go deleted file mode 100644 index c1a765b5..00000000 --- a/lib/ir/node.go +++ /dev/null @@ -1,17 +0,0 @@ -package ir - -// Node is a structure that groups -// the information about IR node. -type Node struct { - key []byte -} - -// SetKey is an IR node public key setter. -func (s *Node) SetKey(v []byte) { - s.key = v -} - -// Key is an IR node public key getter. -func (s Node) Key() []byte { - return s.key -} diff --git a/lib/ir/node_test.go b/lib/ir/node_test.go deleted file mode 100644 index 9663caf9..00000000 --- a/lib/ir/node_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package ir - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestNode(t *testing.T) { - s := Node{} - - key := []byte{1, 2, 3} - s.SetKey(key) - - require.Equal(t, key, s.Key()) -} diff --git a/lib/ir/storage.go b/lib/ir/storage.go deleted file mode 100644 index 8df21933..00000000 --- a/lib/ir/storage.go +++ /dev/null @@ -1,94 +0,0 @@ -package ir - -import ( - "bytes" - - crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/pkg/errors" -) - -// Storage is an interface of the storage of info about NeoFS IR. -type Storage interface { - GetIRInfo(GetInfoParams) (*GetInfoResult, error) -} - -// GetInfoParams is a structure that groups the parameters -// for IR info receiving operation. -type GetInfoParams struct { -} - -// GetInfoResult is a structure that groups -// values returned by IR info receiving operation. -type GetInfoResult struct { - info Info -} - -// ErrNilStorage is returned by functions that expect -// a non-nil Storage, but received nil. -const ErrNilStorage = internal.Error("inner ring storage is nil") - -// SetInfo is an IR info setter. -func (s *GetInfoResult) SetInfo(v Info) { - s.info = v -} - -// Info is an IR info getter. -func (s GetInfoResult) Info() Info { - return s.info -} - -// BinaryKeyList returns the list of binary public key of IR nodes. -// -// If passed Storage is nil, ErrNilStorage returns. -func BinaryKeyList(storage Storage) ([][]byte, error) { - if storage == nil { - return nil, ErrNilStorage - } - - // get IR info - getRes, err := storage.GetIRInfo(GetInfoParams{}) - if err != nil { - return nil, errors.Wrap( - err, - "could not get information about IR", - ) - } - - nodes := getRes.Info().Nodes() - - keys := make([][]byte, 0, len(nodes)) - - for i := range nodes { - keys = append(keys, nodes[i].Key()) - } - - return keys, nil -} - -// IsInnerRingKey checks if the passed argument is the -// key of one of IR nodes. -// -// Uses BinaryKeyList function to receive the key list of IR nodes internally. -// -// If passed key slice is empty, crypto.ErrEmptyPublicKey returns immediately. -func IsInnerRingKey(storage Storage, key []byte) (bool, error) { - // check key emptiness - // TODO: summarize the void check to a full IR key-format check. - if len(key) == 0 { - return false, crypto.ErrEmptyPublicKey - } - - irKeys, err := BinaryKeyList(storage) - if err != nil { - return false, err - } - - for i := range irKeys { - if bytes.Equal(irKeys[i], key) { - return true, nil - } - } - - return false, nil -} diff --git a/lib/ir/storage_test.go b/lib/ir/storage_test.go deleted file mode 100644 index 71a65484..00000000 --- a/lib/ir/storage_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package ir - -import ( - "testing" - - crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/pkg/errors" - "github.com/stretchr/testify/require" -) - -type testInfoReceiver struct { - keys [][]byte - - err error -} - -func (s testInfoReceiver) GetIRInfo(GetInfoParams) (*GetInfoResult, error) { - if s.err != nil { - return nil, s.err - } - - nodes := make([]Node, 0, len(s.keys)) - - for i := range s.keys { - node := Node{} - node.SetKey(s.keys[i]) - - nodes = append(nodes, node) - } - - info := Info{} - info.SetNodes(nodes) - - res := new(GetInfoResult) - res.SetInfo(info) - - return res, nil -} - -func (s *testInfoReceiver) addKey(key []byte) { - s.keys = append(s.keys, key) -} - -func TestGetInfoResult(t *testing.T) { - s := GetInfoResult{} - - info := Info{} - - n := Node{} - n.SetKey([]byte{1, 2, 3}) - - info.SetNodes([]Node{ - n, - }) - - s.SetInfo(info) - - require.Equal(t, info, s.Info()) -} - -func TestIsInnerRingKey(t *testing.T) { - var ( - res bool - err error - s = new(testInfoReceiver) - ) - - // empty public key - res, err = IsInnerRingKey(nil, nil) - require.EqualError(t, err, crypto.ErrEmptyPublicKey.Error()) - - key := []byte{1, 2, 3} - - // nil Storage - res, err = IsInnerRingKey(nil, key) - require.EqualError(t, err, ErrNilStorage.Error()) - - // force Storage to return an error - s.err = errors.New("some error") - - // Storage error - res, err = IsInnerRingKey(s, key) - require.EqualError(t, errors.Cause(err), s.err.Error()) - - // reset Storage error - s.err = nil - - // IR keys don't contain key - s.addKey(append(key, 1)) - - res, err = IsInnerRingKey(s, key) - require.NoError(t, err) - require.False(t, res) - - // IR keys contain key - s.addKey(key) - - res, err = IsInnerRingKey(s, key) - require.NoError(t, err) - require.True(t, res) -} diff --git a/lib/netmap/netmap.go b/lib/netmap/netmap.go deleted file mode 100644 index e339d0f9..00000000 --- a/lib/netmap/netmap.go +++ /dev/null @@ -1,392 +0,0 @@ -package netmap - -import ( - "crypto/sha256" - "encoding/json" - "reflect" - "sort" - "sync" - - "github.com/nspcc-dev/neofs-api-go/bootstrap" - "github.com/nspcc-dev/netmap" - "github.com/pkg/errors" - "github.com/spaolacci/murmur3" -) - -type ( - // Bucket is an alias for github.com/nspcc-dev/netmap.Bucket - Bucket = netmap.Bucket - // SFGroup is an alias for github.com/nspcc-dev/netmap.SFGroup - SFGroup = netmap.SFGroup - // Select is an alias for github.com/nspcc-dev/netmap.Select - Select = netmap.Select - // Filter is an alias for github.com/nspcc-dev/netmap.Filter - Filter = netmap.Filter - // SimpleFilter is an alias for github.com/nspcc-dev/netmap.Filter - SimpleFilter = netmap.SimpleFilter - // PlacementRule is an alias for github.com/nspcc-dev/netmap.Filter - PlacementRule = netmap.PlacementRule - - // NetMap is a general network map structure for NeoFS - NetMap struct { - mu *sync.RWMutex - root Bucket - items Nodes - } - - // Nodes is an alias for slice of NodeInfo which is structure that describes every host - Nodes []bootstrap.NodeInfo -) - -const ( - // Separator separates key:value pairs in string representation of options. - Separator = netmap.Separator - - // NodesBucket is the name for optionless bucket containing only nodes. - NodesBucket = netmap.NodesBucket -) - -var ( - // FilterIn returns filter, which checks if value is in specified list. - FilterIn = netmap.FilterIn - // FilterNotIn returns filter, which checks if value is not in specified list. - FilterNotIn = netmap.FilterNotIn - // FilterOR returns OR combination of filters. - FilterOR = netmap.FilterOR - // FilterAND returns AND combination of filters. - FilterAND = netmap.FilterAND - // FilterEQ returns filter, which checks if value is equal to v. - FilterEQ = netmap.FilterEQ - // FilterNE returns filter, which checks if value is not equal to v. - FilterNE = netmap.FilterNE - // FilterGT returns filter, which checks if value is greater than v. - FilterGT = netmap.FilterGT - // FilterGE returns filter, which checks if value is greater or equal than v. - FilterGE = netmap.FilterGE - // FilterLT returns filter, which checks if value is less than v. - FilterLT = netmap.FilterLT - // FilterLE returns filter, which checks if value is less or equal than v. - FilterLE = netmap.FilterLE -) - -var errNetMapsConflict = errors.New("netmaps are in conflict") - -// Copy creates new slice of copied nodes. -func (n Nodes) Copy() Nodes { - res := make(Nodes, len(n)) - for i := range n { - res[i].Address = n[i].Address - res[i].Status = n[i].Status - - if n[i].PubKey != nil { - res[i].PubKey = make([]byte, len(n[i].PubKey)) - copy(res[i].PubKey, n[i].PubKey) - } - - if n[i].Options != nil { - res[i].Options = make([]string, len(n[i].Options)) - copy(res[i].Options, n[i].Options) - } - } - - return res -} - -// NewNetmap is an constructor. -func NewNetmap() *NetMap { - return &NetMap{ - items: make([]bootstrap.NodeInfo, 0), - mu: new(sync.RWMutex), - } -} - -// Equals return whether two netmap are identical. -func (n *NetMap) Equals(nm *NetMap) bool { - n.mu.RLock() - defer n.mu.RUnlock() - - return len(n.items) == len(nm.items) && - n.root.Equals(nm.root) && - reflect.DeepEqual(n.items, nm.items) -} - -// Root returns netmap root-bucket. -func (n *NetMap) Root() *Bucket { - n.mu.RLock() - cp := n.root.Copy() - n.mu.RUnlock() - - return &cp -} - -// Copy creates and returns full copy of target netmap. -func (n *NetMap) Copy() *NetMap { - n.mu.RLock() - defer n.mu.RUnlock() - - nm := NewNetmap() - nm.items = n.items.Copy() - nm.root = n.root.Copy() - - return nm -} - -type hashedItem struct { - h uint32 - info *bootstrap.NodeInfo -} - -// Normalise reorders netmap items into some canonical order. -func (n *NetMap) Normalise() *NetMap { - nm := NewNetmap() - items := n.items.Copy() - - if len(items) == 0 { - return nm - } - - itemsH := make([]hashedItem, len(n.items)) - for i := range itemsH { - itemsH[i].h = murmur3.Sum32(n.items[i].PubKey) - itemsH[i].info = &items[i] - } - - sort.Slice(itemsH, func(i, j int) bool { - if itemsH[i].h == itemsH[j].h { - return itemsH[i].info.Address < itemsH[j].info.Address - } - return itemsH[i].h < itemsH[j].h - }) - - lastHash := ^itemsH[0].h - lastAddr := "" - - for i := range itemsH { - if itemsH[i].h != lastHash || itemsH[i].info.Address != lastAddr { - _ = nm.AddNode(itemsH[i].info) - lastHash = itemsH[i].h - } - } - - return nm -} - -// Hash returns hash of n. -func (n *NetMap) Hash() (sum [32]byte) { - items := n.Normalise().Items() - w := sha256.New() - - for i := range items { - data, _ := items[i].Marshal() - _, _ = w.Write(data) - } - - s := w.Sum(nil) - copy(sum[:], s) - - return -} - -// InheritWeights calculates average capacity and minimal price, then provides buckets with IQR weight. -func (n *NetMap) InheritWeights() *NetMap { - nm := n.Copy() - - // find average capacity in the network map - meanCap := nm.root.Traverse(netmap.NewMeanAgg(), netmap.CapWeightFunc).Compute() - capNorm := netmap.NewSigmoidNorm(meanCap) - - // find minimal price in the network map - minPrice := nm.root.Traverse(netmap.NewMinAgg(), netmap.PriceWeightFunc).Compute() - priceNorm := netmap.NewReverseMinNorm(minPrice) - - // provide all buckets with - wf := netmap.NewWeightFunc(capNorm, priceNorm) - meanAF := netmap.AggregatorFactory{New: netmap.NewMeanIQRAgg} - nm.root.TraverseTree(meanAF, wf) - - return nm -} - -// Merge checks if merge is possible and then add new elements from given netmap. -func (n *NetMap) Merge(n1 *NetMap) error { - n.mu.Lock() - defer n.mu.Unlock() - - var ( - tr = make(map[uint32]netmap.Node, len(n1.items)) - items = n.items - ) - -loop: - for j := range n1.items { - for i := range n.items { - if n.items[i].Equals(n1.items[j]) { - tr[uint32(j)] = netmap.Node{ - N: uint32(i), - C: n.items[i].Capacity(), - P: n.items[i].Price(), - } - continue loop - } - } - tr[uint32(j)] = netmap.Node{ - N: uint32(len(items)), - C: n1.items[j].Capacity(), - P: n1.items[j].Price(), - } - items = append(items, n1.items[j]) - } - - root := n1.root.UpdateIndices(tr) - if n.root.CheckConflicts(root) { - return errNetMapsConflict - } - - n.items = items - n.root.Merge(root) - - return nil -} - -// FindGraph finds sub-graph filtered by given SFGroup. -func (n *NetMap) FindGraph(pivot []byte, ss ...SFGroup) (c *Bucket) { - n.mu.RLock() - defer n.mu.RUnlock() - - return n.root.FindGraph(pivot, ss...) -} - -// FindNodes finds sub-graph filtered by given SFGroup and returns all sub-graph items. -func (n *NetMap) FindNodes(pivot []byte, ss ...SFGroup) (nodes []uint32) { - n.mu.RLock() - defer n.mu.RUnlock() - - return n.root.FindNodes(pivot, ss...).Nodes() -} - -// Items return slice of all NodeInfo in netmap. -func (n *NetMap) Items() []bootstrap.NodeInfo { - n.mu.RLock() - defer n.mu.RUnlock() - - return n.items -} - -// ItemsCopy return copied slice of all NodeInfo in netmap (is it useful?). -func (n *NetMap) ItemsCopy() Nodes { - n.mu.RLock() - defer n.mu.RUnlock() - - return n.items.Copy() -} - -// Add adds node with given address and given options. -func (n *NetMap) Add(addr string, pk []byte, st bootstrap.NodeStatus, opts ...string) error { - return n.AddNode(&bootstrap.NodeInfo{Address: addr, PubKey: pk, Status: st, Options: opts}) -} - -// Update replaces netmap with given netmap. -func (n *NetMap) Update(nxt *NetMap) { - n.mu.Lock() - defer n.mu.Unlock() - - n.root = nxt.root - n.items = nxt.items -} - -// GetMaxSelection returns 'maximal container' -- subgraph which contains -// any other subgraph satisfying specified selects and filters. -func (n *NetMap) GetMaxSelection(ss []Select, fs []Filter) (r *Bucket) { - return n.root.GetMaxSelection(netmap.SFGroup{Selectors: ss, Filters: fs}) -} - -// AddNode adds to exited or new node slice of given options. -func (n *NetMap) AddNode(nodeInfo *bootstrap.NodeInfo, opts ...string) error { - n.mu.Lock() - defer n.mu.Unlock() - - info := *nodeInfo - - info.Options = append(info.Options, opts...) - - num := -1 - - // looking for existed node info item - for i := range n.items { - if n.items[i].Equals(info) { - num = i - break - } - } - // if item is not existed - add it - if num < 0 { - num = len(n.items) - n.items = append(n.items, info) - } - - return n.root.AddStrawNode(netmap.Node{ - N: uint32(num), - C: n.items[num].Capacity(), - P: n.items[num].Price(), - }, info.Options...) -} - -// GetNodesByOption returns slice of NodeInfo that has given option. -func (n *NetMap) GetNodesByOption(opts ...string) []bootstrap.NodeInfo { - n.mu.RLock() - defer n.mu.RUnlock() - - ns := n.root.GetNodesByOption(opts...) - nodes := make([]bootstrap.NodeInfo, 0, len(ns)) - - for _, info := range ns { - nodes = append(nodes, n.items[info.N]) - } - - return nodes -} - -// MarshalJSON custom marshaller. -func (n *NetMap) MarshalJSON() ([]byte, error) { - n.mu.RLock() - defer n.mu.RUnlock() - - return json.Marshal(n.items) -} - -// UnmarshalJSON custom unmarshaller. -func (n *NetMap) UnmarshalJSON(data []byte) error { - var ( - nm = NewNetmap() - items []bootstrap.NodeInfo - ) - - if err := json.Unmarshal(data, &items); err != nil { - return err - } - - for i := range items { - if err := nm.Add(items[i].Address, items[i].PubKey, items[i].Status, items[i].Options...); err != nil { - return err - } - } - - if n.mu == nil { - n.mu = new(sync.RWMutex) - } - - n.mu.Lock() - n.root = nm.root - n.items = nm.items - n.mu.Unlock() - - return nil -} - -// Size returns number of nodes in network map. -func (n *NetMap) Size() int { - n.mu.RLock() - defer n.mu.RUnlock() - - return len(n.items) -} diff --git a/lib/netmap/netmap_test.go b/lib/netmap/netmap_test.go deleted file mode 100644 index 1cd579b6..00000000 --- a/lib/netmap/netmap_test.go +++ /dev/null @@ -1,261 +0,0 @@ -package netmap - -import ( - "bytes" - "encoding/json" - "math/rand" - "sync" - "testing" - "time" - - "github.com/nspcc-dev/neofs-api-go/bootstrap" - "github.com/nspcc-dev/neofs-api-go/object" - "github.com/nspcc-dev/netmap" - "github.com/stretchr/testify/require" -) - -func TestNetMap_DataRace(t *testing.T) { - var ( - nm = NewNetmap() - wg = new(sync.WaitGroup) - nodes = []bootstrap.NodeInfo{ - {Address: "SPB1", Options: []string{"/Location:Europe/Country:USA"}}, - {Address: "SPB2", Options: []string{"/Location:Europe/Country:Italy"}}, - {Address: "MSK1", Options: []string{"/Location:Europe/Country:Germany"}}, - {Address: "MSK2", Options: []string{"/Location:Europe/Country:Russia"}}, - } - ) - - wg.Add(10) - for i := 0; i < 10; i++ { - go func(n int) { - for _, node := range nodes { - require.NoError(t, nm.Add(node.Address, node.PubKey, 0, node.Options...)) - // t.Logf("%02d: add node %q", n, node.Address) - } - - wg.Done() - }(i) - } - - wg.Add(3 * 10) - for i := 0; i < 10; i++ { - go func(n int) { - nm.Copy() - // t.Logf("%02d: Copy", n) - wg.Done() - }(i) - go func(n int) { - nm.Items() - // t.Logf("%02d: Items", n) - wg.Done() - }(i) - go func(n int) { - nm.Root() - // t.Logf("%02d: Root", n) - wg.Done() - }(i) - } - - wg.Wait() -} - -func TestNetMapSuite(t *testing.T) { - var ( - err error - nm1 = NewNetmap() - nodes = []bootstrap.NodeInfo{ - {Address: "SPB1", Options: []string{"/Location:Europe/Country:USA"}, Status: 1}, - {Address: "SPB2", Options: []string{"/Location:Europe/Country:Italy"}, Status: 2}, - {Address: "MSK1", Options: []string{"/Location:Europe/Country:Germany"}, Status: 3}, - {Address: "MSK2", Options: []string{"/Location:Europe/Country:Russia"}, Status: 4}, - } - ) - - for _, node := range nodes { - err = nm1.Add(node.Address, nil, node.Status, node.Options...) - require.NoError(t, err) - } - - t.Run("copy should work like expected", func(t *testing.T) { - nm2 := nm1.Copy() - require.Equal(t, nm1.root, nm2.root) - require.Equal(t, nm1.items, nm2.items) - }) - - t.Run("add node should not ignore options", func(t *testing.T) { - items := nm1.ItemsCopy() - - nm2 := NewNetmap() - err = nm2.AddNode(&items[0], "/New/Option") - require.NoError(t, err) - require.Len(t, nm2.items, 1) - require.Equal(t, append(items[0].Options, "/New/Option"), nm2.items[0].Options) - }) - - t.Run("copyItems should work like expected", func(t *testing.T) { - require.Equal(t, nm1.items, nm1.ItemsCopy()) - }) - - t.Run("marshal / unmarshal should be identical on same data", func(t *testing.T) { - var nm2 *NetMap - want, err := json.Marshal(nodes) - require.NoError(t, err) - - actual, err := json.Marshal(nm1) - require.NoError(t, err) - - require.Equal(t, want, actual) - - err = json.Unmarshal(actual, &nm2) - require.NoError(t, err) - require.Equal(t, nm1.root, nm2.root) - require.Equal(t, nm1.items, nm2.items) - }) - - t.Run("unmarshal should override existing data", func(t *testing.T) { - var nm2 *NetMap - - want, err := json.Marshal(nodes) - require.NoError(t, err) - - actual, err := json.Marshal(nm1) - require.NoError(t, err) - - require.Equal(t, want, actual) - - nm2 = nm1.Copy() - err = nm2.Add("SOMEADDR", nil, 0, "/Location:Europe/Country:USA") - require.NoError(t, err) - - err = json.Unmarshal(actual, &nm2) - require.NoError(t, err) - require.Equal(t, nm1.root, nm2.root) - require.Equal(t, nm1.items, nm2.items) - }) - - t.Run("unmarshal should fail on bad data", func(t *testing.T) { - var nm2 *NetMap - require.Error(t, json.Unmarshal([]byte(`"some bad data"`), &nm2)) - }) - - t.Run("unmarshal should fail on add nodes", func(t *testing.T) { - var nm2 *NetMap - require.Error(t, json.Unmarshal([]byte(`[{"address": "SPB1","options":["1-2-3-4"]}]`), &nm2)) - }) - - t.Run("merge two netmaps", func(t *testing.T) { - newNodes := []bootstrap.NodeInfo{ - {Address: "SPB3", Options: []string{"/Location:Europe/Country:France"}}, - } - nm2 := NewNetmap() - for _, node := range newNodes { - err = nm2.Add(node.Address, nil, 0, node.Options...) - require.NoError(t, err) - } - - err = nm2.Merge(nm1) - require.NoError(t, err) - require.Len(t, nm2.items, len(nodes)+len(newNodes)) - - ns := nm2.FindNodes([]byte("pivot"), netmap.SFGroup{ - Filters: []Filter{{Key: "Country", F: FilterEQ("Germany")}}, - Selectors: []Select{{Count: 1, Key: NodesBucket}}, - }) - require.Len(t, ns, 1) - }) - - t.Run("weighted netmaps", func(t *testing.T) { - strawNodes := []bootstrap.NodeInfo{ - {Address: "SPB2", Options: []string{"/Location:Europe/Country:Italy", "/Capacity:10", "/Price:100"}}, - {Address: "MSK1", Options: []string{"/Location:Europe/Country:Germany", "/Capacity:10", "/Price:1"}}, - {Address: "MSK2", Options: []string{"/Location:Europe/Country:Russia", "/Capacity:5", "/Price:10"}}, - {Address: "SPB1", Options: []string{"/Location:Europe/Country:France", "/Capacity:20", "/Price:2"}}, - } - nm2 := NewNetmap() - for _, node := range strawNodes { - err = nm2.Add(node.Address, nil, 0, node.Options...) - require.NoError(t, err) - } - - ns1 := nm1.FindNodes([]byte("pivot"), netmap.SFGroup{ - Selectors: []Select{{Count: 2, Key: NodesBucket}}, - }) - require.Len(t, ns1, 2) - - ns2 := nm2.FindNodes([]byte("pivot"), netmap.SFGroup{ - Selectors: []Select{{Count: 2, Key: NodesBucket}}, - }) - require.Len(t, ns2, 2) - require.NotEqual(t, ns1, ns2) - require.Equal(t, []uint32{1, 3}, ns2) - }) -} - -func TestNetMap_Normalise(t *testing.T) { - const testCount = 5 - - nodes := []bootstrap.NodeInfo{ - {Address: "SPB2", PubKey: []byte{4}, Options: []string{"/Location:Europe/Country:Italy", "/Capacity:10", "/Price:100"}}, - {Address: "MSK1", PubKey: []byte{2}, Options: []string{"/Location:Europe/Country:Germany", "/Capacity:10", "/Price:1"}}, - {Address: "MSK2", PubKey: []byte{3}, Options: []string{"/Location:Europe/Country:Russia", "/Capacity:5", "/Price:10"}}, - {Address: "SPB1", PubKey: []byte{1}, Options: []string{"/Location:Europe/Country:France", "/Capacity:20", "/Price:2"}}, - } - - add := func(nm *NetMap, indices ...int) { - for _, i := range indices { - err := nm.Add(nodes[i].Address, nodes[i].PubKey, 0, nodes[i].Options...) - require.NoError(t, err) - } - } - - indices := []int{0, 1, 2, 3} - - nm1 := NewNetmap() - add(nm1, indices...) - norm := nm1.Normalise() - - for i := 0; i < testCount; i++ { - rand.Seed(time.Now().UnixNano()) - rand.Shuffle(len(indices), func(i, j int) { indices[i], indices[j] = indices[j], indices[i] }) - - nm := NewNetmap() - add(nm, indices...) - require.Equal(t, norm, nm.Normalise()) - } - - t.Run("normalise removes duplicates", func(t *testing.T) { - before := NewNetmap() - add(before, indices...) - before.items = append(before.items, before.items...) - - nm := before.Normalise() - require.Len(t, nm.items, len(indices)) - - loop: - for i := range nodes { - for j := range nm.items { - if bytes.Equal(nm.items[j].PubKey, nodes[i].PubKey) { - continue loop - } - } - require.Fail(t, "normalized netmap does not contain '%s' node", nodes[i].Address) - } - }) -} - -func TestNodeInfo_Price(t *testing.T) { - var info bootstrap.NodeInfo - - // too small value - info = bootstrap.NodeInfo{Options: []string{"/Price:0.01048575"}} - require.Equal(t, uint64(0), info.Price()) - - // min value - info = bootstrap.NodeInfo{Options: []string{"/Price:0.01048576"}} - require.Equal(t, uint64(1), info.Price()) - - // big value - info = bootstrap.NodeInfo{Options: []string{"/Price:1000000000.666"}} - require.Equal(t, uint64(1000000000.666*1e8/object.UnitsMB), info.Price()) -} diff --git a/lib/netmap/storage.go b/lib/netmap/storage.go deleted file mode 100644 index fc26bb55..00000000 --- a/lib/netmap/storage.go +++ /dev/null @@ -1,27 +0,0 @@ -package netmap - -// GetParams is a group of parameters -// for network map receiving operation. -type GetParams struct { -} - -// GetResult is a group of values -// returned by container receiving operation. -type GetResult struct { - nm *NetMap -} - -// Storage is an interface of the storage of NeoFS network map. -type Storage interface { - GetNetMap(GetParams) (*GetResult, error) -} - -// NetMap is a network map getter. -func (s GetResult) NetMap() *NetMap { - return s.nm -} - -// SetNetMap is a network map setter. -func (s *GetResult) SetNetMap(v *NetMap) { - s.nm = v -} diff --git a/lib/netmap/storage_test.go b/lib/netmap/storage_test.go deleted file mode 100644 index 27315f8b..00000000 --- a/lib/netmap/storage_test.go +++ /dev/null @@ -1,23 +0,0 @@ -package netmap - -import ( - "testing" - - "github.com/nspcc-dev/neofs-api-go/bootstrap" - "github.com/stretchr/testify/require" -) - -func TestGetResult(t *testing.T) { - s := GetResult{} - - nm := NewNetmap() - require.NoError(t, - nm.AddNode(&bootstrap.NodeInfo{ - Address: "address", - PubKey: []byte{1, 2, 3}, - }), - ) - s.SetNetMap(nm) - - require.Equal(t, nm, s.NetMap()) -} diff --git a/lib/peers/peers.go b/lib/peers/peers.go deleted file mode 100644 index 406e495a..00000000 --- a/lib/peers/peers.go +++ /dev/null @@ -1,455 +0,0 @@ -package peers - -import ( - "context" - "net" - "sync" - "time" - - "github.com/multiformats/go-multiaddr" - manet "github.com/multiformats/go-multiaddr-net" - "github.com/nspcc-dev/neofs-node/lib/transport" - "github.com/pkg/errors" - "go.uber.org/zap" - "google.golang.org/grpc" - "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/keepalive" - "google.golang.org/grpc/status" -) - -type ( - // Interface is an interface of network connections controller. - Interface interface { - Shutdown() error - Job(context.Context) - Address() multiaddr.Multiaddr - RemoveConnection(maddr multiaddr.Multiaddr) error - Listen(maddr multiaddr.Multiaddr) (manet.Listener, error) - Connect(ctx context.Context, maddr multiaddr.Multiaddr) (manet.Conn, error) - GRPCConnector - } - - // GRPCConnector is an interface of gRPC virtual connector. - GRPCConnector interface { - GRPCConnection(ctx context.Context, maddr multiaddr.Multiaddr, reset bool) (*grpc.ClientConn, error) - } - - // Params groups the parameters of Interface. - Params struct { - Address multiaddr.Multiaddr - Transport transport.Transport - Logger *zap.Logger - Attempts int64 - AttemptsTTL time.Duration - ConnectionTTL time.Duration - ConnectionIDLE time.Duration - MetricsTimeout time.Duration - KeepAliveTTL time.Duration - KeepAlivePingTTL time.Duration - } - - connItem struct { - sync.RWMutex - conn *grpc.ClientConn - used time.Time - } - - iface struct { - log *zap.Logger - addr multiaddr.Multiaddr // self address - tr transport.Transport - tick time.Duration - idle time.Duration - - keepAlive time.Duration - pingTTL time.Duration - - metricsTimeout time.Duration - - grpc struct { - // globalMutex used by garbage collector and other high - globalMutex *sync.RWMutex - // bookMutex resolves concurrent access to the new connection - bookMutex *sync.RWMutex - // connBook contains connection info - // it's mutex resolves concurrent access to existed connection - connBook map[string]*connItem - } - - cons struct { - *sync.RWMutex - items map[string]transport.Connection - } - - lis struct { - *sync.RWMutex - items map[string]manet.Listener - } - } -) - -const ( - defaultAttemptsCount = 5 - defaultAttemptsTTL = 30 * time.Second - defaultCloseTimer = 30 * time.Second - defaultConIdleTTL = 30 * time.Second - defaultKeepAliveTTL = 5 * time.Second - defaultMetricsTimeout = 5 * time.Second - defaultKeepAlivePingTTL = 50 * time.Millisecond -) - -var ( - // ErrDialToSelf is returned if we attempt to dial our own peer - ErrDialToSelf = errors.New("dial to self attempted") - // ErrEmptyAddress returns when you try to create Interface with empty address - ErrEmptyAddress = errors.New("self address could not be empty") - // ErrEmptyTransport returns when you try to create Interface with empty transport - ErrEmptyTransport = errors.New("transport could not be empty") -) - -var errNilMultiaddr = errors.New("empty multi-address") - -func (s *iface) Shutdown() error { - s.lis.Lock() - s.cons.Lock() - s.grpc.globalMutex.Lock() - - defer func() { - s.lis.Unlock() - s.cons.Unlock() - s.grpc.globalMutex.Unlock() - }() - - for addr := range s.cons.items { - if err := s.removeNetConnection(addr); err != nil { - return errors.Wrapf(err, "could not remove net connection `%s`", addr) - } - } - - for addr := range s.grpc.connBook { - if err := s.removeGRPCConnection(addr); err != nil { - return errors.Wrapf(err, "could not remove net connection `%s`", addr) - } - } - - for addr := range s.lis.items { - if err := s.removeListener(addr); err != nil { - return errors.Wrapf(err, "could not remove listener `%s`", addr) - } - } - - return nil -} - -// RemoveConnection from Interface. -// Used only in tests, consider removing. -func (s *iface) RemoveConnection(maddr multiaddr.Multiaddr) error { - addr, err := convertAddress(maddr) - if err != nil { - return err - } - - s.cons.Lock() - s.grpc.globalMutex.Lock() - - defer func() { - s.cons.Unlock() - s.grpc.globalMutex.Unlock() - }() - - // Try to remove connection - if err := s.removeNetConnection(maddr.String()); err != nil { - return errors.Wrapf(err, "could not remove net connection `%s`", maddr.String()) - } - - // Try to remove gRPC connection - if err := s.removeGRPCConnection(addr); err != nil { - return errors.Wrapf(err, "could not remove gRPC connection `%s`", addr) - } - - // TODO remove another connections - - return nil -} - -func (s *iface) removeListener(addr string) error { - if lis, ok := s.lis.items[addr]; ok { - if err := lis.Close(); err != nil { - return err - } - - delete(s.lis.items, addr) - } - - return nil -} - -func (s *iface) removeNetConnection(addr string) error { - // Try to remove simple connection - if con, ok := s.cons.items[addr]; ok { - if err := con.Close(); err != nil { - return err - } - - delete(s.cons.items, addr) - } - - return nil -} - -func (s *iface) removeGRPCConnection(addr string) error { - if gCon, ok := s.grpc.connBook[addr]; ok && gCon.conn != nil { - if err := gCon.conn.Close(); err != nil { - state, ok := status.FromError(err) - if !ok { - return err - } - - s.log.Debug("error state", - zap.String("address", addr), - zap.Any("code", state.Code()), - zap.String("state", state.Message()), - zap.Any("details", state.Details())) - } - } - - delete(s.grpc.connBook, addr) - - return nil -} - -// Connect to address -// Used only in tests, consider removing. -func (s *iface) Connect(ctx context.Context, maddr multiaddr.Multiaddr) (manet.Conn, error) { - var ( - err error - con transport.Connection - ) - - if maddr.Equal(s.addr) { - return nil, ErrDialToSelf - } - - s.cons.RLock() - con, ok := s.cons.items[maddr.String()] - s.cons.RUnlock() - - if ok && !con.Closed() { - return con, nil - } - - if con, err = s.newConnection(ctx, maddr, false); err != nil { - return nil, err - } - - s.cons.Lock() - s.cons.items[maddr.String()] = con - s.cons.Unlock() - - return con, nil -} - -// Listen try to find listener or creates new. -func (s *iface) Listen(maddr multiaddr.Multiaddr) (manet.Listener, error) { - // fixme: concurrency issue there, same as 5260f04d - // but it's not so bad, because `Listen()` used - // once during startup routine. - s.lis.RLock() - lis, ok := s.lis.items[maddr.String()] - s.lis.RUnlock() - - if ok { - return lis, nil - } - - lis, err := s.tr.Listen(maddr) - if err != nil { - return nil, err - } - - s.lis.Lock() - s.lis.items[maddr.String()] = lis - s.lis.Unlock() - - return lis, nil -} - -// Address of current Interface instance. -func (s *iface) Address() multiaddr.Multiaddr { - return s.addr -} - -func isGRPCClosed(con *grpc.ClientConn) bool { - switch con.GetState() { - case connectivity.Idle, connectivity.Connecting, connectivity.Ready: - return false - default: - // connectivity.TransientFailure, connectivity.Shutdown - return true - } -} - -func (s *iface) newConnection(ctx context.Context, addr multiaddr.Multiaddr, reset bool) (transport.Connection, error) { - return s.tr.Dial(ctx, addr, reset) -} - -func gRPCKeepAlive(ping, ttl time.Duration) grpc.DialOption { - return grpc.WithKeepaliveParams(keepalive.ClientParameters{ - Time: ping, - Timeout: ttl, - PermitWithoutStream: true, - }) -} - -func convertAddress(maddr multiaddr.Multiaddr) (string, error) { - if maddr == nil { - return "", errNilMultiaddr - } - - addr, err := manet.ToNetAddr(maddr) - if err != nil { - return "", errors.Wrapf(err, "could not convert address `%s`", maddr) - } - - return addr.String(), nil -} - -// GRPCConnection creates gRPC connection over peers connection. -func (s *iface) GRPCConnection(ctx context.Context, maddr multiaddr.Multiaddr, reset bool) (*grpc.ClientConn, error) { - addr, err := convertAddress(maddr) - if err != nil { - return nil, errors.Wrapf(err, "could not convert `%v`", maddr) - } - - // Get global mutex on read. - // All high level function e.g. peers garbage collector - // or shutdown must use globalMutex.Lock instead - s.grpc.globalMutex.RLock() - - // Get connection item from connection book or create a new one. - // Concurrent map access resolved by bookMutex. - s.grpc.bookMutex.Lock() - - item, ok := s.grpc.connBook[addr] - if !ok { - item = new(connItem) - s.grpc.connBook[addr] = item - } - - s.grpc.bookMutex.Unlock() - - // Now lock connection item. - // This denies concurrent access to the same address, - // but allows concurrent access to a different addresses. - item.Lock() - - if item.conn != nil && !isGRPCClosed(item.conn) { - item.used = time.Now() - - item.Unlock() - s.grpc.globalMutex.RUnlock() - - return item.conn, nil - } - - // Если вышеописанные строки переместить внутрь WithDialer, - // мы получим сломанный коннекшн, но ошибка не будет возвращена, - // поэтому мы сначала проверяем коннекшн и лишь потом возвращаем - // *gRPC.ClientConn - // - // Это будет работать с `grpc.WithBlock()`, см. ниже - conn, err := grpc.DialContext(ctx, maddr.String(), - gRPCKeepAlive(s.pingTTL, s.keepAlive), - // TODO: we must provide grpc.WithInsecure() or set credentials - grpc.WithInsecure(), - grpc.WithBlock(), - grpc.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) { - return s.newConnection(ctx, maddr, reset) - }), - ) - if err == nil { - item.conn = conn - item.used = time.Now() - } - - item.Unlock() - s.grpc.globalMutex.RUnlock() - - return conn, err -} - -// New create iface instance and check arguments. -func New(p Params) (Interface, error) { - if p.Address == nil { - return nil, ErrEmptyAddress - } - - if p.Transport == nil { - return nil, ErrEmptyTransport - } - - if p.Attempts <= 0 { - p.Attempts = defaultAttemptsCount - } - - if p.AttemptsTTL <= 0 { - p.AttemptsTTL = defaultAttemptsTTL - } - - if p.ConnectionTTL <= 0 { - p.ConnectionTTL = defaultCloseTimer - } - - if p.ConnectionIDLE <= 0 { - p.ConnectionIDLE = defaultConIdleTTL - } - - if p.KeepAliveTTL <= 0 { - p.KeepAliveTTL = defaultKeepAliveTTL - } - - if p.KeepAlivePingTTL <= 0 { - p.KeepAlivePingTTL = defaultKeepAlivePingTTL - } - - if p.MetricsTimeout <= 0 { - p.MetricsTimeout = defaultMetricsTimeout - } - - return &iface{ - tick: p.ConnectionTTL, - idle: p.ConnectionIDLE, - - keepAlive: p.KeepAliveTTL, - pingTTL: p.KeepAlivePingTTL, - - metricsTimeout: p.MetricsTimeout, - - log: p.Logger, - addr: p.Address, - tr: p.Transport, - grpc: struct { - globalMutex *sync.RWMutex - bookMutex *sync.RWMutex - connBook map[string]*connItem - }{ - globalMutex: new(sync.RWMutex), - bookMutex: new(sync.RWMutex), - connBook: make(map[string]*connItem), - }, - cons: struct { - *sync.RWMutex - items map[string]transport.Connection - }{ - RWMutex: new(sync.RWMutex), - items: make(map[string]transport.Connection), - }, - lis: struct { - *sync.RWMutex - items map[string]manet.Listener - }{ - RWMutex: new(sync.RWMutex), - items: make(map[string]manet.Listener), - }, - }, nil -} diff --git a/lib/peers/peers_test.go b/lib/peers/peers_test.go deleted file mode 100644 index d71ef7b5..00000000 --- a/lib/peers/peers_test.go +++ /dev/null @@ -1,484 +0,0 @@ -package peers - -import ( - "context" - "encoding" - "encoding/json" - "net" - "strings" - "sync" - "testing" - "time" - - "github.com/multiformats/go-multiaddr" - manet "github.com/multiformats/go-multiaddr-net" - "github.com/nspcc-dev/neofs-node/lib/transport" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" -) - -type ( - fakeAddress struct { - json.Marshaler - json.Unmarshaler - encoding.TextMarshaler - encoding.TextUnmarshaler - encoding.BinaryMarshaler - encoding.BinaryUnmarshaler - } - - // service is used to implement GreaterServer. - service struct{} -) - -// Hello is simple handler -func (*service) Hello(ctx context.Context, req *HelloRequest) (*HelloResponse, error) { - return &HelloResponse{ - Message: "Hello " + req.Name, - }, nil -} - -var _ multiaddr.Multiaddr = (*fakeAddress)(nil) - -func (fakeAddress) Equal(multiaddr.Multiaddr) bool { - return false -} - -func (fakeAddress) Bytes() []byte { - return nil -} - -func (fakeAddress) String() string { - return "fake" -} - -func (fakeAddress) Protocols() []multiaddr.Protocol { - return []multiaddr.Protocol{{Name: "fake"}} -} - -func (fakeAddress) Encapsulate(multiaddr.Multiaddr) multiaddr.Multiaddr { - panic("implement me") -} - -func (fakeAddress) Decapsulate(multiaddr.Multiaddr) multiaddr.Multiaddr { - panic("implement me") -} - -func (fakeAddress) ValueForProtocol(code int) (string, error) { - return "", nil -} - -const testCount = 10 - -func newTestAddress(t *testing.T) multiaddr.Multiaddr { - lis, err := net.Listen("tcp", "0.0.0.0:0") // nolint:gosec - require.NoError(t, err) - require.NoError(t, lis.Close()) - - l, ok := lis.(*net.TCPListener) - require.True(t, ok) - - _, port, err := net.SplitHostPort(l.Addr().String()) - require.NoError(t, err) - - items := []string{ - "ip4", - "127.0.0.1", - "tcp", - port, - } - - maddr, err := multiaddr.NewMultiaddr("/" + strings.Join(items, "/")) - require.NoError(t, err) - - return maddr -} - -func createTestInterface(t *testing.T) Interface { - s, err := New(Params{ - Address: newTestAddress(t), - Transport: transport.New(5, time.Second), - }) - require.NoError(t, err) - return s -} - -func createTestInterfaces(t *testing.T) []Interface { - var ifaces = make([]Interface, 0, testCount) - for i := 0; i < testCount; i++ { - ifaces = append(ifaces, createTestInterface(t)) - } - return ifaces -} - -func connectEachOther(t *testing.T, ifaces []Interface) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - for _, s := range ifaces { - _, err := s.Listen(s.Address()) - require.NoError(t, err) - - for _, n := range ifaces { - if s.Address().Equal(n.Address()) { - continue // do not connect itself - } - - _, err = n.Connect(ctx, s.Address()) - require.NoError(t, err) - } - } -} - -func TestInterface(t *testing.T) { - t.Run("should fail on empty address", func(t *testing.T) { - _, err := New(Params{}) - require.EqualError(t, err, ErrEmptyAddress.Error()) - }) - - t.Run("should fail on empty transport", func(t *testing.T) { - _, err := New(Params{Address: newTestAddress(t)}) - require.EqualError(t, err, ErrEmptyTransport.Error()) - }) - - t.Run("try to create multiple Interface and connect each other", func(t *testing.T) { - ifaces := createTestInterfaces(t) - connectEachOther(t, ifaces) - }) - - t.Run("should fail on itself connection", func(t *testing.T) { - s := createTestInterface(t) - _, err := s.Connect(context.Background(), s.Address()) - require.EqualError(t, err, ErrDialToSelf.Error()) - }) - - t.Run("should fail when you try to remove closed connection", func(t *testing.T) { - s1, err := New(Params{ - Address: newTestAddress(t), - Transport: transport.New(5, time.Second), - }) - require.NoError(t, err) - - s2, err := New(Params{ - Address: newTestAddress(t), - Transport: transport.New(5, time.Second), - }) - require.NoError(t, err) - - _, err = s1.Listen(s1.Address()) - require.NoError(t, err) - - _, err = s2.Listen(s2.Address()) - require.NoError(t, err) - - con, err := s1.Connect(context.Background(), s2.Address()) - require.NoError(t, err) - require.NoError(t, con.Close()) - - err = s1.RemoveConnection(s2.Address()) - require.NoError(t, err) - }) - - t.Run("should not create connection / listener twice", func(t *testing.T) { - s1, err := New(Params{ - Address: newTestAddress(t), - Transport: transport.New(5, time.Second), - }) - require.NoError(t, err) - - s2, err := New(Params{ - Address: newTestAddress(t), - Transport: transport.New(5, time.Second), - }) - require.NoError(t, err) - - l1, err := s1.Listen(s1.Address()) - require.NoError(t, err) - - l2, err := s1.Listen(s1.Address()) - require.NoError(t, err) - - require.Equal(t, l1, l2) - - _, err = s2.Listen(s2.Address()) - require.NoError(t, err) - - c1, err := s1.Connect(context.Background(), s2.Address()) - require.NoError(t, err) - - c2, err := s1.Connect(context.Background(), s2.Address()) - require.NoError(t, err) - - require.Equal(t, c1, c2) - require.NoError(t, c1.Close()) - - err = s1.RemoveConnection(s2.Address()) - require.NoError(t, err) - }) - - t.Run("should not try to close unknown connection", func(t *testing.T) { - s1, err := New(Params{ - Address: newTestAddress(t), - Transport: transport.New(5, time.Second), - }) - require.NoError(t, err) - - s2, err := New(Params{ - Address: newTestAddress(t), - Transport: transport.New(5, time.Second), - }) - require.NoError(t, err) - - l1, err := s1.Listen(s1.Address()) - require.NoError(t, err) - - l2, err := s1.Listen(s1.Address()) - require.NoError(t, err) - - require.Equal(t, l1, l2) - - _, err = s2.Listen(s2.Address()) - require.NoError(t, err) - - _, err = s1.Connect(context.Background(), s2.Address()) - require.NoError(t, err) - - err = s1.RemoveConnection(s2.Address()) - require.NoError(t, err) - - err = s1.RemoveConnection(s2.Address()) - require.NoError(t, err) - }) - - t.Run("should shutdown without errors", func(t *testing.T) { - s1, err := New(Params{ - Address: newTestAddress(t), - Transport: transport.New(5, time.Second), - }) - require.NoError(t, err) - - s2, err := New(Params{ - Address: newTestAddress(t), - Transport: transport.New(5, time.Second), - }) - require.NoError(t, err) - - l1, err := s1.Listen(s1.Address()) - require.NoError(t, err) - - l2, err := s1.Listen(s1.Address()) - require.NoError(t, err) - - require.Equal(t, l1, l2) - - _, err = s2.Listen(s2.Address()) - require.NoError(t, err) - - _, err = s1.Connect(context.Background(), s2.Address()) - require.NoError(t, err) - - err = s1.Shutdown() - require.NoError(t, err) - - err = s2.Shutdown() - require.NoError(t, err) - }) - - t.Run("should fail, when shutdown with closed connections or listeners", func(t *testing.T) { - s1, err := New(Params{ - Address: newTestAddress(t), - Transport: transport.New(5, time.Second), - }) - require.NoError(t, err) - - s2, err := New(Params{ - Address: newTestAddress(t), - Transport: transport.New(5, time.Second), - }) - require.NoError(t, err) - - l1, err := s1.Listen(s1.Address()) - require.NoError(t, err) - - l2, err := s1.Listen(s1.Address()) - require.NoError(t, err) - - require.Equal(t, l1, l2) - - lis, err := s2.Listen(s2.Address()) - require.NoError(t, err) - - con, err := s1.Connect(context.Background(), s2.Address()) - require.NoError(t, err) - - require.NoError(t, con.Close()) - - err = s1.Shutdown() - require.NoError(t, err) - - require.NoError(t, lis.Close()) - - err = s2.Shutdown() - require.Error(t, err) - }) - - t.Run("circuit breaker should start fail connection after N-fails", func(t *testing.T) { - s1, err := New(Params{ - Address: newTestAddress(t), - Transport: transport.New(5, time.Second), - }) - require.NoError(t, err) - - addr := newTestAddress(t) - for i := 0; i < defaultAttemptsCount*2; i++ { - _, err = s1.Connect(context.Background(), addr) - require.Error(t, err) - - if i+1 == defaultAttemptsCount { - _, err = s1.Listen(addr) - require.NoError(t, err) - } - } - }) - - t.Run("should return error on bad multi-address", func(t *testing.T) { - s1, err := New(Params{ - Address: newTestAddress(t), - Transport: transport.New(5, time.Second), - }) - require.NoError(t, err) - - _, err = s1.Listen(&fakeAddress{}) - require.Error(t, err) - }) - - t.Run("gRPC connection test", func(t *testing.T) { - var ( - err error - s1, s2 Interface - h = &service{} - g = grpc.NewServer() - a1, a2 = newTestAddress(t), newTestAddress(t) - tr = transport.New(5, time.Second) - _ = h - done = make(chan struct{}) - ) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - s1, err = New(Params{ - Address: a1, - Transport: tr, - }) - require.NoError(t, err) - - s2, err = New(Params{ - Address: a2, - Transport: tr, - }) - require.NoError(t, err) - - RegisterGreeterServer(g, h) // register service - - l, err := s1.Listen(a1) - require.NoError(t, err) - - defer l.Close() // nolint:golint - - wg := new(sync.WaitGroup) - wg.Add(1) - - go func() { - close(done) - - _ = g.Serve(manet.NetListener(l)) - - wg.Done() - }() - - <-done // wait for server is start listening connections: - - // Fail connection - con, err := s2.GRPCConnection(ctx, &fakeAddress{}, false) - require.Nil(t, con) - require.Error(t, err) - - con, err = s2.GRPCConnection(ctx, a1, false) - require.NoError(t, err) - - cli := NewGreeterClient(con) - resp, err := cli.Hello(ctx, &HelloRequest{ - Name: "Interface test", - }) - require.NoError(t, err) - require.NotNil(t, resp) - require.Equal(t, "Hello Interface test", resp.Message) - - g.GracefulStop() - - wg.Wait() - }) - - t.Run("test grpc connections", func(t *testing.T) { - var ( - ifaces = make([]Interface, 0, testCount) - addresses = make([]multiaddr.Multiaddr, 0, testCount) - ) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - for i := 0; i < testCount; i++ { - addresses = append(addresses, newTestAddress(t)) - - s, err := New(Params{ - Address: addresses[i], - Transport: transport.New(5, time.Second), - }) - require.NoError(t, err) - - lis, err := s.Listen(addresses[i]) - require.NoError(t, err) - - svc := &service{} - srv := grpc.NewServer() - - RegisterGreeterServer(srv, svc) - - ifaces = append(ifaces, s) - - go func() { - l := manet.NetListener(lis) - require.NoError(t, srv.Serve(l)) - }() - } - - const reqName = "test" - wg := new(sync.WaitGroup) - - for i := 0; i < testCount; i++ { - for j := 0; j < testCount; j++ { - wg.Add(1) - go func(i, j int) { - defer wg.Done() - - con, err := ifaces[i].GRPCConnection(ctx, addresses[j], false) - require.NoError(t, err) - - cli := NewGreeterClient(con) - - resp, err := cli.Hello(ctx, &HelloRequest{Name: reqName}) - require.NoError(t, err) - - require.Equal(t, "Hello "+reqName, resp.Message) - - require.NoError(t, con.Close()) - }(i, j) - - } - } - - wg.Wait() - }) -} diff --git a/lib/storage/storage.go b/lib/storage/storage.go deleted file mode 100644 index 11775c3d..00000000 --- a/lib/storage/storage.go +++ /dev/null @@ -1,122 +0,0 @@ -package storage - -import ( - "io" - - "github.com/nspcc-dev/neofs-node/lib/buckets" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/pkg/errors" - "github.com/spf13/viper" - "go.uber.org/zap" -) - -type ( - store struct { - blob core.Bucket - - meta core.Bucket - - spaceMetrics core.Bucket - } - - sizer interface { - Size() int64 - } - - // Params for create Core.Storage component - Params struct { - Buckets []core.BucketType - Viper *viper.Viper - Logger *zap.Logger - } -) - -// New creates Core.Storage component. -func New(p Params) (core.Storage, error) { - var ( - err error - bs = make(map[core.BucketType]core.Bucket) - ) - - for _, name := range p.Buckets { - if bs[name], err = buckets.NewBucket(name, p.Logger, p.Viper); err != nil { - return nil, err - } - } - - return &store{ - blob: bs[core.BlobStore], - - meta: bs[core.MetaStore], - - spaceMetrics: bs[core.SpaceMetricsStore], - }, nil -} - -// GetBucket returns available bucket by type or an error. -func (s *store) GetBucket(name core.BucketType) (core.Bucket, error) { - switch name { - case core.BlobStore: - if s.blob == nil { - return nil, errors.Errorf("bucket(`%s`) not initialized", core.BlobStore) - } - - return s.blob, nil - case core.MetaStore: - if s.meta == nil { - return nil, errors.Errorf("bucket(`%s`) not initialized", core.MetaStore) - } - - return s.meta, nil - case core.SpaceMetricsStore: - if s.spaceMetrics == nil { - return nil, errors.Errorf("bucket(`%s`) not initialized", core.SpaceMetricsStore) - } - - return s.spaceMetrics, nil - default: - return nil, errors.Errorf("bucket for type `%s` not implemented", name) - } -} - -// Size of all buckets. -func (s *store) Size() int64 { - var ( - all int64 - sizers = []sizer{ - s.blob, - s.meta, - s.spaceMetrics, - } - ) - - for _, item := range sizers { - if item == nil { - continue - } - - all += item.Size() - } - - return all -} - -// Close all buckets. -func (s *store) Close() error { - var closers = []io.Closer{ - s.blob, - s.meta, - } - - for _, item := range closers { - if item == nil { - continue - } - - if err := item.Close(); err != nil { - return err - } - } - - return nil -} diff --git a/lib/transport/connection.go b/lib/transport/connection.go deleted file mode 100644 index bb051b4a..00000000 --- a/lib/transport/connection.go +++ /dev/null @@ -1,39 +0,0 @@ -package transport - -import ( - "sync/atomic" - - manet "github.com/multiformats/go-multiaddr-net" -) - -type ( - // Connection is an interface of network connection. - Connection interface { - manet.Conn - Closed() bool - } - - conn struct { - manet.Conn - closed *int32 - } -) - -func newConnection(con manet.Conn) Connection { - return &conn{ - Conn: con, - closed: new(int32), - } -} - -// Closed checks that connection closed. -func (c *conn) Closed() bool { return atomic.LoadInt32(c.closed) == 1 } - -// Close connection and write state. -func (c *conn) Close() error { - if atomic.CompareAndSwapInt32(c.closed, 0, 1) { - return c.Conn.Close() - } - - return nil -} diff --git a/lib/transport/transport.go b/lib/transport/transport.go deleted file mode 100644 index 4e06fedd..00000000 --- a/lib/transport/transport.go +++ /dev/null @@ -1,76 +0,0 @@ -package transport - -import ( - "context" - "fmt" - "time" - - "github.com/multiformats/go-multiaddr" - manet "github.com/multiformats/go-multiaddr-net" - circuit "github.com/rubyist/circuitbreaker" -) - -type ( - // Transport is an interface of network connection listener. - Transport interface { - Dial(context.Context, multiaddr.Multiaddr, bool) (Connection, error) - Listen(multiaddr.Multiaddr) (manet.Listener, error) - } - - transport struct { - threshold int64 - timeout time.Duration - panel *circuit.Panel - } -) - -const defaultBreakerName = "_NeoFS" - -func (t *transport) Dial(ctx context.Context, addr multiaddr.Multiaddr, reset bool) (Connection, error) { - var ( - con manet.Conn - breaker = t.breakerLookup(addr) - ) - - if reset { - breaker.Reset() - } - - err := breaker.CallContext(ctx, func() (errCall error) { - var d manet.Dialer - con, errCall = d.DialContext(ctx, addr) - return errCall - }, t.timeout) - - if err != nil { - return nil, err - } - - return newConnection(con), nil -} - -func (t *transport) Listen(addr multiaddr.Multiaddr) (manet.Listener, error) { - return manet.Listen(addr) -} - -func (t *transport) breakerLookup(addr fmt.Stringer) *circuit.Breaker { - panel := defaultBreakerName + addr.String() - - cb, ok := t.panel.Get(panel) - if !ok { - cb = circuit.NewConsecutiveBreaker(t.threshold) - t.panel.Add(panel, cb) - } - - return cb -} - -// New is a transport component's constructor. -func New(threshold int64, timeout time.Duration) Transport { - breaker := circuit.NewConsecutiveBreaker(threshold) - - panel := circuit.NewPanel() - panel.Add(defaultBreakerName, breaker) - - return &transport{panel: panel, threshold: threshold, timeout: timeout} -} diff --git a/lib/transport/transport_test.go b/lib/transport/transport_test.go deleted file mode 100644 index bd3bd283..00000000 --- a/lib/transport/transport_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package transport - -import ( - "context" - "net" - "testing" - "time" - - manet "github.com/multiformats/go-multiaddr-net" - circuit "github.com/rubyist/circuitbreaker" - "github.com/stretchr/testify/require" -) - -func TestTransport(t *testing.T) { - var ( - attempts = int64(5) - lc net.ListenConfig - tr = New(attempts, time.Second) - ctx, cancel = context.WithCancel(context.TODO()) - ) - - defer cancel() - - lis1, err := lc.Listen(ctx, "tcp", ":0") - require.NoError(t, err) - - addr1, err := manet.FromNetAddr(lis1.Addr()) - require.NoError(t, err) - - _, err = tr.Dial(ctx, addr1, false) - require.NoError(t, err) - - lis2, err := lc.Listen(ctx, "tcp", ":0") - require.NoError(t, err) - - addr2, err := manet.FromNetAddr(lis2.Addr()) - require.NoError(t, err) - - _, err = tr.Dial(ctx, addr1, false) - require.NoError(t, err) - - require.NoError(t, lis1.Close()) - - for i := int64(0); i < 10; i++ { - _, err = tr.Dial(ctx, addr1, false) - require.Error(t, err) - - if i >= attempts { - require.EqualError(t, err, circuit.ErrBreakerOpen.Error()) - } - - _, err = tr.Dial(ctx, addr2, false) - require.NoError(t, err) - } - - time.Sleep(time.Second) - - _, err = tr.Dial(ctx, addr1, false) - require.Error(t, err) - require.NotContains(t, err.Error(), circuit.ErrBreakerOpen.Error()) -} diff --git a/misc/build.go b/misc/build.go index f4dab306..61b4613e 100644 --- a/misc/build.go +++ b/misc/build.go @@ -1,12 +1,21 @@ package misc const ( - // NodeName is an application name. + // NodeName is a neofs node application name. NodeName = "neofs-node" - // Prefix is an application prefix. + // Prefix is a neofs node application prefix. Prefix = "neofs" + // InnerRingName is an inner ring application name. + InnerRingName = "neofs-ir" + + // InnerRingPrefix is an inner ring application prefix. + InnerRingPrefix = "neofs_ir" +) + +// These variables are changed in compile time. +var ( // Build is an application build time. Build = "now" diff --git a/modules/morph/goclient.go b/modules/morph/goclient.go deleted file mode 100644 index dd0359f2..00000000 --- a/modules/morph/goclient.go +++ /dev/null @@ -1,32 +0,0 @@ -package morph - -import ( - "context" - "crypto/ecdsa" - - "github.com/nspcc-dev/neo-go/pkg/config/netmode" - "github.com/nspcc-dev/neofs-node/lib/blockchain/goclient" - "github.com/spf13/viper" - "go.uber.org/dig" - "go.uber.org/zap" -) - -type morphClientParams struct { - dig.In - - Viper *viper.Viper - - Logger *zap.Logger - - Key *ecdsa.PrivateKey -} - -func newMorphClient(p morphClientParams) (*goclient.Client, error) { - return goclient.New(context.Background(), &goclient.Params{ - Log: p.Logger, - Key: p.Key, - Endpoint: p.Viper.GetString(optPath(prefix, endpointOpt)), - DialTimeout: p.Viper.GetDuration(optPath(prefix, dialTimeoutOpt)), - Magic: netmode.Magic(p.Viper.GetUint32(optPath(prefix, magicNumberOpt))), - }) -} diff --git a/modules/morph/reputation.go b/modules/morph/reputation.go deleted file mode 100644 index e8c12434..00000000 --- a/modules/morph/reputation.go +++ /dev/null @@ -1,59 +0,0 @@ -package morph - -import ( - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/peers" - "github.com/pkg/errors" - "go.uber.org/dig" -) - -type reputationContractResult struct { - dig.Out - - ReputationContract implementations.MorphReputationContract -} - -const ( - reputationContractName = "reputation" - - reputationContractPutOpt = "put_method" - - reputationContractListOpt = "list_method" -) - -// ReputationContractPutOptPath returns the config path to put method of Reputation contract. -func ReputationContractPutOptPath() string { - return optPath(prefix, reputationContractName, reputationContractPutOpt) -} - -// ReputationContractListOptPath returns the config path to list method of Reputation contract. -func ReputationContractListOptPath() string { - return optPath(prefix, reputationContractName, reputationContractListOpt) -} - -func newReputationContract(p contractParams, ps peers.Store) (res reputationContractResult, err error) { - cli, ok := p.MorphContracts[reputationContractName] - if !ok { - err = errors.Errorf("missing %s contract client", reputationContractName) - return - } - - morphClient := implementations.MorphReputationContract{} - morphClient.SetReputationContractClient(cli) - morphClient.SetPublicKeyStore(ps) - - morphClient.SetPutMethodName( - p.Viper.GetString( - ReputationContractPutOptPath(), - ), - ) - morphClient.SetListMethodName( - p.Viper.GetString( - ReputationContractListOptPath(), - ), - ) - - res.ReputationContract = morphClient - - return -} diff --git a/modules/node/core.go b/modules/node/core.go deleted file mode 100644 index 665836ee..00000000 --- a/modules/node/core.go +++ /dev/null @@ -1,29 +0,0 @@ -package node - -import ( - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/lib/storage" - "github.com/spf13/viper" - "go.uber.org/zap" -) - -func listBuckets(v *viper.Viper) []core.BucketType { - var ( - items = v.GetStringMap("storage") - result = make([]core.BucketType, 0, len(items)) - ) - - for name := range items { - result = append(result, core.BucketType(name)) - } - - return result -} - -func newStorage(l *zap.Logger, v *viper.Viper) (core.Storage, error) { - return storage.New(storage.Params{ - Viper: v, - Logger: l, - Buckets: listBuckets(v), - }) -} diff --git a/modules/node/placement.go b/modules/node/placement.go deleted file mode 100644 index 9834f7b6..00000000 --- a/modules/node/placement.go +++ /dev/null @@ -1,33 +0,0 @@ -package node - -import ( - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/placement" - "go.uber.org/dig" -) - -type ( - placementToolParams struct { - dig.In - - Placement placement.Component - } - - placementToolResult struct { - dig.Out - - Placer implementations.ObjectPlacer - - Receiver implementations.EpochReceiver - } -) - -func newPlacementTool(p placementToolParams) (res placementToolResult, err error) { - if res.Placer, err = implementations.NewObjectPlacer(p.Placement); err != nil { - return - } - - res.Receiver = res.Placer - - return -} diff --git a/modules/node/services.go b/modules/node/services.go deleted file mode 100644 index d6c3cadc..00000000 --- a/modules/node/services.go +++ /dev/null @@ -1,36 +0,0 @@ -package node - -import ( - "github.com/nspcc-dev/neofs-node/modules/grpc" - "github.com/nspcc-dev/neofs-node/services/metrics" - "github.com/nspcc-dev/neofs-node/services/public/accounting" - "github.com/nspcc-dev/neofs-node/services/public/container" - "github.com/nspcc-dev/neofs-node/services/public/object" - "github.com/nspcc-dev/neofs-node/services/public/session" - "github.com/nspcc-dev/neofs-node/services/public/state" - "go.uber.org/dig" -) - -type servicesParams struct { - dig.In - - Status state.Service - Container container.Service - Object object.Service - Session session.Service - Accounting accounting.Service - Metrics metrics.Service -} - -func attachServices(p servicesParams) grpc.ServicesResult { - return grpc.ServicesResult{ - Services: []grpc.Service{ - p.Status, - p.Container, - p.Accounting, - p.Metrics, - p.Session, - p.Object, - }, - } -} diff --git a/pkg/core/container/acl/basic/enum.go b/pkg/core/container/acl/basic/enum.go new file mode 100644 index 00000000..e4de0cdd --- /dev/null +++ b/pkg/core/container/acl/basic/enum.go @@ -0,0 +1,24 @@ +package basic + +const ( + // OpGetRangeHash is an index of GetRangeHash operation in basic ACL bitmask order. + OpGetRangeHash uint8 = iota + + // OpGetRange is an index of GetRange operation in basic ACL bitmask order. + OpGetRange + + // OpSearch is an index of Search operation in basic ACL bitmask order. + OpSearch + + // OpDelete is an index of Delete operation in basic ACL bitmask order. + OpDelete + + // OpPut is an index of Put operation in basic ACL bitmask order. + OpPut + + // OpHead is an index of Head operation in basic ACL bitmask order. + OpHead + + // OpGet is an index of Get operation in basic ACL bitmask order. + OpGet +) diff --git a/pkg/core/container/acl/basic/types.go b/pkg/core/container/acl/basic/types.go new file mode 100644 index 00000000..682fff36 --- /dev/null +++ b/pkg/core/container/acl/basic/types.go @@ -0,0 +1,159 @@ +package basic + +// ACL represents a container's +// basic permission bits. +type ACL uint32 + +const ( + reservedBitNumber = 2 // first left bits are reserved + + stickyBitPos = reservedBitNumber // X-bit after reserved bits + + finalBitPos = stickyBitPos + 1 // F-bit after X-bit +) + +const ( + opOffset = finalBitPos + 1 // offset of operation bits + + bitsPerOp = 4 // number of bits per operation + + opNumber = 7 // number of operation bit sections +) + +const ( + bitUser uint8 = iota + bitSystem + bitOthers + bitBearer +) + +const leftACLBitPos = opOffset + bitsPerOp*opNumber - 1 + +// returns true if n-th left bit is set (starting at 0). +func isLeftBitSet(value ACL, n uint8) bool { + bitMask := ACL(1 << (leftACLBitPos - n)) + return bitMask != 0 && value&bitMask == bitMask +} + +// sets n-th left bit (starting at 0). +func setLeftBit(value *ACL, n uint8) { + *value |= ACL(1 << (leftACLBitPos - n)) +} + +// resets n-th left bit (starting at 0). +func resetLeftBit(value *ACL, n uint8) { + *value &= ^ACL(1 << (leftACLBitPos - n)) +} + +// Reserved returns true if n-th reserved option is enabled in basic ACL. +func (a ACL) Reserved(n uint8) bool { + return n < reservedBitNumber && isLeftBitSet(a, n) +} + +// SetReserved enables the n-th reserved option in basic ACL. +func (a *ACL) SetReserved(bit uint8) { + if bit < reservedBitNumber { + setLeftBit(a, bit) + } +} + +// ResetReserved disables the n-th reserved option in basic ACL. +func (a *ACL) ResetReserved(bit uint8) { + if bit < reservedBitNumber { + resetLeftBit(a, bit) + } +} + +// Final returns true if final option is enabled in basic ACL. +func (a ACL) Final() bool { + return isLeftBitSet(a, finalBitPos) +} + +// SetFinal enables final option in basic ACL. +func (a *ACL) SetFinal() { + setLeftBit(a, finalBitPos) +} + +// ResetFinal disables final option in basic ACL. +func (a *ACL) ResetFinal() { + resetLeftBit(a, finalBitPos) +} + +// Sticky returns true if sticky option is enabled in basic ACL. +func (a ACL) Sticky() bool { + return isLeftBitSet(a, stickyBitPos) +} + +// SetSticky enables the sticky option in basic ACL. +func (a *ACL) SetSticky() { + setLeftBit(a, stickyBitPos) +} + +// ResetSticky disables the sticky option in basic ACL. +func (a *ACL) ResetSticky() { + resetLeftBit(a, stickyBitPos) +} + +// UserAllowed returns true if user allowed the n-th operation in basic ACL. +func (a ACL) UserAllowed(n uint8) bool { + return isLeftBitSet(a, opOffset+n*bitsPerOp+bitUser) +} + +// AllowUser allows user the n-th operation in basic ACL. +func (a *ACL) AllowUser(n uint8) { + setLeftBit(a, opOffset+n*bitsPerOp+bitUser) +} + +// ForbidUser forbids user the n-th operation in basic ACL. +func (a *ACL) ForbidUser(n uint8) { + resetLeftBit(a, opOffset+n*bitsPerOp+bitUser) +} + +// SystemAllowed returns true if System group allowed the n-th operation is set in basic ACL. +func (a ACL) SystemAllowed(n uint8) bool { + if n != OpDelete && n != OpGetRange { + return true + } + + return isLeftBitSet(a, opOffset+n*bitsPerOp+bitSystem) +} + +// AllowSystem allows System group the n-th operation in basic ACL. +func (a *ACL) AllowSystem(op uint8) { + setLeftBit(a, opOffset+op*bitsPerOp+bitSystem) +} + +// ForbidSystem forbids System group the n-th operation in basic ACL. +func (a *ACL) ForbidSystem(op uint8) { + resetLeftBit(a, opOffset+op*bitsPerOp+bitSystem) +} + +// OthersAllowed returns true if Others group allowed the n-th operation is set in basic ACL. +func (a ACL) OthersAllowed(op uint8) bool { + return isLeftBitSet(a, opOffset+op*bitsPerOp+bitOthers) +} + +// AllowOthers allows Others group the n-th operation in basic ACL. +func (a *ACL) AllowOthers(op uint8) { + setLeftBit(a, opOffset+op*bitsPerOp+bitOthers) +} + +// ForbidOthers forbids Others group the n-th operation in basic ACL. +func (a *ACL) ForbidOthers(op uint8) { + resetLeftBit(a, opOffset+op*bitsPerOp+bitOthers) +} + +// BearerAllowed returns true if Bearer token usage is allowed for n-th operation in basic ACL. +func (a ACL) BearerAllowed(op uint8) bool { + return isLeftBitSet(a, opOffset+op*bitsPerOp+bitBearer) +} + +// AllowBearer allows Bearer token usage for n-th operation in basic ACL. +func (a *ACL) AllowBearer(op uint8) { + setLeftBit(a, opOffset+op*bitsPerOp+bitBearer) +} + +// ForbidBearer forbids Bearer token usage for n-th operation in basic ACL. +func (a *ACL) ForbidBearer(op uint8) { + resetLeftBit(a, opOffset+op*bitsPerOp+bitBearer) +} diff --git a/pkg/core/container/acl/basic/types_test.go b/pkg/core/container/acl/basic/types_test.go new file mode 100644 index 00000000..7628aec1 --- /dev/null +++ b/pkg/core/container/acl/basic/types_test.go @@ -0,0 +1,189 @@ +package basic + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestACLValues(t *testing.T) { + t.Run("private", func(t *testing.T) { + acl := FromUint32(0x1C8C8CCC) + + require.False(t, acl.Reserved(0)) + require.False(t, acl.Reserved(1)) + require.False(t, acl.Sticky()) + require.True(t, acl.Final()) + + require.True(t, acl.UserAllowed(OpGetRangeHash)) + require.True(t, acl.SystemAllowed(OpGetRangeHash)) + require.False(t, acl.OthersAllowed(OpGetRangeHash)) + require.False(t, acl.BearerAllowed(OpGetRangeHash)) + + require.True(t, acl.UserAllowed(OpGetRange)) + require.False(t, acl.SystemAllowed(OpGetRange)) + require.False(t, acl.OthersAllowed(OpGetRange)) + require.False(t, acl.BearerAllowed(OpGetRange)) + + require.True(t, acl.UserAllowed(OpSearch)) + require.True(t, acl.SystemAllowed(OpSearch)) + require.False(t, acl.OthersAllowed(OpSearch)) + require.False(t, acl.BearerAllowed(OpSearch)) + + require.True(t, acl.UserAllowed(OpDelete)) + require.False(t, acl.SystemAllowed(OpDelete)) + require.False(t, acl.OthersAllowed(OpDelete)) + require.False(t, acl.BearerAllowed(OpDelete)) + + require.True(t, acl.UserAllowed(OpPut)) + require.True(t, acl.SystemAllowed(OpPut)) + require.False(t, acl.OthersAllowed(OpPut)) + require.False(t, acl.BearerAllowed(OpPut)) + + require.True(t, acl.UserAllowed(OpHead)) + require.True(t, acl.SystemAllowed(OpHead)) + require.False(t, acl.OthersAllowed(OpHead)) + require.False(t, acl.BearerAllowed(OpHead)) + + require.True(t, acl.UserAllowed(OpGet)) + require.True(t, acl.SystemAllowed(OpGet)) + require.False(t, acl.OthersAllowed(OpGet)) + require.False(t, acl.BearerAllowed(OpGet)) + }) + + t.Run("public with X-bit", func(t *testing.T) { + acl := FromUint32(0x3FFFFFFF) + + require.False(t, acl.Reserved(0)) + require.False(t, acl.Reserved(1)) + require.True(t, acl.Sticky()) + require.True(t, acl.Final()) + + require.True(t, acl.UserAllowed(OpGetRangeHash)) + require.True(t, acl.SystemAllowed(OpGetRangeHash)) + require.True(t, acl.OthersAllowed(OpGetRangeHash)) + require.True(t, acl.BearerAllowed(OpGetRangeHash)) + + require.True(t, acl.UserAllowed(OpGetRange)) + require.True(t, acl.SystemAllowed(OpGetRange)) + require.True(t, acl.OthersAllowed(OpGetRange)) + require.True(t, acl.BearerAllowed(OpGetRange)) + + require.True(t, acl.UserAllowed(OpSearch)) + require.True(t, acl.SystemAllowed(OpSearch)) + require.True(t, acl.OthersAllowed(OpSearch)) + require.True(t, acl.BearerAllowed(OpSearch)) + + require.True(t, acl.UserAllowed(OpDelete)) + require.True(t, acl.SystemAllowed(OpDelete)) + require.True(t, acl.OthersAllowed(OpDelete)) + require.True(t, acl.BearerAllowed(OpDelete)) + + require.True(t, acl.UserAllowed(OpPut)) + require.True(t, acl.SystemAllowed(OpPut)) + require.True(t, acl.OthersAllowed(OpPut)) + require.True(t, acl.BearerAllowed(OpPut)) + + require.True(t, acl.UserAllowed(OpHead)) + require.True(t, acl.SystemAllowed(OpHead)) + require.True(t, acl.OthersAllowed(OpHead)) + require.True(t, acl.BearerAllowed(OpHead)) + + require.True(t, acl.UserAllowed(OpGet)) + require.True(t, acl.SystemAllowed(OpGet)) + require.True(t, acl.OthersAllowed(OpGet)) + require.True(t, acl.BearerAllowed(OpGet)) + }) + + t.Run("read only", func(t *testing.T) { + acl := FromUint32(0x1FFFCCFF) + + require.False(t, acl.Reserved(0)) + require.False(t, acl.Reserved(1)) + require.False(t, acl.Sticky()) + require.True(t, acl.Final()) + + require.True(t, acl.UserAllowed(OpGetRangeHash)) + require.True(t, acl.SystemAllowed(OpGetRangeHash)) + require.True(t, acl.OthersAllowed(OpGetRangeHash)) + require.True(t, acl.BearerAllowed(OpGetRangeHash)) + + require.True(t, acl.UserAllowed(OpGetRange)) + require.True(t, acl.SystemAllowed(OpGetRange)) + require.True(t, acl.OthersAllowed(OpGetRange)) + require.True(t, acl.BearerAllowed(OpGetRange)) + + require.True(t, acl.UserAllowed(OpSearch)) + require.True(t, acl.SystemAllowed(OpSearch)) + require.True(t, acl.OthersAllowed(OpSearch)) + require.True(t, acl.BearerAllowed(OpSearch)) + + require.True(t, acl.UserAllowed(OpDelete)) + require.True(t, acl.SystemAllowed(OpDelete)) + require.False(t, acl.OthersAllowed(OpDelete)) + require.False(t, acl.BearerAllowed(OpDelete)) + + require.True(t, acl.UserAllowed(OpPut)) + require.True(t, acl.SystemAllowed(OpPut)) + require.False(t, acl.OthersAllowed(OpPut)) + require.False(t, acl.BearerAllowed(OpPut)) + + require.True(t, acl.UserAllowed(OpHead)) + require.True(t, acl.SystemAllowed(OpHead)) + require.True(t, acl.OthersAllowed(OpHead)) + require.True(t, acl.BearerAllowed(OpHead)) + + require.True(t, acl.UserAllowed(OpGet)) + require.True(t, acl.SystemAllowed(OpGet)) + require.True(t, acl.OthersAllowed(OpGet)) + require.True(t, acl.BearerAllowed(OpGet)) + }) +} + +func TestACLMethods(t *testing.T) { + acl := new(ACL) + + for i := uint8(0); i < reservedBitNumber; i++ { + acl.SetReserved(i) + require.True(t, acl.Reserved(i)) + acl.ResetReserved(i) + require.False(t, acl.Reserved(i)) + } + + acl.SetSticky() + require.True(t, acl.Sticky()) + acl.ResetSticky() + require.False(t, acl.Sticky()) + + acl.SetFinal() + require.True(t, acl.Final()) + acl.ResetFinal() + require.False(t, acl.Final()) + + for i := OpGetRangeHash; i <= OpGet; i++ { + acl.AllowUser(i) + require.True(t, acl.UserAllowed(i)) + acl.ForbidUser(i) + require.False(t, acl.UserAllowed(i)) + + acl.AllowOthers(i) + require.True(t, acl.OthersAllowed(i)) + acl.ForbidOthers(i) + require.False(t, acl.OthersAllowed(i)) + + acl.AllowBearer(i) + require.True(t, acl.BearerAllowed(i)) + acl.ForbidBearer(i) + require.False(t, acl.BearerAllowed(i)) + + acl.AllowSystem(i) + require.True(t, acl.SystemAllowed(i)) + acl.ForbidSystem(i) + + if i == OpDelete || i == OpGetRange { + require.False(t, acl.SystemAllowed(i)) + } else { + require.True(t, acl.SystemAllowed(i)) + } + } +} diff --git a/pkg/core/container/acl/basic/util.go b/pkg/core/container/acl/basic/util.go new file mode 100644 index 00000000..77ab5829 --- /dev/null +++ b/pkg/core/container/acl/basic/util.go @@ -0,0 +1,64 @@ +package basic + +import ( + "encoding/binary" + "io" +) + +// Size is a size of ACL +// in a binary form. +const Size = 4 + +// FromUint32 converts builtin +// uint32 value to ACL. +// +// Try to avoid direct cast for +// better portability. +func FromUint32(v uint32) ACL { + return ACL(v) +} + +// ToUint32 converts ACL value +// to builtin uint32. +// +// Try to avoid direct cast for +// better portability. +func ToUint32(v ACL) uint32 { + return uint32(v) +} + +// Equal reports whether e and e2 are the same ACL. +// +// Function defines the relation of equality +// between two ACL. Try to avoid comparison through +// "==" operator for better portability. +func Equal(a, b ACL) bool { + return ToUint32(a) == ToUint32(b) +} + +// Marshal encodes ACL into a +// binary form and returns the result. +// +// Result slice has Size length. +func Marshal(a ACL) []byte { + d := make([]byte, Size) + + binary.BigEndian.PutUint32(d, ToUint32(a)) + + return d +} + +// UnmarshalBinary unmarshals ACL from +// a binary representation. +// +// If buffer size is insufficient, +// io.ErrUnexpectedEOF is returned. +func (a *ACL) UnmarshalBinary(data []byte) error { + if len(data) < Size { + return io.ErrUnexpectedEOF + } + + *a = FromUint32(binary.BigEndian.Uint32(data)) + + return nil +} diff --git a/pkg/core/container/acl/basic/util_test.go b/pkg/core/container/acl/basic/util_test.go new file mode 100644 index 00000000..f48eecf2 --- /dev/null +++ b/pkg/core/container/acl/basic/util_test.go @@ -0,0 +1,36 @@ +package basic + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestEqual(t *testing.T) { + require.True(t, + Equal( + FromUint32(1), + FromUint32(1), + ), + ) + + require.False(t, + Equal( + FromUint32(1), + FromUint32(2), + ), + ) +} + +func TestMarshal(t *testing.T) { + a := FromUint32(1) + a2 := new(ACL) + + require.NoError(t, + a2.UnmarshalBinary( + Marshal(a), + ), + ) + + require.True(t, Equal(a, *a2)) +} diff --git a/pkg/core/container/acl/extended/storage/storage.go b/pkg/core/container/acl/extended/storage/storage.go new file mode 100644 index 00000000..bdd0559b --- /dev/null +++ b/pkg/core/container/acl/extended/storage/storage.go @@ -0,0 +1,53 @@ +package storage + +import ( + "errors" + + eacl "github.com/nspcc-dev/neofs-api-go/acl/extended" + "github.com/nspcc-dev/neofs-node/pkg/core/container" +) + +// CID represents the container identifier. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/container.ID. +type CID = container.ID + +// Table represents extended ACL rule table. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/container/eacl/extended.Table. +type Table = eacl.Table + +// Storage is the interface that wraps +// basic methods of extended ACL table storage. +type Storage interface { + // GetEACL reads the table from the storage by identifier. + // It returns any error encountered. + // + // GetEACL must return exactly one non-nil value. + // GetEACL must return ErrNotFound if the table is not in storage. + // + // Implementations must not retain or modify the table + // (even temporarily). + GetEACL(CID) (Table, error) + + // PutEACL saves the table to the underlying storage. + // It returns any error encountered that caused the saving to interrupt. + // + // PutEACL must return extended.ErrNilTable on nil table. + // + // Implementations must not retain or modify the table (even temporarily). + // + // Table rewriting behavior is dictated by implementation. + PutEACL(CID, Table, []byte) error +} + +// ErrNotFound is the error returned when eACL table +// was not found in storage. +var ErrNotFound = errors.New("container not found") + +// ErrNilStorage is the error returned by functions that +// expect a non-nil eACL table storage implementation, +// but received nil. +var ErrNilStorage = errors.New("eACL storage is nil") diff --git a/pkg/core/container/acl/extended/storage/test/storage.go b/pkg/core/container/acl/extended/storage/test/storage.go new file mode 100644 index 00000000..7a5242e5 --- /dev/null +++ b/pkg/core/container/acl/extended/storage/test/storage.go @@ -0,0 +1,48 @@ +package test + +import ( + "sync" + + "github.com/nspcc-dev/neofs-node/pkg/core/container" + "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended" + "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage" +) + +type testStorage struct { + *sync.RWMutex + + items map[container.ID]storage.Table +} + +func (s *testStorage) GetEACL(cid storage.CID) (storage.Table, error) { + s.RLock() + table, ok := s.items[cid] + s.RUnlock() + + if !ok { + return nil, storage.ErrNotFound + } + + return table, nil +} + +func (s *testStorage) PutEACL(cid storage.CID, table storage.Table, _ []byte) error { + if table == nil { + return extended.ErrNilTable + } + + s.Lock() + s.items[cid] = table + s.Unlock() + + return nil +} + +// New creates new eACL table storage +// that stores table in go-builtin map. +func New() storage.Storage { + return &testStorage{ + RWMutex: new(sync.RWMutex), + items: make(map[container.ID]storage.Table), + } +} diff --git a/pkg/core/container/acl/extended/types.go b/pkg/core/container/acl/extended/types.go new file mode 100644 index 00000000..a1442fcf --- /dev/null +++ b/pkg/core/container/acl/extended/types.go @@ -0,0 +1,102 @@ +package extended + +import ( + "errors" + + eacl "github.com/nspcc-dev/neofs-api-go/acl/extended" + "github.com/nspcc-dev/neofs-node/pkg/core/container" +) + +// FIXME: do not duplicate constants + +// OperationType represents the enumeration +// of different destination types of request. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-api-go/eacl.OperationType. +// FIXME: operation type should be defined in core lib. +type OperationType = eacl.OperationType + +// Group represents the enumeration +// of different authorization groups. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-api-go/acl/extended.Group. +// FIXME: target should be defined in core lib. +type Group = eacl.Group + +// HeaderType represents the enumeration +// of different types of header. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-api-go/eacl.HeaderType. +// FIXME: header type enum should be defined in core lib. +type HeaderType = eacl.HeaderType + +// CID represents the container identifier. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/container.ID. +type CID = container.ID + +// Header is an interface that wraps +// methods of string key-value header. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-api-go/eacl.Header. +// FIXME: header should be defined in core lib. +type Header = eacl.Header + +// Table represents extended ACL rule table. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-api-go/eacl.ExtendedACLTable. +// FIXME: eacl table should be defined in core package. +// type Table = eacl.ExtendedACLTable + +// TypedHeaderSource is the interface that wraps +// method for selecting typed headers by type. +type TypedHeaderSource interface { + // HeadersOfType returns the list of key-value headers + // of particular type. + // + // It returns any problem encountered through the boolean + // false value. + HeadersOfType(HeaderType) ([]Header, bool) +} + +// RequestInfo is an interface that wraps +// request with authority methods. +type RequestInfo interface { + TypedHeaderSource + + // CID returns container identifier from request context. + CID() CID + + // Key returns the binary representation of + // author's public key. + // + // Any problem encountered can be reflected + // through an empty slice. + // + // Binary key format is dictated by implementation. + Key() []byte + + // OperationType returns the type of request destination. + // + // Any problem encountered can be reflected + // through OpTypeUnknown value. Caller should handle + // OpTypeUnknown value according to its internal logic. + OperationType() OperationType + + // Group returns the authority group type. + // + // Any problem encountered can be reflected + // through GroupUnknown value. Caller should handle + // TargetUnknown value according to its internal logic. + Group() Group +} + +// ErrNilTable is the error returned by functions that +// expect a non-nil eACL table, but received nil. +var ErrNilTable = errors.New("eACL table is nil") diff --git a/pkg/core/container/container.go b/pkg/core/container/container.go new file mode 100644 index 00000000..8f1282ff --- /dev/null +++ b/pkg/core/container/container.go @@ -0,0 +1,82 @@ +package container + +import ( + "errors" + + "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic" + "github.com/nspcc-dev/netmap" +) + +// BasicACL represents the basic +// ACL rules. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/container/basic.ACL. +type BasicACL = basic.ACL + +// PlacementRule represents placement +// rules of the container. +// +// It is a type alias of +// github.com/nspcc-dev/netmap.PlacementRule. +// FIXME: container placement rules should be defined in core lib. +type PlacementRule = netmap.PlacementRule + +// Container represents NeoFS container. +type Container struct { + basicACL BasicACL // basic ACL mask + + ownerID OwnerID // the identifier of container's owner + + salt []byte // unique container bytes + + placementRule PlacementRule // placement rules +} + +// ErrNilContainer is the error returned by functions that +// expect a non-nil container pointer, but received nil. +var ErrNilContainer = errors.New("container is nil") + +// OwnerID returns an ID of the container's owner. +func (c *Container) OwnerID() OwnerID { + return c.ownerID +} + +// SetOwnerID sets the ID of the container's owner. +func (c *Container) SetOwnerID(v OwnerID) { + c.ownerID = v +} + +// Salt returns the container salt. +// +// Slice is returned by reference without copying. +func (c *Container) Salt() []byte { + return c.salt +} + +// SetSalt sets the container salt. +// +// Slice is assigned by reference without copying. +func (c *Container) SetSalt(v []byte) { + c.salt = v +} + +// BasicACL returns the mask of basic container permissions. +func (c *Container) BasicACL() BasicACL { + return c.basicACL +} + +// SetBasicACL sets the mask of basic container permissions. +func (c *Container) SetBasicACL(v BasicACL) { + c.basicACL = v +} + +// PlacementRule returns placement rule of the container. +func (c *Container) PlacementRule() PlacementRule { + return c.placementRule +} + +// SetPlacementRule sets placement rule of the container. +func (c *Container) SetPlacementRule(v PlacementRule) { + c.placementRule = v +} diff --git a/pkg/core/container/container_test.go b/pkg/core/container/container_test.go new file mode 100644 index 00000000..21fc1e15 --- /dev/null +++ b/pkg/core/container/container_test.go @@ -0,0 +1,30 @@ +package container + +import ( + "testing" + + "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic" + "github.com/stretchr/testify/require" +) + +func TestContainerMethods(t *testing.T) { + c := new(Container) + + acl := basic.FromUint32(1) + c.SetBasicACL(acl) + require.True(t, basic.Equal(acl, c.BasicACL())) + + ownerID := OwnerID{1, 2, 3} + c.SetOwnerID(ownerID) + require.Equal(t, ownerID, c.OwnerID()) + + salt := []byte{4, 5, 6} + c.SetSalt(salt) + require.Equal(t, salt, c.Salt()) + + rule := PlacementRule{ + ReplFactor: 1, + } + c.SetPlacementRule(rule) + require.Equal(t, rule, c.PlacementRule()) +} diff --git a/pkg/core/container/id.go b/pkg/core/container/id.go new file mode 100644 index 00000000..251053c2 --- /dev/null +++ b/pkg/core/container/id.go @@ -0,0 +1,49 @@ +package container + +import ( + "crypto/sha256" + + "github.com/nspcc-dev/neofs-api-go/refs" +) + +// ID represents the +// container identifier. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-api-go/refs.CID. +// FIXME: container id should be defined in core package. +type ID = refs.CID + +// OwnerID represents the +// container owner identifier. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-api-go/refs.OwnerID. +// FIXME: owner ID should be defined in core lib. +type OwnerID = refs.OwnerID + +// OwnerIDSize is a size of OwnerID +// in a binary form. +const OwnerIDSize = refs.OwnerIDSize + +// CalculateID calculates container identifier +// as SHA256 checksum of the binary form. +// +// If container is nil, ErrNilContainer is returned. +func CalculateID(cnr *Container) (*ID, error) { + if cnr == nil { + return nil, ErrNilContainer + } + + data, err := cnr.MarshalBinary() + if err != nil { + return nil, err + } + + res := new(ID) + sh := sha256.Sum256(data) + + copy(res[:], sh[:]) + + return res, nil +} diff --git a/pkg/core/container/id_test.go b/pkg/core/container/id_test.go new file mode 100644 index 00000000..142375ca --- /dev/null +++ b/pkg/core/container/id_test.go @@ -0,0 +1,38 @@ +package container + +import ( + "crypto/sha256" + "testing" + + "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic" + "github.com/pkg/errors" + "github.com/stretchr/testify/require" +) + +func TestCalculateID(t *testing.T) { + _, err := CalculateID(nil) + require.True(t, errors.Is(err, ErrNilContainer)) + + cnr := new(Container) + cnr.SetBasicACL(basic.FromUint32(1)) + cnr.SetOwnerID(OwnerID{1, 2, 3}) + cnr.SetSalt([]byte{4, 5, 6}) + + id1, err := CalculateID(cnr) + require.NoError(t, err) + + data, err := cnr.MarshalBinary() + require.NoError(t, err) + + sh := sha256.Sum256(data) + + require.Equal(t, id1.Bytes(), sh[:]) + + // change the container + cnr.SetSalt(append(cnr.Salt(), 1)) + + id2, err := CalculateID(cnr) + require.NoError(t, err) + + require.NotEqual(t, id1, id2) +} diff --git a/pkg/core/container/marshal.go b/pkg/core/container/marshal.go new file mode 100644 index 00000000..1cb938ec --- /dev/null +++ b/pkg/core/container/marshal.go @@ -0,0 +1,75 @@ +package container + +import ( + "encoding/binary" + "io" + + "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic" +) + +const ( + saltLenSize = 2 + + fixedSize = 0 + + basic.Size + + OwnerIDSize + + saltLenSize +) + +// MarshalBinary encodes the container into a binary form +// and returns the result. +func (c *Container) MarshalBinary() ([]byte, error) { + data := make([]byte, binaryContainerSize(c)) + + off := copy(data, basic.Marshal(c.basicACL)) + + off += copy(data[off:], c.ownerID.Bytes()) + + binary.BigEndian.PutUint16(data[off:], uint16(len(c.salt))) + off += saltLenSize + + off += copy(data[off:], c.salt) + + if _, err := c.placementRule.MarshalTo(data[off:]); err != nil { + return nil, err + } + + return data, nil +} + +// UnmarshalBinary unmarshals container from a binary +// representation. +// +// If buffer size is insufficient, io.ErrUnexpectedEOF is returned. +func (c *Container) UnmarshalBinary(data []byte) error { + if len(data) < binaryContainerSize(c) { + return io.ErrUnexpectedEOF + } + + if err := c.basicACL.UnmarshalBinary(data); err != nil { + return err + } + + off := basic.Size + + off += copy(c.ownerID[:], data[off:]) + + saltLen := binary.BigEndian.Uint16(data[off:]) + off += saltLenSize + + c.salt = make([]byte, saltLen) + off += copy(c.salt, data[off:]) + + if err := c.placementRule.Unmarshal(data[off:]); err != nil { + return err + } + + return nil +} + +// returns the length of the container in binary form. +func binaryContainerSize(cnr *Container) int { + return fixedSize + + len(cnr.salt) + + cnr.placementRule.Size() +} diff --git a/pkg/core/container/marshal_test.go b/pkg/core/container/marshal_test.go new file mode 100644 index 00000000..20579b8e --- /dev/null +++ b/pkg/core/container/marshal_test.go @@ -0,0 +1,26 @@ +package container + +import ( + "testing" + + "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic" + "github.com/stretchr/testify/require" +) + +func TestContainerMarshal(t *testing.T) { + srcCnr := new(Container) + srcCnr.SetBasicACL(basic.FromUint32(1)) + srcCnr.SetOwnerID(OwnerID{1, 2, 3}) + srcCnr.SetSalt([]byte{4, 5, 6}) + srcCnr.SetPlacementRule(PlacementRule{ + ReplFactor: 3, + }) + + data, err := srcCnr.MarshalBinary() + require.NoError(t, err) + + dstCnr := new(Container) + require.NoError(t, dstCnr.UnmarshalBinary(data)) + + require.Equal(t, srcCnr, dstCnr) +} diff --git a/pkg/core/container/storage/storage.go b/pkg/core/container/storage/storage.go new file mode 100644 index 00000000..9a5faadc --- /dev/null +++ b/pkg/core/container/storage/storage.go @@ -0,0 +1,79 @@ +package storage + +import ( + "errors" + + "github.com/nspcc-dev/neofs-node/pkg/core/container" +) + +// Container represents the NeoFS container. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/container.Container. +type Container = container.Container + +// OwnerID represents the container +// owner identifier. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/container.OwnerID. +type OwnerID = container.OwnerID + +// CID represents the container identifier. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/container.ID. +type CID = container.ID + +// Storage is an interface that wraps +// basic container storage methods. +type Storage interface { + // Put saves pointed container to the underlying storage. + // It returns calculated container identifier and any error + // encountered that caused the saving to interrupt. + // + // Put must return container.ErrNilContainer on nil-pointer. + // + // Implementations must not modify the container through the pointer (even temporarily). + // Implementations must not retain the container pointer. + // + // Container rewriting behavior is dictated by implementation. + Put(*Container) (*CID, error) + + // Get reads the container from the storage by identifier. + // It returns the pointer to requested container and any error encountered. + // + // Get must return exactly one non-nil value. + // Get must return ErrNotFound if the container is not in storage. + // + // Implementations must not retain the container pointer and modify + // the container through it. + Get(CID) (*Container, error) + + // Delete removes the container from the storage. + // It returns any error encountered that caused the deletion to interrupt. + // + // Delete must return nil if container was successfully deleted. + // + // Behavior when deleting a nonexistent container is dictated by implementation. + Delete(CID) error + + // List returns a list of container identifiers belonging to the specified owner. + // It returns any error encountered that caused the listing to interrupt. + // + // List must return the identifiers of all stored containers if owner pointer is nil. + // List must return the empty list and no error in the absence of containers in storage. + // + // Result slice can be either empty slice or nil, so empty list should be checked + // by comparing with zero length (not nil). + // + // Callers should carefully handle the incomplete list in case of interrupt error. + List(*OwnerID) ([]CID, error) +} + +// ErrNotFound is the error returned when container was not found in storage. +var ErrNotFound = errors.New("container not found") + +// ErrNilStorage is the error returned by functions that +// expect a non-nil container storage implementation, but received nil. +var ErrNilStorage = errors.New("container storage is nil") diff --git a/pkg/core/container/storage/test/storage.go b/pkg/core/container/storage/test/storage.go new file mode 100644 index 00000000..b2fe18ab --- /dev/null +++ b/pkg/core/container/storage/test/storage.go @@ -0,0 +1,127 @@ +package test + +import ( + "sync" + "testing" + + "github.com/nspcc-dev/neofs-node/pkg/core/container" + "github.com/nspcc-dev/neofs-node/pkg/core/container/storage" + "github.com/stretchr/testify/require" +) + +type testStorage struct { + *sync.RWMutex + + items map[container.ID]*container.Container +} + +func (s *testStorage) Put(cnr *storage.Container) (*storage.CID, error) { + if cnr == nil { + return nil, container.ErrNilContainer + } + + cid, err := container.CalculateID(cnr) + if err != nil { + return nil, err + } + + s.Lock() + s.items[*cid] = cnr + s.Unlock() + + return cid, nil +} + +func (s *testStorage) Get(cid storage.CID) (*storage.Container, error) { + s.RLock() + cnr, ok := s.items[cid] + s.RUnlock() + + if !ok { + return nil, storage.ErrNotFound + } + + return cnr, nil +} + +func (s *testStorage) Delete(cid storage.CID) error { + s.Lock() + delete(s.items, cid) + s.Unlock() + + return nil +} + +func (s *testStorage) List(ownerID *storage.OwnerID) ([]storage.CID, error) { + s.RLock() + defer s.RUnlock() + + res := make([]storage.CID, 0) + + for cid, cnr := range s.items { + if ownerID == nil || ownerID.Equal(cnr.OwnerID()) { + res = append(res, cid) + } + } + + return res, nil +} + +// New creates new container storage +// that stores containers in go-builtin map. +func New() storage.Storage { + return &testStorage{ + RWMutex: new(sync.RWMutex), + items: make(map[container.ID]*container.Container), + } +} + +// Storage conducts testing of container +// storage for interface specification. +// +// Storage must be empty. +func Storage(t *testing.T, s storage.Storage) { + list, err := s.List(nil) + require.NoError(t, err) + require.Empty(t, list) + + cnr1 := new(container.Container) + cnr1.SetOwnerID(container.OwnerID{1, 2, 3}) + + id1, err := s.Put(cnr1) + require.NoError(t, err) + + res, err := s.Get(*id1) + require.NoError(t, err) + require.Equal(t, cnr1, res) + + cnr2 := new(container.Container) + owner1 := cnr1.OwnerID() + owner1[0]++ + cnr2.SetOwnerID(owner1) + + id2, err := s.Put(cnr2) + require.NoError(t, err) + + res, err = s.Get(*id2) + require.NoError(t, err) + require.Equal(t, cnr2, res) + + list, err = s.List(nil) + require.NoError(t, err) + require.Len(t, list, 2) + require.Contains(t, list, *id1) + require.Contains(t, list, *id2) + + owner1 = cnr1.OwnerID() + list, err = s.List(&owner1) + require.NoError(t, err) + require.Len(t, list, 1) + require.Equal(t, *id1, list[0]) + + owner2 := cnr2.OwnerID() + list, err = s.List(&owner2) + require.NoError(t, err) + require.Len(t, list, 1) + require.Equal(t, *id2, list[0]) +} diff --git a/pkg/core/container/storage/test/storage_test.go b/pkg/core/container/storage/test/storage_test.go new file mode 100644 index 00000000..7614f467 --- /dev/null +++ b/pkg/core/container/storage/test/storage_test.go @@ -0,0 +1,11 @@ +package test + +import ( + "testing" +) + +func TestNewStorage(t *testing.T) { + s := New() + + Storage(t, s) +} diff --git a/pkg/core/netmap/epoch/marshal.go b/pkg/core/netmap/epoch/marshal.go new file mode 100644 index 00000000..a15fc9ca --- /dev/null +++ b/pkg/core/netmap/epoch/marshal.go @@ -0,0 +1,37 @@ +package epoch + +import ( + "encoding/binary" + "io" +) + +// Size is a size of Epoch +// in a binary form. +const Size = 8 + +// Marshal encodes Epoch into a +// binary form and returns the result. +// +// Result slice has Size length. +func Marshal(e Epoch) []byte { + d := make([]byte, Size) + + binary.BigEndian.PutUint64(d, ToUint64(e)) + + return d +} + +// UnmarshalBinary unmarshals Epoch from +// a binary representation. +// +// If buffer size is insufficient, +// io.ErrUnexpectedEOF is returned. +func (e *Epoch) UnmarshalBinary(data []byte) error { + if len(data) < Size { + return io.ErrUnexpectedEOF + } + + *e = FromUint64(binary.BigEndian.Uint64(data)) + + return nil +} diff --git a/pkg/core/netmap/epoch/marshal_test.go b/pkg/core/netmap/epoch/marshal_test.go new file mode 100644 index 00000000..7d20ae43 --- /dev/null +++ b/pkg/core/netmap/epoch/marshal_test.go @@ -0,0 +1,20 @@ +package epoch + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestEpochMarshal(t *testing.T) { + e := FromUint64(1) + e2 := new(Epoch) + + require.NoError(t, + e2.UnmarshalBinary( + Marshal(e), + ), + ) + + require.True(t, EQ(e, *e2)) +} diff --git a/pkg/core/netmap/epoch/math.go b/pkg/core/netmap/epoch/math.go new file mode 100644 index 00000000..7f68d3c7 --- /dev/null +++ b/pkg/core/netmap/epoch/math.go @@ -0,0 +1,12 @@ +package epoch + +// Sum returns the result of +// summing up two Epoch. +// +// Function defines a binary +// operation of summing two Epoch. +// Try to avoid using operator +// "+" for better portability. +func Sum(a, b Epoch) Epoch { + return FromUint64(ToUint64(a) + ToUint64(b)) +} diff --git a/pkg/core/netmap/epoch/math_test.go b/pkg/core/netmap/epoch/math_test.go new file mode 100644 index 00000000..a1ae8826 --- /dev/null +++ b/pkg/core/netmap/epoch/math_test.go @@ -0,0 +1,28 @@ +package epoch + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestEpochMath(t *testing.T) { + items := []struct { + mathFn func(Epoch, Epoch) Epoch + + a, b, c uint64 + }{ + { + mathFn: Sum, a: 1, b: 2, c: 3}, + } + + for _, item := range items { + require.Equal(t, + item.mathFn( + FromUint64(item.a), + FromUint64(item.b), + ), + FromUint64(item.c), + ) + } +} diff --git a/pkg/core/netmap/epoch/relation.go b/pkg/core/netmap/epoch/relation.go new file mode 100644 index 00000000..56ae0a1d --- /dev/null +++ b/pkg/core/netmap/epoch/relation.go @@ -0,0 +1,55 @@ +package epoch + +// EQ reports whether e and e2 are the same Epoch. +// +// Function defines the relation of equality +// between two Epoch. Try to avoid comparison through +// "==" operator for better portability. +func EQ(e1, e2 Epoch) bool { + return ToUint64(e1) == ToUint64(e2) +} + +// NE reports whether e1 and e2 are the different Epoch. +// +// Method defines the relation of inequality +// between two Epoch. Try to avoid comparison through +// "!=" operator for better portability. +func NE(e1, e2 Epoch) bool { + return ToUint64(e1) != ToUint64(e2) +} + +// LT reports whether e1 is less Epoch than e2. +// +// Method defines the "less than" relation +// between two Epoch. Try to avoid comparison through +// "<" operator for better portability. +func LT(e1, e2 Epoch) bool { + return ToUint64(e1) < ToUint64(e2) +} + +// GT reports whether e1 is greater Epoch than e2. +// +// Method defines the "greater than" relation +// between two Epoch. Try to avoid comparison through +// ">" operator for better portability. +func GT(e1, e2 Epoch) bool { + return ToUint64(e1) > ToUint64(e2) +} + +// LE reports whether e1 is less or equal Epoch than e2. +// +// Method defines the "less or equal" relation +// between two Epoch. Try to avoid comparison through +// "<=" operator for better portability. +func LE(e1, e2 Epoch) bool { + return ToUint64(e1) <= ToUint64(e2) +} + +// GE reports whether e1 is greater or equal Epoch than e2. +// +// Method defines the "greater or equal" relation +// between two Epoch. Try to avoid comparison through +// ">=" operator for better portability. +func GE(e1, e2 Epoch) bool { + return ToUint64(e1) >= ToUint64(e2) +} diff --git a/pkg/core/netmap/epoch/relation_test.go b/pkg/core/netmap/epoch/relation_test.go new file mode 100644 index 00000000..003cfc1d --- /dev/null +++ b/pkg/core/netmap/epoch/relation_test.go @@ -0,0 +1,40 @@ +package epoch + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestEpochRelations(t *testing.T) { + items := []struct { + relFn func(Epoch, Epoch) bool + + base, ok, fail uint64 + }{ + {relFn: EQ, base: 1, ok: 1, fail: 2}, + {relFn: NE, base: 1, ok: 2, fail: 1}, + {relFn: LT, base: 1, ok: 2, fail: 0}, + {relFn: GT, base: 1, ok: 0, fail: 2}, + {relFn: LE, base: 1, ok: 1, fail: 0}, + {relFn: LE, base: 1, ok: 2, fail: 0}, + {relFn: GE, base: 1, ok: 0, fail: 2}, + {relFn: GE, base: 1, ok: 1, fail: 2}, + } + + for _, item := range items { + require.True(t, + item.relFn( + FromUint64(item.base), + FromUint64(item.ok), + ), + ) + + require.False(t, + item.relFn( + FromUint64(item.base), + FromUint64(item.fail), + ), + ) + } +} diff --git a/pkg/core/netmap/epoch/type.go b/pkg/core/netmap/epoch/type.go new file mode 100644 index 00000000..79b3da55 --- /dev/null +++ b/pkg/core/netmap/epoch/type.go @@ -0,0 +1,23 @@ +package epoch + +// Epoch represents the +// number of NeoFS epoch. +type Epoch uint64 + +// FromUint64 converts builtin +// uint64 value to Epoch. +// +// Try to avoid direct cast for +// better portability. +func FromUint64(e uint64) Epoch { + return Epoch(e) +} + +// ToUint64 converts Epoch value +// to builtin uint64. +// +// Try to avoid direct cast for +// better portability. +func ToUint64(e Epoch) uint64 { + return uint64(e) +} diff --git a/pkg/core/netmap/netmap.go b/pkg/core/netmap/netmap.go new file mode 100644 index 00000000..17c60834 --- /dev/null +++ b/pkg/core/netmap/netmap.go @@ -0,0 +1,119 @@ +package netmap + +import ( + "bytes" + "sync" + + "github.com/nspcc-dev/neofs-node/pkg/core/netmap/node" + "github.com/nspcc-dev/netmap" +) + +// Info represent node information. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/netmap/node.Info. +type Info = node.Info + +// Bucket represents NeoFS network map as a graph. +// +// If is a type alias of +// github.com/nspcc-dev/netmap.Bucket. +type Bucket = netmap.Bucket + +// NetMap represents NeoFS network map +// with concurrent access support. +type NetMap struct { + mtx *sync.RWMutex + + root *Bucket + + items []Info +} + +// New creates and initializes a new NetMap. +// +// Using the NetMap that has been created with new(NetMap) +// expression (or just declaring a NetMap variable) is unsafe +// and can lead to panic. +func New() *NetMap { + return &NetMap{ + mtx: new(sync.RWMutex), + root: new(Bucket), + } +} + +// Root returns the root bucket of the network map. +// +// Changing the result is unsafe and +// affects the network map. +func (n NetMap) Root() *Bucket { + n.mtx.RLock() + defer n.mtx.RUnlock() + + return n.root +} + +// SetRoot sets the root bucket of the network map. +// +// Subsequent changing the source bucket +// is unsafe and affects the network map. +func (n *NetMap) SetRoot(v *Bucket) { + n.mtx.Lock() + n.root = v + n.mtx.Unlock() +} + +// Nodes returns node list of the network map. +// +// Changing the result is unsafe and +// affects the network map. +func (n NetMap) Nodes() []Info { + n.mtx.RLock() + defer n.mtx.RUnlock() + + return n.items +} + +// SetNodes sets node list of the network map. +// +// Subsequent changing the source slice +// is unsafe and affects the network map. +func (n *NetMap) SetNodes(v []Info) { + n.mtx.Lock() + n.items = v + n.mtx.Unlock() +} + +// AddNode adds node information to the network map +// +// If node with provided information is already presented +// in network map, nothing happens, +func (n *NetMap) AddNode(nodeInfo Info) error { + n.mtx.Lock() + defer n.mtx.Unlock() + + num := -1 + + // looking for existed node info item + for i := range n.items { + if bytes.Equal( + n.items[i].PublicKey(), + nodeInfo.PublicKey(), + ) { + num = i + break + } + } + + // add node if it does not exist + if num < 0 { + n.items = append(n.items, nodeInfo) + num = len(n.items) - 1 + } + + return n.root.AddStrawNode(netmap.Node{ + N: uint32(num), + C: n.items[num].Capacity(), + P: n.items[num].Price(), + }, nodeInfo.Options()...) +} diff --git a/pkg/core/netmap/netmap_test.go b/pkg/core/netmap/netmap_test.go new file mode 100644 index 00000000..e3bf1385 --- /dev/null +++ b/pkg/core/netmap/netmap_test.go @@ -0,0 +1,39 @@ +package netmap + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestNetMap_Nodes(t *testing.T) { + nm := New() + + info1 := Info{} + info1.SetPublicKey([]byte{1, 2, 3}) + + info2 := Info{} + info2.SetPublicKey([]byte{4, 5, 6}) + + nodes := []Info{ + info1, + info2, + } + + nm.SetNodes(nodes) + + require.Equal(t, nodes, nm.Nodes()) +} + +func TestNetMap_Root(t *testing.T) { + nm := New() + + bucket := &Bucket{ + Key: "key", + Value: "value", + } + + nm.SetRoot(bucket) + + require.Equal(t, bucket, nm.Root()) +} diff --git a/pkg/core/netmap/node/info.go b/pkg/core/netmap/node/info.go new file mode 100644 index 00000000..e8ca3def --- /dev/null +++ b/pkg/core/netmap/node/info.go @@ -0,0 +1,154 @@ +package node + +import ( + "errors" +) + +// Info represents the information +// about NeoFS storage node. +type Info struct { + address string // net address + + key []byte // public key + + opts []string // options + + status Status // status bits +} + +// ErrNilInfo is returned by functions that expect +// a non-nil Info pointer, but received nil. +var ErrNilInfo = errors.New("node info is nil") + +// Address returns node network address. +// +// Address format is dictated by +// application architecture. +func (i Info) Address() string { + return i.address +} + +// SetAddress sets node network address. +func (i *Info) SetAddress(v string) { + i.address = v +} + +// Status returns the node status. +func (i Info) Status() Status { + return i.status +} + +// SetStatus sets the node status. +func (i *Info) SetStatus(v Status) { + i.status = v +} + +// PublicKey returns node public key in +// a binary format. +// +// Changing the result is unsafe and +// affects the node info. In order to +// prevent state mutations, use +// CopyPublicKey. +// +// Key format is dictated by +// application architecture. +func (i Info) PublicKey() []byte { + return i.key +} + +// CopyPublicKey returns the copy of +// node public key. +// +// Changing the result is safe and +// does not affect the node info. +func CopyPublicKey(i Info) []byte { + res := make([]byte, len(i.key)) + + copy(res, i.key) + + return res +} + +// SetPublicKey sets node public key +// in a binary format. +// +// Subsequent changing the source slice +// is unsafe and affects node info. +// In order to prevent state mutations, +// use SetPublicKeyCopy. +func (i *Info) SetPublicKey(v []byte) { + i.key = v +} + +// SetPublicKeyCopy copies public key and +// sets the copy as node public key. +// +// Subsequent changing the source slice +// is safe and does not affect node info. +// +// Returns ErrNilInfo on nil node info. +func SetPublicKeyCopy(i *Info, key []byte) error { + if i == nil { + return ErrNilInfo + } + + i.key = make([]byte, len(key)) + + copy(i.key, key) + + return nil +} + +// Options returns node option list. +// +// Changing the result is unsafe and +// affects the node info. In order to +// prevent state mutations, use +// CopyOptions. +// +// Option format is dictated by +// application architecture. +func (i Info) Options() []string { + return i.opts +} + +// CopyOptions returns the copy of +// node options list. +// +// Changing the result is safe and +// does not affect the node info. +func CopyOptions(i Info) []string { + res := make([]string, len(i.opts)) + + copy(res, i.opts) + + return res +} + +// SetOptions sets node option list. +// +// Subsequent changing the source slice +// is unsafe and affects node info. +// In order to prevent state mutations, +// use SetOptionsCopy. +func (i *Info) SetOptions(v []string) { + i.opts = v +} + +// SetOptionsCopy copies option list and sets +// the copy as node options list. +// +// Subsequent changing the source slice +// is safe and does not affect node info. +// +// SetOptionsCopy does nothing if Info is nil. +func SetOptionsCopy(i *Info, opts []string) { + if i == nil { + return + } + + i.opts = make([]string, len(opts)) + + copy(i.opts, opts) +} diff --git a/pkg/core/netmap/node/info_test.go b/pkg/core/netmap/node/info_test.go new file mode 100644 index 00000000..d3a183b3 --- /dev/null +++ b/pkg/core/netmap/node/info_test.go @@ -0,0 +1,133 @@ +package node + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestInfo_Address(t *testing.T) { + i := new(Info) + + addr := "address" + i.SetAddress(addr) + + require.Equal(t, addr, i.Address()) +} + +func TestInfo_Status(t *testing.T) { + i := new(Info) + + st := StatusFromUint64(1) + i.SetStatus(st) + + require.Equal(t, st, i.Status()) +} + +func TestInfo_PublicKey(t *testing.T) { + i := new(Info) + + key := []byte{1, 2, 3} + i.SetPublicKey(key) + + require.Equal(t, key, i.PublicKey()) +} + +func TestCopyPublicKey(t *testing.T) { + i := Info{} + + // set initial node key + initKey := []byte{1, 2, 3} + i.SetPublicKey(initKey) + + // get node key copy + keyCopy := CopyPublicKey(i) + + // change the copy + keyCopy[0]++ + + // check that node key has not changed + require.Equal(t, initKey, i.PublicKey()) +} + +func TestSetPublicKeyCopy(t *testing.T) { + require.EqualError(t, + SetPublicKeyCopy(nil, nil), + ErrNilInfo.Error(), + ) + + i := new(Info) + + // create source key + srcKey := []byte{1, 2, 3} + + // copy and set node key + require.NoError(t, SetPublicKeyCopy(i, srcKey)) + + // get node key + nodeKey := i.PublicKey() + + // change the source key + srcKey[0]++ + + // check that node key has not changed + require.Equal(t, nodeKey, i.PublicKey()) +} + +func TestInfo_Options(t *testing.T) { + i := new(Info) + + opts := []string{ + "opt1", + "opt2", + } + i.SetOptions(opts) + + require.Equal(t, opts, i.Options()) +} + +func TestCopyOptions(t *testing.T) { + i := Info{} + + // set initial node options + initOpts := []string{ + "opt1", + "opt2", + } + i.SetOptions(initOpts) + + // get node options copy + optsCopy := CopyOptions(i) + + // change the copy + optsCopy[0] = "some other opt" + + // check that node options have not changed + require.Equal(t, initOpts, i.Options()) +} + +func TestSetOptionsCopy(t *testing.T) { + require.NotPanics(t, func() { + SetOptionsCopy(nil, nil) + }) + + i := new(Info) + + // create source options + srcOpts := []string{ + "opt1", + "opt2", + } + + // copy and set node options + SetOptionsCopy(i, srcOpts) + + // get node options + nodeOpts := i.Options() + + // change the source options + srcOpts[0] = "some other opt" + + // check that node options have not changed + require.Equal(t, nodeOpts, i.Options()) +} diff --git a/pkg/core/netmap/node/options.go b/pkg/core/netmap/node/options.go new file mode 100644 index 00000000..692c7a7e --- /dev/null +++ b/pkg/core/netmap/node/options.go @@ -0,0 +1,46 @@ +package node + +import ( + "strconv" + "strings" + + "github.com/nspcc-dev/neofs-api-go/object" +) + +const optionPrice = "/Price:" + +const optionCapacity = "/Capacity:" + +// Price parses node options and returns the price in 1e-8*GAS/Megabyte per month. +// +// User sets the price in GAS/Terabyte per month. +func (i Info) Price() uint64 { + for j := range i.opts { + if strings.HasPrefix(i.opts[j], optionPrice) { + n, err := strconv.ParseFloat(i.opts[j][len(optionPrice):], 64) + if err != nil { + return 0 + } + + return uint64(n*1e8) / uint64(object.UnitsMB) // UnitsMB == megabytes in 1 terabyte + } + } + + return 0 +} + +// Capacity parses node options and returns the capacity . +func (i Info) Capacity() uint64 { + for j := range i.opts { + if strings.HasPrefix(i.opts[j], optionCapacity) { + n, err := strconv.ParseUint(i.opts[j][len(optionCapacity):], 10, 64) + if err != nil { + return 0 + } + + return n + } + } + + return 0 +} diff --git a/pkg/core/netmap/node/options_test.go b/pkg/core/netmap/node/options_test.go new file mode 100644 index 00000000..3965b70e --- /dev/null +++ b/pkg/core/netmap/node/options_test.go @@ -0,0 +1,24 @@ +package node + +import ( + "testing" + + "github.com/nspcc-dev/neofs-api-go/object" + "github.com/stretchr/testify/require" +) + +func TestInfo_Price(t *testing.T) { + var info Info + + // too small value + info.opts = []string{"/Price:0.01048575"} + require.Equal(t, uint64(0), info.Price()) + + // min value + info.opts = []string{"/Price:0.01048576"} + require.Equal(t, uint64(1), info.Price()) + + // big value + info.opts = []string{"/Price:1000000000.666"} + require.Equal(t, uint64(1000000000.666*1e8/object.UnitsMB), info.Price()) +} diff --git a/pkg/core/netmap/node/status.go b/pkg/core/netmap/node/status.go new file mode 100644 index 00000000..1f080642 --- /dev/null +++ b/pkg/core/netmap/node/status.go @@ -0,0 +1,63 @@ +package node + +// Status represents a node +// status bits. +type Status uint64 + +const leftBitPos = 64 + +const ( + bitFullStorage = 1 +) + +// returns true if n-th left bit is set (starting at 0). +func isLeftBitSet(value Status, n uint8) bool { + bitMask := Status(1 << (leftBitPos - n)) + return bitMask != 0 && value&bitMask == bitMask +} + +// sets n-th left bit (starting at 0). +func setLeftBit(value *Status, n uint8) { + *value |= Status(1 << (leftBitPos - n)) +} + +// resets n-th left bit (starting at 0). +func resetLeftBit(value *Status, n uint8) { + *value &= ^Status(1 << (leftBitPos - n)) +} + +// Full returns true if node is in Full status. +// +// Full status marks node has enough space +// for storing users objects. +func (n Status) Full() bool { + return isLeftBitSet(n, bitFullStorage) +} + +// SetFull sets Full status of node. +func (n *Status) SetFull() { + setLeftBit(n, bitFullStorage) +} + +// ResetFull resets Full status of node. +func (n *Status) ResetFull() { + resetLeftBit(n, bitFullStorage) +} + +// StatusFromUint64 converts builtin +// uint64 value to Status. +// +// Try to avoid direct cast for +// better portability. +func StatusFromUint64(v uint64) Status { + return Status(v) +} + +// StatusToUint64 converts Status value +// to builtin uint64. +// +// Try to avoid direct cast for +// better portability. +func StatusToUint64(s Status) uint64 { + return uint64(s) +} diff --git a/pkg/core/netmap/node/status_test.go b/pkg/core/netmap/node/status_test.go new file mode 100644 index 00000000..5d540f9e --- /dev/null +++ b/pkg/core/netmap/node/status_test.go @@ -0,0 +1,17 @@ +package node + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestStatus_Full(t *testing.T) { + st := new(Status) + + st.SetFull() + require.True(t, st.Full()) + + st.ResetFull() + require.False(t, st.Full()) +} diff --git a/pkg/core/object/extended.go b/pkg/core/object/extended.go new file mode 100644 index 00000000..330169a1 --- /dev/null +++ b/pkg/core/object/extended.go @@ -0,0 +1,94 @@ +package object + +// ExtendedHeaderType represents the enumeration +// of extended header types of the NeoFS object. +type ExtendedHeaderType uint32 + +// ExtendedHeader represents the extended +// header of NeoFS object. +type ExtendedHeader struct { + typ ExtendedHeaderType + + val interface{} +} + +// Type returns the extended header type. +func (h ExtendedHeader) Type() ExtendedHeaderType { + return h.typ +} + +// SetType sets the extended header type. +func (h *ExtendedHeader) SetType(v ExtendedHeaderType) { + h.typ = v +} + +// Value returns the extended header value. +// +// In the case of a reference type, the value is +// returned by reference, so value mutations affect +// header state. Therefore, callers must first copy +// the value before changing manipulations. +func (h ExtendedHeader) Value() interface{} { + return h.val +} + +// SetValue sets the extended header value. +// +// Caller must take into account that each type of +// header usually has a limited set of expected +// value types. +// +// In the case of a reference type, the value is set +// by reference, so source value mutations affect +// header state. Therefore, callers must first copy +// the source value before changing manipulations. +func (h *ExtendedHeader) SetValue(v interface{}) { + h.val = v +} + +// TypeFromUint32 converts builtin +// uint32 value to Epoch. +// +// Try to avoid direct cast for +// better portability. +func TypeFromUint32(v uint32) ExtendedHeaderType { + return ExtendedHeaderType(v) +} + +// TypeToUint32 converts Epoch value +// to builtin uint32. +// +// Try to avoid direct cast for +// better portability. +func TypeToUint32(v ExtendedHeaderType) uint32 { + return uint32(v) +} + +// TypesEQ reports whether t1 and t2 are the same ExtendedHeaderType. +// +// Function defines the relation of equality +// between two ExtendedHeaderType. Try to avoid comparison through +// "==" operator for better portability. +func TypesEQ(t1, t2 ExtendedHeaderType) bool { + return TypeToUint32(t1) == TypeToUint32(t2) +} + +// TypesLT reports whether t1 ExtendedHeaderType +// is less than t2. +// +// Function defines the "less than" relation +// between two ExtendedHeaderType. Try to avoid +// comparison through "<" operator for better portability. +func TypesLT(t1, t2 ExtendedHeaderType) bool { + return TypeToUint32(t1) < TypeToUint32(t2) +} + +// TypesGT reports whether t1 ExtendedHeaderType +// is greater than t2. +// +// Function defines the "greater than" relation +// between two ExtendedHeaderType. Try to avoid +// comparison through ">" operator for better portability. +func TypesGT(t1, t2 ExtendedHeaderType) bool { + return TypeToUint32(t1) > TypeToUint32(t2) +} diff --git a/pkg/core/object/extended_test.go b/pkg/core/object/extended_test.go new file mode 100644 index 00000000..a8bd709c --- /dev/null +++ b/pkg/core/object/extended_test.go @@ -0,0 +1,25 @@ +package object + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestExtendedHeader_Type(t *testing.T) { + h := new(ExtendedHeader) + + ht := TypeFromUint32(3) + h.SetType(ht) + + require.True(t, TypesEQ(ht, h.Type())) +} + +func TestExtendedHeader_Value(t *testing.T) { + h := new(ExtendedHeader) + + val := 100 + h.SetValue(val) + + require.Equal(t, val, h.Value()) +} diff --git a/pkg/core/object/header.go b/pkg/core/object/header.go new file mode 100644 index 00000000..a55e7683 --- /dev/null +++ b/pkg/core/object/header.go @@ -0,0 +1,73 @@ +package object + +import ( + "errors" +) + +// Header represents NeoFS object header. +type Header struct { + // SystemHeader is an obligatory part of any object header. + // It is used to set the identity and basic parameters of + // the object. + // + // Header must inherit all the methods of SystemHeader, + // so the SystemHeader is embedded in Header. + SystemHeader + + extendedHeaders []ExtendedHeader // extended headers +} + +// ErrNilHeader is returned by functions that expect +// a non-nil Header pointer, but received nil. +var ErrNilHeader = errors.New("object header is nil") + +// ExtendedHeaders returns the extended headers of header. +// +// Changing the result is unsafe and affects the header. +// In order to prevent state mutations, use CopyExtendedHeaders. +func (h *Header) ExtendedHeaders() []ExtendedHeader { + return h.extendedHeaders +} + +// CopyExtendedHeaders returns the copy of extended headers. +// +// Changing the result is safe and does not affect the header. +// +// Returns nil if header is nil. +func CopyExtendedHeaders(h *Header) []ExtendedHeader { + if h == nil { + return nil + } + + res := make([]ExtendedHeader, len(h.extendedHeaders)) + + copy(res, h.extendedHeaders) + + return res +} + +// SetExtendedHeaders sets the extended headers of the header. +// +// Subsequent changing the source slice is unsafe and affects +// the header. In order to prevent state mutations, use +// SetExtendedHeadersCopy. +func (h *Header) SetExtendedHeaders(v []ExtendedHeader) { + h.extendedHeaders = v +} + +// SetExtendedHeadersCopy copies extended headers and sets the copy +// as the object extended headers. +// +// Subsequent changing the source slice is safe and does not affect +// the header. +// +// SetExtendedHeadersCopy does nothing if Header is nil. +func SetExtendedHeadersCopy(h *Header, hs []ExtendedHeader) { + if h == nil { + return + } + + h.extendedHeaders = make([]ExtendedHeader, len(hs)) + + copy(h.extendedHeaders, hs) +} diff --git a/pkg/core/object/header_test.go b/pkg/core/object/header_test.go new file mode 100644 index 00000000..8164bf10 --- /dev/null +++ b/pkg/core/object/header_test.go @@ -0,0 +1,98 @@ +package object + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func testHeaders(num uint32) []ExtendedHeader { + res := make([]ExtendedHeader, num) + + for i := uint32(0); i < num; i++ { + res[i].SetType(TypeFromUint32(i)) + res[i].SetValue(i) + } + + return res +} + +func TestObject_ExtendedHeaders(t *testing.T) { + h := new(Header) + + hs := testHeaders(2) + + h.SetExtendedHeaders(hs) + + require.Equal(t, hs, h.ExtendedHeaders()) +} + +func TestCopyExtendedHeaders(t *testing.T) { + require.Nil(t, CopyExtendedHeaders(nil)) + + h := new(Header) + + // set initial headers + initHs := testHeaders(2) + h.SetExtendedHeaders(initHs) + + // get extended headers copy + hsCopy := CopyExtendedHeaders(h) + + // change the copy + hsCopy[0] = hsCopy[1] + + // check that extended headers have not changed + require.Equal(t, initHs, h.ExtendedHeaders()) +} + +func TestSetExtendedHeadersCopy(t *testing.T) { + require.NotPanics(t, func() { + SetExtendedHeadersCopy(nil, nil) + }) + + h := new(Header) + + // create source headers + srcHs := testHeaders(2) + + // copy and set headers + SetExtendedHeadersCopy(h, srcHs) + + // get extended headers + objHs := h.ExtendedHeaders() + + // change the source headers + srcHs[0] = srcHs[1] + + // check that headeres have not changed + require.Equal(t, objHs, h.ExtendedHeaders()) +} + +func TestHeaderRelations(t *testing.T) { + items := []struct { + relFn func(ExtendedHeaderType, ExtendedHeaderType) bool + + base, ok, fail uint32 + }{ + {relFn: TypesEQ, base: 1, ok: 1, fail: 2}, + {relFn: TypesLT, base: 1, ok: 2, fail: 0}, + {relFn: TypesGT, base: 1, ok: 0, fail: 2}, + } + + for _, item := range items { + require.True(t, + item.relFn( + TypeFromUint32(item.base), + TypeFromUint32(item.ok), + ), + ) + + require.False(t, + item.relFn( + TypeFromUint32(item.base), + TypeFromUint32(item.fail), + ), + ) + } +} diff --git a/pkg/core/object/headers/enum.go b/pkg/core/object/headers/enum.go new file mode 100644 index 00000000..730d3559 --- /dev/null +++ b/pkg/core/object/headers/enum.go @@ -0,0 +1,63 @@ +package headers + +import ( + "github.com/nspcc-dev/neofs-node/pkg/core/object" +) + +// Header represents object extended header. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/object.ExtendedHeader. +type Header = object.ExtendedHeader + +// Type represents extended header type. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/object.ExtendedHeaderType. +type Type = object.ExtendedHeaderType + +const ( + // this is the only place where this cast is appropriate, + // use object.TypeFromUint32 instead. + lowerUndefined = Type(iota) // lower unsupported Type value + + // TypeLink is the type of object reference header. + TypeLink + + // TypeUser is the of user key-value string header. + TypeUser + + // TypeTransform is the type of transformation mark header. + TypeTransform + + // TypeTombstone is the type of tombstone mark header. + TypeTombstone + + // TypeSessionToken is the type of session token header. + TypeSessionToken + + // TypeHomomorphicHash is the type of homomorphic hash header. + TypeHomomorphicHash + + // TypePayloadChecksum is the type of payload checksum header. + TypePayloadChecksum + + // TypeIntegrity is the type of integrity header. + TypeIntegrity + + // TypeStorageGroup is the type of storage group header. + TypeStorageGroup + + // TypePublicKey is the type of public key header. + TypePublicKey + + upperUndefined // upper unsupported Type value +) + +// SupportedType returns true if Type is +// the known type of extended header. Each +// supported type has named constant. +func SupportedType(t Type) bool { + return object.TypesGT(t, lowerUndefined) && + object.TypesLT(t, upperUndefined) +} diff --git a/pkg/core/object/headers/enum_test.go b/pkg/core/object/headers/enum_test.go new file mode 100644 index 00000000..346948ab --- /dev/null +++ b/pkg/core/object/headers/enum_test.go @@ -0,0 +1,34 @@ +package headers + +import ( + "testing" + + "github.com/nspcc-dev/neofs-node/pkg/core/object" + "github.com/stretchr/testify/require" +) + +func TestSupportedType(t *testing.T) { + for _, typ := range []Type{ + TypeLink, + TypeUser, + TypeTransform, + TypeTombstone, + TypeSessionToken, + TypeHomomorphicHash, + TypePayloadChecksum, + TypeIntegrity, + TypeStorageGroup, + TypePublicKey, + } { + require.True(t, SupportedType(typ)) + } + + for _, typ := range []Type{ + lowerUndefined, + upperUndefined, + object.TypeFromUint32(object.TypeToUint32(lowerUndefined) - 1), + object.TypeFromUint32(object.TypeToUint32(upperUndefined) + 1), + } { + require.False(t, SupportedType(typ)) + } +} diff --git a/pkg/core/object/headers/user.go b/pkg/core/object/headers/user.go new file mode 100644 index 00000000..9ef738d8 --- /dev/null +++ b/pkg/core/object/headers/user.go @@ -0,0 +1,45 @@ +package headers + +// UserHeader is a value of object extended header +// that carries user string key-value pairs. +// +// All user headers must be type of TypeUser. +// All user header must have UserHeader pointer value. +type UserHeader struct { + key, val string +} + +// NewUserHeader creates, initialized and returns +// the user extended header. +func NewUserHeader(key, val string) *Header { + res := new(Header) + + res.SetType(TypeUser) + + res.SetValue(&UserHeader{ + key: key, + val: val, + }) + + return res +} + +// Key returns the user header key. +func (u UserHeader) Key() string { + return u.key +} + +// SetKey sets the user header key. +func (u *UserHeader) SetKey(key string) { + u.key = key +} + +// Value returns the user header value. +func (u UserHeader) Value() string { + return u.val +} + +// SetValue sets the user header value. +func (u *UserHeader) SetValue(val string) { + u.val = val +} diff --git a/pkg/core/object/headers/user_test.go b/pkg/core/object/headers/user_test.go new file mode 100644 index 00000000..91903ed0 --- /dev/null +++ b/pkg/core/object/headers/user_test.go @@ -0,0 +1,45 @@ +package headers + +import ( + "testing" + + "github.com/nspcc-dev/neofs-node/pkg/core/object" + "github.com/stretchr/testify/require" +) + +func TestUserHeader_Key(t *testing.T) { + h := new(UserHeader) + + key := "random key" + h.SetKey(key) + + require.Equal(t, key, h.Key()) +} + +func TestUserHeader_Value(t *testing.T) { + h := new(UserHeader) + + val := "random value" + h.SetValue(val) + + require.Equal(t, val, h.Value()) +} + +func TestNewUserHeader(t *testing.T) { + key := "user key" + val := "user val" + + h := NewUserHeader(key, val) + + require.True(t, + object.TypesEQ( + TypeUser, + h.Type(), + ), + ) + + uh := h.Value().(*UserHeader) + + require.Equal(t, key, uh.Key()) + require.Equal(t, val, uh.Value()) +} diff --git a/pkg/core/object/id.go b/pkg/core/object/id.go new file mode 100644 index 00000000..c6a5fa58 --- /dev/null +++ b/pkg/core/object/id.go @@ -0,0 +1,61 @@ +package object + +import ( + "github.com/nspcc-dev/neofs-api-go/refs" +) + +// ID represents the object identifier. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-api-go/refs.ObjectID. +// FIXME: object ID should be defined in core package. +type ID = refs.ObjectID + +// Address represents NeoFS Object address. +// Acts as a reference to the object. +type Address struct { + cid CID + + id ID +} + +// CID return the identifier of the container +// that the object belongs to. +func (a Address) CID() CID { + return a.cid +} + +// SetCID sets the identifier of the container +// that the object belongs to. +func (a *Address) SetCID(v CID) { + a.cid = v +} + +// ID returns the unique identifier of the +// object in container. +func (a Address) ID() ID { + return a.id +} + +// SetID sets the unique identifier of the +// object in container. +func (a *Address) SetID(v ID) { + a.id = v +} + +// AddressFromObject returns an address based +// on the object's header. +// +// Returns nil on nil object. +func AddressFromObject(o *Object) *Address { + if o == nil { + return nil + } + + a := new(Address) + + a.SetCID(o.CID()) + a.SetID(o.ID()) + + return a +} diff --git a/pkg/core/object/id_test.go b/pkg/core/object/id_test.go new file mode 100644 index 00000000..853c2817 --- /dev/null +++ b/pkg/core/object/id_test.go @@ -0,0 +1,42 @@ +package object + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestAddress_CID(t *testing.T) { + a := new(Address) + + cid := CID{1, 2, 3} + a.SetCID(cid) + + require.Equal(t, cid, a.CID()) +} + +func TestAddress_ID(t *testing.T) { + a := new(Address) + + id := ID{1, 2, 3} + a.SetID(id) + + require.Equal(t, id, a.ID()) +} + +func TestAddressFromObject(t *testing.T) { + require.Nil(t, AddressFromObject(nil)) + + o := new(Object) + + cid := CID{4, 5, 6} + o.SetCID(cid) + + id := ID{1, 2, 3} + o.SetID(id) + + a := AddressFromObject(o) + + require.Equal(t, cid, a.CID()) + require.Equal(t, id, a.ID()) +} diff --git a/pkg/core/object/object.go b/pkg/core/object/object.go new file mode 100644 index 00000000..57e87446 --- /dev/null +++ b/pkg/core/object/object.go @@ -0,0 +1,76 @@ +package object + +import ( + "errors" +) + +// Object represents NeoFS Object. +type Object struct { + // Header is an obligatory part of any object. + // It is used to carry any additional information + // besides payload. + // + // Object must inherit all the methods of Header, + // so the Header is embedded in Object. + Header + + payload []byte // payload bytes +} + +// ErrNilObject is returned by functions that expect +// a non-nil Object pointer, but received nil. +var ErrNilObject = errors.New("object is nil") + +// Payload returns payload bytes of the object. +// +// Changing the result is unsafe and affects +// the object. In order to prevent state +// mutations, use CopyPayload. +func (o *Object) Payload() []byte { + return o.payload +} + +// CopyPayload returns the copy of +// object payload. +// +// Changing the result is safe and +// does not affect the object. +// +// CopyPayload returns nil if object is nil. +func CopyPayload(o *Object) []byte { + if o == nil { + return nil + } + + res := make([]byte, len(o.payload)) + copy(res, o.payload) + + return res +} + +// SetPayload sets objecyt payload bytes. +// +// Subsequent changing the source slice +// is unsafe and affects the object. +// In order to prevent state mutations, +// use SetPayloadCopy. +func (o *Object) SetPayload(v []byte) { + o.payload = v +} + +// SetPayloadCopy copies slice bytes and sets +// the copy as object payload. +// +// Subsequent changing the source slice +// is safe and does not affect the object. +// +// SetPayloadCopy does nothing if object is nil. +func SetPayloadCopy(o *Object, payload []byte) { + if o == nil { + return + } + + o.payload = make([]byte, len(payload)) + + copy(o.payload, payload) +} diff --git a/pkg/core/object/object_test.go b/pkg/core/object/object_test.go new file mode 100644 index 00000000..9b1bba54 --- /dev/null +++ b/pkg/core/object/object_test.go @@ -0,0 +1,58 @@ +package object + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestObject_Payload(t *testing.T) { + o := new(Object) + + payload := []byte{1, 2, 3} + o.SetPayload(payload) + + require.Equal(t, payload, o.Payload()) +} + +func TestCopyPayload(t *testing.T) { + require.Nil(t, CopyPayload(nil)) + + o := new(Object) + + // set initial node key + initPayload := []byte{1, 2, 3} + o.SetPayload(initPayload) + + // get payload copy + pCopy := CopyPayload(o) + + // change the copy + pCopy[0]++ + + // check that payload has not changed + require.Equal(t, initPayload, o.Payload()) +} + +func TestSetPayloadCopy(t *testing.T) { + require.NotPanics(t, func() { + SetExtendedHeadersCopy(nil, nil) + }) + + o := new(Object) + + // create source payload + srcPayload := []byte{1, 2, 3} + + // copy and set payload + SetPayloadCopy(o, srcPayload) + + // get payload + objPayload := o.Payload() + + // change the source payload + srcPayload[0]++ + + // check that payload has not changed + require.Equal(t, objPayload, o.Payload()) +} diff --git a/pkg/core/object/storage/storage.go b/pkg/core/object/storage/storage.go new file mode 100644 index 00000000..16905926 --- /dev/null +++ b/pkg/core/object/storage/storage.go @@ -0,0 +1,61 @@ +package storage + +import ( + "errors" + + "github.com/nspcc-dev/neofs-node/pkg/core/object" +) + +// Object represents the NeoFS Object. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/object.Object. +type Object = object.Object + +// Address represents the address of +// NeoFS Object. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/object.Address. +type Address = object.Address + +// Storage is an interface that wraps +// basic object storage methods. +type Storage interface { + // Put saves pointed object to the underlying storage. + // It returns object address for reference and any error + // encountered that caused the saving to interrupt. + // + // Put must return object.ErrNilObject on nil-pointer. + // + // Implementations must not modify the object through the pointer (even temporarily). + // Implementations must not retain the object pointer. + // + // Object rewriting behavior is dictated by implementation. + Put(*Object) (*Address, error) + + // Get reads the object from the storage by address. + // It returns the pointer to requested object and any error encountered. + // + // Get must return exactly one non-nil value. + // Get must return ErrNotFound if the object is not in storage. + // + // Implementations must not retain the object pointer and modify + // the object through it. + Get(Address) (*Object, error) + + // Delete removes the object from the storage. + // It returns any error encountered that caused the deletion to interrupt. + // + // Delete must return nil if object was successfully deleted. + // + // Behavior when deleting a nonexistent object is dictated by implementation. + Delete(Address) error +} + +// ErrNotFound is the error returned when object was not found in storage. +var ErrNotFound = errors.New("object not found") + +// ErrNilStorage is the error returned by functions that +// expect a non-nil object storage implementation, but received nil. +var ErrNilStorage = errors.New("object storage is nil") diff --git a/pkg/core/object/storage/test/storage.go b/pkg/core/object/storage/test/storage.go new file mode 100644 index 00000000..fbea9e77 --- /dev/null +++ b/pkg/core/object/storage/test/storage.go @@ -0,0 +1,88 @@ +package test + +import ( + "sync" + "testing" + + "github.com/nspcc-dev/neofs-node/pkg/core/object" + "github.com/nspcc-dev/neofs-node/pkg/core/object/storage" + "github.com/pkg/errors" + "github.com/stretchr/testify/require" +) + +type testStorage struct { + *sync.RWMutex + + items map[storage.Address]*storage.Object +} + +func (s *testStorage) Put(o *storage.Object) (*storage.Address, error) { + if o == nil { + return nil, object.ErrNilObject + } + + a := object.AddressFromObject(o) + + s.Lock() + s.items[*a] = o + s.Unlock() + + return a, nil +} + +func (s *testStorage) Get(a storage.Address) (*storage.Object, error) { + s.RLock() + o, ok := s.items[a] + s.RUnlock() + + if !ok { + return nil, storage.ErrNotFound + } + + return o, nil +} + +func (s *testStorage) Delete(a storage.Address) error { + s.Lock() + delete(s.items, a) + s.Unlock() + + return nil +} + +// New creates new container storage +// that stores containers in go-builtin map. +func New() storage.Storage { + return &testStorage{ + RWMutex: new(sync.RWMutex), + items: make(map[storage.Address]*storage.Object), + } +} + +// Storage conducts testing of object +// storage for interface specification. +// +// Storage must be empty. +func Storage(t *testing.T, s storage.Storage) { + _, err := s.Put(nil) + require.True(t, errors.Is(err, object.ErrNilObject)) + + a := new(object.Address) + _, err = s.Get(*a) + require.True(t, errors.Is(err, storage.ErrNotFound)) + + o := new(object.Object) + o.SetID(object.ID{1, 2, 3}) + + a, err = s.Put(o) + require.NoError(t, err) + + o2, err := s.Get(*a) + require.NoError(t, err) + + require.Equal(t, o, o2) + + require.NoError(t, s.Delete(*a)) + _, err = s.Get(*a) + require.True(t, errors.Is(err, storage.ErrNotFound)) +} diff --git a/pkg/core/object/storage/test/storage_test.go b/pkg/core/object/storage/test/storage_test.go new file mode 100644 index 00000000..7614f467 --- /dev/null +++ b/pkg/core/object/storage/test/storage_test.go @@ -0,0 +1,11 @@ +package test + +import ( + "testing" +) + +func TestNewStorage(t *testing.T) { + s := New() + + Storage(t, s) +} diff --git a/pkg/core/object/sys.go b/pkg/core/object/sys.go new file mode 100644 index 00000000..a73a9ba1 --- /dev/null +++ b/pkg/core/object/sys.go @@ -0,0 +1,107 @@ +package object + +import ( + "github.com/nspcc-dev/neofs-node/pkg/core/container" + "github.com/nspcc-dev/neofs-node/pkg/core/netmap/epoch" +) + +// CID represents the container identifier. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/container.ID. +type CID = container.ID + +// OwnerID represents the container +// owner identifier. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/container.OwnerID. +type OwnerID = container.OwnerID + +// Epoch represents the NeoFS epoch number. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/netmap/epoch.Epoch. +type Epoch = epoch.Epoch + +// SystemHeader represents the +// system header of NeoFS Object. +type SystemHeader struct { + version uint64 // object version + + payloadLen uint64 // length of the payload bytes + + id ID // object ID + + cid CID // container ID + + ownerID OwnerID // object owner ID + + creatEpoch Epoch // creation epoch number +} + +// Version returns the object version number. +func (s *SystemHeader) Version() uint64 { + return s.version +} + +// SetVersion sets the object version number. +func (s *SystemHeader) SetVersion(v uint64) { + s.version = v +} + +// PayloadLength returns the length of the +// object payload bytes. +func (s *SystemHeader) PayloadLength() uint64 { + return s.payloadLen +} + +// SetPayloadLength sets the length of the object +// payload bytes. +func (s *SystemHeader) SetPayloadLength(v uint64) { + s.payloadLen = v +} + +// ID returns the object identifier. +func (s *SystemHeader) ID() ID { + return s.id +} + +// SetID sets the object identifier. +func (s *SystemHeader) SetID(v ID) { + s.id = v +} + +// CID returns the container identifier +// to which the object belongs. +func (s *SystemHeader) CID() CID { + return s.cid +} + +// SetCID sets the container identifier +// to which the object belongs. +func (s *SystemHeader) SetCID(v CID) { + s.cid = v +} + +// OwnerID returns the object owner identifier. +func (s *SystemHeader) OwnerID() OwnerID { + return s.ownerID +} + +// SetOwnerID sets the object owner identifier. +func (s *SystemHeader) SetOwnerID(v OwnerID) { + s.ownerID = v +} + +// CreationEpoch returns the epoch number +// in which the object was created. +func (s *SystemHeader) CreationEpoch() Epoch { + return s.creatEpoch +} + +// SetCreationEpoch sets the epoch number +// in which the object was created. +func (s *SystemHeader) SetCreationEpoch(v Epoch) { + s.creatEpoch = v +} diff --git a/pkg/core/object/sys_test.go b/pkg/core/object/sys_test.go new file mode 100644 index 00000000..f14ee82a --- /dev/null +++ b/pkg/core/object/sys_test.go @@ -0,0 +1,62 @@ +package object + +import ( + "testing" + + "github.com/nspcc-dev/neofs-node/pkg/core/netmap/epoch" + "github.com/stretchr/testify/require" +) + +func TestSystemHeader_Version(t *testing.T) { + h := new(SystemHeader) + + v := uint64(7) + h.SetVersion(v) + + require.Equal(t, v, h.Version()) +} + +func TestSystemHeader_PayloadLength(t *testing.T) { + h := new(SystemHeader) + + ln := uint64(3) + h.SetPayloadLength(ln) + + require.Equal(t, ln, h.PayloadLength()) +} + +func TestSystemHeader_ID(t *testing.T) { + h := new(SystemHeader) + + id := ID{1, 2, 3} + h.SetID(id) + + require.Equal(t, id, h.ID()) +} + +func TestSystemHeader_CID(t *testing.T) { + h := new(SystemHeader) + + cid := CID{1, 2, 3} + h.SetCID(cid) + + require.Equal(t, cid, h.CID()) +} + +func TestSystemHeader_OwnerID(t *testing.T) { + h := new(SystemHeader) + + ownerID := OwnerID{1, 2, 3} + h.SetOwnerID(ownerID) + + require.Equal(t, ownerID, h.OwnerID()) +} + +func TestSystemHeader_CreationEpoch(t *testing.T) { + h := new(SystemHeader) + + ep := epoch.FromUint64(1) + h.SetCreationEpoch(ep) + + require.True(t, epoch.EQ(ep, h.CreationEpoch())) +} diff --git a/pkg/innerring/bindings.go b/pkg/innerring/bindings.go new file mode 100644 index 00000000..ee0e8652 --- /dev/null +++ b/pkg/innerring/bindings.go @@ -0,0 +1,54 @@ +package innerring + +import ( + "github.com/nspcc-dev/neofs-node/pkg/innerring/timers" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" +) + +type ( + // ContractProcessor interface defines functions for binding event producers + // such as event.Listener and Timers with contract processor. + ContractProcessor interface { + ListenerParsers() []event.ParserInfo + ListenerHandlers() []event.HandlerInfo + TimersHandlers() []event.HandlerInfo + } +) + +func connectListenerWithProcessor(l event.Listener, p ContractProcessor) { + // register parsers + for _, parser := range p.ListenerParsers() { + l.SetParser(parser) + } + + // register handlers + for _, handler := range p.ListenerHandlers() { + l.RegisterHandler(handler) + } +} + +func connectTimerWithProcessor(t *timers.Timers, p ContractProcessor) error { + var err error + for _, parser := range p.TimersHandlers() { + err = t.RegisterHandler(parser) + if err != nil { + return err + } + } + + return nil +} + +// bindMorphProcessor connects both morph chain listener handlers and +// local timers handlers. +func bindMorphProcessor(proc ContractProcessor, s *Server) error { + connectListenerWithProcessor(s.morphListener, proc) + return connectTimerWithProcessor(s.localTimers, proc) +} + +// bindMainnetProcessor connects both mainnet chain listener handlers and +// local timers handlers. +func bindMainnetProcessor(proc ContractProcessor, s *Server) error { + connectListenerWithProcessor(s.mainnetListener, proc) + return connectTimerWithProcessor(s.localTimers, proc) +} diff --git a/pkg/innerring/innerring.go b/pkg/innerring/innerring.go new file mode 100644 index 00000000..0b486b90 --- /dev/null +++ b/pkg/innerring/innerring.go @@ -0,0 +1,302 @@ +package innerring + +import ( + "context" + "crypto/ecdsa" + + "github.com/nspcc-dev/neo-go/pkg/config/netmode" + "github.com/nspcc-dev/neo-go/pkg/util" + crypto "github.com/nspcc-dev/neofs-crypto" + "github.com/nspcc-dev/neofs-node/pkg/innerring/invoke" + "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/balance" + "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/neofs" + "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/netmap" + "github.com/nspcc-dev/neofs-node/pkg/innerring/timers" + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + "github.com/nspcc-dev/neofs-node/pkg/morph/subscriber" + "github.com/pkg/errors" + "github.com/spf13/viper" + "go.uber.org/atomic" + "go.uber.org/zap" +) + +type ( + // Server is the inner ring application structure, that contains all event + // processors, shared variables and event handlers. + Server struct { + log *zap.Logger + + // event producers + morphListener event.Listener + mainnetListener event.Listener + localTimers *timers.Timers + + // global state + morphClient *client.Client + mainnetClient *client.Client + epochCounter atomic.Uint64 + activeState atomic.Bool + + // todo: export error channel + } + + contracts struct { + neofs util.Uint160 // in mainnet + netmap util.Uint160 // in morph + balance util.Uint160 // in morph + container util.Uint160 // in morph + audit util.Uint160 // in morph + reputation util.Uint160 // in morph + neofsid util.Uint160 // in morph + gas util.Uint160 // native contract in both chains + } + + chainParams struct { + log *zap.Logger + cfg *viper.Viper + key *ecdsa.PrivateKey + name string + gas util.Uint160 + } +) + +const ( + morphPrefix = "morph" + mainnetPrefix = "mainnet" +) + +// Start runs all event providers. +func (s *Server) Start(ctx context.Context) error { + s.localTimers.Start(ctx) // local timers start ticking + + go s.morphListener.Listen(ctx) // listen for neo:morph events + go s.mainnetListener.Listen(ctx) // listen for neo:mainnet events + + return nil +} + +// Stop closes all subscription channels. +func (s *Server) Stop() { + go s.morphListener.Stop() + go s.mainnetListener.Stop() +} + +// New creates instance of inner ring sever structure. +func New(ctx context.Context, log *zap.Logger, cfg *viper.Viper) (*Server, error) { + server := &Server{log: log} + + // prepare inner ring node private key + key, err := crypto.LoadPrivateKey(cfg.GetString("key")) + if err != nil { + return nil, errors.Wrap(err, "ir: can't create private key") + } + + // get all script hashes of contracts + contracts, err := parseContracts(cfg) + if err != nil { + return nil, err + } + + // create local timer instance + server.localTimers = timers.New(&timers.Params{ + Log: log, + EpochDuration: cfg.GetDuration("timers.epoch"), + }) + + morphChain := &chainParams{ + log: log, + cfg: cfg, + key: key, + gas: contracts.gas, + name: morphPrefix, + } + + // create morph listener + server.morphListener, err = createListener(ctx, morphChain) + if err != nil { + return nil, err + } + + // create morph client + server.morphClient, err = createClient(ctx, morphChain) + if err != nil { + return nil, err + } + + mainnetChain := morphChain + mainnetChain.name = mainnetPrefix + + // create mainnet listener + server.mainnetListener, err = createListener(ctx, mainnetChain) + if err != nil { + return nil, err + } + + // create mainnet client + server.mainnetClient, err = createClient(ctx, mainnetChain) + if err != nil { + return nil, err + } + + // create netmap processor + netmapProcessor, err := netmap.New(&netmap.Params{ + Log: log, + PoolSize: cfg.GetInt("workers.netmap"), + NetmapContract: contracts.netmap, + EpochTimer: server.localTimers, + MorphClient: server.morphClient, + EpochState: server, + ActiveState: server, + }) + if err != nil { + return nil, err + } + + err = bindMorphProcessor(netmapProcessor, server) + if err != nil { + return nil, err + } + + // todo: create container processor + + // create balance processor + balanceProcessor, err := balance.New(&balance.Params{ + Log: log, + PoolSize: cfg.GetInt("workers.balance"), + NeoFSContract: contracts.neofs, + BalanceContract: contracts.balance, + MainnetClient: server.mainnetClient, + ActiveState: server, + }) + if err != nil { + return nil, err + } + + err = bindMorphProcessor(balanceProcessor, server) + if err != nil { + return nil, err + } + + // todo: create reputation processor + + // create mainnnet neofs processor + neofsProcessor, err := neofs.New(&neofs.Params{ + Log: log, + PoolSize: cfg.GetInt("workers.neofs"), + NeoFSContract: contracts.neofs, + BalanceContract: contracts.balance, + MorphClient: server.morphClient, + EpochState: server, + ActiveState: server, + }) + if err != nil { + return nil, err + } + + err = bindMainnetProcessor(neofsProcessor, server) + if err != nil { + return nil, err + } + + // todo: create vivid id component + // todo: create audit scheduler + + err = initConfigFromBlockchain(server, contracts, &key.PublicKey) + if err != nil { + return nil, err + } + + return server, nil +} + +func createListener(ctx context.Context, p *chainParams) (event.Listener, error) { + sub, err := subscriber.New(ctx, &subscriber.Params{ + Log: p.log, + Endpoint: p.cfg.GetString(p.name + ".endpoint.notification"), + DialTimeout: p.cfg.GetDuration(p.name + ".dial_timeouts"), + }) + if err != nil { + return nil, err + } + + listener, err := event.NewListener(event.ListenerParams{ + Logger: p.log, + Subscriber: sub, + }) + if err != nil { + return nil, err + } + + return listener, err +} + +func createClient(ctx context.Context, p *chainParams) (*client.Client, error) { + return client.New( + p.key, + p.cfg.GetString(p.name+".endpoint.client"), + client.WithContext(ctx), + client.WithLogger(p.log), + client.WithDialTimeout(p.cfg.GetDuration(p.name+".dial_timeouts")), + client.WithMagic(netmode.Magic(p.cfg.GetUint32(p.name+".magic_number"))), + client.WithGasContract(p.gas), + ) +} + +func parseContracts(cfg *viper.Viper) (*contracts, error) { + var ( + result = new(contracts) + err error + ) + + netmapContractStr := cfg.GetString("contracts.netmap") + neofsContractStr := cfg.GetString("contracts.neofs") + balanceContractStr := cfg.GetString("contracts.balance") + nativeGasContractStr := cfg.GetString("contracts.gas") + + result.netmap, err = util.Uint160DecodeStringLE(netmapContractStr) + if err != nil { + return nil, errors.Wrap(err, "ir: can't read netmap script-hash") + } + + result.neofs, err = util.Uint160DecodeStringLE(neofsContractStr) + if err != nil { + return nil, errors.Wrap(err, "ir: can't read neofs script-hash") + } + + result.balance, err = util.Uint160DecodeStringLE(balanceContractStr) + if err != nil { + return nil, errors.Wrap(err, "ir: can't read balance script-hash") + } + + result.gas, err = util.Uint160DecodeStringLE(nativeGasContractStr) + if err != nil { + return nil, errors.Wrap(err, "ir: can't read native gas script-hash") + } + + return result, nil +} + +func initConfigFromBlockchain(s *Server, c *contracts, key *ecdsa.PublicKey) error { + // get current epoch + epoch, err := invoke.Epoch(s.morphClient, c.netmap) + if err != nil { + return err + } + + // check if node inside inner ring list + state, err := invoke.IsInnerRing(s.mainnetClient, c.neofs, key) + if err != nil { + return err + } + + s.epochCounter.Store(uint64(epoch)) + s.activeState.Store(state) + + s.log.Debug("read config from blockchain", + zap.Bool("active", state), + zap.Int64("epoch", epoch), + ) + + return nil +} diff --git a/pkg/innerring/invoke/balance.go b/pkg/innerring/invoke/balance.go new file mode 100644 index 00000000..9dc30da9 --- /dev/null +++ b/pkg/innerring/invoke/balance.go @@ -0,0 +1,59 @@ +package invoke + +import ( + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neofs-node/pkg/morph/client" +) + +type ( + // TransferXParams for TransferBalanceX invocation. + TransferXParams struct { + Sender []byte + Receiver []byte + Amount int64 // in Fixed16 + Comment []byte + } + + // LockParams for LockAsset invocation. + LockParams struct { + ID []byte + User util.Uint160 + LockAccount util.Uint160 + Amount int64 // in Fixed16 + Until uint64 // epochs + } +) + +const ( + transferXMethod = "transferX" + lockMethod = "Lock" +) + +// TransferBalanceX invokes transferX method. +func TransferBalanceX(cli *client.Client, con util.Uint160, p *TransferXParams) error { + if cli == nil { + return client.ErrNilClient + } + + return cli.Invoke(con, extraFee, transferXMethod, + p.Sender, + p.Receiver, + p.Amount, + p.Comment, + ) +} + +// LockAsset invokes Lock method. +func LockAsset(cli *client.Client, con util.Uint160, p *LockParams) error { + if cli == nil { + return client.ErrNilClient + } + + return cli.Invoke(con, extraFee, lockMethod, + p.ID, + p.User.BytesBE(), + p.LockAccount.BytesBE(), + p.Amount, + int64(p.Until), // fixme: invoke can work only with int64 values + ) +} diff --git a/pkg/innerring/invoke/neofs.go b/pkg/innerring/invoke/neofs.go new file mode 100644 index 00000000..e7df4151 --- /dev/null +++ b/pkg/innerring/invoke/neofs.go @@ -0,0 +1,68 @@ +package invoke + +import ( + "crypto/ecdsa" + + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neofs-crypto" + "github.com/nspcc-dev/neofs-node/pkg/morph/client" +) + +type ( + // ChequeParams for CashOutCheque invocation. + ChequeParams struct { + ID []byte + Amount int64 // Fixed8 + User util.Uint160 + LockAccount util.Uint160 + } +) + +const ( + // Extra SysFee for contract invocations. Contracts execute inner ring + // invocations in two steps: collection and execution. At collection + // step contract waits for (2\3*n + 1) invocations, and then in execution + // stage contract actually makes changes in the contract storage. SysFee + // for invocation calculated based on testinvoke which happens at collection + // stage. Therefore client has to provide some extra SysFee to operate at + // execution stage. Otherwise invocation will fail due to gas limit. + extraFee = 5000_0000 // 0.5 Fixed8 gas + + checkIsInnerRingMethod = "IsInnerRing" + chequeMethod = "Cheque" +) + +// IsInnerRing returns true if 'key' is presented in inner ring list. +func IsInnerRing(cli *client.Client, con util.Uint160, key *ecdsa.PublicKey) (bool, error) { + if cli == nil { + return false, client.ErrNilClient + } + + pubKey := crypto.MarshalPublicKey(key) + + val, err := cli.TestInvoke(con, checkIsInnerRingMethod, pubKey) + if err != nil { + return false, err + } + + isInnerRing, err := client.BoolFromStackParameter(val[0]) + if err != nil { + return false, err + } + + return isInnerRing, nil +} + +// CashOutCheque invokes Cheque method. +func CashOutCheque(cli *client.Client, con util.Uint160, p *ChequeParams) error { + if cli == nil { + return client.ErrNilClient + } + + return cli.Invoke(con, extraFee, chequeMethod, + p.ID, + p.User.BytesBE(), + p.Amount, + p.LockAccount.BytesBE(), + ) +} diff --git a/pkg/innerring/invoke/netmap.go b/pkg/innerring/invoke/netmap.go new file mode 100644 index 00000000..7bfd3475 --- /dev/null +++ b/pkg/innerring/invoke/netmap.go @@ -0,0 +1,39 @@ +package invoke + +import ( + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neofs-node/pkg/morph/client" +) + +const ( + getEpochMethod = "Epoch" + setNewEpochMethod = "NewEpoch" +) + +// Epoch return epoch value from contract. +func Epoch(cli *client.Client, con util.Uint160) (int64, error) { + if cli == nil { + return 0, client.ErrNilClient + } + + val, err := cli.TestInvoke(con, getEpochMethod) + if err != nil { + return 0, err + } + + epoch, err := client.IntFromStackParameter(val[0]) + if err != nil { + return 0, err + } + + return epoch, nil +} + +// SetNewEpoch invokes NewEpoch method. +func SetNewEpoch(cli *client.Client, con util.Uint160, epoch uint64) error { + if cli == nil { + return client.ErrNilClient + } + + return cli.Invoke(con, extraFee, setNewEpochMethod, int64(epoch)) +} diff --git a/pkg/innerring/processors/balance/handlers.go b/pkg/innerring/processors/balance/handlers.go new file mode 100644 index 00000000..8aa8ed9d --- /dev/null +++ b/pkg/innerring/processors/balance/handlers.go @@ -0,0 +1,25 @@ +package balance + +import ( + "encoding/hex" + + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + balanceEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/balance" + "go.uber.org/zap" +) + +func (bp *Processor) handleLock(ev event.Event) { + lock := ev.(balanceEvent.Lock) // todo: check panic in production + bp.log.Info("notification", + zap.String("type", "lock"), + zap.String("value", hex.EncodeToString(lock.ID()))) + + // send event to the worker pool + + err := bp.pool.Submit(func() { bp.processLock(&lock) }) + if err != nil { + // todo: move into controlled degradation stage + bp.log.Warn("balance worker pool drained", + zap.Int("capacity", bp.pool.Cap())) + } +} diff --git a/pkg/innerring/processors/balance/process_assets.go b/pkg/innerring/processors/balance/process_assets.go new file mode 100644 index 00000000..acb18039 --- /dev/null +++ b/pkg/innerring/processors/balance/process_assets.go @@ -0,0 +1,27 @@ +package balance + +import ( + "github.com/nspcc-dev/neofs-node/pkg/innerring/invoke" + balanceEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/balance" + "go.uber.org/zap" +) + +// Process lock event by invoking Cheque method in main net to send assets +// back to the withdraw issuer. +func (bp *Processor) processLock(lock *balanceEvent.Lock) { + if !bp.activeState.IsActive() { + bp.log.Info("passive mode, ignore balance lock") + return + } + + err := invoke.CashOutCheque(bp.mainnetClient, bp.neofsContract, + &invoke.ChequeParams{ + ID: lock.ID(), + Amount: lock.Amount() / 1_0000_0000, // todo: Fixed16 to Fixed8 + User: lock.User(), + LockAccount: lock.LockAccount(), + }) + if err != nil { + bp.log.Error("can't send lock asset tx", zap.Error(err)) + } +} diff --git a/pkg/innerring/processors/balance/processor.go b/pkg/innerring/processors/balance/processor.go new file mode 100644 index 00000000..d317aff6 --- /dev/null +++ b/pkg/innerring/processors/balance/processor.go @@ -0,0 +1,103 @@ +package balance + +import ( + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + balanceEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/balance" + "github.com/panjf2000/ants/v2" + "github.com/pkg/errors" + "go.uber.org/zap" +) + +type ( + // ActiveState is a callback interface for inner ring global state + ActiveState interface { + IsActive() bool + } + + // Processor of events produced by balance contract in morph chain. + Processor struct { + log *zap.Logger + pool *ants.Pool + neofsContract util.Uint160 + balanceContract util.Uint160 + mainnetClient *client.Client + activeState ActiveState + } + + // Params of the processor constructor. + Params struct { + Log *zap.Logger + PoolSize int + NeoFSContract util.Uint160 + BalanceContract util.Uint160 + MainnetClient *client.Client + ActiveState ActiveState + } +) + +const ( + lockNotification = "Lock" +) + +// New creates balance contract processor instance. +func New(p *Params) (*Processor, error) { + switch { + case p.Log == nil: + return nil, errors.New("ir/balance: logger is not set") + case p.MainnetClient == nil: + return nil, errors.New("ir/balance: neo:mainnet client is not set") + case p.ActiveState == nil: + return nil, errors.New("ir/balance: global state is not set") + } + + p.Log.Debug("balance worker pool", zap.Int("size", p.PoolSize)) + + pool, err := ants.NewPool(p.PoolSize, ants.WithNonblocking(true)) + if err != nil { + return nil, errors.Wrap(err, "ir/balance: can't create worker pool") + } + + return &Processor{ + log: p.Log, + pool: pool, + neofsContract: p.NeoFSContract, + balanceContract: p.BalanceContract, + mainnetClient: p.MainnetClient, + activeState: p.ActiveState, + }, nil +} + +// ListenerParsers for the 'event.Listener' event producer. +func (bp *Processor) ListenerParsers() []event.ParserInfo { + var parsers []event.ParserInfo + + // new lock event + lock := event.ParserInfo{} + lock.SetType(lockNotification) + lock.SetScriptHash(bp.balanceContract) + lock.SetParser(balanceEvent.ParseLock) + parsers = append(parsers, lock) + + return parsers +} + +// ListenerHandlers for the 'event.Listener' event producer. +func (bp *Processor) ListenerHandlers() []event.HandlerInfo { + var handlers []event.HandlerInfo + + // lock handler + lock := event.HandlerInfo{} + lock.SetType(lockNotification) + lock.SetScriptHash(bp.balanceContract) + lock.SetHandler(bp.handleLock) + handlers = append(handlers, lock) + + return handlers +} + +// TimersHandlers for the 'Timers' event producer. +func (bp *Processor) TimersHandlers() []event.HandlerInfo { + return nil +} diff --git a/pkg/innerring/processors/neofs/handlers.go b/pkg/innerring/processors/neofs/handlers.go new file mode 100644 index 00000000..a64f6e77 --- /dev/null +++ b/pkg/innerring/processors/neofs/handlers.go @@ -0,0 +1,57 @@ +package neofs + +import ( + "encoding/hex" + + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + neofsEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/neofs" + "go.uber.org/zap" +) + +func (np *Processor) handleDeposit(ev event.Event) { + deposit := ev.(neofsEvent.Deposit) // todo: check panic in production + np.log.Info("notification", + zap.String("type", "deposit"), + zap.String("id", hex.EncodeToString(deposit.ID()))) + + // send event to the worker pool + + err := np.pool.Submit(func() { np.processDeposit(&deposit) }) + if err != nil { + // todo: move into controlled degradation stage + np.log.Warn("neofs processor worker pool drained", + zap.Int("capacity", np.pool.Cap())) + } +} + +func (np *Processor) handleWithdraw(ev event.Event) { + withdraw := ev.(neofsEvent.Withdraw) // todo: check panic in production + np.log.Info("notification", + zap.String("type", "withdraw"), + zap.String("id", hex.EncodeToString(withdraw.ID()))) + + // send event to the worker pool + + err := np.pool.Submit(func() { np.processWithdraw(&withdraw) }) + if err != nil { + // todo: move into controlled degradation stage + np.log.Warn("neofs processor worker pool drained", + zap.Int("capacity", np.pool.Cap())) + } +} + +func (np *Processor) handleCheque(ev event.Event) { + cheque := ev.(neofsEvent.Cheque) // todo: check panic in production + np.log.Info("notification", + zap.String("type", "cheque"), + zap.String("id", hex.EncodeToString(cheque.ID()))) + + // send event to the worker pool + + err := np.pool.Submit(func() { np.processCheque(&cheque) }) + if err != nil { + // todo: move into controlled degradation stage + np.log.Warn("neofs processor worker pool drained", + zap.Int("capacity", np.pool.Cap())) + } +} diff --git a/pkg/innerring/processors/neofs/process_assets.go b/pkg/innerring/processors/neofs/process_assets.go new file mode 100644 index 00000000..5a126604 --- /dev/null +++ b/pkg/innerring/processors/neofs/process_assets.go @@ -0,0 +1,104 @@ +package neofs + +import ( + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neofs-node/pkg/innerring/invoke" + neofsEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/neofs" + "go.uber.org/zap" +) + +const ( + // txLogPrefix used for balance transfer comments in balance contract. + txLogPrefix = "mainnet:" + + // lockAccountLifeTime defines amount of epochs when lock account is valid. + lockAccountLifetime uint64 = 20 +) + +var ( + // fedReserveAddr is a special account in balance contract. + fedReserveAddr = []byte{0x0F, 0xED} +) + +// Process deposit event by invoking balance contract and sending native +// gas in morph chain. +func (np *Processor) processDeposit(deposit *neofsEvent.Deposit) { + if !np.activeState.IsActive() { + np.log.Info("passive mode, ignore deposit") + return + } + + // send transferX to balance contract + err := invoke.TransferBalanceX(np.morphClient, np.balanceContract, + &invoke.TransferXParams{ + Sender: fedReserveAddr, + Receiver: deposit.To().BytesBE(), + Amount: deposit.Amount() * 1_0000_0000, // from Fixed8 to Fixed16 + Comment: append([]byte(txLogPrefix), deposit.ID()...), + }) + if err != nil { + np.log.Error("can't transfer assets to balance contract", zap.Error(err)) + } + + // fixme: send gas when ^ tx accepted + // fixme: do not send gas to the same user twice per epoch + err = np.morphClient.TransferGas(deposit.To(), util.Fixed8FromInt64(2)) + if err != nil { + np.log.Error("can't transfer native gas to receiver", zap.Error(err)) + } +} + +// Process withdraw event by locking assets in balance account. +func (np *Processor) processWithdraw(withdraw *neofsEvent.Withdraw) { + if !np.activeState.IsActive() { + np.log.Info("passive mode, ignore withdraw") + return + } + + if len(withdraw.ID()) < util.Uint160Size { + np.log.Error("tx id size is less than script hash size") + return + } + + // create lock account + // todo: check collision there, consider reversed script hash + lock, err := util.Uint160DecodeBytesBE(withdraw.ID()[:util.Uint160Size]) + if err != nil { + np.log.Error("can't create lock account", zap.Error(err)) + return + } + + curEpoch := np.epochState.EpochCounter() + + err = invoke.LockAsset(np.morphClient, np.balanceContract, + &invoke.LockParams{ + ID: withdraw.ID(), + User: withdraw.User(), + LockAccount: lock, + Amount: withdraw.Amount() * 1_0000_0000, // todo: from Fixed8 to Fixed16 + Until: curEpoch + lockAccountLifetime, + }) + if err != nil { + np.log.Error("can't lock assets for withdraw", zap.Error(err)) + } +} + +// Process cheque event by transferring assets from lock account back to +// reserve account. +func (np *Processor) processCheque(cheque *neofsEvent.Cheque) { + if !np.activeState.IsActive() { + np.log.Info("passive mode, ignore cheque") + return + } + + err := invoke.TransferBalanceX(np.morphClient, np.balanceContract, + &invoke.TransferXParams{ + Sender: cheque.LockAccount().BytesBE(), + Receiver: fedReserveAddr, + Amount: cheque.Amount() * 1_0000_0000, // from Fixed8 to Fixed16 + Comment: append([]byte(txLogPrefix), cheque.ID()...), + }) + if err != nil { + np.log.Error("can't transfer assets to fed contract", zap.Error(err)) + } +} diff --git a/pkg/innerring/processors/neofs/processor.go b/pkg/innerring/processors/neofs/processor.go new file mode 100644 index 00000000..6a60f520 --- /dev/null +++ b/pkg/innerring/processors/neofs/processor.go @@ -0,0 +1,143 @@ +package neofs + +import ( + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + neofsEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/neofs" + "github.com/panjf2000/ants/v2" + "github.com/pkg/errors" + "go.uber.org/zap" +) + +type ( + // EpochState is a callback interface for inner ring global state + EpochState interface { + EpochCounter() uint64 + } + + // ActiveState is a callback interface for inner ring global state + ActiveState interface { + IsActive() bool + } + + // Processor of events produced by neofs contract in main net. + Processor struct { + log *zap.Logger + pool *ants.Pool + neofsContract util.Uint160 + balanceContract util.Uint160 + morphClient *client.Client + epochState EpochState + activeState ActiveState + } + + // Params of the processor constructor. + Params struct { + Log *zap.Logger + PoolSize int + NeoFSContract util.Uint160 + BalanceContract util.Uint160 + MorphClient *client.Client + EpochState EpochState + ActiveState ActiveState + } +) + +const ( + depositNotification = "Deposit" + withdrawNotification = "Withdraw" + chequeNotification = "Cheque" +) + +// New creates neofs mainnet contract processor instance. +func New(p *Params) (*Processor, error) { + switch { + case p.Log == nil: + return nil, errors.New("ir/neofs: logger is not set") + case p.MorphClient == nil: + return nil, errors.New("ir/neofs: neo:morph client is not set") + case p.EpochState == nil: + return nil, errors.New("ir/neofs: global state is not set") + case p.ActiveState == nil: + return nil, errors.New("ir/neofs: global state is not set") + } + + p.Log.Debug("neofs worker pool", zap.Int("size", p.PoolSize)) + + pool, err := ants.NewPool(p.PoolSize, ants.WithNonblocking(true)) + if err != nil { + return nil, errors.Wrap(err, "ir/neofs: can't create worker pool") + } + + return &Processor{ + log: p.Log, + pool: pool, + neofsContract: p.NeoFSContract, + balanceContract: p.BalanceContract, + morphClient: p.MorphClient, + epochState: p.EpochState, + activeState: p.ActiveState, + }, nil +} + +// ListenerParsers for the 'event.Listener' event producer. +func (np *Processor) ListenerParsers() []event.ParserInfo { + var parsers []event.ParserInfo + + // deposit event + deposit := event.ParserInfo{} + deposit.SetType(depositNotification) + deposit.SetScriptHash(np.neofsContract) + deposit.SetParser(neofsEvent.ParseDeposit) + parsers = append(parsers, deposit) + + // withdraw event + withdraw := event.ParserInfo{} + withdraw.SetType(withdrawNotification) + withdraw.SetScriptHash(np.neofsContract) + withdraw.SetParser(neofsEvent.ParseWithdraw) + parsers = append(parsers, withdraw) + + // cheque event + cheque := event.ParserInfo{} + cheque.SetType(chequeNotification) + cheque.SetScriptHash(np.neofsContract) + cheque.SetParser(neofsEvent.ParseCheque) + parsers = append(parsers, cheque) + + return parsers +} + +// ListenerHandlers for the 'event.Listener' event producer. +func (np *Processor) ListenerHandlers() []event.HandlerInfo { + var handlers []event.HandlerInfo + + // deposit handler + deposit := event.HandlerInfo{} + deposit.SetType(depositNotification) + deposit.SetScriptHash(np.neofsContract) + deposit.SetHandler(np.handleDeposit) + handlers = append(handlers, deposit) + + // withdraw handler + withdraw := event.HandlerInfo{} + withdraw.SetType(withdrawNotification) + withdraw.SetScriptHash(np.neofsContract) + withdraw.SetHandler(np.handleWithdraw) + handlers = append(handlers, withdraw) + + // cheque handler + cheque := event.HandlerInfo{} + cheque.SetType(chequeNotification) + cheque.SetScriptHash(np.neofsContract) + cheque.SetHandler(np.handleCheque) + handlers = append(handlers, cheque) + + return handlers +} + +// TimersHandlers for the 'Timers' event producer. +func (np *Processor) TimersHandlers() []event.HandlerInfo { + return nil +} diff --git a/pkg/innerring/processors/netmap/handlers.go b/pkg/innerring/processors/netmap/handlers.go new file mode 100644 index 00000000..b403d8c7 --- /dev/null +++ b/pkg/innerring/processors/netmap/handlers.go @@ -0,0 +1,40 @@ +package netmap + +import ( + timerEvent "github.com/nspcc-dev/neofs-node/pkg/innerring/timers" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + netmapEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/netmap" + "go.uber.org/zap" +) + +func (np *Processor) handleNewEpochTick(ev event.Event) { + _ = ev.(timerEvent.NewEpochTick) // todo: check panic in production + np.log.Info("tick", zap.String("type", "epoch")) + + // send event to the worker pool + + err := np.pool.Submit(func() { np.processNewEpochTick() }) + if err != nil { + // todo: move into controlled degradation stage + np.log.Warn("netmap worker pool drained", + zap.Int("capacity", np.pool.Cap())) + } +} + +func (np *Processor) handleNewEpoch(ev event.Event) { + epochEvent := ev.(netmapEvent.NewEpoch) // todo: check panic in production + np.log.Info("notification", + zap.String("type", "new epoch"), + zap.Uint64("value", epochEvent.EpochNumber())) + + // send event to the worker pool + + err := np.pool.Submit(func() { + np.processNewEpoch(epochEvent.EpochNumber()) + }) + if err != nil { + // todo: move into controlled degradation stage + np.log.Warn("netmap worker pool drained", + zap.Int("capacity", np.pool.Cap())) + } +} diff --git a/pkg/innerring/processors/netmap/process_epoch.go b/pkg/innerring/processors/netmap/process_epoch.go new file mode 100644 index 00000000..abafd800 --- /dev/null +++ b/pkg/innerring/processors/netmap/process_epoch.go @@ -0,0 +1,29 @@ +package netmap + +import ( + "github.com/nspcc-dev/neofs-node/pkg/innerring/invoke" + "go.uber.org/zap" +) + +// Process new epoch notification by setting global epoch value and resetting +// local epoch timer. +func (np *Processor) processNewEpoch(epoch uint64) { + np.epochState.SetEpochCounter(epoch) + np.epochTimer.ResetEpochTimer() +} + +// Process new epoch tick by invoking new epoch method in network map contract. +func (np *Processor) processNewEpochTick() { + if !np.activeState.IsActive() { + np.log.Info("passive mode, ignore new epoch tick") + return + } + + nextEpoch := np.epochState.EpochCounter() + 1 + np.log.Debug("next epoch", zap.Uint64("value", nextEpoch)) + + err := invoke.SetNewEpoch(np.morphClient, np.netmapContract, nextEpoch) + if err != nil { + np.log.Error("can't invoke netmap.NewEpoch", zap.Error(err)) + } +} diff --git a/pkg/innerring/processors/netmap/processor.go b/pkg/innerring/processors/netmap/processor.go new file mode 100644 index 00000000..d762c7b0 --- /dev/null +++ b/pkg/innerring/processors/netmap/processor.go @@ -0,0 +1,131 @@ +package netmap + +import ( + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neofs-node/pkg/innerring/timers" + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + netmapEvent "github.com/nspcc-dev/neofs-node/pkg/morph/event/netmap" + "github.com/panjf2000/ants/v2" + "github.com/pkg/errors" + "go.uber.org/zap" +) + +type ( + // EpochTimerReseter is a callback interface for tickers component. + EpochTimerReseter interface { + ResetEpochTimer() + } + + // EpochState is a callback interface for inner ring global state. + EpochState interface { + SetEpochCounter(uint64) + EpochCounter() uint64 + } + + // ActiveState is a callback interface for inner ring global state. + ActiveState interface { + IsActive() bool + } + + // Processor of events produced by network map contract + // and new epoch ticker, because it is related to contract. + Processor struct { + log *zap.Logger + pool *ants.Pool + netmapContract util.Uint160 + epochTimer EpochTimerReseter + epochState EpochState + activeState ActiveState + morphClient *client.Client + } + + // Params of the processor constructor. + Params struct { + Log *zap.Logger + PoolSize int + NetmapContract util.Uint160 + EpochTimer EpochTimerReseter + MorphClient *client.Client + EpochState EpochState + ActiveState ActiveState + } +) + +const ( + newEpochNotification = "NewEpoch" +) + +// New creates network map contract processor instance. +func New(p *Params) (*Processor, error) { + switch { + case p.Log == nil: + return nil, errors.New("ir/netmap: logger is not set") + case p.MorphClient == nil: + return nil, errors.New("ir/netmap: morph client is not set") + case p.EpochTimer == nil: + return nil, errors.New("ir/netmap: epoch itmer is not set") + case p.EpochState == nil: + return nil, errors.New("ir/netmap: global state is not set") + case p.ActiveState == nil: + return nil, errors.New("ir/netmap: global state is not set") + } + + p.Log.Debug("netmap worker pool", zap.Int("size", p.PoolSize)) + + pool, err := ants.NewPool(p.PoolSize, ants.WithNonblocking(true)) + if err != nil { + return nil, errors.Wrap(err, "ir/netmap: can't create worker pool") + } + + return &Processor{ + log: p.Log, + pool: pool, + netmapContract: p.NetmapContract, + epochTimer: p.EpochTimer, + epochState: p.EpochState, + activeState: p.ActiveState, + morphClient: p.MorphClient, + }, nil +} + +// ListenerParsers for the 'event.Listener' event producer. +func (np *Processor) ListenerParsers() []event.ParserInfo { + var parsers []event.ParserInfo + + // new epoch event + newEpoch := event.ParserInfo{} + newEpoch.SetType(newEpochNotification) + newEpoch.SetScriptHash(np.netmapContract) + newEpoch.SetParser(netmapEvent.ParseNewEpoch) + parsers = append(parsers, newEpoch) + + return parsers +} + +// ListenerHandlers for the 'event.Listener' event producer. +func (np *Processor) ListenerHandlers() []event.HandlerInfo { + var handlers []event.HandlerInfo + + // new epoch handler + newEpoch := event.HandlerInfo{} + newEpoch.SetType(newEpochNotification) + newEpoch.SetScriptHash(np.netmapContract) + newEpoch.SetHandler(np.handleNewEpoch) + handlers = append(handlers, newEpoch) + + return handlers +} + +// TimersHandlers for the 'Timers' event producer. +func (np *Processor) TimersHandlers() []event.HandlerInfo { + var handlers []event.HandlerInfo + + // new epoch handler + newEpoch := event.HandlerInfo{} + newEpoch.SetType(timers.EpochTimer) + newEpoch.SetHandler(np.handleNewEpochTick) + handlers = append(handlers, newEpoch) + + return handlers +} diff --git a/pkg/innerring/state.go b/pkg/innerring/state.go new file mode 100644 index 00000000..378cc241 --- /dev/null +++ b/pkg/innerring/state.go @@ -0,0 +1,17 @@ +package innerring + +// EpochCounter is a getter for a global epoch counter. +func (s *Server) EpochCounter() uint64 { + return s.epochCounter.Load() +} + +// SetEpochCounter is a setter for contract processors to update global +// epoch counter. +func (s *Server) SetEpochCounter(val uint64) { + s.epochCounter.Store(val) +} + +// IsActive is a getter for a global active flag state. +func (s *Server) IsActive() bool { + return s.activeState.Load() +} diff --git a/pkg/innerring/timers/epoch.go b/pkg/innerring/timers/epoch.go new file mode 100644 index 00000000..a7da6bbb --- /dev/null +++ b/pkg/innerring/timers/epoch.go @@ -0,0 +1,12 @@ +package timers + +// NewEpochTick is a new epoch local ticker event. +type NewEpochTick struct{} + +// MorphEvent implements Event interface. +func (NewEpochTick) MorphEvent() {} + +// ResetEpochTimer to start it again when event has been processed. +func (t *Timers) ResetEpochTimer() { + t.epoch.timer.Reset(t.epoch.duration) +} diff --git a/pkg/innerring/timers/timers.go b/pkg/innerring/timers/timers.go new file mode 100644 index 00000000..8c019719 --- /dev/null +++ b/pkg/innerring/timers/timers.go @@ -0,0 +1,86 @@ +package timers + +import ( + "context" + "time" + + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + "github.com/pkg/errors" + "go.uber.org/zap" +) + +type ( + localTimer struct { + duration time.Duration + timer *time.Timer + handler event.Handler + } + + // Timers is a component for local inner ring timers to produce local events. + Timers struct { + log *zap.Logger + + epoch localTimer + } + + // Params for timers instance constructor. + Params struct { + Log *zap.Logger + EpochDuration time.Duration + } +) + +const ( + // EpochTimer is a type for HandlerInfo structure. + EpochTimer = "EpochTimer" +) + +// New creates instance of timers component. +func New(p *Params) *Timers { + return &Timers{ + log: p.Log, + epoch: localTimer{duration: p.EpochDuration}, + } +} + +// Start runs all available local timers. +func (t *Timers) Start(ctx context.Context) { + t.epoch.timer = time.NewTimer(t.epoch.duration) + go t.serve(ctx) +} + +func (t *Timers) serve(ctx context.Context) { + for { + select { + case <-ctx.Done(): + t.log.Info("timers are getting stopped") + t.epoch.timer.Stop() + + return + case <-t.epoch.timer.C: + // reset timer so it can tick once again + t.epoch.timer.Reset(t.epoch.duration) + + // call handler if it is set + if t.epoch.handler != nil { + t.epoch.handler(NewEpochTick{}) + } + } + } +} + +// RegisterHandler of local timers events. +func (t *Timers) RegisterHandler(h event.HandlerInfo) error { + if h.Handler() == nil { + return errors.New("ir/timers: can't register nil handler") + } + + switch h.GetType() { + case EpochTimer: + t.epoch.handler = h.Handler() + default: + return errors.New("ir/timers: unknown handler type") + } + + return nil +} diff --git a/lib/buckets/boltdb/boltdb.go b/pkg/local_object_storage/bucket/boltdb/boltdb.go similarity index 58% rename from lib/buckets/boltdb/boltdb.go rename to pkg/local_object_storage/bucket/boltdb/boltdb.go index 4310151b..72c3e87d 100644 --- a/lib/buckets/boltdb/boltdb.go +++ b/pkg/local_object_storage/bucket/boltdb/boltdb.go @@ -6,15 +6,14 @@ import ( "os" "path" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/core" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket" "github.com/pkg/errors" "github.com/spf13/viper" "go.etcd.io/bbolt" ) type ( - bucket struct { + boltBucket struct { db *bbolt.DB name []byte } @@ -28,13 +27,11 @@ type ( } ) -const ( - defaultFilePermission = 0777 +const defaultFilePermission = 0777 - errEmptyPath = internal.Error("database empty path") -) +var errEmptyPath = errors.New("database empty path") -var _ core.Bucket = (*bucket)(nil) +const name = "boltbucket" func makeCopy(val []byte) []byte { tmp := make([]byte, len(val)) @@ -44,8 +41,7 @@ func makeCopy(val []byte) []byte { } // NewOptions prepares options for badger instance. -func NewOptions(name core.BucketType, v *viper.Viper) (opts Options, err error) { - key := string(name) +func NewOptions(v *viper.Viper) (opts Options, err error) { opts = Options{ Options: bbolt.Options{ // set defaults: @@ -53,30 +49,30 @@ func NewOptions(name core.BucketType, v *viper.Viper) (opts Options, err error) FreelistType: bbolt.DefaultOptions.FreelistType, // set config options: - NoSync: v.GetBool(key + ".no_sync"), - ReadOnly: v.GetBool(key + ".read_only"), - NoGrowSync: v.GetBool(key + ".no_grow_sync"), - NoFreelistSync: v.GetBool(key + ".no_freelist_sync"), + NoSync: v.GetBool(name + ".no_sync"), + ReadOnly: v.GetBool(name + ".read_only"), + NoGrowSync: v.GetBool(name + ".no_grow_sync"), + NoFreelistSync: v.GetBool(name + ".no_freelist_sync"), - PageSize: v.GetInt(key + ".page_size"), - MmapFlags: v.GetInt(key + ".mmap_flags"), - InitialMmapSize: v.GetInt(key + ".initial_mmap_size"), + PageSize: v.GetInt(name + ".page_size"), + MmapFlags: v.GetInt(name + ".mmap_flags"), + InitialMmapSize: v.GetInt(name + ".initial_mmap_size"), }, Name: []byte(name), Perm: defaultFilePermission, - Path: v.GetString(key + ".path"), + Path: v.GetString(name + ".path"), } if opts.Path == "" { return opts, errEmptyPath } - if tmp := v.GetDuration(key + ".lock_timeout"); tmp > 0 { + if tmp := v.GetDuration(name + ".lock_timeout"); tmp > 0 { opts.Timeout = tmp } - if perm := v.GetUint32(key + ".perm"); perm != 0 { + if perm := v.GetUint32(name + ".perm"); perm != 0 { opts.Perm = os.FileMode(perm) } @@ -89,7 +85,7 @@ func NewOptions(name core.BucketType, v *viper.Viper) (opts Options, err error) } // NewBucket creates badger-bucket instance. -func NewBucket(opts *Options) (core.Bucket, error) { +func NewBucket(opts *Options) (bucket.Bucket, error) { log.SetOutput(ioutil.Discard) // disable default logger db, err := bbolt.Open(opts.Path, opts.Perm, &opts.Options) @@ -105,5 +101,5 @@ func NewBucket(opts *Options) (core.Bucket, error) { return nil, err } - return &bucket{db: db, name: opts.Name}, nil + return &boltBucket{db: db, name: opts.Name}, nil } diff --git a/lib/buckets/boltdb/methods.go b/pkg/local_object_storage/bucket/boltdb/methods.go similarity index 67% rename from lib/buckets/boltdb/methods.go rename to pkg/local_object_storage/bucket/boltdb/methods.go index b302a7db..b226bf7b 100644 --- a/lib/buckets/boltdb/methods.go +++ b/pkg/local_object_storage/bucket/boltdb/methods.go @@ -4,18 +4,18 @@ import ( "os" "github.com/mr-tron/base58" - "github.com/nspcc-dev/neofs-node/lib/core" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket" "github.com/pkg/errors" "go.etcd.io/bbolt" ) // Get value by key or return error. -func (b *bucket) Get(key []byte) (data []byte, err error) { +func (b *boltBucket) Get(key []byte) (data []byte, err error) { err = b.db.View(func(txn *bbolt.Tx) error { txn.Bucket(b.name).Cursor().Seek(key) val := txn.Bucket(b.name).Get(key) if val == nil { - return errors.Wrapf(core.ErrNotFound, "key=%s", base58.Encode(key)) + return errors.Wrapf(bucket.ErrNotFound, "key=%s", base58.Encode(key)) } data = makeCopy(val) @@ -26,7 +26,7 @@ func (b *bucket) Get(key []byte) (data []byte, err error) { } // Set value for key. -func (b *bucket) Set(key, value []byte) error { +func (b *boltBucket) Set(key, value []byte) error { return b.db.Update(func(txn *bbolt.Tx) error { k, v := makeCopy(key), makeCopy(value) return txn.Bucket(b.name).Put(k, v) @@ -34,20 +34,20 @@ func (b *bucket) Set(key, value []byte) error { } // Del removes item from bucket by key. -func (b *bucket) Del(key []byte) error { +func (b *boltBucket) Del(key []byte) error { return b.db.Update(func(txn *bbolt.Tx) error { return txn.Bucket(b.name).Delete(key) }) } // Has checks key exists. -func (b *bucket) Has(key []byte) bool { +func (b *boltBucket) Has(key []byte) bool { _, err := b.Get(key) - return !errors.Is(errors.Cause(err), core.ErrNotFound) + return !errors.Is(errors.Cause(err), bucket.ErrNotFound) } // Size returns size of database. -func (b *bucket) Size() int64 { +func (b *boltBucket) Size() int64 { info, err := os.Stat(b.db.Path()) if err != nil { return 0 @@ -57,7 +57,7 @@ func (b *bucket) Size() int64 { } // List all items in bucket. -func (b *bucket) List() ([][]byte, error) { +func (b *boltBucket) List() ([][]byte, error) { var items [][]byte if err := b.db.View(func(txn *bbolt.Tx) error { @@ -73,15 +73,15 @@ func (b *bucket) List() ([][]byte, error) { } // Filter elements by filter closure. -func (b *bucket) Iterate(handler core.FilterHandler) error { +func (b *boltBucket) Iterate(handler bucket.FilterHandler) error { if handler == nil { - return core.ErrNilFilterHandler + return bucket.ErrNilFilterHandler } return b.db.View(func(txn *bbolt.Tx) error { return txn.Bucket(b.name).ForEach(func(k, v []byte) error { if !handler(makeCopy(k), makeCopy(v)) { - return core.ErrIteratingAborted + return bucket.ErrIteratingAborted } return nil }) @@ -89,6 +89,6 @@ func (b *bucket) Iterate(handler core.FilterHandler) error { } // Close bucket database. -func (b *bucket) Close() error { +func (b *boltBucket) Close() error { return b.db.Close() } diff --git a/pkg/local_object_storage/bucket/bucket.go b/pkg/local_object_storage/bucket/bucket.go new file mode 100644 index 00000000..612e8991 --- /dev/null +++ b/pkg/local_object_storage/bucket/bucket.go @@ -0,0 +1,41 @@ +package bucket + +import ( + "errors" +) + +// FilterHandler where you receive key/val in your closure. +type FilterHandler func(key, val []byte) bool + +// BucketItem used in filter. +type BucketItem struct { + Key []byte + Val []byte +} + +// Bucket is sub-store interface. +type Bucket interface { + Get(key []byte) ([]byte, error) + Set(key, value []byte) error + Del(key []byte) error + Has(key []byte) bool + Size() int64 + List() ([][]byte, error) + Iterate(FilterHandler) error + // Steam can be implemented by badger.Stream, but not for now + // Stream(ctx context.Context, key []byte, cb func(io.ReadWriter) error) error + Close() error +} + +var ( + // ErrNilFilterHandler when FilterHandler is empty + ErrNilFilterHandler = errors.New("handler can't be nil") + + // ErrNotFound is returned by key-value storage methods + // that could not find element by key. + ErrNotFound = errors.New("key not found") +) + +// ErrIteratingAborted is returned by storage iterator +// after iteration has been interrupted. +var ErrIteratingAborted = errors.New("iteration aborted") diff --git a/lib/buckets/fsbucket/bucket.go b/pkg/local_object_storage/bucket/fsbucket/bucket.go similarity index 60% rename from lib/buckets/fsbucket/bucket.go rename to pkg/local_object_storage/bucket/fsbucket/bucket.go index 029d509c..24c9e314 100644 --- a/lib/buckets/fsbucket/bucket.go +++ b/pkg/local_object_storage/bucket/fsbucket/bucket.go @@ -4,15 +4,14 @@ import ( "os" "github.com/mr-tron/base58" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/core" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket" "github.com/pkg/errors" "github.com/spf13/viper" "go.uber.org/atomic" ) type ( - bucket struct { + Bucket struct { dir string perm os.FileMode } @@ -27,6 +26,8 @@ type ( } ) +const name = "fsbucket" + const ( defaultDirectory = "fsbucket" defaultPermissions = 0755 @@ -34,9 +35,7 @@ const ( defaultPrefixLen = 2 ) -const errShortKey = internal.Error("key is too short for tree fs bucket") - -var _ core.Bucket = (*bucket)(nil) +var errShortKey = errors.New("key is too short for tree fs bucket") func stringifyKey(key []byte) string { return base58.Encode(key) @@ -51,10 +50,9 @@ func decodeKey(key string) []byte { return k } -// NewBucket creates new in-memory bucket instance. -func NewBucket(name core.BucketType, v *viper.Viper) (core.Bucket, error) { +// NewBucket creates new file system bucket instance. +func NewBucket(v *viper.Viper) (bucket.Bucket, error) { var ( - key = "storage." + string(name) dir string perm os.FileMode @@ -62,27 +60,27 @@ func NewBucket(name core.BucketType, v *viper.Viper) (core.Bucket, error) { depth int ) - if dir = v.GetString(key + ".directory"); dir == "" { + if dir = v.GetString(name + ".directory"); dir == "" { dir = defaultDirectory } - if perm = os.FileMode(v.GetInt(key + ".permissions")); perm == 0 { + if perm = os.FileMode(v.GetInt(name + ".permissions")); perm == 0 { perm = defaultPermissions } - if depth = v.GetInt(key + ".depth"); depth <= 0 { + if depth = v.GetInt(name + ".depth"); depth <= 0 { depth = defaultDepth } - if prefixLen = v.GetInt(key + ".prefix_len"); prefixLen <= 0 { + if prefixLen = v.GetInt(name + ".prefix_len"); prefixLen <= 0 { prefixLen = defaultPrefixLen } if err := os.MkdirAll(dir, perm); err != nil { - return nil, errors.Wrapf(err, "could not create bucket %s", string(name)) + return nil, errors.Wrapf(err, "could not create bucket %s", name) } - if v.GetBool(key + ".tree_enabled") { + if v.GetBool(name + ".tree_enabled") { b := &treeBucket{ dir: dir, perm: perm, @@ -94,7 +92,7 @@ func NewBucket(name core.BucketType, v *viper.Viper) (core.Bucket, error) { return b, nil } - return &bucket{ + return &Bucket{ dir: dir, perm: perm, }, nil diff --git a/lib/buckets/fsbucket/methods.go b/pkg/local_object_storage/bucket/fsbucket/methods.go similarity index 74% rename from lib/buckets/fsbucket/methods.go rename to pkg/local_object_storage/bucket/fsbucket/methods.go index 9aeaf45f..1dd2aea9 100644 --- a/lib/buckets/fsbucket/methods.go +++ b/pkg/local_object_storage/bucket/fsbucket/methods.go @@ -6,38 +6,38 @@ import ( "path" "path/filepath" - "github.com/nspcc-dev/neofs-node/lib/core" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket" ) // Get value by key. -func (b *bucket) Get(key []byte) ([]byte, error) { +func (b *Bucket) Get(key []byte) ([]byte, error) { p := path.Join(b.dir, stringifyKey(key)) if _, err := os.Stat(p); os.IsNotExist(err) { - return nil, core.ErrNotFound + return nil, bucket.ErrNotFound } return ioutil.ReadFile(p) } // Set value by key. -func (b *bucket) Set(key, value []byte) error { +func (b *Bucket) Set(key, value []byte) error { p := path.Join(b.dir, stringifyKey(key)) return ioutil.WriteFile(p, value, b.perm) } // Del value by key. -func (b *bucket) Del(key []byte) error { +func (b *Bucket) Del(key []byte) error { p := path.Join(b.dir, stringifyKey(key)) if _, err := os.Stat(p); os.IsNotExist(err) { - return core.ErrNotFound + return bucket.ErrNotFound } return os.Remove(p) } // Has checks key exists. -func (b *bucket) Has(key []byte) bool { +func (b *Bucket) Has(key []byte) bool { p := path.Join(b.dir, stringifyKey(key)) _, err := os.Stat(p) @@ -59,7 +59,7 @@ func listing(root string, fn func(path string, info os.FileInfo) error) error { } // Size of bucket. -func (b *bucket) Size() (size int64) { +func (b *Bucket) Size() (size int64) { err := listing(b.dir, func(_ string, info os.FileInfo) error { size += info.Size() return nil @@ -73,7 +73,7 @@ func (b *bucket) Size() (size int64) { } // List all bucket items. -func (b *bucket) List() ([][]byte, error) { +func (b *Bucket) List() ([][]byte, error) { buckets := make([][]byte, 0) err := listing(b.dir, func(p string, info os.FileInfo) error { @@ -85,7 +85,7 @@ func (b *bucket) List() ([][]byte, error) { } // Filter bucket items by closure. -func (b *bucket) Iterate(handler core.FilterHandler) error { +func (b *Bucket) Iterate(handler bucket.FilterHandler) error { return listing(b.dir, func(p string, info os.FileInfo) error { key := decodeKey(info.Name()) val, err := ioutil.ReadFile(p) @@ -94,7 +94,7 @@ func (b *bucket) Iterate(handler core.FilterHandler) error { } if !handler(key, val) { - return core.ErrIteratingAborted + return bucket.ErrIteratingAborted } return nil @@ -102,6 +102,6 @@ func (b *bucket) Iterate(handler core.FilterHandler) error { } // Close bucket (just empty). -func (b *bucket) Close() error { +func (b *Bucket) Close() error { return os.RemoveAll(b.dir) } diff --git a/lib/buckets/fsbucket/queue.go b/pkg/local_object_storage/bucket/fsbucket/queue.go similarity index 100% rename from lib/buckets/fsbucket/queue.go rename to pkg/local_object_storage/bucket/fsbucket/queue.go diff --git a/lib/buckets/fsbucket/treemethods.go b/pkg/local_object_storage/bucket/fsbucket/treemethods.go similarity index 95% rename from lib/buckets/fsbucket/treemethods.go rename to pkg/local_object_storage/bucket/fsbucket/treemethods.go index 1a1927a8..b427e0c7 100644 --- a/lib/buckets/fsbucket/treemethods.go +++ b/pkg/local_object_storage/bucket/fsbucket/treemethods.go @@ -7,7 +7,7 @@ import ( "path" "strings" - "github.com/nspcc-dev/neofs-node/lib/core" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket" ) const queueCap = 1000 @@ -55,7 +55,7 @@ func (b *treeBucket) Get(key []byte) ([]byte, error) { p := path.Join(b.dir, path.Join(dirPaths...), filename) if _, err := os.Stat(p); os.IsNotExist(err) { - return nil, core.ErrNotFound + return nil, bucket.ErrNotFound } return ioutil.ReadFile(p) @@ -99,7 +99,7 @@ func (b *treeBucket) Del(key []byte) error { ) if fi, err = os.Stat(p); os.IsNotExist(err) { - return core.ErrNotFound + return bucket.ErrNotFound } else if err = os.Remove(p); err == nil { b.sz.Sub(fi.Size()) } @@ -218,9 +218,9 @@ func (b *treeBucket) List() ([][]byte, error) { } // Filter bucket items by closure. -func (b *treeBucket) Iterate(handler core.FilterHandler) error { +func (b *treeBucket) Iterate(handler bucket.FilterHandler) error { return b.listing(b.dir, func(p string, info os.FileInfo) error { - val, err := ioutil.ReadFile(path.Join(b.dir, p)) + val, err := ioutil.ReadFile(p) if err != nil { return err } @@ -231,7 +231,7 @@ func (b *treeBucket) Iterate(handler core.FilterHandler) error { } if !handler(key, val) { - return core.ErrIteratingAborted + return bucket.ErrIteratingAborted } return nil diff --git a/lib/buckets/fsbucket/treemethods_test.go b/pkg/local_object_storage/bucket/fsbucket/treemethods_test.go similarity index 97% rename from lib/buckets/fsbucket/treemethods_test.go rename to pkg/local_object_storage/bucket/fsbucket/treemethods_test.go index f0e88e55..402fcf00 100644 --- a/lib/buckets/fsbucket/treemethods_test.go +++ b/pkg/local_object_storage/bucket/fsbucket/treemethods_test.go @@ -11,10 +11,9 @@ import ( "strings" "testing" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket" "github.com/stretchr/testify/require" "go.uber.org/atomic" - - "github.com/nspcc-dev/neofs-node/lib/core" ) func prepareTree(badFiles bool) (string, error) { @@ -272,7 +271,7 @@ func BenchmarkFilewalkBucket_List(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - buckets := make([]core.BucketItem, 0) + buckets := make([]bucket.BucketItem, 0) filepath.Walk(root, func(path string, info os.FileInfo, err error) error { if err != nil || info.IsDir() { @@ -289,7 +288,7 @@ func BenchmarkFilewalkBucket_List(b *testing.B) { return err } - buckets = append(buckets, core.BucketItem{ + buckets = append(buckets, bucket.BucketItem{ Key: key, Val: val, }) diff --git a/lib/test/bucket.go b/pkg/local_object_storage/bucket/test/bucket.go similarity index 82% rename from lib/test/bucket.go rename to pkg/local_object_storage/bucket/test/bucket.go index 024a2ab4..78cdf240 100644 --- a/lib/test/bucket.go +++ b/pkg/local_object_storage/bucket/test/bucket.go @@ -1,12 +1,12 @@ package test import ( + "errors" "sync" "github.com/mr-tron/base58" "github.com/nspcc-dev/neofs-api-go/object" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/core" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket" ) type ( @@ -16,13 +16,13 @@ type ( } ) -const ( - errOverflow = internal.Error("overflow") - errNotFound = internal.Error("not found") +var ( + errOverflow = errors.New("overflow") + errNotFound = errors.New("not found") ) -// Bucket constructs test core.Bucket implementation. -func Bucket() core.Bucket { +// Bucket constructs test Bucket implementation. +func Bucket() bucket.Bucket { return &testBucket{ items: make(map[string][]byte), } @@ -34,7 +34,7 @@ func (t *testBucket) Get(key []byte) ([]byte, error) { val, ok := t.items[base58.Encode(key)] if !ok { - return nil, core.ErrNotFound + return nil, bucket.ErrNotFound } return val, nil @@ -96,7 +96,7 @@ func (t *testBucket) List() ([][]byte, error) { return res, nil } -func (t *testBucket) Iterate(f core.FilterHandler) error { +func (t *testBucket) Iterate(f bucket.FilterHandler) error { t.RLock() defer t.RUnlock() @@ -107,7 +107,7 @@ func (t *testBucket) Iterate(f core.FilterHandler) error { } if !f(key, v) { - return core.ErrIteratingAborted + return bucket.ErrIteratingAborted } } diff --git a/lib/localstore/alias.go b/pkg/local_object_storage/localstore/alias.go similarity index 100% rename from lib/localstore/alias.go rename to pkg/local_object_storage/localstore/alias.go diff --git a/lib/localstore/del.go b/pkg/local_object_storage/localstore/del.go similarity index 89% rename from lib/localstore/del.go rename to pkg/local_object_storage/localstore/del.go index f09f4086..1a1859f1 100644 --- a/lib/localstore/del.go +++ b/pkg/local_object_storage/localstore/del.go @@ -2,7 +2,7 @@ package localstore import ( "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-node/lib/metrics" + metrics2 "github.com/nspcc-dev/neofs-node/pkg/services/metrics" "github.com/pkg/errors" "go.uber.org/zap" ) @@ -31,7 +31,7 @@ func (l *localstore) Del(key refs.Address) error { l.col.UpdateContainer( key.CID, obj.SystemHeader.PayloadLength, - metrics.RemSpace) + metrics2.RemSpace) } return nil diff --git a/lib/localstore/filter.go b/pkg/local_object_storage/localstore/filter.go similarity index 98% rename from lib/localstore/filter.go rename to pkg/local_object_storage/localstore/filter.go index a568e7d9..f27a3767 100644 --- a/lib/localstore/filter.go +++ b/pkg/local_object_storage/localstore/filter.go @@ -6,7 +6,6 @@ import ( "sort" "sync" - "github.com/nspcc-dev/neofs-node/internal" "github.com/pkg/errors" ) @@ -228,7 +227,7 @@ func (p *filterPipeline) PutSubFilter(params SubFilterParams) error { defer p.Unlock() if params.FilterPipeline == nil { - return internal.Error("could not put sub filter: empty filter pipeline") + return errors.New("could not put sub filter: empty filter pipeline") } name := params.FilterPipeline.GetName() diff --git a/lib/localstore/filter_funcs.go b/pkg/local_object_storage/localstore/filter_funcs.go similarity index 100% rename from lib/localstore/filter_funcs.go rename to pkg/local_object_storage/localstore/filter_funcs.go diff --git a/lib/localstore/filter_test.go b/pkg/local_object_storage/localstore/filter_test.go similarity index 89% rename from lib/localstore/filter_test.go rename to pkg/local_object_storage/localstore/filter_test.go index c07b9fe0..2da66b7f 100644 --- a/lib/localstore/filter_test.go +++ b/pkg/local_object_storage/localstore/filter_test.go @@ -2,9 +2,9 @@ package localstore import ( "context" + "errors" "testing" - "github.com/nspcc-dev/neofs-node/internal" "github.com/stretchr/testify/require" ) @@ -17,7 +17,7 @@ func TestFilterResult(t *testing.T) { var ( r *FilterResult c = CodePass - e = internal.Error("test error") + e = errors.New("test error") ) r = ResultPass() diff --git a/lib/localstore/get.go b/pkg/local_object_storage/localstore/get.go similarity index 100% rename from lib/localstore/get.go rename to pkg/local_object_storage/localstore/get.go diff --git a/lib/localstore/has.go b/pkg/local_object_storage/localstore/has.go similarity index 100% rename from lib/localstore/has.go rename to pkg/local_object_storage/localstore/has.go diff --git a/lib/localstore/interface.go b/pkg/local_object_storage/localstore/interface.go similarity index 84% rename from lib/localstore/interface.go rename to pkg/local_object_storage/localstore/interface.go index b1b14b4d..236c8952 100644 --- a/lib/localstore/interface.go +++ b/pkg/local_object_storage/localstore/interface.go @@ -4,8 +4,8 @@ import ( "context" "github.com/nspcc-dev/neofs-api-go/object" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/lib/metrics" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket" + metrics2 "github.com/nspcc-dev/neofs-node/pkg/services/metrics" "github.com/pkg/errors" "go.uber.org/zap" ) @@ -41,18 +41,18 @@ type ( // Params groups the parameters of // local object storage constructor. Params struct { - BlobBucket core.Bucket - MetaBucket core.Bucket + BlobBucket bucket.Bucket + MetaBucket bucket.Bucket Logger *zap.Logger - Collector metrics.Collector + Collector metrics2.Collector } localstore struct { - metaBucket core.Bucket - blobBucket core.Bucket + metaBucket bucket.Bucket + blobBucket bucket.Bucket log *zap.Logger - col metrics.Collector + col metrics2.Collector } ) @@ -72,9 +72,9 @@ var errNilCollector = errors.New("metrics collector is nil") func New(p Params) (Localstore, error) { switch { case p.MetaBucket == nil: - return nil, errors.Errorf("%s bucket is nil", core.MetaStore) + return nil, errors.New("meta bucket is nil") case p.BlobBucket == nil: - return nil, errors.Errorf("%s bucket is nil", core.BlobStore) + return nil, errors.New("blob bucket is nil") case p.Logger == nil: return nil, errNilLogger case p.Collector == nil: diff --git a/lib/localstore/list.go b/pkg/local_object_storage/localstore/list.go similarity index 100% rename from lib/localstore/list.go rename to pkg/local_object_storage/localstore/list.go diff --git a/lib/localstore/localstore.pb.go b/pkg/local_object_storage/localstore/localstore.pb.go similarity index 84% rename from lib/localstore/localstore.pb.go rename to pkg/local_object_storage/localstore/localstore.pb.go index e6c13b373ecc2993589ee6867f0a780b993e3e95..8700f28ea2a06d30f060bf807786415f046641d5 100644 GIT binary patch delta 1697 zcmZ{kL24C25QaAz$YhhaiW20Z8inOuwEG4JITZJP9&aP~Hu^ zf#ei!a}5_>!S3&W;v?XusHv{1|F6Hg=hyq|y|2$VPd*;(93A`aX#cqX5d8W6+4=El zcMyK;K3Q3>yQZBEP0^WYGuXjhU9P;iH9ySO@4r4g3XdNAulU>U*8F#N=grOf$Jv)G zyXa0QLo9z4&iv`&NrcdwxjtmkjnKHW3r_9UN`ZL;1aYeTZr zW`4c)aCABtIKcW2Uv`@Ok^*IxFzc4mBJNz9oOG0ti+oP3mNz)Eh9#>hKM| zx@u+vf&sHqExtM^9{nUz#X*5BHDR*jtP*eV^EzP$tb?TxY9=g*i+2z5RH~L5fn#k( zwgIiDSQns2n2zEy2{X>LR!b-8Q$erBO50W<SV{65TXjGq!BrHuy=iu#9&J%=_L&K<+A9+J?k!6*dmi|)&QW3rX41Q$G92l92K5^ zk|f}Zy%yS_n}cHoqk){jgq2={g=7sq-8$WO3E)UAupwqf{D3mTH?ZjFU1>t*X_=v@ zn4#dqa8OAqQd^Mq>VzX=Jvp+A`58HGHQ_hHVKb3xT7yM;&-&C=H!!EJgr4RsC~k-^ zuXrG0#g0CTc->X)CtmI`F4rSyF6luQZ_QZ~H+nK;)`+A(fXG|o16*pYA-?O_TEw&l zrS?kVG(P%-uUp@;T>UvztoS0rl60YJy@mzC-%hbnr#ntW?;uEM1Y5iV4NFdZH}j_( mn=g~(Im;UuU)B+^4BnAHXmB>ARj4Z3`N{3OSKl{&Zv6p>7_Qp@ delta 1592 zcmZ9M&uSDw5XKKmAiE$TLJ-Me)QiHB^mO;^%m@a(c=X^^bC|!f5R9xVtazAL2+V_s zi>x;~(pLdk4eOP_obnDlvi|*-Tg`FJU4tbW0XY->+x6R3me*kWUB|2%4uC1sa)Y-H_@+L7O2yY@K>84tmi}rtG9F&18rvxwXUw zgo4~Dx3G4ETBwnhwO`q*+NeAcQV3XqsVg+T9XkSaK5@Oilx|Nd;pw`0Q?o z5yoKze0?AaOyMXaLPLMldJpt26PT&!PCgmhTPI8sP0Myzw32ly3MBGwF@$N9T1ui- zjOTKCRl!(Qw_f$-$mv3ckCwjq|KW%f6s!m7?IfLogZ)jsD8@a_)GId=({ej})DhFd tp@YMctdic0-{(iqdsVONS2uJTxN30}pmPXI6e|ut 0 { + c.dialTimeout = dur + } + } +} + +// WithMagic returns a client constructor option +// that specifies neo blockchain network type. +// +// If option not provided, netmode.PrivNet is used. +func WithMagic(mag netmode.Magic) Option { + return func(c *cfg) { + c.magic = mag + } +} + +// WithLogger returns a client constructor option +// that specifies the component for writing log messages. +// +// Ignores nil value. +// +// If option not provided, zap.L() is used. +func WithLogger(logger *logger.Logger) Option { + return func(c *cfg) { + if logger != nil { + c.logger = logger + } + } +} + +// WithGasContract returns a client constructor option +// that specifies native gas contract script hash. +// +// If option not provided, empty script hash is used. +func WithGasContract(gas util.Uint160) Option { + return func(c *cfg) { + c.gas = gas + } +} diff --git a/pkg/morph/client/container/client.go b/pkg/morph/client/container/client.go new file mode 100644 index 00000000..e8ad001e --- /dev/null +++ b/pkg/morph/client/container/client.go @@ -0,0 +1,174 @@ +package container + +import ( + "errors" + + "github.com/nspcc-dev/neofs-node/pkg/morph/client" +) + +// Client is a wrapper over StaticClient +// which makes calls with the names and arguments +// of the NeoFS Container contract. +// +// Working client must be created via constructor New. +// Using the Client that has been created with new(Client) +// expression (or just declaring a Client variable) is unsafe +// and can lead to panic. +type Client struct { + client *client.StaticClient // static Container contract client + + *cfg // contract method names +} + +// ErrNilClient is returned by functions that expect +// a non-nil Client pointer, but received nil. +var ErrNilClient = errors.New("container contract client is nil") + +// Option is a client configuration change function. +type Option func(*cfg) + +type cfg struct { + putMethod, // put container method name for invocation + deleteMethod, // delete container method name for invocation + getMethod, // get container method name for invocation + listMethod, // list container method name for invocation + setEACLMethod, // set eACL method name for invocation + eaclMethod string // get eACL method name for invocation +} + +const ( + defaultPutMethod = "Put" // default put container method name + defaultDeleteMethod = "Delete" // default delete container method name + defaultGetMethod = "Get" // default get container method name + defaultListMethod = "List" // default list containers method name + defaultEACLMethod = "EACL" // default get eACL method name + defaultSetEACLMethod = "SetEACL" // default set eACL method name +) + +func defaultConfig() *cfg { + return &cfg{ + putMethod: defaultPutMethod, + deleteMethod: defaultDeleteMethod, + getMethod: defaultGetMethod, + listMethod: defaultListMethod, + setEACLMethod: defaultSetEACLMethod, + eaclMethod: defaultEACLMethod, + } +} + +// New creates, initializes and returns the Client instance. +// +// If StaticClient is nil, client.ErrNilStaticClient is returned. +// +// Other values are set according to provided options, or by default: +// * put container method name: Put; +// * delete container method name: Delete; +// * get container method name: Get; +// * list containers method name: List; +// * set eACL method name: SetEACL; +// * get eACL method name: EACL. +// +// If desired option satisfies the default value, it can be omitted. +// If multiple options of the same config value are supplied, +// the option with the highest index in the arguments will be used. +func New(c *client.StaticClient, opts ...Option) (*Client, error) { + if c == nil { + return nil, client.ErrNilStaticClient + } + + res := &Client{ + client: c, + cfg: defaultConfig(), // build default configuration + } + + // apply options + for _, opt := range opts { + opt(res.cfg) + } + + return res, nil +} + +// WithPutMethod returns a client constructor option that +// specifies the method name of container storing operation. +// +// Ignores empty value. +// +// If option not provided, "Put" is used. +func WithPutMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.putMethod = n + } + } +} + +// WithDeleteMethod returns a client constructor option that +// specifies the method name of container removal operation. +// +// Ignores empty value. +// +// If option not provided, "Delete" is used. +func WithDeleteMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.deleteMethod = n + } + } +} + +// WithGetMethod returns a client constructor option that +// specifies the method name of container receiving operation. +// +// Ignores empty value. +// +// If option not provided, "Get" is used. +func WithGetMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.getMethod = n + } + } +} + +// WithListMethod returns a client constructor option that +// specifies the method name of container listing operation. +// +// Ignores empty value. +// +// If option not provided, "List" is used. +func WithListMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.listMethod = n + } + } +} + +// WithSetEACLMethod returns a client constructor option that +// specifies the method name of eACL storing operation. +// +// Ignores empty value. +// +// If option not provided, "SetEACL" is used. +func WithSetEACLMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.setEACLMethod = n + } + } +} + +// WithEACLMethod returns a client constructor option that +// specifies the method name of eACL receiving operation. +// +// Ignores empty value. +// +// If option not provided, "EACL" is used. +func WithEACLMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.eaclMethod = n + } + } +} diff --git a/pkg/morph/client/container/delete.go b/pkg/morph/client/container/delete.go new file mode 100644 index 00000000..76bdce3e --- /dev/null +++ b/pkg/morph/client/container/delete.go @@ -0,0 +1,42 @@ +package container + +import "github.com/pkg/errors" + +// DeleteArgs groups the arguments +// of delete container invocation call. +type DeleteArgs struct { + cid []byte // container identifier + + ownerID []byte // container owner identifier + + sig []byte // container identifier signature +} + +// SetOwnerID sets the container owner identifier +// in a binary format. +func (p *DeleteArgs) SetOwnerID(v []byte) { + p.ownerID = v +} + +// SetCID sets the container identifier +// in a binary format. +func (p *DeleteArgs) SetCID(v []byte) { + p.cid = v +} + +// SetSignature sets the container identifier +// owner's signature. +func (p *DeleteArgs) SetSignature(v []byte) { + p.sig = v +} + +// Delete invokes the call of delete container +// method of NeoFS Container contract. +func (c *Client) Delete(args DeleteArgs) error { + return errors.Wrapf(c.client.Invoke( + c.deleteMethod, + args.cid, + args.ownerID, + args.sig, + ), "could not invoke method (%s)", c.deleteMethod) +} diff --git a/pkg/morph/client/container/eacl.go b/pkg/morph/client/container/eacl.go new file mode 100644 index 00000000..b2ee42b7 --- /dev/null +++ b/pkg/morph/client/container/eacl.go @@ -0,0 +1,53 @@ +package container + +import ( + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/pkg/errors" +) + +// EACLArgs groups the arguments +// of get eACL test invoke call. +type EACLArgs struct { + cid []byte // container identifier +} + +// EACLValues groups the stack parameters +// returned by get eACL test invoke. +type EACLValues struct { + eacl []byte // extended ACL table +} + +// SetCID sets the container identifier +// in a binary format. +func (g *EACLArgs) SetCID(v []byte) { + g.cid = v +} + +// EACL returns the eACL table +// in a binary format. +func (g *EACLValues) EACL() []byte { + return g.eacl +} + +// EACL performs the test invoke of get eACL +// method of NeoFS Container contract. +func (c *Client) EACL(args EACLArgs) (*EACLValues, error) { + prms, err := c.client.TestInvoke( + c.eaclMethod, + args.cid, + ) + if err != nil { + return nil, errors.Wrapf(err, "could not perform test invocation (%s)", c.eaclMethod) + } else if ln := len(prms); ln != 1 { + return nil, errors.Errorf("unexpected stack item count (%s): %d", c.eaclMethod, ln) + } + + eacl, err := client.BytesFromStackParameter(prms[0]) + if err != nil { + return nil, errors.Wrapf(err, "could not get byte array from stack item (%s)", c.eaclMethod) + } + + return &EACLValues{ + eacl: eacl, + }, nil +} diff --git a/pkg/morph/client/container/eacl_set.go b/pkg/morph/client/container/eacl_set.go new file mode 100644 index 00000000..fe75f9ce --- /dev/null +++ b/pkg/morph/client/container/eacl_set.go @@ -0,0 +1,42 @@ +package container + +import "github.com/pkg/errors" + +// SetEACLArgs groups the arguments +// of set eACL invocation call. +type SetEACLArgs struct { + cid []byte // container identifier in a binary format + + eacl []byte // extended ACL table + + sig []byte // eACL table signature +} + +// SetCID sets the container identifier +// in a binary format. +func (p *SetEACLArgs) SetCID(v []byte) { + p.cid = v +} + +// SetEACL sets the extended ACL table +// in a binary format. +func (p *SetEACLArgs) SetEACL(v []byte) { + p.eacl = v +} + +// SetSignature sets the eACL table structure +// owner's signature. +func (p *SetEACLArgs) SetSignature(v []byte) { + p.sig = v +} + +// SetEACL invokes the call of set eACL method +// of NeoFS Container contract. +func (c *Client) SetEACL(args SetEACLArgs) error { + return errors.Wrapf(c.client.Invoke( + c.setEACLMethod, + args.cid, + args.eacl, + args.sig, + ), "could not invoke method (%s)", c.setEACLMethod) +} diff --git a/pkg/morph/client/container/get.go b/pkg/morph/client/container/get.go new file mode 100644 index 00000000..fb9e6d86 --- /dev/null +++ b/pkg/morph/client/container/get.go @@ -0,0 +1,53 @@ +package container + +import ( + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/pkg/errors" +) + +// GetArgs groups the arguments +// of get container test invoke call. +type GetArgs struct { + cid []byte // container identifier +} + +// GetValues groups the stack parameters +// returned by get container test invoke. +type GetValues struct { + cnr []byte // container in a binary form +} + +// SetCID sets the container identifier +// in a binary format. +func (g *GetArgs) SetCID(v []byte) { + g.cid = v +} + +// Container returns the container +// in a binary format. +func (g *GetValues) Container() []byte { + return g.cnr +} + +// Get performs the test invoke of get container +// method of NeoFS Container contract. +func (c *Client) Get(args GetArgs) (*GetValues, error) { + prms, err := c.client.TestInvoke( + c.getMethod, + args.cid, + ) + if err != nil { + return nil, errors.Wrapf(err, "could not perform test invocation (%s)", c.getMethod) + } else if ln := len(prms); ln != 1 { + return nil, errors.Errorf("unexpected stack item count (%s): %d", c.getMethod, ln) + } + + cnrBytes, err := client.BytesFromStackParameter(prms[0]) + if err != nil { + return nil, errors.Wrapf(err, "could not get byte array from stack item (%s)", c.getMethod) + } + + return &GetValues{ + cnr: cnrBytes, + }, nil +} diff --git a/pkg/morph/client/container/list.go b/pkg/morph/client/container/list.go new file mode 100644 index 00000000..4ea6dde2 --- /dev/null +++ b/pkg/morph/client/container/list.go @@ -0,0 +1,70 @@ +package container + +import ( + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/pkg/errors" +) + +// ListArgs groups the arguments +// of list containers test invoke call. +type ListArgs struct { + ownerID []byte // container owner identifier +} + +// ListValues groups the stack parameters +// returned by list containers test invoke. +type ListValues struct { + cidList [][]byte // list of container identifiers +} + +// SetOwnerID sets the container owner identifier +// in a binary format. +func (l *ListArgs) SetOwnerID(v []byte) { + l.ownerID = v +} + +// CIDList returns the list of container +// identifiers in a binary format. +func (l *ListValues) CIDList() [][]byte { + return l.cidList +} + +// List performs the test invoke of list container +// method of NeoFS Container contract. +func (c *Client) List(args ListArgs) (*ListValues, error) { + invokeArgs := make([]interface{}, 0, 1) + + if len(args.ownerID) > 0 { + invokeArgs = append(invokeArgs, args.ownerID) + } + + prms, err := c.client.TestInvoke( + c.listMethod, + invokeArgs..., + ) + if err != nil { + return nil, errors.Wrapf(err, "could not perform test invocation (%s)", c.listMethod) + } else if ln := len(prms); ln != 1 { + return nil, errors.Errorf("unexpected stack item count (%s): %d", c.listMethod, ln) + } + + prms, err = client.ArrayFromStackParameter(prms[0]) + if err != nil { + return nil, errors.Wrapf(err, "could not get stack item array from stack item (%s)", c.listMethod) + } + + res := &ListValues{ + cidList: make([][]byte, 0, len(prms)), + } + + for i := range prms { + cid, err := client.BytesFromStackParameter(prms[i]) + if err != nil { + return nil, errors.Wrapf(err, "could not get byte array from stack item (%s)", c.listMethod) + } + + res.cidList = append(res.cidList, cid) + } + + return res, nil +} diff --git a/pkg/morph/client/container/put.go b/pkg/morph/client/container/put.go new file mode 100644 index 00000000..45f9b5fb --- /dev/null +++ b/pkg/morph/client/container/put.go @@ -0,0 +1,44 @@ +package container + +import ( + "github.com/pkg/errors" +) + +// PutArgs groups the arguments +// of put container invocation call. +type PutArgs struct { + ownerID []byte // container owner identifier + + cnr []byte // container in a binary format + + sig []byte // binary container signature +} + +// SetOwnerID sets the container owner identifier +// in a binary format. +func (p *PutArgs) SetOwnerID(v []byte) { + p.ownerID = v +} + +// SetContainer sets the container structure +// in a binary format. +func (p *PutArgs) SetContainer(v []byte) { + p.cnr = v +} + +// SetSignature sets the container structure +// owner's signature. +func (p *PutArgs) SetSignature(v []byte) { + p.sig = v +} + +// Put invokes the call of put container method +// of NeoFS Container contract. +func (c *Client) Put(args PutArgs) error { + return errors.Wrapf(c.client.Invoke( + c.putMethod, + args.ownerID, + args.cnr, + args.sig, + ), "could not invoke method (%s)", c.putMethod) +} diff --git a/pkg/morph/client/container/wrapper/container.go b/pkg/morph/client/container/wrapper/container.go new file mode 100644 index 00000000..5a390b40 --- /dev/null +++ b/pkg/morph/client/container/wrapper/container.go @@ -0,0 +1,148 @@ +package wrapper + +import ( + "github.com/nspcc-dev/neofs-api-go/refs" + "github.com/nspcc-dev/neofs-node/pkg/core/container" + "github.com/nspcc-dev/neofs-node/pkg/core/container/storage" + contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/container" + "github.com/pkg/errors" +) + +// OwnerID represents the container owner identifier. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/container/storage.OwnerID. +type OwnerID = storage.OwnerID + +// Container represents the NeoFS Container structure. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/container/storage.Container. +type Container = storage.Container + +// Put saves passed container structure in NeoFS system +// through Container contract call. +// +// Returns calculated container identifier and any error +// encountered that caused the saving to interrupt. +func (w *Wrapper) Put(cnr *Container) (*CID, error) { + // calculate container identifier + // + // Note: cid is used as return value only, but the calculation is performed + // primarily in order to catch potential error before contract client call. + cid, err := container.CalculateID(cnr) + if err != nil { + return nil, errors.Wrap(err, "could not calculate container identifier") + } + + // marshal the container + cnrBytes, err := cnr.MarshalBinary() + if err != nil { + return nil, errors.Wrap(err, "could not marshal the container") + } + + // prepare invocation arguments + args := contract.PutArgs{} + args.SetOwnerID(cnr.OwnerID().Bytes()) + args.SetContainer(cnrBytes) + args.SetSignature(nil) // TODO: set signature from request when will appear. + + // invoke smart contract call + if err := w.client.Put(args); err != nil { + return nil, errors.Wrap(err, "could not invoke smart contract") + } + + return cid, nil +} + +// Get reads the container from NeoFS system by identifier +// through Container contract call. +// +// If an empty slice is returned for the requested identifier, +// storage.ErrNotFound error is returned. +func (w *Wrapper) Get(cid CID) (*Container, error) { + // prepare invocation arguments + args := contract.GetArgs{} + args.SetCID(cid.Bytes()) + + // invoke smart contract call + values, err := w.client.Get(args) + if err != nil { + return nil, errors.Wrap(err, "could not invoke smart contract") + } + + cnrBytes := values.Container() + if len(cnrBytes) == 0 { + return nil, storage.ErrNotFound + } + + cnr := new(Container) + + // unmarshal the container + if err := cnr.UnmarshalBinary(cnrBytes); err != nil { + return nil, errors.Wrap(err, "could not unmarshal container") + } + + return cnr, nil +} + +// Delete removes the container from NeoFS system +// through Container contract call. +// +// Returns any error encountered that caused +// the removal to interrupt. +func (w *Wrapper) Delete(cid CID) error { + // prepare invocation arguments + args := contract.DeleteArgs{} + args.SetCID(cid.Bytes()) + args.SetOwnerID(nil) // TODO: add owner ID when will appear. + args.SetSignature(nil) // TODO: add CID signature when will appear. + + // invoke smart contract call + // + // Note: errors.Wrap return nil on nil error arg. + return errors.Wrap( + w.client.Delete(args), + "could not invoke smart contract", + ) +} + +// List returns a list of container identifiers belonging +// to the specified owner of NeoFS system. The list is composed +// through Container contract call. +// +// Returns the identifiers of all NeoFS containers if pointer +// to owner identifier is nil. +func (w *Wrapper) List(ownerID *OwnerID) ([]CID, error) { + // prepare invocation arguments + args := contract.ListArgs{} + + // Note: by default owner identifier slice is nil, + // so client won't attach invocation arguments. + // This behavior matches the nil argument of current method. + // If argument is not nil, we must specify owner identifier. + if ownerID != nil { + args.SetOwnerID(ownerID.Bytes()) + } + + // invoke smart contract call + values, err := w.client.List(args) + if err != nil { + return nil, errors.Wrap(err, "could not invoke smart contract") + } + + binCIDList := values.CIDList() + cidList := make([]CID, 0, len(binCIDList)) + + // unmarshal all container identifiers + for i := range binCIDList { + cid, err := refs.CIDFromBytes(binCIDList[i]) + if err != nil { + return nil, errors.Wrapf(err, "could not decode container ID #%d", i) + } + + cidList = append(cidList, cid) + } + + return cidList, nil +} diff --git a/pkg/morph/client/container/wrapper/eacl.go b/pkg/morph/client/container/wrapper/eacl.go new file mode 100644 index 00000000..4300186a --- /dev/null +++ b/pkg/morph/client/container/wrapper/eacl.go @@ -0,0 +1,51 @@ +package wrapper + +import ( + eacl "github.com/nspcc-dev/neofs-api-go/acl/extended" + "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage" + contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/container" + "github.com/pkg/errors" +) + +// Table represents extended ACL rule table. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage.Table. +type Table = storage.Table + +// GetEACL reads the extended ACL table from NeoFS system +// through Container contract call. +func (w *Wrapper) GetEACL(cid CID) (Table, error) { + // prepare invocation arguments + args := contract.EACLArgs{} + args.SetCID(cid.Bytes()) + + // invoke smart contract call + values, err := w.client.EACL(args) + if err != nil { + return nil, errors.Wrap(err, "could not invoke smart contract") + } + + // unmarshal and return eACL table + return eacl.UnmarshalTable(values.EACL()) +} + +// PutEACL saves the extended ACL table in NeoFS system +// through Container contract call. +// +// Returns any error encountered that caused the saving to interrupt. +func (w *Wrapper) PutEACL(cid CID, table Table, sig []byte) error { + // prepare invocation arguments + args := contract.SetEACLArgs{} + args.SetEACL(eacl.MarshalTable(table)) + args.SetCID(cid.Bytes()) + args.SetSignature(sig) + + // invoke smart contract call + // + // Note: errors.Wrap return nil on nil error arg. + return errors.Wrap( + w.client.SetEACL(args), + "could not invoke smart contract", + ) +} diff --git a/pkg/morph/client/container/wrapper/wrapper.go b/pkg/morph/client/container/wrapper/wrapper.go new file mode 100644 index 00000000..9d44c41e --- /dev/null +++ b/pkg/morph/client/container/wrapper/wrapper.go @@ -0,0 +1,43 @@ +package wrapper + +import ( + "github.com/nspcc-dev/neofs-node/pkg/core/container/storage" + "github.com/nspcc-dev/neofs-node/pkg/morph/client/container" +) + +// Client represents the Container contract client. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/morph/client/container.Client. +type Client = container.Client + +// CID represents the container identifier. +// +// CID is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/container/storage.CID. +type CID = storage.CID + +// Wrapper is a wrapper over container contract +// client which implements container storage and +// eACL storage methods. +// +// Working wrapper must be created via constructor New. +// Using the Wrapper that has been created with new(Wrapper) +// expression (or just declaring a Wrapper variable) is unsafe +// and can lead to panic. +type Wrapper struct { + client *Client +} + +// New creates, initializes and returns the Wrapper instance. +// +// If Client is nil, container.ErrNilClient is returned. +func New(c *Client) (*Wrapper, error) { + if c == nil { + return nil, container.ErrNilClient + } + + return &Wrapper{ + client: c, + }, nil +} diff --git a/pkg/morph/client/netmap/add_peer.go b/pkg/morph/client/netmap/add_peer.go new file mode 100644 index 00000000..60eef92b --- /dev/null +++ b/pkg/morph/client/netmap/add_peer.go @@ -0,0 +1,104 @@ +package netmap + +import ( + "github.com/pkg/errors" +) + +// PeerInfo groups the parameters of +// new NeoFS peer. +type PeerInfo struct { + address []byte // peer network address in a binary format + + key []byte // peer public key + + opts [][]byte // binary peer options +} + +// AddPeerArgs groups the arguments +// of add peer invocation call. +type AddPeerArgs struct { + info PeerInfo // peer information +} + +const addPeerFixedArgNumber = 2 + +// Address returns the peer network address +// in a binary format. +// +// Address format is dictated by application +// architecture. +func (a PeerInfo) Address() []byte { + return a.address +} + +// SetAddress sets the peer network address +// in a binary format. +// +// Address format is dictated by application +// architecture. +func (a *PeerInfo) SetAddress(v []byte) { + a.address = v +} + +// PublicKey returns the peer public key +// in a binary format. +// +// Key format is dictated by application +// architecture. +func (a PeerInfo) PublicKey() []byte { + return a.key +} + +// SetPublicKey sets the peer public key +// in a binary format. +// +// Key format is dictated by application +// architecture. +func (a *PeerInfo) SetPublicKey(v []byte) { + a.key = v +} + +// Options returns the peer options +// in a binary format. +// +// Option format is dictated by application +// architecture. +func (a PeerInfo) Options() [][]byte { + return a.opts +} + +// SetOptions sets the peer options +// in a binary format. +// +// Option format is dictated by application +// architecture. +func (a *PeerInfo) SetOptions(v [][]byte) { + a.opts = v +} + +// SetInfo sets the peer information. +func (a *AddPeerArgs) SetInfo(v PeerInfo) { + a.info = v +} + +// AddPeer invokes the call of add peer method +// of NeoFS Netmap contract. +func (c *Client) AddPeer(args AddPeerArgs) error { + info := args.info + + invokeArgs := make([]interface{}, 0, addPeerFixedArgNumber+len(info.opts)) + + invokeArgs = append(invokeArgs, + info.address, + info.key, + ) + + for i := range info.opts { + invokeArgs = append(invokeArgs, info.opts[i]) + } + + return errors.Wrapf(c.client.Invoke( + c.addPeerMethod, + invokeArgs..., + ), "could not invoke method (%s)", c.addPeerMethod) +} diff --git a/pkg/morph/client/netmap/client.go b/pkg/morph/client/netmap/client.go new file mode 100644 index 00000000..ab33ea11 --- /dev/null +++ b/pkg/morph/client/netmap/client.go @@ -0,0 +1,156 @@ +package netmap + +import ( + "errors" + + "github.com/nspcc-dev/neofs-node/pkg/morph/client" +) + +// Client is a wrapper over StaticClient +// which makes calls with the names and arguments +// of the NeoFS Netmap contract. +// +// Working client must be created via constructor New. +// Using the Client that has been created with new(Client) +// expression (or just declaring a Client variable) is unsafe +// and can lead to panic. +type Client struct { + client *client.StaticClient // static Netmap contract client + + *cfg // contract method names +} + +// ErrNilClient is returned by functions that expect +// a non-nil Client pointer, but received nil. +var ErrNilClient = errors.New("netmap contract client is nil") + +// Option is a client configuration change function. +type Option func(*cfg) + +type cfg struct { + addPeerMethod, // add peer method name for invocation + newEpochMethod, // new epoch method name for invocation + netMapMethod, // get network map method name + updateStateMethod, // update state method name for invocation + innerRingListMethod string // IR list method name for invocation +} + +const ( + defaultAddPeerMethod = "AddPeer" // default add peer method name + defaultNewEpochMethod = "NewEpoch" // default new epoch method name + defaultNetMapMethod = "Netmap" // default get network map method name + defaultUpdateStateMethod = "UpdateState" // default update state method name + defaultInnerRIngListMethod = "InnerRingList" // default IR list method name +) + +func defaultConfig() *cfg { + return &cfg{ + addPeerMethod: defaultAddPeerMethod, + newEpochMethod: defaultNewEpochMethod, + netMapMethod: defaultNetMapMethod, + updateStateMethod: defaultUpdateStateMethod, + innerRingListMethod: defaultInnerRIngListMethod, + } +} + +// New creates, initializes and returns the Client instance. +// +// If StaticClient is nil, client.ErrNilStaticClient is returned. +// +// Other values are set according to provided options, or by default: +// * add peer method name: AddPeer; +// * new epoch method name: NewEpoch; +// * get network map method name: Netmap; +// * update state method name: UpdateState; +// * inner ring list method name: InnerRingList. +// +// If desired option satisfies the default value, it can be omitted. +// If multiple options of the same config value are supplied, +// the option with the highest index in the arguments will be used. +func New(c *client.StaticClient, opts ...Option) (*Client, error) { + if c == nil { + return nil, client.ErrNilStaticClient + } + + res := &Client{ + client: c, + cfg: defaultConfig(), // build default configuration + } + + // apply options + for _, opt := range opts { + opt(res.cfg) + } + + return res, nil +} + +// WithAddPeerMethod returns a client constructor option that +// specifies the method name of adding peer operation. +// +// Ignores empty value. +// +// If option not provided, "AddPeer" is used. +func WithAddPeerMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.addPeerMethod = n + } + } +} + +// WithNewEpochMethod returns a client constructor option that +// specifies the method name of new epoch operation. +// +// Ignores empty value. +// +// If option not provided, "NewEpoch" is used. +func WithNewEpochMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.newEpochMethod = n + } + } +} + +// WithNetMapMethod returns a client constructor option that +// specifies the method name of network map receiving operation. +// +// Ignores empty value. +// +// If option not provided, "Netmap" is used. +func WithNetMapMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.netMapMethod = n + } + } +} + +// WithUpdateStateMethod returns a client constructor option that +// specifies the method name of peer state updating operation. +// +// Ignores empty value. +// +// If option not provided, "UpdateState" is used. +func WithUpdateStateMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.updateStateMethod = n + } + } +} + +// WithInnerRingListMethod returns a client constructor option that +// specifies the method name of inner ring listing operation. +// +// Ignores empty value. +// +// If option not provided, "InnerRingList" is used. +func WithInnerRingListMethod(n string) Option { + return func(c *cfg) { + if n != "" { + c.innerRingListMethod = n + } + } +} diff --git a/pkg/morph/client/netmap/ir_list.go b/pkg/morph/client/netmap/ir_list.go new file mode 100644 index 00000000..035c0553 --- /dev/null +++ b/pkg/morph/client/netmap/ir_list.go @@ -0,0 +1,61 @@ +package netmap + +import ( + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/pkg/errors" +) + +// InnerRingListArgs groups the arguments +// of inner ring list test invoke call. +type InnerRingListArgs struct { +} + +// InnerRingListValues groups the stack parameters +// returned by inner ring list test invoke. +type InnerRingListValues struct { + keys [][]byte // list of keys of IR nodes in a binary format +} + +// KeyList return the list of IR node keys +// in a binary format. +func (g InnerRingListValues) KeyList() [][]byte { + return g.keys +} + +// InnerRingList performs the test invoke of inner ring list +// method of NeoFS Netmap contract. +func (c *Client) InnerRingList(args InnerRingListArgs) (*InnerRingListValues, error) { + prms, err := c.client.TestInvoke( + c.innerRingListMethod, + ) + if err != nil { + return nil, errors.Wrapf(err, "could not perform test invocation (%s)", c.innerRingListMethod) + } else if ln := len(prms); ln != 1 { + return nil, errors.Errorf("unexpected stack item count (%s): %d", c.innerRingListMethod, ln) + } + + prms, err = client.ArrayFromStackParameter(prms[0]) + if err != nil { + return nil, errors.Wrapf(err, "could not get stack item array from stack item (%s)", c.innerRingListMethod) + } + + res := &InnerRingListValues{ + keys: make([][]byte, 0, len(prms)), + } + + for i := range prms { + nodePrms, err := client.ArrayFromStackParameter(prms[i]) + if err != nil { + return nil, errors.Wrap(err, "could not get stack item array (Node #%d)") + } + + key, err := client.BytesFromStackParameter(nodePrms[0]) + if err != nil { + return nil, errors.Wrapf(err, "could not parse stack item (Key #%d)", i) + } + + res.keys = append(res.keys, key) + } + + return res, nil +} diff --git a/pkg/morph/client/netmap/netmap.go b/pkg/morph/client/netmap/netmap.go new file mode 100644 index 00000000..8efb4238 --- /dev/null +++ b/pkg/morph/client/netmap/netmap.go @@ -0,0 +1,99 @@ +package netmap + +import ( + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/pkg/errors" +) + +// GetNetMapArgs groups the arguments +// of get network map test invoke call. +type GetNetMapArgs struct { +} + +// GetNetMapValues groups the stack parameters +// returned by get network map test invoke. +type GetNetMapValues struct { + peers []PeerInfo // peer list in a binary format +} + +const nodeInfoFixedPrmNumber = 3 + +// Peers return the list of peers from +// network map in a binary format. +func (g GetNetMapValues) Peers() []PeerInfo { + return g.peers +} + +// NetMap performs the test invoke of get network map +// method of NeoFS Netmap contract. +func (c *Client) NetMap(args GetNetMapArgs) (*GetNetMapValues, error) { + prms, err := c.client.TestInvoke( + c.netMapMethod, + ) + if err != nil { + return nil, errors.Wrapf(err, "could not perform test invocation (%s)", c.netMapMethod) + } else if ln := len(prms); ln != 1 { + return nil, errors.Errorf("unexpected stack item count (%s): %d", c.netMapMethod, ln) + } + + prms, err = client.ArrayFromStackParameter(prms[0]) + if err != nil { + return nil, errors.Wrapf(err, "could not get stack item array from stack item (%s)", c.netMapMethod) + } + + res := &GetNetMapValues{ + peers: make([]PeerInfo, 0, len(prms)), + } + + for i := range prms { + peer, err := peerInfoFromStackItem(prms[i]) + if err != nil { + return nil, errors.Wrapf(err, "could not parse stack item (Peer #%d)", i) + } + + res.peers = append(res.peers, *peer) + } + + return res, nil +} + +func peerInfoFromStackItem(prm smartcontract.Parameter) (*PeerInfo, error) { + prms, err := client.ArrayFromStackParameter(prm) + if err != nil { + return nil, errors.Wrapf(err, "could not get stack item array (PeerInfo)") + } else if ln := len(prms); ln != nodeInfoFixedPrmNumber { + return nil, errors.Errorf("unexpected stack item count (PeerInfo): expected %d, has %d", 3, ln) + } + + res := new(PeerInfo) + + // Address + res.address, err = client.BytesFromStackParameter(prms[0]) + if err != nil { + return nil, errors.Wrap(err, "could not get byte array from stack item (Address)") + } + + // Public key + if res.key, err = client.BytesFromStackParameter(prms[1]); err != nil { + return nil, errors.Wrap(err, "could not get byte array from stack item (Public key)") + } + + // Options + if prms, err = client.ArrayFromStackParameter(prms[2]); err != nil { + return nil, errors.Wrapf(err, "could not get stack item array (Options)") + } + + res.opts = make([][]byte, 0, len(prms)) + + for i := range prms { + opt, err := client.BytesFromStackParameter(prms[i]) + if err != nil { + return nil, errors.Wrapf(err, "could not get byte array from stack item (Option #%d)", i) + } + + res.opts = append(res.opts, opt) + } + + return res, nil +} diff --git a/pkg/morph/client/netmap/new_epoch.go b/pkg/morph/client/netmap/new_epoch.go new file mode 100644 index 00000000..67190179 --- /dev/null +++ b/pkg/morph/client/netmap/new_epoch.go @@ -0,0 +1,23 @@ +package netmap + +import "github.com/pkg/errors" + +// NewEpochArgs groups the arguments +// of new epoch invocation call. +type NewEpochArgs struct { + number int64 // new epoch number +} + +// SetEpochNumber sets the new epoch number. +func (a *NewEpochArgs) SetEpochNumber(v int64) { + a.number = v +} + +// NewEpoch invokes the call of new epoch method +// of NeoFS Netmap contract. +func (c *Client) NewEpoch(args NewEpochArgs) error { + return errors.Wrapf(c.client.Invoke( + c.addPeerMethod, + args.number, + ), "could not invoke method (%s)", c.newEpochMethod) +} diff --git a/pkg/morph/client/netmap/update_state.go b/pkg/morph/client/netmap/update_state.go new file mode 100644 index 00000000..2a825a30 --- /dev/null +++ b/pkg/morph/client/netmap/update_state.go @@ -0,0 +1,34 @@ +package netmap + +import ( + "github.com/pkg/errors" +) + +// UpdateStateArgs groups the arguments +// of update state invocation call. +type UpdateStateArgs struct { + key []byte // peer public key + + state int64 // new peer state +} + +// SetPublicKey sets peer public key +// in a binary format. +func (u *UpdateStateArgs) SetPublicKey(v []byte) { + u.key = v +} + +// SetState sets the new peer state. +func (u *UpdateStateArgs) SetState(v int64) { + u.state = v +} + +// UpdateState invokes the call of update state method +// of NeoFS Netmap contract. +func (c *Client) UpdateState(args UpdateStateArgs) error { + return errors.Wrapf(c.client.Invoke( + c.addPeerMethod, + args.key, + args.state, + ), "could not invoke method (%s)", c.updateStateMethod) +} diff --git a/pkg/morph/client/netmap/wrapper/add_peer.go b/pkg/morph/client/netmap/wrapper/add_peer.go new file mode 100644 index 00000000..4afcee70 --- /dev/null +++ b/pkg/morph/client/netmap/wrapper/add_peer.go @@ -0,0 +1,37 @@ +package wrapper + +import ( + "github.com/nspcc-dev/neofs-node/pkg/core/netmap" + contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap" + "github.com/pkg/errors" +) + +// AddPeer registers peer in NeoFS network through +// Netmap contract call. +func (w *Wrapper) AddPeer(nodeInfo netmap.Info) error { + // prepare invocation arguments + args := contract.AddPeerArgs{} + + info := contract.PeerInfo{} + info.SetPublicKey(nodeInfo.PublicKey()) + info.SetAddress([]byte(nodeInfo.Address())) + + opts := nodeInfo.Options() + binOpts := make([][]byte, 0, len(opts)) + + for i := range opts { + binOpts = append(binOpts, []byte(opts[i])) + } + + info.SetOptions(binOpts) + + args.SetInfo(info) + + // invoke smart contract call + // + // Note: errors.Wrap returns nil on nil error arg. + return errors.Wrap( + w.client.AddPeer(args), + "could not invoke smart contract", + ) +} diff --git a/pkg/morph/client/netmap/wrapper/irlist.go b/pkg/morph/client/netmap/wrapper/irlist.go new file mode 100644 index 00000000..e339d40e --- /dev/null +++ b/pkg/morph/client/netmap/wrapper/irlist.go @@ -0,0 +1,21 @@ +package wrapper + +import ( + contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap" + "github.com/pkg/errors" +) + +// InnerRingKeys receives public key list of inner +// ring nodes through Netmap contract call and returns it. +func (w *Wrapper) InnerRingKeys() ([][]byte, error) { + // prepare invocation arguments + args := contract.InnerRingListArgs{} + + // invoke smart contract call + values, err := w.client.InnerRingList(args) + if err != nil { + return nil, errors.Wrap(err, "could not invoke smart contract") + } + + return values.KeyList(), nil +} diff --git a/pkg/morph/client/netmap/wrapper/netmap.go b/pkg/morph/client/netmap/wrapper/netmap.go new file mode 100644 index 00000000..250ac145 --- /dev/null +++ b/pkg/morph/client/netmap/wrapper/netmap.go @@ -0,0 +1,60 @@ +package wrapper + +import ( + "github.com/nspcc-dev/neofs-node/pkg/core/netmap" + contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap" + "github.com/pkg/errors" +) + +// NetMap represents the NeoFS network map. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/NetMap. +type NetMap = netmap.NetMap + +// Info represents node information. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/netmap.Info. +type Info = netmap.Info + +// GetNetMap receives information list about storage nodes +// through the Netmap contract call, composes network map +// from them and returns it. +func (w *Wrapper) GetNetMap() (*NetMap, error) { + // prepare invocation arguments + args := contract.GetNetMapArgs{} + + // invoke smart contract call + values, err := w.client.NetMap(args) + if err != nil { + return nil, errors.Wrap(err, "could not invoke smart contract") + } + + // parse response and fill the network map + nm := netmap.New() + + peerList := values.Peers() + + for i := range peerList { + info := Info{} + + info.SetPublicKey(peerList[i].PublicKey()) + info.SetAddress(string(peerList[i].Address())) + + binOpts := peerList[i].Options() + opts := make([]string, 0, len(binOpts)) + + for j := range binOpts { + opts = append(opts, string(binOpts[j])) + } + + info.SetOptions(opts) + + if err := nm.AddNode(info); err != nil { + return nil, errors.Wrapf(err, "could not add node #%d to network map", i) + } + } + + return nm, nil +} diff --git a/pkg/morph/client/netmap/wrapper/new_epoch.go b/pkg/morph/client/netmap/wrapper/new_epoch.go new file mode 100644 index 00000000..d4eae653 --- /dev/null +++ b/pkg/morph/client/netmap/wrapper/new_epoch.go @@ -0,0 +1,23 @@ +package wrapper + +import ( + "github.com/nspcc-dev/neofs-node/pkg/core/netmap/epoch" + contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap" + "github.com/pkg/errors" +) + +// NewEpoch updates NeoFS epoch number through +// Netmap contract call. +func (w *Wrapper) NewEpoch(e epoch.Epoch) error { + // prepare invocation arguments + args := contract.NewEpochArgs{} + args.SetEpochNumber(int64(epoch.ToUint64(e))) + + // invoke smart contract call + // + // Note: errors.Wrap returns nil on nil error arg. + return errors.Wrap( + w.client.NewEpoch(args), + "could not invoke smart contract", + ) +} diff --git a/pkg/morph/client/netmap/wrapper/update_state.go b/pkg/morph/client/netmap/wrapper/update_state.go new file mode 100644 index 00000000..ad296f0f --- /dev/null +++ b/pkg/morph/client/netmap/wrapper/update_state.go @@ -0,0 +1,32 @@ +package wrapper + +import ( + contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap" + "github.com/pkg/errors" +) + +// NodeState is a type of node states enumeration. +type NodeState int64 + +const ( + _ NodeState = iota + + // StateOffline is an offline node state value. + StateOffline +) + +// UpdatePeerState changes peer status through Netmap contract +// call. +func (w *Wrapper) UpdatePeerState(key []byte, state NodeState) error { + args := contract.UpdateStateArgs{} + args.SetPublicKey(key) + args.SetState(int64(state)) + + // invoke smart contract call + // + // Note: errors.Wrap returns nil on nil error arg. + return errors.Wrap( + w.client.UpdateState(args), + "could not invoke smart contract", + ) +} diff --git a/pkg/morph/client/netmap/wrapper/wrapper.go b/pkg/morph/client/netmap/wrapper/wrapper.go new file mode 100644 index 00000000..5a8eabf7 --- /dev/null +++ b/pkg/morph/client/netmap/wrapper/wrapper.go @@ -0,0 +1,43 @@ +package wrapper + +import ( + "errors" + + "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap" +) + +// Client represents the Netmap contract client. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap.Client. +type Client = netmap.Client + +// Wrapper is a wrapper over netmap contract +// client which implements: +// * network map storage; +// * tool for peer state updating. +// +// Working wrapper must be created via constructor New. +// Using the Wrapper that has been created with new(Wrapper) +// expression (or just declaring a Wrapper variable) is unsafe +// and can lead to panic. +type Wrapper struct { + client *Client +} + +// ErrNilWrapper is returned by functions that expect +// a non-nil Wrapper pointer, but received nil. +var ErrNilWrapper = errors.New("netmap contract client wrapper is nil") + +// New creates, initializes and returns the Wrapper instance. +// +// If Client is nil, netmap.ErrNilClient is returned. +func New(c *Client) (*Wrapper, error) { + if c == nil { + return nil, netmap.ErrNilClient + } + + return &Wrapper{ + client: c, + }, nil +} diff --git a/pkg/morph/client/static.go b/pkg/morph/client/static.go new file mode 100644 index 00000000..c903b3af --- /dev/null +++ b/pkg/morph/client/static.go @@ -0,0 +1,62 @@ +package client + +import ( + "errors" + + sc "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" +) + +// StaticClient is a wrapper over Neo:Morph client +// that invokes single smart contract methods with fixed fee. +// +// Working static client must be created via constructor NewStatic. +// Using the StaticClient that has been created with new(StaticClient) +// expression (or just declaring a StaticClient variable) is unsafe +// and can lead to panic. +type StaticClient struct { + client *Client // neo-go client instance + + scScriptHash util.Uint160 // contract script-hash + + fee util.Fixed8 // invocation fee +} + +// ErrNilStaticClient is returned by functions that expect +// a non-nil StaticClient pointer, but received nil. +var ErrNilStaticClient = errors.New("static client is nil") + +// NewStatic creates, initializes and returns the StaticClient instance. +// +// If provided Client instance is nil, ErrNilClient is returned. +func NewStatic(client *Client, scriptHash util.Uint160, fee util.Fixed8) (*StaticClient, error) { + if client == nil { + return nil, ErrNilClient + } + + return &StaticClient{ + client: client, + scScriptHash: scriptHash, + fee: fee, + }, nil +} + +// Invoke calls Invoke method of Client with static internal script hash and fee. +// Supported args types are the same as in Client. +func (s StaticClient) Invoke(method string, args ...interface{}) error { + return s.client.Invoke( + s.scScriptHash, + s.fee, + method, + args..., + ) +} + +// TestInvoke calls TestInvoke method of Client with static internal script hash. +func (s StaticClient) TestInvoke(method string, args ...interface{}) ([]sc.Parameter, error) { + return s.client.TestInvoke( + s.scScriptHash, + method, + args..., + ) +} diff --git a/lib/blockchain/goclient/util.go b/pkg/morph/client/util.go similarity index 91% rename from lib/blockchain/goclient/util.go rename to pkg/morph/client/util.go index 82e30f49..2fec25f5 100644 --- a/lib/blockchain/goclient/util.go +++ b/pkg/morph/client/util.go @@ -1,10 +1,9 @@ -package goclient +package client import ( "encoding/binary" sc "github.com/nspcc-dev/neo-go/pkg/smartcontract" - "github.com/nspcc-dev/neo-go/pkg/util" "github.com/pkg/errors" ) @@ -71,6 +70,10 @@ func IntFromStackParameter(param sc.Parameter) (int64, error) { // BytesFromStackParameter receives binary value from the value of a smart contract parameter. func BytesFromStackParameter(param sc.Parameter) ([]byte, error) { if param.Type != sc.ByteArrayType { + if param.Type == sc.AnyType && param.Value == nil { + return nil, nil + } + return nil, errors.Errorf("chain/client: %s is not a byte array type", param.Type) } @@ -123,9 +126,3 @@ func StringFromStackParameter(param sc.Parameter) (string, error) { return "", errors.Errorf("chain/client: %s is not a string type", param.Type) } } - -// ReadStorage of the contract directly. Use it for debug, try to obtain -// smart-contract data from contract method with TestInvoke function. -func ReadStorage(c *Client, contract util.Uint160, key []byte) ([]byte, error) { - return c.cli.GetStorageByHash(contract, key) -} diff --git a/lib/blockchain/goclient/util_test.go b/pkg/morph/client/util_test.go similarity index 99% rename from lib/blockchain/goclient/util_test.go rename to pkg/morph/client/util_test.go index 5752e2dd..37899f2f 100644 --- a/lib/blockchain/goclient/util_test.go +++ b/pkg/morph/client/util_test.go @@ -1,4 +1,4 @@ -package goclient +package client import ( "testing" diff --git a/pkg/morph/event/balance/lock.go b/pkg/morph/event/balance/lock.go new file mode 100644 index 00000000..39ab6b1f --- /dev/null +++ b/pkg/morph/event/balance/lock.go @@ -0,0 +1,90 @@ +package balance + +import ( + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + "github.com/pkg/errors" +) + +// Lock structure of balance.Lock notification from morph chain. +type Lock struct { + id []byte + user util.Uint160 + lock util.Uint160 + amount int64 // Fixed16 + until int64 +} + +// MorphEvent implements Neo:Morph Event interface. +func (Lock) MorphEvent() {} + +// ID is a withdraw transaction hash. +func (l Lock) ID() []byte { return l.id } + +// User returns withdraw receiver script hash from main net. +func (l Lock) User() util.Uint160 { return l.user } + +// LockAccount return script hash for balance contract wallet. +func (l Lock) LockAccount() util.Uint160 { return l.lock } + +// Amount of the locked assets. +func (l Lock) Amount() int64 { return l.amount } + +// Until is a epoch before locked account exists. +func (l Lock) Until() int64 { return l.until } + +// ParseLock from notification into lock structure. +func ParseLock(params []smartcontract.Parameter) (event.Event, error) { + var ( + ev Lock + err error + ) + + if ln := len(params); ln != 5 { + return nil, event.WrongNumberOfParameters(5, ln) + } + + // parse id + ev.id, err = client.BytesFromStackParameter(params[0]) + if err != nil { + return nil, errors.Wrap(err, "could not get lock id") + } + + // parse user + user, err := client.BytesFromStackParameter(params[1]) + if err != nil { + return nil, errors.Wrap(err, "could not get lock user value") + } + + ev.user, err = util.Uint160DecodeBytesBE(user) + if err != nil { + return nil, errors.Wrap(err, "could not convert lock user value to uint160") + } + + // parse lock account + lock, err := client.BytesFromStackParameter(params[2]) + if err != nil { + return nil, errors.Wrap(err, "could not get lock account value") + } + + ev.lock, err = util.Uint160DecodeBytesBE(lock) + if err != nil { + return nil, errors.Wrap(err, "could not convert lock account value to uint160") + } + + // parse amount + ev.amount, err = client.IntFromStackParameter(params[3]) + if err != nil { + return nil, errors.Wrap(err, "could not get lock amount") + } + + // parse until deadline + ev.until, err = client.IntFromStackParameter(params[4]) + if err != nil { + return nil, errors.Wrap(err, "could not get lock deadline") + } + + return ev, nil +} diff --git a/pkg/morph/event/balance/lock_test.go b/pkg/morph/event/balance/lock_test.go new file mode 100644 index 00000000..28aedc5c --- /dev/null +++ b/pkg/morph/event/balance/lock_test.go @@ -0,0 +1,155 @@ +package balance + +import ( + "testing" + + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + "github.com/stretchr/testify/require" +) + +func TestParseLock(t *testing.T) { + var ( + id = []byte("Hello World") + user = util.Uint160{0x1, 0x2, 0x3} + lock = util.Uint160{0x3, 0x2, 0x1} + + amount int64 = 10 + until int64 = 20 + ) + + t.Run("wrong number of parameters", func(t *testing.T) { + prms := []smartcontract.Parameter{ + {}, + {}, + } + + _, err := ParseLock(prms) + require.EqualError(t, err, event.WrongNumberOfParameters(5, len(prms)).Error()) + }) + + t.Run("wrong id parameter", func(t *testing.T) { + _, err := ParseLock([]smartcontract.Parameter{ + { + Type: smartcontract.IntegerType, + }, + }) + + require.Error(t, err) + }) + + t.Run("wrong from parameter", func(t *testing.T) { + _, err := ParseLock([]smartcontract.Parameter{ + { + Type: smartcontract.ByteArrayType, + Value: id, + }, + { + Type: smartcontract.IntegerType, + }, + }) + + require.Error(t, err) + }) + + t.Run("wrong lock parameter", func(t *testing.T) { + _, err := ParseLock([]smartcontract.Parameter{ + { + Type: smartcontract.ByteArrayType, + Value: id, + }, + { + Type: smartcontract.ByteArrayType, + Value: user.BytesBE(), + }, + { + Type: smartcontract.ArrayType, + }, + }) + + require.Error(t, err) + }) + + t.Run("wrong amount parameter", func(t *testing.T) { + _, err := ParseLock([]smartcontract.Parameter{ + { + Type: smartcontract.ByteArrayType, + Value: id, + }, + { + Type: smartcontract.ByteArrayType, + Value: user.BytesBE(), + }, + { + Type: smartcontract.ByteArrayType, + Value: lock.BytesBE(), + }, + { + Type: smartcontract.ArrayType, + }, + }) + + require.Error(t, err) + }) + + t.Run("wrong until parameter", func(t *testing.T) { + _, err := ParseLock([]smartcontract.Parameter{ + { + Type: smartcontract.ByteArrayType, + Value: id, + }, + { + Type: smartcontract.ByteArrayType, + Value: user.BytesBE(), + }, + { + Type: smartcontract.ByteArrayType, + Value: lock.BytesBE(), + }, + { + Type: smartcontract.IntegerType, + Value: amount, + }, + { + Type: smartcontract.ArrayType, + }, + }) + + require.Error(t, err) + }) + + t.Run("correct behavior", func(t *testing.T) { + ev, err := ParseLock([]smartcontract.Parameter{ + { + Type: smartcontract.ByteArrayType, + Value: id, + }, + { + Type: smartcontract.ByteArrayType, + Value: user.BytesBE(), + }, + { + Type: smartcontract.ByteArrayType, + Value: lock.BytesBE(), + }, + { + Type: smartcontract.IntegerType, + Value: amount, + }, + { + Type: smartcontract.IntegerType, + Value: until, + }, + }) + + require.NoError(t, err) + require.Equal(t, Lock{ + id: id, + user: user, + lock: lock, + amount: amount, + until: until, + }, ev) + }) +} diff --git a/lib/blockchain/event/event.go b/pkg/morph/event/event.go similarity index 100% rename from lib/blockchain/event/event.go rename to pkg/morph/event/event.go diff --git a/lib/blockchain/event/handler.go b/pkg/morph/event/handler.go similarity index 82% rename from lib/blockchain/event/handler.go rename to pkg/morph/event/handler.go index 2d9c5b77..433880ff 100644 --- a/lib/blockchain/event/handler.go +++ b/pkg/morph/event/handler.go @@ -17,6 +17,7 @@ func (s *HandlerInfo) SetHandler(v Handler) { s.h = v } -func (s HandlerInfo) handler() Handler { +// Handler returns an event handler. +func (s HandlerInfo) Handler() Handler { return s.h } diff --git a/lib/blockchain/event/listener.go b/pkg/morph/event/listener.go similarity index 86% rename from lib/blockchain/event/listener.go rename to pkg/morph/event/listener.go index 2dcfceb3..a2fec83d 100644 --- a/lib/blockchain/event/listener.go +++ b/pkg/morph/event/listener.go @@ -6,9 +6,8 @@ import ( "github.com/nspcc-dev/neo-go/pkg/rpc/response/result" "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/blockchain/goclient" - "github.com/nspcc-dev/neofs-node/lib/blockchain/subscriber" + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/nspcc-dev/neofs-node/pkg/morph/subscriber" "github.com/pkg/errors" "go.uber.org/zap" ) @@ -35,6 +34,9 @@ type Listener interface { // // Must ignore nil handlers. RegisterHandler(HandlerInfo) + + // Must stop the event listener. + Stop() } // ListenerParams is a group of parameters @@ -61,12 +63,12 @@ type listener struct { subscriber subscriber.Subscriber } -const ( - newListenerFailMsg = "could not instantiate Listener" +const newListenerFailMsg = "could not instantiate Listener" - errNilLogger = internal.Error("nil logger") +var ( + errNilLogger = errors.New("nil logger") - errNilSubscriber = internal.Error("nil event subscriber") + errNilSubscriber = errors.New("nil event subscriber") ) // Listen starts the listening for events with registered handlers. @@ -91,7 +93,7 @@ func (s listener) listen(ctx context.Context) error { // fill the list with the contracts with set event parsers. s.mtx.RLock() for hashType := range s.parsers { - scHash := hashType.scriptHash() + scHash := hashType.ScriptHash() // prevent repetitions for _, hash := range hashes { @@ -100,7 +102,7 @@ func (s listener) listen(ctx context.Context) error { } } - hashes = append(hashes, hashType.scriptHash()) + hashes = append(hashes, hashType.ScriptHash()) } // mark listener as started @@ -123,8 +125,8 @@ loop: for { select { case <-ctx.Done(): - s.log.Warn("stop event listener by context", - zap.String("error", ctx.Err().Error()), + s.log.Info("stop event listener by context", + zap.String("reason", ctx.Err().Error()), ) break loop case notifyEvent, ok := <-chEvent: @@ -147,7 +149,7 @@ func (s listener) parseAndHandle(notifyEvent *result.NotificationEvent) { ) // stack item must be an array of items - arr, err := goclient.ArrayFromStackParameter(notifyEvent.Item) + arr, err := client.ArrayFromStackParameter(notifyEvent.Item) if err != nil { log.Warn("stack item is not an array type", zap.String("error", err.Error()), @@ -160,7 +162,7 @@ func (s listener) parseAndHandle(notifyEvent *result.NotificationEvent) { } // first item must be a byte array - typBytes, err := goclient.BytesFromStackParameter(arr[0]) + typBytes, err := client.BytesFromStackParameter(arr[0]) if err != nil { log.Warn("first array item is not a byte array", zap.String("error", err.Error()), @@ -225,7 +227,7 @@ func (s listener) parseAndHandle(notifyEvent *result.NotificationEvent) { // Ignores the parser if listener is started. func (s listener) SetParser(p ParserInfo) { log := s.log.With( - zap.String("script hash LE", p.scriptHash().StringLE()), + zap.String("script hash LE", p.ScriptHash().StringLE()), zap.Stringer("event type", p.getType()), ) @@ -258,11 +260,11 @@ func (s listener) SetParser(p ParserInfo) { // Ignores handlers of event without parser. func (s listener) RegisterHandler(p HandlerInfo) { log := s.log.With( - zap.String("script hash LE", p.scriptHash().StringLE()), - zap.Stringer("event type", p.getType()), + zap.String("script hash LE", p.ScriptHash().StringLE()), + zap.Stringer("event type", p.GetType()), ) - handler := p.handler() + handler := p.Handler() if handler == nil { log.Warn("ignore nil event handler") return @@ -282,13 +284,18 @@ func (s listener) RegisterHandler(p HandlerInfo) { s.mtx.Lock() s.handlers[p.scriptHashWithType] = append( s.handlers[p.scriptHashWithType], - p.handler(), + p.Handler(), ) s.mtx.Unlock() log.Info("registered new event handler") } +// Stop closes subscription channel with remote neo node. +func (s listener) Stop() { + s.subscriber.Close() +} + // NewListener create the notification event listener instance and returns Listener interface. func NewListener(p ListenerParams) (Listener, error) { switch { diff --git a/pkg/morph/event/neofs/cheque.go b/pkg/morph/event/neofs/cheque.go new file mode 100644 index 00000000..6374392d --- /dev/null +++ b/pkg/morph/event/neofs/cheque.go @@ -0,0 +1,80 @@ +package neofs + +import ( + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + "github.com/pkg/errors" +) + +// Cheque structure of neofs.Cheque notification from mainnet chain. +type Cheque struct { + id []byte + amount int64 // Fixed8 + user util.Uint160 + lock util.Uint160 +} + +// MorphEvent implements Neo:Morph Event interface. +func (Cheque) MorphEvent() {} + +// ID is a withdraw transaction hash. +func (c Cheque) ID() []byte { return c.id } + +// User returns withdraw receiver script hash from main net. +func (c Cheque) User() util.Uint160 { return c.user } + +// Amount of the sent assets. +func (c Cheque) Amount() int64 { return c.amount } + +// LockAccount return script hash for balance contract wallet. +func (c Cheque) LockAccount() util.Uint160 { return c.lock } + +// ParseCheque from notification into cheque structure. +func ParseCheque(params []smartcontract.Parameter) (event.Event, error) { + var ( + ev Cheque + err error + ) + + if ln := len(params); ln != 4 { + return nil, event.WrongNumberOfParameters(4, ln) + } + + // parse id + ev.id, err = client.BytesFromStackParameter(params[0]) + if err != nil { + return nil, errors.Wrap(err, "could not get cheque id") + } + + // parse user + user, err := client.BytesFromStackParameter(params[1]) + if err != nil { + return nil, errors.Wrap(err, "could not get cheque user") + } + + ev.user, err = util.Uint160DecodeBytesBE(user) + if err != nil { + return nil, errors.Wrap(err, "could not convert cheque user to uint160") + } + + // parse amount + ev.amount, err = client.IntFromStackParameter(params[2]) + if err != nil { + return nil, errors.Wrap(err, "could not get cheque amount") + } + + // parse lock account + lock, err := client.BytesFromStackParameter(params[3]) + if err != nil { + return nil, errors.Wrap(err, "could not get cheque lock account") + } + + ev.lock, err = util.Uint160DecodeBytesBE(lock) + if err != nil { + return nil, errors.Wrap(err, "could not convert cheque lock account to uint160") + } + + return ev, nil +} diff --git a/pkg/morph/event/neofs/cheque_test.go b/pkg/morph/event/neofs/cheque_test.go new file mode 100644 index 00000000..f0ff71ee --- /dev/null +++ b/pkg/morph/event/neofs/cheque_test.go @@ -0,0 +1,123 @@ +package neofs + +import ( + "testing" + + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + "github.com/stretchr/testify/require" +) + +func TestParseCheque(t *testing.T) { + var ( + id = []byte("Hello World") + user = util.Uint160{0x1, 0x2, 0x3} + lock = util.Uint160{0x3, 0x2, 0x1} + + amount int64 = 10 + ) + + t.Run("wrong number of parameters", func(t *testing.T) { + prms := []smartcontract.Parameter{ + {}, + {}, + } + + _, err := ParseCheque(prms) + require.EqualError(t, err, event.WrongNumberOfParameters(4, len(prms)).Error()) + }) + + t.Run("wrong id parameter", func(t *testing.T) { + _, err := ParseCheque([]smartcontract.Parameter{ + { + Type: smartcontract.ArrayType, + }, + }) + + require.Error(t, err) + }) + + t.Run("wrong user parameter", func(t *testing.T) { + _, err := ParseCheque([]smartcontract.Parameter{ + { + Type: smartcontract.ByteArrayType, + Value: id, + }, + { + Type: smartcontract.ArrayType, + }, + }) + + require.Error(t, err) + }) + + t.Run("wrong amount parameter", func(t *testing.T) { + _, err := ParseCheque([]smartcontract.Parameter{ + { + Type: smartcontract.ByteArrayType, + Value: id, + }, + { + Type: smartcontract.ByteArrayType, + Value: user.BytesBE(), + }, + { + Type: smartcontract.ArrayType, + }, + }) + + require.Error(t, err) + }) + + t.Run("wrong lock parameter", func(t *testing.T) { + _, err := ParseCheque([]smartcontract.Parameter{ + { + Type: smartcontract.ByteArrayType, + Value: id, + }, + { + Type: smartcontract.ByteArrayType, + Value: user.BytesBE(), + }, + { + Type: smartcontract.IntegerType, + Value: amount, + }, + { + Type: smartcontract.ArrayType, + }, + }) + + require.Error(t, err) + }) + + t.Run("correct behavior", func(t *testing.T) { + ev, err := ParseCheque([]smartcontract.Parameter{ + { + Type: smartcontract.ByteArrayType, + Value: id, + }, + { + Type: smartcontract.ByteArrayType, + Value: user.BytesBE(), + }, + { + Type: smartcontract.IntegerType, + Value: amount, + }, + { + Type: smartcontract.ByteArrayType, + Value: lock.BytesBE(), + }, + }) + + require.NoError(t, err) + require.Equal(t, Cheque{ + id: id, + amount: amount, + user: user, + lock: lock, + }, ev) + }) +} diff --git a/pkg/morph/event/neofs/deposit.go b/pkg/morph/event/neofs/deposit.go new file mode 100644 index 00000000..36dae62e --- /dev/null +++ b/pkg/morph/event/neofs/deposit.go @@ -0,0 +1,77 @@ +package neofs + +import ( + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + "github.com/pkg/errors" +) + +// Deposit structure of neofs.Deposit notification from mainnet chain. +type Deposit struct { + id []byte + amount int64 // Fixed8 + from util.Uint160 + to util.Uint160 +} + +// MorphEvent implements Neo:Morph Event interface. +func (Deposit) MorphEvent() {} + +// ID is a deposit transaction hash. +func (d Deposit) ID() []byte { return d.id } + +// From is a script hash of asset sender in main net. +func (d Deposit) From() util.Uint160 { return d.from } + +// To is a script hash of asset receiver in balance contract. +func (d Deposit) To() util.Uint160 { return d.to } + +// Amount of transferred assets. +func (d Deposit) Amount() int64 { return d.amount } + +// ParseDeposit notification into deposit structure. +func ParseDeposit(params []smartcontract.Parameter) (event.Event, error) { + var ev Deposit + + if ln := len(params); ln != 4 { + return nil, event.WrongNumberOfParameters(4, ln) + } + + // parse from + from, err := client.BytesFromStackParameter(params[0]) + if err != nil { + return nil, errors.Wrap(err, "could not get deposit sender") + } + + ev.from, err = util.Uint160DecodeBytesBE(from) + if err != nil { + return nil, errors.Wrap(err, "could not convert deposit sender to uint160") + } + + // parse amount + ev.amount, err = client.IntFromStackParameter(params[1]) + if err != nil { + return nil, errors.Wrap(err, "could not get deposit amount") + } + + // parse to + to, err := client.BytesFromStackParameter(params[2]) + if err != nil { + return nil, errors.Wrap(err, "could not get deposit receiver") + } + + ev.to, err = util.Uint160DecodeBytesBE(to) + if err != nil { + return nil, errors.Wrap(err, "could not convert deposit receiver to uint160") + } + + // parse id + ev.id, err = client.BytesFromStackParameter(params[3]) + if err != nil { + return nil, errors.Wrap(err, "could not get deposit id") + } + + return ev, nil +} diff --git a/pkg/morph/event/neofs/deposit_test.go b/pkg/morph/event/neofs/deposit_test.go new file mode 100644 index 00000000..4c9e4e08 --- /dev/null +++ b/pkg/morph/event/neofs/deposit_test.go @@ -0,0 +1,123 @@ +package neofs + +import ( + "testing" + + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + "github.com/stretchr/testify/require" +) + +func TestParseDeposit(t *testing.T) { + var ( + id = []byte("Hello World") + from = util.Uint160{0x1, 0x2, 0x3} + to = util.Uint160{0x3, 0x2, 0x1} + + amount int64 = 10 + ) + + t.Run("wrong number of parameters", func(t *testing.T) { + prms := []smartcontract.Parameter{ + {}, + {}, + } + + _, err := ParseDeposit(prms) + require.EqualError(t, err, event.WrongNumberOfParameters(4, len(prms)).Error()) + }) + + t.Run("wrong from parameter", func(t *testing.T) { + _, err := ParseDeposit([]smartcontract.Parameter{ + { + Type: smartcontract.ArrayType, + }, + }) + + require.Error(t, err) + }) + + t.Run("wrong amount parameter", func(t *testing.T) { + _, err := ParseDeposit([]smartcontract.Parameter{ + { + Type: smartcontract.ByteArrayType, + Value: from.BytesBE(), + }, + { + Type: smartcontract.ArrayType, + }, + }) + + require.Error(t, err) + }) + + t.Run("wrong to parameter", func(t *testing.T) { + _, err := ParseDeposit([]smartcontract.Parameter{ + { + Type: smartcontract.ByteArrayType, + Value: from.BytesBE(), + }, + { + Type: smartcontract.IntegerType, + Value: amount, + }, + { + Type: smartcontract.ArrayType, + }, + }) + + require.Error(t, err) + }) + + t.Run("wrong id parameter", func(t *testing.T) { + _, err := ParseDeposit([]smartcontract.Parameter{ + { + Type: smartcontract.ByteArrayType, + Value: from.BytesBE(), + }, + { + Type: smartcontract.IntegerType, + Value: amount, + }, + { + Type: smartcontract.ByteArrayType, + Value: to.BytesBE(), + }, + { + Type: smartcontract.ArrayType, + }, + }) + + require.Error(t, err) + }) + + t.Run("correct behavior", func(t *testing.T) { + ev, err := ParseDeposit([]smartcontract.Parameter{ + { + Type: smartcontract.ByteArrayType, + Value: from.BytesBE(), + }, + { + Type: smartcontract.IntegerType, + Value: amount, + }, + { + Type: smartcontract.ByteArrayType, + Value: to.BytesBE(), + }, + { + Type: smartcontract.ByteArrayType, + Value: id, + }, + }) + + require.NoError(t, err) + require.Equal(t, Deposit{ + id: id, + amount: amount, + from: from, + to: to, + }, ev) + }) +} diff --git a/pkg/morph/event/neofs/withdraw.go b/pkg/morph/event/neofs/withdraw.go new file mode 100644 index 00000000..f8718b71 --- /dev/null +++ b/pkg/morph/event/neofs/withdraw.go @@ -0,0 +1,62 @@ +package neofs + +import ( + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + "github.com/pkg/errors" +) + +// Withdraw structure of neofs.Withdraw notification from mainnet chain. +type Withdraw struct { + id []byte + amount int64 // Fixed8 + user util.Uint160 +} + +// MorphEvent implements Neo:Morph Event interface. +func (Withdraw) MorphEvent() {} + +// ID is a withdraw transaction hash. +func (w Withdraw) ID() []byte { return w.id } + +// User returns withdraw receiver script hash from main net. +func (w Withdraw) User() util.Uint160 { return w.user } + +// Amount of the withdraw assets. +func (w Withdraw) Amount() int64 { return w.amount } + +// ParseWithdraw notification into withdraw structure. +func ParseWithdraw(params []smartcontract.Parameter) (event.Event, error) { + var ev Withdraw + + if ln := len(params); ln != 3 { + return nil, event.WrongNumberOfParameters(3, ln) + } + + // parse user + user, err := client.BytesFromStackParameter(params[0]) + if err != nil { + return nil, errors.Wrap(err, "could not get withdraw user") + } + + ev.user, err = util.Uint160DecodeBytesBE(user) + if err != nil { + return nil, errors.Wrap(err, "could not convert withdraw user to uint160") + } + + // parse amount + ev.amount, err = client.IntFromStackParameter(params[1]) + if err != nil { + return nil, errors.Wrap(err, "could not get withdraw amount") + } + + // parse id + ev.id, err = client.BytesFromStackParameter(params[2]) + if err != nil { + return nil, errors.Wrap(err, "could not get withdraw id") + } + + return ev, nil +} diff --git a/pkg/morph/event/neofs/withdraw_test.go b/pkg/morph/event/neofs/withdraw_test.go new file mode 100644 index 00000000..2ebccfdb --- /dev/null +++ b/pkg/morph/event/neofs/withdraw_test.go @@ -0,0 +1,95 @@ +package neofs + +import ( + "testing" + + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" + "github.com/stretchr/testify/require" +) + +func TestParseWithdraw(t *testing.T) { + var ( + id = []byte("Hello World") + user = util.Uint160{0x1, 0x2, 0x3} + + amount int64 = 10 + ) + + t.Run("wrong number of parameters", func(t *testing.T) { + prms := []smartcontract.Parameter{ + {}, + {}, + } + + _, err := ParseWithdraw(prms) + require.EqualError(t, err, event.WrongNumberOfParameters(3, len(prms)).Error()) + }) + + t.Run("wrong user parameter", func(t *testing.T) { + _, err := ParseWithdraw([]smartcontract.Parameter{ + { + Type: smartcontract.ArrayType, + }, + }) + + require.Error(t, err) + }) + + t.Run("wrong amount parameter", func(t *testing.T) { + _, err := ParseWithdraw([]smartcontract.Parameter{ + { + Type: smartcontract.ByteArrayType, + Value: user.BytesBE(), + }, + { + Type: smartcontract.ArrayType, + }, + }) + + require.Error(t, err) + }) + + t.Run("wrong id parameter", func(t *testing.T) { + _, err := ParseWithdraw([]smartcontract.Parameter{ + { + Type: smartcontract.ByteArrayType, + Value: user.BytesBE(), + }, + { + Type: smartcontract.IntegerType, + Value: amount, + }, + { + Type: smartcontract.ArrayType, + }, + }) + + require.Error(t, err) + }) + + t.Run("correct behavior", func(t *testing.T) { + ev, err := ParseWithdraw([]smartcontract.Parameter{ + { + Type: smartcontract.ByteArrayType, + Value: user.BytesBE(), + }, + { + Type: smartcontract.IntegerType, + Value: amount, + }, + { + Type: smartcontract.ByteArrayType, + Value: id, + }, + }) + + require.NoError(t, err) + require.Equal(t, Withdraw{ + id: id, + amount: amount, + user: user, + }, ev) + }) +} diff --git a/lib/blockchain/event/netmap/epoch.go b/pkg/morph/event/netmap/epoch.go similarity index 81% rename from lib/blockchain/event/netmap/epoch.go rename to pkg/morph/event/netmap/epoch.go index 2445b85a..09aad628 100644 --- a/lib/blockchain/event/netmap/epoch.go +++ b/pkg/morph/event/netmap/epoch.go @@ -2,8 +2,8 @@ package netmap import ( "github.com/nspcc-dev/neo-go/pkg/smartcontract" - "github.com/nspcc-dev/neofs-node/lib/blockchain/event" - "github.com/nspcc-dev/neofs-node/lib/blockchain/goclient" + "github.com/nspcc-dev/neofs-node/pkg/morph/client" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" "github.com/pkg/errors" ) @@ -28,7 +28,7 @@ func ParseNewEpoch(prms []smartcontract.Parameter) (event.Event, error) { return nil, event.WrongNumberOfParameters(1, ln) } - prmEpochNum, err := goclient.IntFromStackParameter(prms[0]) + prmEpochNum, err := client.IntFromStackParameter(prms[0]) if err != nil { return nil, errors.Wrap(err, "could not get integer epoch number") } diff --git a/lib/blockchain/event/netmap/epoch_test.go b/pkg/morph/event/netmap/epoch_test.go similarity index 94% rename from lib/blockchain/event/netmap/epoch_test.go rename to pkg/morph/event/netmap/epoch_test.go index 48342697..72ce8ea9 100644 --- a/lib/blockchain/event/netmap/epoch_test.go +++ b/pkg/morph/event/netmap/epoch_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/nspcc-dev/neo-go/pkg/smartcontract" - "github.com/nspcc-dev/neofs-node/lib/blockchain/event" + "github.com/nspcc-dev/neofs-node/pkg/morph/event" "github.com/stretchr/testify/require" ) diff --git a/lib/blockchain/event/parser.go b/pkg/morph/event/parser.go similarity index 100% rename from lib/blockchain/event/parser.go rename to pkg/morph/event/parser.go diff --git a/lib/blockchain/event/utils.go b/pkg/morph/event/utils.go similarity index 73% rename from lib/blockchain/event/utils.go rename to pkg/morph/event/utils.go index 66ef187d..77adcee5 100644 --- a/lib/blockchain/event/utils.go +++ b/pkg/morph/event/utils.go @@ -20,7 +20,8 @@ func (s *scriptHashValue) SetScriptHash(v util.Uint160) { s.hash = v } -func (s scriptHashValue) scriptHash() util.Uint160 { +// ScriptHash is script hash getter. +func (s scriptHashValue) ScriptHash() util.Uint160 { return s.hash } @@ -29,6 +30,7 @@ func (s *typeValue) SetType(v Type) { s.typ = v } -func (s typeValue) getType() Type { +// GetType is an event type getter. +func (s typeValue) GetType() Type { return s.typ } diff --git a/lib/blockchain/subscriber/subscriber.go b/pkg/morph/subscriber/subscriber.go similarity index 98% rename from lib/blockchain/subscriber/subscriber.go rename to pkg/morph/subscriber/subscriber.go index 5d2528e9..c71e7f4d 100644 --- a/lib/blockchain/subscriber/subscriber.go +++ b/pkg/morph/subscriber/subscriber.go @@ -18,6 +18,7 @@ type ( Subscriber interface { SubscribeForNotification(...util.Uint160) (<-chan *result.NotificationEvent, error) UnsubscribeForNotification() + Close() } subscriber struct { @@ -94,6 +95,10 @@ func (s *subscriber) UnsubscribeForNotification() { } } +func (s *subscriber) Close() { + s.client.Close() +} + func (s *subscriber) routeNotifications(ctx context.Context) { for { select { diff --git a/pkg/network/address.go b/pkg/network/address.go new file mode 100644 index 00000000..5cc4e71c --- /dev/null +++ b/pkg/network/address.go @@ -0,0 +1,12 @@ +package network + +import ( + "github.com/multiformats/go-multiaddr" +) + +// Address represents the NeoFS node +// network address. +// +// It is a type alias of +// github.com/multiformats/go-multiaddr.Multiaddr. +type Address = multiaddr.Multiaddr diff --git a/pkg/network/bootstrap/bootstrap.go b/pkg/network/bootstrap/bootstrap.go new file mode 100644 index 00000000..041041df --- /dev/null +++ b/pkg/network/bootstrap/bootstrap.go @@ -0,0 +1,60 @@ +package bootstrap + +import ( + "context" + + "github.com/nspcc-dev/neofs-node/pkg/core/netmap" + "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap/wrapper" +) + +// ContractClient represents the Netmap +// contract client. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap/wrapper.Wrapper. +type ContractClient = *wrapper.Wrapper + +// NodeInfo represents the +// information about storage node. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/netmap.Info. +type NodeInfo = netmap.Info + +// Registerer represents the tool that +// registers storage node in NeoFS system. +// +// Working Registerer must be created via constructor New. +// Using the Registerer that has been created with new(Registerer) +// expression (or just declaring a Registerer variable) is unsafe +// and can lead to panic. +type Registerer struct { + client ContractClient + + info NodeInfo +} + +// New creates, initializes and returns the Registerer instance. +// +// If passed contract client is nil, wrapper.ErrNilWrapper is returned. +func New(client ContractClient, info NodeInfo) (*Registerer, error) { + if client == nil { + return nil, wrapper.ErrNilWrapper + } + + return &Registerer{ + client: client, + info: info, + }, nil +} + +// Bootstrap registers storage node in NeoFS system +// through Netmap contract client. +// +// If contract client returns error, panic arises without retry. +func (r *Registerer) Bootstrap(context.Context) { + // register peer in NeoFS network + if err := r.client.AddPeer(r.info); err != nil { + panic(err) + } +} diff --git a/pkg/network/dial.go b/pkg/network/dial.go new file mode 100644 index 00000000..a506aeab --- /dev/null +++ b/pkg/network/dial.go @@ -0,0 +1,26 @@ +package network + +import ( + "context" + "net" + "time" + + manet "github.com/multiformats/go-multiaddr-net" +) + +// Dial connects to a remote node by address. +func Dial(ctx context.Context, addr Address) (net.Conn, error) { + return dialContext(ctx, addr, 0) +} + +// DialWithTimeout connects to a remote node by address with timeout. +func DialWithTimeout(ctx context.Context, addr Address, timeout time.Duration) (net.Conn, error) { + return dialContext(ctx, addr, timeout) +} + +func dialContext(ctx context.Context, addr Address, timeout time.Duration) (net.Conn, error) { + dialer := manet.Dialer{} + dialer.Timeout = timeout + + return dialer.DialContext(ctx, addr) +} diff --git a/pkg/network/listen.go b/pkg/network/listen.go new file mode 100644 index 00000000..14cd1122 --- /dev/null +++ b/pkg/network/listen.go @@ -0,0 +1,17 @@ +package network + +import ( + "net" + + manet "github.com/multiformats/go-multiaddr-net" +) + +// Listen announces on the local network address. +func Listen(addr Address) (net.Listener, error) { + mLis, err := manet.Listen(addr) + if err != nil { + return nil, err + } + + return manet.NetListener(mLis), nil +} diff --git a/lib/muxer/listener.go b/pkg/network/muxer/listener.go similarity index 100% rename from lib/muxer/listener.go rename to pkg/network/muxer/listener.go diff --git a/lib/muxer/muxer.go b/pkg/network/muxer/muxer.go similarity index 92% rename from lib/muxer/muxer.go rename to pkg/network/muxer/muxer.go index 9aff7cbb..3f864038 100644 --- a/lib/muxer/muxer.go +++ b/pkg/network/muxer/muxer.go @@ -8,7 +8,7 @@ import ( "time" "github.com/multiformats/go-multiaddr" - "github.com/nspcc-dev/neofs-node/lib/peers" + "github.com/nspcc-dev/neofs-node/pkg/network" "github.com/soheilhy/cmux" "github.com/valyala/fasthttp" "go.uber.org/zap" @@ -23,7 +23,6 @@ type ( Address multiaddr.Multiaddr ShutdownTTL time.Duration P2P *grpc.Server - Peers peers.Interface } // Mux is an interface of network connections muxer. @@ -33,7 +32,6 @@ type ( } muxer struct { - peers peers.Interface maddr multiaddr.Multiaddr run *int32 lis net.Listener @@ -71,8 +69,8 @@ func New(p Params) Mux { api: p.API, p2p: p.P2P, log: p.Logger, - peers: p.Peers, - done: make(chan struct{}), + + done: make(chan struct{}), } } @@ -109,17 +107,11 @@ func (m *muxer) Start(ctx context.Context) { } } - lis, err := m.peers.Listen(m.maddr) - if err != nil { + if m.lis, err = network.Listen(m.maddr); err != nil { m.log.Fatal("could not close old listener", zap.Error(err)) } - m.lis = NetListener(lis) - - m.log.Info("create mux-listener", - zap.String("bind-address", m.lis.Addr().String())) - mux := cmux.New(m.lis) mux.HandleError(func(e error) bool { if needCatch(e) { diff --git a/lib/muxer/muxer_test.go b/pkg/network/muxer/muxer_test.go similarity index 91% rename from lib/muxer/muxer_test.go rename to pkg/network/muxer/muxer_test.go index fc728d3c..79a7e1f3 100644 --- a/lib/muxer/muxer_test.go +++ b/pkg/network/muxer/muxer_test.go @@ -13,9 +13,7 @@ import ( "bou.ke/monkey" "github.com/multiformats/go-multiaddr" - "github.com/nspcc-dev/neofs-node/lib/peers" - "github.com/nspcc-dev/neofs-node/lib/test" - "github.com/nspcc-dev/neofs-node/lib/transport" + manet "github.com/multiformats/go-multiaddr-net" "github.com/pkg/errors" "github.com/soheilhy/cmux" "github.com/spf13/viper" @@ -94,16 +92,6 @@ func testMultiAddr(is *require.Assertions) multiaddr.Multiaddr { return mAddr } -func testPeers(is *require.Assertions, a multiaddr.Multiaddr) peers.Interface { - s, err := peers.New(peers.Params{ - Address: a, - Transport: transport.New(5, time.Second), - Logger: test.NewTestLogger(false), - }) - is.NoError(err) - return s -} - func testLogger() *zap.Logger { encoderCfg := zapcore.EncoderConfig{ MessageKey: "msg", @@ -145,7 +133,6 @@ func TestSuite(t *testing.T) { ShutdownTTL: s, API: testHTTPServer(), P2P: g, - Peers: testPeers(is, a), }) is.NotPanics(func() { @@ -181,8 +168,6 @@ func TestSuite(t *testing.T) { addr, err := multiaddr.NewMultiaddr("/ip4/127.0.0.1/tcp/63090") is.NoError(err) - ps := testPeers(is, addr) - RegisterGreeterServer(g, &service{}) m := New(Params{ @@ -190,15 +175,17 @@ func TestSuite(t *testing.T) { Address: addr, ShutdownTTL: s, P2P: g, - Peers: ps, }) is.NotPanics(func() { m.Start(ctx) }) - con, err := ps.GRPCConnection(ctx, addr, false) - is.NoError(err) + a, err := manet.ToNetAddr(addr) + require.NoError(t, err) + + con, err := grpc.DialContext(ctx, a.String(), grpc.WithInsecure()) + require.NoError(t, err) res, err := NewGreeterClient(con).Hello(ctx, &HelloRequest{Name: "test"}) is.NoError(err) @@ -227,7 +214,6 @@ func TestSuite(t *testing.T) { ShutdownTTL: s, API: testHTTPServer(), P2P: g, - Peers: testPeers(is, a), }) is.NotNil(m) @@ -264,7 +250,6 @@ func TestSuite(t *testing.T) { ShutdownTTL: s, API: testHTTPServer(), P2P: g, - Peers: testPeers(is, a), }) is.NotNil(m) @@ -288,7 +273,6 @@ func TestSuite(t *testing.T) { var ( is = require.New(t) l = testLogger() - a = testMultiAddr(is) err error ) @@ -297,7 +281,6 @@ func TestSuite(t *testing.T) { mux := new(muxer) mux.log = l - mux.peers = testPeers(is, a) mux.run = new(int32) mux.done = make(chan struct{}) mux.maddr, err = multiaddr.NewMultiaddr("/ip4/1.1.1.1/tcp/2") @@ -336,7 +319,6 @@ func TestSuite(t *testing.T) { is = require.New(t) g = grpc.NewServer() l = testLogger() - a = testMultiAddr(is) err error ) @@ -347,7 +329,6 @@ func TestSuite(t *testing.T) { mux.api = testHTTPServer() mux.p2p = g mux.log = l - mux.peers = testPeers(is, a) mux.run = new(int32) mux.done = make(chan struct{}) mux.maddr, err = multiaddr.NewMultiaddr("/ip4/1.1.1.1/tcp/2") diff --git a/lib/muxer/muxer_test.pb.go b/pkg/network/muxer/muxer_test.pb.go similarity index 89% rename from lib/muxer/muxer_test.pb.go rename to pkg/network/muxer/muxer_test.pb.go index f998ce85b1cf0abc53c9b56b086b8905989fa417..a7a905a3714342296b98795cbc2474209adcac6b 100644 GIT binary patch delta 570 zcmZ8eyGjE=6van`-M}W<2udI_78b(7PG&c=n?fOAYhf)S*_TK#iY%Bl1i>FrnL#gp!V-HX$+Vk| z$PP~B%Ne8**9cKhT%0xpY%#f#xHX~AhlzcDkgQk91w~5-7h)WD59CE?-$!(CcaD{n tm8v%d4@FEcSs_Qii%Js*Zy-{fP~I9oSEWhRhxvQ;RhznN{!ss2`2)1}my`ei delta 500 zcmaD{IjeGl9dk}*(&Tfp;u{n0u<%)!8>d=WCR(N@8JiiVZ1!T+ViiXf(@}_y&CDyQ zHk^EcO=R*1E}qS+x%Mza4UwGO%_oFr$mG?0QVOVMDA)oGNvbSKt>)s?*HoRb7VY(oJMdvYR}0~7*r z4uLtJAsl5vkg^a#uuKb>vlYU53g(Cj>GN=L8dMmWgRDtSnj9dc3e-?8q;CWgHZukZ z8-rb9VFqFwS%Apo6cCwa0%n6<2y*%48$w`vID|o#S_*?XnZgS7NnlNeU^^_qMy5cF zF#s80mg0 RHgk&X;snLd=5w;5@&IAWf++w1 diff --git a/lib/muxer/muxer_test.proto b/pkg/network/muxer/muxer_test.proto similarity index 78% rename from lib/muxer/muxer_test.proto rename to pkg/network/muxer/muxer_test.proto index b3a723f9..3079865c 100644 --- a/lib/muxer/muxer_test.proto +++ b/pkg/network/muxer/muxer_test.proto @@ -1,5 +1,5 @@ syntax = "proto3"; -option go_package = "github.com/nspcc-dev/neofs-node/lib/muxer"; +option go_package = "github.com/nspcc-dev/neofs-node/pkg/network/muxer"; package muxer; diff --git a/lib/peers/metrics.go b/pkg/network/peers/metrics.go similarity index 100% rename from lib/peers/metrics.go rename to pkg/network/peers/metrics.go diff --git a/pkg/network/peers/peers.go b/pkg/network/peers/peers.go new file mode 100644 index 00000000..0536bd22 --- /dev/null +++ b/pkg/network/peers/peers.go @@ -0,0 +1,234 @@ +package peers + +import ( + "context" + "net" + "sync" + "time" + + "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr-net" + "github.com/nspcc-dev/neofs-node/pkg/network" + "github.com/pkg/errors" + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/keepalive" + "google.golang.org/grpc/status" +) + +type ( + // Interface is an interface of network connections controller. + Interface interface { + Job(context.Context) + GRPCConnector + } + + // GRPCConnector is an interface of gRPC virtual connector. + GRPCConnector interface { + GRPCConnection(ctx context.Context, maddr multiaddr.Multiaddr) (*grpc.ClientConn, error) + } + + // Params groups the parameters of Interface. + Params struct { + Logger *zap.Logger + ConnectionTTL time.Duration + ConnectionIDLE time.Duration + MetricsTimeout time.Duration + KeepAliveTTL time.Duration + KeepAlivePingTTL time.Duration + } + + connItem struct { + sync.RWMutex + conn *grpc.ClientConn + used time.Time + } + + iface struct { + log *zap.Logger + tick time.Duration + idle time.Duration + + keepAlive time.Duration + pingTTL time.Duration + + metricsTimeout time.Duration + + grpc struct { + // globalMutex used by garbage collector and other high + globalMutex *sync.RWMutex + // bookMutex resolves concurrent access to the new connection + bookMutex *sync.RWMutex + // connBook contains connection info + // it's mutex resolves concurrent access to existed connection + connBook map[string]*connItem + } + } +) + +const ( + defaultCloseTimer = 30 * time.Second + defaultConIdleTTL = 30 * time.Second + defaultKeepAliveTTL = 5 * time.Second + defaultMetricsTimeout = 5 * time.Second + defaultKeepAlivePingTTL = 50 * time.Millisecond +) + +var errNilMultiaddr = errors.New("empty multi-address") + +func (s *iface) removeGRPCConnection(addr string) error { + if gCon, ok := s.grpc.connBook[addr]; ok && gCon.conn != nil { + if err := gCon.conn.Close(); err != nil { + state, ok := status.FromError(err) + if !ok { + return err + } + + s.log.Debug("error state", + zap.String("address", addr), + zap.Any("code", state.Code()), + zap.String("state", state.Message()), + zap.Any("details", state.Details())) + } + } + + delete(s.grpc.connBook, addr) + + return nil +} + +func isGRPCClosed(con *grpc.ClientConn) bool { + switch con.GetState() { + case connectivity.Idle, connectivity.Connecting, connectivity.Ready: + return false + default: + // connectivity.TransientFailure, connectivity.Shutdown + return true + } +} + +func convertAddress(maddr multiaddr.Multiaddr) (string, error) { + if maddr == nil { + return "", errNilMultiaddr + } + + addr, err := manet.ToNetAddr(maddr) + if err != nil { + return "", errors.Wrapf(err, "could not convert address `%s`", maddr) + } + + return addr.String(), nil +} + +// GRPCConnection creates gRPC connection over peers connection. +func (s *iface) GRPCConnection(ctx context.Context, maddr multiaddr.Multiaddr) (*grpc.ClientConn, error) { + addr, err := convertAddress(maddr) + if err != nil { + return nil, errors.Wrapf(err, "could not convert `%v`", maddr) + } + + // Get global mutex on read. + // All high level function e.g. peers garbage collector + // or shutdown must use globalMutex.Lock instead + s.grpc.globalMutex.RLock() + + // Get connection item from connection book or create a new one. + // Concurrent map access resolved by bookMutex. + s.grpc.bookMutex.Lock() + + item, ok := s.grpc.connBook[addr] + if !ok { + item = new(connItem) + s.grpc.connBook[addr] = item + } + + s.grpc.bookMutex.Unlock() + + // Now lock connection item. + // This denies concurrent access to the same address, + // but allows concurrent access to a different addresses. + item.Lock() + + if item.conn != nil && !isGRPCClosed(item.conn) { + item.used = time.Now() + + item.Unlock() + s.grpc.globalMutex.RUnlock() + + return item.conn, nil + } + + // Если вышеописанные строки переместить внутрь WithDialer, + // мы получим сломанный коннекшн, но ошибка не будет возвращена, + // поэтому мы сначала проверяем коннекшн и лишь потом возвращаем + // *gRPC.ClientConn + // + // Это будет работать с `grpc.WithBlock()`, см. ниже + conn, err := grpc.DialContext(ctx, maddr.String(), + grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: s.pingTTL, + Timeout: s.keepAlive, + PermitWithoutStream: true, + }), + // TODO: we must provide grpc.WithInsecure() or set credentials + grpc.WithInsecure(), + grpc.WithBlock(), + grpc.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) { + return network.Dial(ctx, maddr) + }), + ) + if err == nil { + item.conn = conn + item.used = time.Now() + } + + item.Unlock() + s.grpc.globalMutex.RUnlock() + + return conn, err +} + +// New create iface instance and check arguments. +func New(p Params) (Interface, error) { + if p.ConnectionTTL <= 0 { + p.ConnectionTTL = defaultCloseTimer + } + + if p.ConnectionIDLE <= 0 { + p.ConnectionIDLE = defaultConIdleTTL + } + + if p.KeepAliveTTL <= 0 { + p.KeepAliveTTL = defaultKeepAliveTTL + } + + if p.KeepAlivePingTTL <= 0 { + p.KeepAlivePingTTL = defaultKeepAlivePingTTL + } + + if p.MetricsTimeout <= 0 { + p.MetricsTimeout = defaultMetricsTimeout + } + + return &iface{ + tick: p.ConnectionTTL, + idle: p.ConnectionIDLE, + + keepAlive: p.KeepAliveTTL, + pingTTL: p.KeepAlivePingTTL, + + metricsTimeout: p.MetricsTimeout, + + log: p.Logger, + grpc: struct { + globalMutex *sync.RWMutex + bookMutex *sync.RWMutex + connBook map[string]*connItem + }{ + globalMutex: new(sync.RWMutex), + bookMutex: new(sync.RWMutex), + connBook: make(map[string]*connItem), + }, + }, nil +} diff --git a/pkg/network/peers/peers_test.go b/pkg/network/peers/peers_test.go new file mode 100644 index 00000000..fc29228c --- /dev/null +++ b/pkg/network/peers/peers_test.go @@ -0,0 +1,211 @@ +package peers + +import ( + "context" + "encoding" + "encoding/json" + "net" + "strings" + "sync" + "testing" + + "github.com/multiformats/go-multiaddr" + "github.com/nspcc-dev/neofs-node/pkg/network" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" +) + +type ( + fakeAddress struct { + json.Marshaler + json.Unmarshaler + encoding.TextMarshaler + encoding.TextUnmarshaler + encoding.BinaryMarshaler + encoding.BinaryUnmarshaler + } + + // service is used to implement GreaterServer. + service struct{} +) + +// Hello is simple handler +func (*service) Hello(ctx context.Context, req *HelloRequest) (*HelloResponse, error) { + return &HelloResponse{ + Message: "Hello " + req.Name, + }, nil +} + +var _ multiaddr.Multiaddr = (*fakeAddress)(nil) + +func (fakeAddress) Equal(multiaddr.Multiaddr) bool { + return false +} + +func (fakeAddress) Bytes() []byte { + return nil +} + +func (fakeAddress) String() string { + return "fake" +} + +func (fakeAddress) Protocols() []multiaddr.Protocol { + return []multiaddr.Protocol{{Name: "fake"}} +} + +func (fakeAddress) Encapsulate(multiaddr.Multiaddr) multiaddr.Multiaddr { + panic("implement me") +} + +func (fakeAddress) Decapsulate(multiaddr.Multiaddr) multiaddr.Multiaddr { + panic("implement me") +} + +func (fakeAddress) ValueForProtocol(code int) (string, error) { + return "", nil +} + +const testCount = 10 + +func newTestAddress(t *testing.T) multiaddr.Multiaddr { + lis, err := net.Listen("tcp", "0.0.0.0:0") // nolint:gosec + require.NoError(t, err) + require.NoError(t, lis.Close()) + + l, ok := lis.(*net.TCPListener) + require.True(t, ok) + + _, port, err := net.SplitHostPort(l.Addr().String()) + require.NoError(t, err) + + items := []string{ + "ip4", + "127.0.0.1", + "tcp", + port, + } + + maddr, err := multiaddr.NewMultiaddr("/" + strings.Join(items, "/")) + require.NoError(t, err) + + return maddr +} + +func TestInterface(t *testing.T) { + t.Run("gRPC connection test", func(t *testing.T) { + var ( + err error + s Interface + h = &service{} + g = grpc.NewServer() + a1 = newTestAddress(t) + _ = h + done = make(chan struct{}) + ) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + s, err = New(Params{}) + require.NoError(t, err) + + RegisterGreeterServer(g, h) // register service + + l, err := network.Listen(a1) + require.NoError(t, err) + + defer l.Close() // nolint:golint + + wg := new(sync.WaitGroup) + wg.Add(1) + + go func() { + close(done) + + _ = g.Serve(l) + + wg.Done() + }() + + <-done // wait for server is start listening connections: + + // Fail connection + con, err := s.GRPCConnection(ctx, &fakeAddress{}) + require.Nil(t, con) + require.Error(t, err) + + con, err = s.GRPCConnection(ctx, a1) + require.NoError(t, err) + + cli := NewGreeterClient(con) + resp, err := cli.Hello(ctx, &HelloRequest{ + Name: "Interface test", + }) + require.NoError(t, err) + require.NotNil(t, resp) + require.Equal(t, "Hello Interface test", resp.Message) + + g.GracefulStop() + + wg.Wait() + }) + + t.Run("test grpc connections", func(t *testing.T) { + var ( + ifaces = make([]Interface, 0, testCount) + addresses = make([]multiaddr.Multiaddr, 0, testCount) + ) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + for i := 0; i < testCount; i++ { + addresses = append(addresses, newTestAddress(t)) + + s, err := New(Params{}) + require.NoError(t, err) + + lis, err := network.Listen(addresses[i]) + require.NoError(t, err) + + svc := &service{} + srv := grpc.NewServer() + + RegisterGreeterServer(srv, svc) + + ifaces = append(ifaces, s) + + go func() { + require.NoError(t, srv.Serve(lis)) + }() + } + + const reqName = "test" + wg := new(sync.WaitGroup) + + for i := 0; i < testCount; i++ { + for j := 0; j < testCount; j++ { + wg.Add(1) + go func(i, j int) { + defer wg.Done() + + con, err := ifaces[i].GRPCConnection(ctx, addresses[j]) + require.NoError(t, err) + + cli := NewGreeterClient(con) + + resp, err := cli.Hello(ctx, &HelloRequest{Name: reqName}) + require.NoError(t, err) + + require.Equal(t, "Hello "+reqName, resp.Message) + + require.NoError(t, con.Close()) + }(i, j) + + } + } + + wg.Wait() + }) +} diff --git a/lib/peers/peers_test.pb.go b/pkg/network/peers/peers_test.pb.go similarity index 89% rename from lib/peers/peers_test.pb.go rename to pkg/network/peers/peers_test.pb.go index aa6fe950de37e1ac55f2a9349099ed0ad610e111..0a26c5a04f671e367a38b4bbe6f675bd1e6cda6c 100644 GIT binary patch delta 597 zcmZ8eu}Z^G6h&=^yh59%gAP`ngF_*ZmzS8NgF>N`;NUD$k~EQ0ZA%PlEJ5%GR5A$S z;@}5pMi)2zfqp@8*Ud?M@4iK{9CFS*_nmXeeKJpOwkwn2p*9NQvvD+h?tGV44bvqK z^>oWK1JkhkD&9XBjN&OtQJOtASMso2D$7Mzet|xh{=aqGSI~Z)g-&7W!*btJan`G8i%80@YuWgzq+17D4CG0W zSzW8}V4DXAb|xzuJj7Bo$^^^cvH`6e)`1z}3?KB+gtk7%Hf1MecM8w>P$1_ANUMP_ z30Pb>h+1&Fr}Hfb(16=5cv)D9_$JF<7XoKQHWgWhK2q4gq=nx>*7@{}+f7kv1hNYnY delta 527 zcmZ8eJxjw-6h&*rJW89URq0TCAc8_6@4eJE8FX^$_OBx(ogRb8mF%a=7Q*ch0#aUxSCi-EO86x@ufqeLneI&^_*prtLKY zTUwl3GHzuzvz1x3Mh}m|vuMcGWU;KiNiKdOZ<)WKin`6~ty#+!9T9Tb?$RLa$UWKf zy5U81-aVduM|YBJcM!=T(M*#HCe$>4T6IK?vcA_wbO7rfpf74fQ;!96j;cw)oG047 z4l~zbR&6AS1mqpbPfFH{@a&+7n;RrADVZW!G0Ys%SYI@uM!IU>*hmalhABX9SRh+? ziQRx%*npl77}Rmb3*mD0VPMZv2}+$3lCVUtT{Lm*a4^zOV;BT*3233tu!LbS!)=0a mNf2NoqR-KQHwgSq(P1A2FH zc<<>I=m*0O!{Hm-k9+`m>{e|*U#=JZi}p(1=;7%3Y4dLF-VYBZlTm8Q-S5NMcG`|d zW>fiSndt;?cqDJY3s7b69JT1H>cB{^PI*s6-J4f)#u z5gEFg3aCr44Stn^N?n7WC{sBgskTrY=9Dfx%tZk~$(*UCY9eS7XWnjdvK-hI4+hj6 zyh>s)zb#@}O)oWqIc(CvkTH;&uW7HQSc9YxI3kheDB93^Q!B8Vl!=P#}MHq>s z8|n}*iV^sAIND4OPbJZ5(qTLG(!r2u0i@PoH~11j&`%ej4F zv^qDG^dKd?W<)YjrD|w9;Ixd;{kv!{VRmfSlZ-LbMC$8)ID{3e!ckAe7JvhWtVRoV^@n;wp})6_2v2d z$r+c(%H-A+X`*f6)_*2nCs#+{JbL=9dEFk$quJ5$?b@Z`!Hv`F-_zq$H={SpgZAL= z6T9E6TDyGMytKEkk9CI6TYKk5ly{=x=hn{N2#xM}CH=0ohqLKo5lIyfm$&zO8_y%r zm58wp=hTK6*ND}8HWi&=!i>{(9uVVzvw^V$UI9l}3{uvsBH#kq-YO`_EMPJX(c)a9 z$^sG4*Aje5h>A6c3N;#<0%wJq3_~dmfecUzB83D<`%cmTc98o-HGf+=fryWhYXt} zF;xsG@y-3e-OEwSsT`P6`iaFUpl@({R&rVeBu1sHq2O>CLwn$-!nwvdMpk3Yuv=c= zA)O(54*zZ%XMP#n6|P~<8EO>q?Q8HQeAW}q#7V7DlcR>c6bRWzetx7g#FAe_4u5E< zh5_6+atK<52jG`USLN{I{O%RMjeZ2r9qcagRvTA`$J-azw=Nvd{` return its owner + if token := req.GetSessionToken(); token != nil { + return token.GetOwnerID(), crypto.UnmarshalPublicKey(token.GetOwnerKey()), nil + } + + signKeys := req.GetSignKeyPairs() + if len(signKeys) == 0 { + return OwnerID{}, nil, errMissingSignatures + } + + firstKey := signKeys[0].GetPublicKey() + if firstKey == nil { + return OwnerID{}, nil, crypto.ErrEmptyPublicKey + } + + owner, err := refs.NewOwnerID(firstKey) + + return owner, firstKey, err +} + +// HeadersOfType returns request or object headers. +func (s serviceRequestInfo) HeadersOfType(typ eacl.HeaderType) ([]eacl.Header, bool) { + switch typ { + default: + return nil, true + case eacl.HdrTypeRequest: + return TypedHeaderSourceFromExtendedHeaders(s.req).HeadersOfType(typ) + case eacl.HdrTypeObjSys, eacl.HdrTypeObjUsr: + obj, ok := s.objHdrSrc.getHeaders() + if !ok { + return nil, false + } + + return TypedHeaderSourceFromObject(obj).HeadersOfType(typ) + } +} + +// Key returns a binary representation of sender public key. +func (s serviceRequestInfo) Key() []byte { + _, key, err := requestOwner(s.req) + if err != nil { + return nil + } + + return crypto.MarshalPublicKey(key) +} + +// TypeOf returns true of object request type corresponds to passed OperationType. +func (s serviceRequestInfo) OperationType() eacl.OperationType { + switch t := s.req.Type(); t { + case object.RequestGet: + return eacl.OpTypeGet + case object.RequestPut: + return eacl.OpTypePut + case object.RequestHead: + return eacl.OpTypeHead + case object.RequestSearch: + return eacl.OpTypeSearch + case object.RequestDelete: + return eacl.OpTypeDelete + case object.RequestRange: + return eacl.OpTypeRange + case object.RequestRangeHash: + return eacl.OpTypeRangeHash + default: + panic(fmt.Sprintf("unknown request type (serviceRequestInfo): %d", t)) + } +} + +// Group returns the access group of the request. +func (s serviceRequestInfo) Group() eacl.Group { + return s.group +} + +// CID returns the container identifier of request. +func (s serviceRequestInfo) CID() CID { + return s.req.CID() +} + +func (s requestObjHdrSrc) getHeaders() (*Object, bool) { + switch s.req.Type() { + case object.RequestSearch: + // object header filters is not supported in Search request now + return nil, true + case object.RequestPut: + // for Put we get object headers from request + return s.req.(transport.PutInfo).GetHead(), true + default: + tReq := &transportRequest{ + serviceRequest: s.req, + } + + // for other requests we get object headers from local storage + m, err := s.ls.Meta(tReq.GetAddress()) + if err == nil { + return m.GetObject(), true + } + + return nil, false + } +} + +type requestActionParams struct { + eaclSrc eaclstorage.Storage + + request serviceRequest + + objHdrSrc objectHeadersSource + + group eacl.Group +} + +func (s reqActionCalc) calculateRequestAction(ctx context.Context, p requestActionParams) eacl.Action { + // build eACL validator + validator, err := eaclcheck.NewValidator(p.eaclSrc, s.log) + if err != nil { + s.log.Warn("could not build eacl acl validator", + zap.String("error", err.Error()), + ) + + return eacl.ActionUnknown + } + + // create RequestInfo instance + reqInfo := &serviceRequestInfo{ + group: p.group, + req: p.request, + objHdrSrc: p.objHdrSrc, + } + + // calculate ACL action + return validator.CalculateAction(reqInfo) +} + +func (s aclInfoReceiver) getACLInfo(ctx context.Context, req serviceRequest) (*aclInfo, error) { + cnr, err := s.cnrStorage.Get(req.CID()) + if err != nil { + return nil, err + } + + rule := cnr.BasicACL() + + isBearer := rule.BearerAllowed(requestACLSection(req.Type())) + + // fetch group from the request + t := s.targetFinder.Target(ctx, req) + + return &aclInfo{ + rule: rule, + + checkExtended: !rule.Final(), + + targetInfo: t, + + checkBearer: t.group != eacl.GroupSystem && isBearer && req.GetBearerToken() != nil, + }, nil +} + +func (s eaclFromBearer) GetEACL(cid CID) (eaclstorage.Table, error) { + return eacl.UnmarshalTable(s.bearer.GetACLRules()) +} + +// returns true if request of type argument is allowed for IR needs (audit). +func allowedInnerRingRequest(t object.RequestType) (res bool) { + switch t { + case + object.RequestSearch, + object.RequestHead, + object.RequestRangeHash: + res = true + } + + return +} + +// returns the index number of request section bits. +func requestACLSection(t object.RequestType) uint8 { + switch t { + case object.RequestRangeHash: + return 0 + case object.RequestRange: + return 1 + case object.RequestSearch: + return 2 + case object.RequestDelete: + return 3 + case object.RequestPut: + return 4 + case object.RequestHead: + return 5 + case object.RequestGet: + return 6 + default: + panic(fmt.Sprintf("unknown request type (requestACLSection): %d", t)) + } +} + +type objectHeaderSource struct { + obj *Object +} + +type typedHeader struct { + n string + v string + t eacl.HeaderType +} + +type extendedHeadersWrapper struct { + hdrSrc service.ExtendedHeadersSource +} + +type typedExtendedHeader struct { + hdr service.ExtendedHeader +} + +func newTypedObjSysHdr(name, value string) eacl.TypedHeader { + return &typedHeader{ + n: name, + v: value, + t: eacl.HdrTypeObjSys, + } +} + +// Name is a name field getter. +func (s typedHeader) Name() string { + return s.n +} + +// Value is a value field getter. +func (s typedHeader) Value() string { + return s.v +} + +// HeaderType is a type field getter. +func (s typedHeader) HeaderType() eacl.HeaderType { + return s.t +} + +// TypedHeaderSourceFromObject wraps passed object and returns TypedHeaderSource interface. +func TypedHeaderSourceFromObject(obj *object.Object) extended.TypedHeaderSource { + return &objectHeaderSource{ + obj: obj, + } +} + +// HeaderOfType gathers object headers of passed type and returns Header list. +// +// If value of some header can not be calculated (e.g. nil eacl header), it does not appear in list. +// +// Always returns true. +func (s objectHeaderSource) HeadersOfType(typ eacl.HeaderType) ([]eacl.Header, bool) { + if s.obj == nil { + return nil, true + } + + var res []eacl.Header + + switch typ { + case eacl.HdrTypeObjUsr: + objHeaders := s.obj.GetHeaders() + + res = make([]eacl.Header, 0, len(objHeaders)) // 7 system header fields + + for i := range objHeaders { + if h := newTypedObjectExtendedHeader(objHeaders[i]); h != nil { + res = append(res, h) + } + } + case eacl.HdrTypeObjSys: + res = make([]eacl.Header, 0, 7) + + sysHdr := s.obj.GetSystemHeader() + + created := sysHdr.GetCreatedAt() + + res = append(res, + // ID + newTypedObjSysHdr( + eacl.HdrObjSysNameID, + sysHdr.ID.String(), + ), + + // CID + newTypedObjSysHdr( + eacl.HdrObjSysNameCID, + sysHdr.CID.String(), + ), + + // OwnerID + newTypedObjSysHdr( + eacl.HdrObjSysNameOwnerID, + sysHdr.OwnerID.String(), + ), + + // Version + newTypedObjSysHdr( + eacl.HdrObjSysNameVersion, + strconv.FormatUint(sysHdr.GetVersion(), 10), + ), + + // PayloadLength + newTypedObjSysHdr( + eacl.HdrObjSysNamePayloadLength, + strconv.FormatUint(sysHdr.GetPayloadLength(), 10), + ), + + // CreatedAt.UnitTime + newTypedObjSysHdr( + eacl.HdrObjSysNameCreatedUnix, + strconv.FormatUint(uint64(created.GetUnixTime()), 10), + ), + + // CreatedAt.Epoch + newTypedObjSysHdr( + eacl.HdrObjSysNameCreatedEpoch, + strconv.FormatUint(created.GetEpoch(), 10), + ), + ) + } + + return res, true +} + +func newTypedObjectExtendedHeader(h object.Header) eacl.TypedHeader { + val := h.GetValue() + if val == nil { + return nil + } + + res := new(typedHeader) + res.t = eacl.HdrTypeObjSys + + switch hdr := val.(type) { + case *object.Header_UserHeader: + if hdr.UserHeader == nil { + return nil + } + + res.t = eacl.HdrTypeObjUsr + res.n = hdr.UserHeader.GetKey() + res.v = hdr.UserHeader.GetValue() + case *object.Header_Link: + if hdr.Link == nil { + return nil + } + + switch hdr.Link.GetType() { + case object.Link_Previous: + res.n = eacl.HdrObjSysLinkPrev + case object.Link_Next: + res.n = eacl.HdrObjSysLinkNext + case object.Link_Child: + res.n = eacl.HdrObjSysLinkChild + case object.Link_Parent: + res.n = eacl.HdrObjSysLinkPar + case object.Link_StorageGroup: + res.n = eacl.HdrObjSysLinkSG + default: + return nil + } + + res.v = hdr.Link.ID.String() + default: + return nil + } + + return res +} + +// TypedHeaderSourceFromExtendedHeaders wraps passed ExtendedHeadersSource and returns TypedHeaderSource interface. +func TypedHeaderSourceFromExtendedHeaders(hdrSrc service.ExtendedHeadersSource) extended.TypedHeaderSource { + return &extendedHeadersWrapper{ + hdrSrc: hdrSrc, + } +} + +// Name returns the result of Key method. +func (s typedExtendedHeader) Name() string { + return s.hdr.Key() +} + +// Value returns the result of Value method. +func (s typedExtendedHeader) Value() string { + return s.hdr.Value() +} + +// HeaderType always returns HdrTypeRequest. +func (s typedExtendedHeader) HeaderType() eacl.HeaderType { + return eacl.HdrTypeRequest +} + +// TypedHeaders gathers eacl request headers and returns TypedHeader list. +// +// Nil headers are ignored. +// +// Always returns true. +func (s extendedHeadersWrapper) HeadersOfType(typ eacl.HeaderType) ([]eacl.Header, bool) { + if s.hdrSrc == nil { + return nil, true + } + + var res []eacl.Header + + if typ == eacl.HdrTypeRequest { + hs := s.hdrSrc.ExtendedHeaders() + + res = make([]eacl.Header, 0, len(hs)) + + for i := range hs { + if hs[i] == nil { + continue + } + + res = append(res, &typedExtendedHeader{ + hdr: hs[i], + }) + } + } + + return res, true +} + +func isContainerOwner(storage storage.Storage, cid CID, ownerID OwnerID) (bool, error) { + cnr, err := storage.Get(cid) + if err != nil { + return false, err + } + + return cnr.OwnerID().Equal(ownerID), nil +} diff --git a/services/public/object/acl_test.go b/pkg/network/transport/object/grpc/acl_test.go similarity index 58% rename from services/public/object/acl_test.go rename to pkg/network/transport/object/grpc/acl_test.go index 05279137..ca4ebe7b 100644 --- a/services/public/object/acl_test.go +++ b/pkg/network/transport/object/grpc/acl_test.go @@ -6,17 +6,18 @@ import ( "errors" "testing" - "github.com/nspcc-dev/neofs-api-go/acl" - "github.com/nspcc-dev/neofs-api-go/bootstrap" + eacl "github.com/nspcc-dev/neofs-api-go/acl/extended" "github.com/nspcc-dev/neofs-api-go/container" "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" crypto "github.com/nspcc-dev/neofs-crypto" - libacl "github.com/nspcc-dev/neofs-node/lib/acl" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/ir" - "github.com/nspcc-dev/neofs-node/lib/test" + libcnr "github.com/nspcc-dev/neofs-node/pkg/core/container" + "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic" + "github.com/nspcc-dev/neofs-node/pkg/core/container/storage" + "github.com/nspcc-dev/neofs-node/pkg/core/netmap" + testlogger "github.com/nspcc-dev/neofs-node/pkg/util/logger/test" + "github.com/nspcc-dev/neofs-node/pkg/util/test" "github.com/stretchr/testify/require" ) @@ -25,11 +26,9 @@ type ( // Set of interfaces which testCommonEntity must implement, but some methods from those does not call. serviceRequest RequestTargeter - implementations.ACLHelper - implementations.ContainerNodesLister - implementations.ContainerOwnerChecker - acl.ExtendedACLTable - libacl.RequestInfo + eacl.Table + storage.Storage + containerNodesLister // Argument interceptor. Used for ascertain of correct parameter passage between components. f func(...interface{}) @@ -41,8 +40,6 @@ type ( ) type testBasicChecker struct { - libacl.BasicChecker - actionErr error action bool @@ -53,68 +50,28 @@ type testBasicChecker struct { bearer bool } -func (t *testACLEntity) calculateRequestAction(context.Context, requestActionParams) acl.ExtendedACLAction { - return t.res.(acl.ExtendedACLAction) -} - -func (t *testACLEntity) buildRequestInfo(req serviceRequest, target acl.Target) (libacl.RequestInfo, error) { - if t.f != nil { - t.f(req, target) - } - +func (t *testACLEntity) Get(cid storage.CID) (*storage.Container, error) { if t.err != nil { return nil, t.err } - return t.res.(libacl.RequestInfo), nil + return t.res.(*storage.Container), nil } -func (t *testACLEntity) Action(table acl.ExtendedACLTable, req libacl.RequestInfo) acl.ExtendedACLAction { - if t.f != nil { - t.f(table, req) - } - - return t.res.(acl.ExtendedACLAction) +func (t *testACLEntity) calculateRequestAction(context.Context, requestActionParams) eacl.Action { + return t.res.(eacl.Action) } -func (t *testACLEntity) GetExtendedACLTable(_ context.Context, cid CID) (acl.ExtendedACLTable, error) { - if t.f != nil { - t.f(cid) - } - - if t.err != nil { - return nil, t.err - } - - return t.res.(acl.ExtendedACLTable), nil -} - -func (s *testBasicChecker) Extended(uint32) bool { - return s.extended -} - -func (s *testBasicChecker) Sticky(uint32) bool { - return s.sticky -} - -func (s *testBasicChecker) Bearer(uint32, object.RequestType) (bool, error) { - return s.bearer, nil -} - -func (s *testBasicChecker) Action(uint32, object.RequestType, acl.Target) (bool, error) { - return s.action, s.actionErr -} - -func (t *testACLEntity) GetBasicACL(context.Context, CID) (uint32, error) { +func (t *testACLEntity) GetBasicACL(context.Context, CID) (libcnr.BasicACL, error) { if t.err != nil { return 0, t.err } - return t.res.(uint32), nil + return t.res.(libcnr.BasicACL), nil } -func (t *testACLEntity) Target(context.Context, serviceRequest) acl.Target { - return t.res.(acl.Target) +func (t *testACLEntity) Target(context.Context, serviceRequest) requestTarget { + return t.res.(requestTarget) } func (t *testACLEntity) CID() CID { return CID{} } @@ -131,34 +88,20 @@ func (t *testACLEntity) GetOwner() (*ecdsa.PublicKey, error) { return t.res.(*ecdsa.PublicKey), nil } -func (t testACLEntity) GetIRInfo(ir.GetInfoParams) (*ir.GetInfoResult, error) { +func (t testACLEntity) InnerRingKeys() ([][]byte, error) { if t.err != nil { return nil, t.err } - res := new(ir.GetInfoResult) - res.SetInfo(*t.res.(*ir.Info)) - - return res, nil + return t.res.([][]byte), nil } -func (t *testACLEntity) ContainerNodesInfo(ctx context.Context, cid CID, prev int) ([]bootstrap.NodeInfo, error) { +func (t *testACLEntity) ContainerNodesInfo(ctx context.Context, cid CID, prev int) ([]netmap.Info, error) { if t.err != nil { return nil, t.err } - return t.res.([][]bootstrap.NodeInfo)[prev], nil -} - -func (t *testACLEntity) IsContainerOwner(_ context.Context, cid CID, owner OwnerID) (bool, error) { - if t.f != nil { - t.f(cid, owner) - } - if t.err != nil { - return false, t.err - } - - return t.res.(bool), nil + return t.res.([][]netmap.Info)[prev], nil } func (t testACLEntity) GetSignKeyPairs() []service.SignKeyPair { @@ -178,35 +121,45 @@ func TestPreprocessor(t *testing.T) { }) t.Run("everything is okay", func(t *testing.T) { - rule := uint32(0x00000003) - // set F-bit - rule |= 1 << 28 + var rule basic.ACL + rule.SetFinal() + rule.AllowOthers(requestACLSection(object.RequestGet)) - checker := new(libacl.BasicACLChecker) + cnr := new(storage.Container) + cnr.SetBasicACL(rule) + + reqTarget := requestTarget{ + group: eacl.GroupOthers, + } preprocessor := aclPreProcessor{ - log: test.NewTestLogger(false), + log: testlogger.NewLogger(false), aclInfoReceiver: aclInfoReceiver{ - basicACLGetter: &testACLEntity{res: rule}, - basicChecker: checker, - targetFinder: &testACLEntity{res: acl.Target_Others}, + cnrStorage: &testACLEntity{ + res: cnr, + }, + targetFinder: &testACLEntity{res: reqTarget}, }, - basicChecker: checker, } require.NoError(t, preprocessor.preProcess(ctx, &testACLEntity{res: object.RequestGet})) - preprocessor.aclInfoReceiver.targetFinder = &testACLEntity{res: acl.Target_System} - require.Error(t, preprocessor.preProcess(ctx, &testACLEntity{res: object.RequestGet})) - preprocessor.aclInfoReceiver.targetFinder = &testACLEntity{res: acl.Target_User} + reqTarget.group = eacl.GroupSystem + preprocessor.aclInfoReceiver.targetFinder = &testACLEntity{res: reqTarget} + require.NoError(t, preprocessor.preProcess(ctx, &testACLEntity{res: object.RequestGet})) + + reqTarget.group = eacl.GroupUser + preprocessor.aclInfoReceiver.targetFinder = &testACLEntity{res: reqTarget} require.Error(t, preprocessor.preProcess(ctx, &testACLEntity{res: object.RequestGet})) }) t.Run("can't fetch container", func(t *testing.T) { preprocessor := aclPreProcessor{ - log: test.NewTestLogger(false), + log: testlogger.NewLogger(false), aclInfoReceiver: aclInfoReceiver{ - basicACLGetter: &testACLEntity{err: container.ErrNotFound}, - targetFinder: &testACLEntity{res: acl.Target_Others}, + cnrStorage: &testACLEntity{err: container.ErrNotFound}, + targetFinder: &testACLEntity{res: requestTarget{ + group: eacl.GroupOthers, + }}, }, } require.Error(t, preprocessor.preProcess(ctx, &testACLEntity{res: object.RequestGet})) @@ -214,24 +167,28 @@ func TestPreprocessor(t *testing.T) { }) t.Run("sticky bit", func(t *testing.T) { - checker := &testBasicChecker{ - actionErr: nil, - action: true, - sticky: true, + var rule basic.ACL + rule.SetSticky() + rule.SetFinal() + for i := uint8(0); i < 7; i++ { + rule.AllowUser(i) } + cnr := new(storage.Container) + cnr.SetBasicACL(rule) + s := &aclPreProcessor{ - log: test.NewTestLogger(false), + log: testlogger.NewLogger(false), aclInfoReceiver: aclInfoReceiver{ - basicACLGetter: &testACLEntity{ - res: uint32(0), + cnrStorage: &testACLEntity{ + res: cnr, }, - basicChecker: checker, targetFinder: &testACLEntity{ - res: acl.Target_User, + res: requestTarget{ + group: eacl.GroupUser, + }, }, }, - basicChecker: checker, } ownerKey := &test.DecodeKey(0).PublicKey @@ -354,8 +311,10 @@ func TestPreprocessor(t *testing.T) { } }) - t.Run("extended ACL", func(t *testing.T) { - target := acl.Target_Others + t.Run("eacl ACL", func(t *testing.T) { + target := requestTarget{ + group: eacl.GroupOthers, + } req := &testACLEntity{ res: object.RequestGet, @@ -363,35 +322,78 @@ func TestPreprocessor(t *testing.T) { actCalc := new(testACLEntity) - checker := &testBasicChecker{ - action: true, - extended: true, - } + var rule basic.ACL + rule.AllowOthers(requestACLSection(object.RequestGet)) + + cnr := new(storage.Container) + cnr.SetBasicACL(rule) s := &aclPreProcessor{ - log: test.NewTestLogger(false), + log: testlogger.NewLogger(false), aclInfoReceiver: aclInfoReceiver{ - basicACLGetter: &testACLEntity{ - res: uint32(1), + cnrStorage: &testACLEntity{ + res: cnr, }, - basicChecker: checker, targetFinder: &testACLEntity{ res: target, }, }, - basicChecker: checker, reqActionCalc: actCalc, } // force to return non-ActionAllow - actCalc.res = acl.ActionAllow + 1 + actCalc.res = eacl.ActionAllow + 1 require.EqualError(t, s.preProcess(ctx, req), errAccessDenied.Error()) // force to return ActionAllow - actCalc.res = acl.ActionAllow + actCalc.res = eacl.ActionAllow require.NoError(t, s.preProcess(ctx, req)) }) + + t.Run("inner ring group", func(t *testing.T) { + reqTarget := requestTarget{ + group: eacl.GroupSystem, + ir: true, + } + + cnr := new(storage.Container) + cnr.SetBasicACL(basic.FromUint32(^uint32(0))) + + preprocessor := aclPreProcessor{ + log: testlogger.NewLogger(false), + aclInfoReceiver: aclInfoReceiver{ + cnrStorage: &testACLEntity{res: cnr}, + targetFinder: &testACLEntity{res: reqTarget}, + }, + } + + for _, rt := range []object.RequestType{ + object.RequestSearch, + object.RequestHead, + object.RequestRangeHash, + } { + require.NoError(t, + preprocessor.preProcess(ctx, &testACLEntity{ + res: rt, + }), + ) + } + + for _, rt := range []object.RequestType{ + object.RequestRange, + object.RequestPut, + object.RequestDelete, + object.RequestGet, + } { + require.EqualError(t, + preprocessor.preProcess(ctx, &testACLEntity{ + res: rt, + }), + errAccessDenied.Error(), + ) + } + }) } func TestTargetFinder(t *testing.T) { @@ -400,19 +402,23 @@ func TestTargetFinder(t *testing.T) { containerKey := test.DecodeKey(3) prevContainerKey := test.DecodeKey(4) - irInfo := new(ir.Info) - irNode := ir.Node{} - irNode.SetKey(crypto.MarshalPublicKey(&irKey.PublicKey)) - irInfo.SetNodes([]ir.Node{irNode}) + var infoList1 []netmap.Info + info := netmap.Info{} + info.SetPublicKey(crypto.MarshalPublicKey(&containerKey.PublicKey)) + infoList1 = append(infoList1, info) + + var infoList2 []netmap.Info + info.SetPublicKey(crypto.MarshalPublicKey(&prevContainerKey.PublicKey)) + infoList2 = append(infoList2, info) finder := &targetFinder{ - log: test.NewTestLogger(false), - irStorage: &testACLEntity{ - res: irInfo, + log: testlogger.NewLogger(false), + irKeysRecv: &testACLEntity{ + res: [][]byte{crypto.MarshalPublicKey(&irKey.PublicKey)}, }, - cnrLister: &testACLEntity{res: [][]bootstrap.NodeInfo{ - {{PubKey: crypto.MarshalPublicKey(&containerKey.PublicKey)}}, - {{PubKey: crypto.MarshalPublicKey(&prevContainerKey.PublicKey)}}, + cnrLister: &testACLEntity{res: [][]netmap.Info{ + infoList1, + infoList2, }}, } @@ -434,79 +440,136 @@ func TestTargetFinder(t *testing.T) { req.SetToken(token) req.AddSignKey(nil, pk) - finder.cnrOwnerChecker = &testACLEntity{ - f: func(items ...interface{}) { - require.Equal(t, req.CID(), items[0]) - require.Equal(t, owner, items[1]) - }, - res: true, + cnr := new(storage.Container) + cnr.SetOwnerID(owner) + + finder.cnrStorage = &testACLEntity{ + res: cnr, } - require.Equal(t, acl.Target_User, finder.Target(ctx, req)) + require.Equal(t, + requestTarget{ + group: eacl.GroupUser, + }, + finder.Target(ctx, req), + ) }) t.Run("container owner", func(t *testing.T) { - finder.cnrOwnerChecker = &testACLEntity{res: true} + key := &test.DecodeKey(0).PublicKey + owner, err := refs.NewOwnerID(key) + require.NoError(t, err) + + cnr := new(storage.Container) + cnr.SetOwnerID(owner) + + finder.cnrStorage = &testACLEntity{res: cnr} req := new(object.SearchRequest) - req.AddSignKey(nil, &test.DecodeKey(0).PublicKey) + req.AddSignKey(nil, key) - require.Equal(t, acl.Target_User, finder.Target(ctx, req)) + require.Equal(t, + requestTarget{ + group: eacl.GroupUser, + }, + finder.Target(ctx, req), + ) }) t.Run("system owner", func(t *testing.T) { - finder.cnrOwnerChecker = &testACLEntity{res: false} + finder.cnrStorage = &testACLEntity{res: new(storage.Container)} req := new(object.SearchRequest) req.AddSignKey(nil, &irKey.PublicKey) - require.Equal(t, acl.Target_System, finder.Target(ctx, req)) + require.Equal(t, + requestTarget{ + group: eacl.GroupSystem, + ir: true, + }, + finder.Target(ctx, req), + ) req = new(object.SearchRequest) req.AddSignKey(nil, &containerKey.PublicKey) - require.Equal(t, acl.Target_System, finder.Target(ctx, req)) + require.Equal(t, + requestTarget{ + group: eacl.GroupSystem, + }, + finder.Target(ctx, req), + ) req = new(object.SearchRequest) req.AddSignKey(nil, &prevContainerKey.PublicKey) - require.Equal(t, acl.Target_System, finder.Target(ctx, req)) + require.Equal(t, + requestTarget{ + group: eacl.GroupSystem, + }, + finder.Target(ctx, req), + ) }) t.Run("other owner", func(t *testing.T) { - finder.cnrOwnerChecker = &testACLEntity{res: false} + finder.cnrStorage = &testACLEntity{res: new(storage.Container)} req := new(object.SearchRequest) req.AddSignKey(nil, &test.DecodeKey(0).PublicKey) - require.Equal(t, acl.Target_Others, finder.Target(ctx, req)) + require.Equal(t, + requestTarget{ + group: eacl.GroupOthers, + }, + finder.Target(ctx, req), + ) }) t.Run("can't fetch request owner", func(t *testing.T) { req := new(object.SearchRequest) - require.Equal(t, acl.Target_Unknown, finder.Target(ctx, req)) + require.Equal(t, + requestTarget{ + group: eacl.GroupUnknown, + }, + finder.Target(ctx, req), + ) }) t.Run("can't fetch container", func(t *testing.T) { - finder.cnrOwnerChecker = &testACLEntity{err: container.ErrNotFound} + finder.cnrStorage = &testACLEntity{err: container.ErrNotFound} req := new(object.SearchRequest) req.AddSignKey(nil, &test.DecodeKey(0).PublicKey) - require.Equal(t, acl.Target_Unknown, finder.Target(ctx, req)) + require.Equal(t, + requestTarget{ + group: eacl.GroupUnknown, + }, + finder.Target(ctx, req), + ) }) t.Run("can't fetch ir list", func(t *testing.T) { - finder.cnrOwnerChecker = &testACLEntity{res: false} - finder.irStorage = &testACLEntity{err: errors.New("blockchain is busy")} + finder.cnrStorage = &testACLEntity{res: new(storage.Container)} + finder.irKeysRecv = &testACLEntity{err: errors.New("blockchain is busy")} req := new(object.SearchRequest) req.AddSignKey(nil, &test.DecodeKey(0).PublicKey) - require.Equal(t, acl.Target_Unknown, finder.Target(ctx, req)) + require.Equal(t, + requestTarget{ + group: eacl.GroupUnknown, + }, + finder.Target(ctx, req), + ) }) t.Run("can't fetch container list", func(t *testing.T) { - finder.cnrOwnerChecker = &testACLEntity{res: false} + finder.cnrStorage = &testACLEntity{res: new(storage.Container)} finder.cnrLister = &testACLEntity{err: container.ErrNotFound} req := new(object.SearchRequest) req.AddSignKey(nil, &test.DecodeKey(0).PublicKey) - require.Equal(t, acl.Target_Unknown, finder.Target(ctx, req)) + require.Equal(t, + requestTarget{ + group: eacl.GroupUnknown, + }, + finder.Target(ctx, req), + ) }) } diff --git a/services/public/object/bearer.go b/pkg/network/transport/object/grpc/bearer.go similarity index 89% rename from services/public/object/bearer.go rename to pkg/network/transport/object/grpc/bearer.go index ec01dc58..019d3db6 100644 --- a/services/public/object/bearer.go +++ b/pkg/network/transport/object/grpc/bearer.go @@ -5,7 +5,7 @@ import ( "github.com/nspcc-dev/neofs-api-go/service" crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-node/lib/implementations" + "github.com/nspcc-dev/neofs-node/pkg/core/container/storage" "github.com/pkg/errors" ) @@ -22,7 +22,7 @@ type bearerActualityVerifier struct { } type bearerOwnershipVerifier struct { - cnrOwnerChecker implementations.ContainerOwnerChecker + cnrStorage storage.Storage } type bearerSignatureVerifier struct{} @@ -54,7 +54,7 @@ func (s bearerActualityVerifier) verifyBearerToken(_ context.Context, _ CID, tok } func (s bearerOwnershipVerifier) verifyBearerToken(ctx context.Context, cid CID, token service.BearerToken) error { - isOwner, err := s.cnrOwnerChecker.IsContainerOwner(ctx, cid, token.GetOwnerID()) + isOwner, err := isContainerOwner(s.cnrStorage, cid, token.GetOwnerID()) if err != nil { return err } else if !isOwner { diff --git a/services/public/object/capacity.go b/pkg/network/transport/object/grpc/capacity.go similarity index 100% rename from services/public/object/capacity.go rename to pkg/network/transport/object/grpc/capacity.go diff --git a/services/public/object/capacity_test.go b/pkg/network/transport/object/grpc/capacity_test.go similarity index 96% rename from services/public/object/capacity_test.go rename to pkg/network/transport/object/grpc/capacity_test.go index deb34afb..7054f44c 100644 --- a/services/public/object/capacity_test.go +++ b/pkg/network/transport/object/grpc/capacity_test.go @@ -3,7 +3,7 @@ package object import ( "testing" - "github.com/nspcc-dev/neofs-node/lib/localstore" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" "github.com/stretchr/testify/require" ) diff --git a/services/public/object/delete.go b/pkg/network/transport/object/grpc/delete.go similarity index 93% rename from services/public/object/delete.go rename to pkg/network/transport/object/grpc/delete.go index 8e8c5e2a..e333d258 100644 --- a/services/public/object/delete.go +++ b/pkg/network/transport/object/grpc/delete.go @@ -8,9 +8,9 @@ import ( "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/service" "github.com/nspcc-dev/neofs-api-go/session" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/transformer" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transformer" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport/storagegroup" "github.com/pkg/errors" "go.uber.org/zap" ) @@ -139,8 +139,8 @@ func (s *coreObjRemover) delete(ctx context.Context, dInfo deleteInfo) error { ctx = contextWithValues(ctx, transformer.PrivateSessionToken, pToken, transformer.PublicSessionToken, token, - implementations.BearerToken, dInfo.GetBearerToken(), - implementations.ExtendedHeaders, dInfo.ExtendedHeaders(), + storagegroup.BearerToken, dInfo.GetBearerToken(), + storagegroup.ExtendedHeaders, dInfo.ExtendedHeaders(), ) for i := range deleteList { @@ -172,8 +172,8 @@ func (s *coreDelPreparer) prepare(ctx context.Context, src deleteInfo) ([]delete ctx = contextWithValues(ctx, transformer.PublicSessionToken, src.GetSessionToken(), - implementations.BearerToken, bearer, - implementations.ExtendedHeaders, extHdrs, + storagegroup.BearerToken, bearer, + storagegroup.ExtendedHeaders, extHdrs, ) children := s.childLister.children(ctx, addr) diff --git a/services/public/object/delete_test.go b/pkg/network/transport/object/grpc/delete_test.go similarity index 95% rename from services/public/object/delete_test.go rename to pkg/network/transport/object/grpc/delete_test.go index c954a7c3..af4e8cf7 100644 --- a/services/public/object/delete_test.go +++ b/pkg/network/transport/object/grpc/delete_test.go @@ -8,10 +8,9 @@ import ( "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/service" "github.com/nspcc-dev/neofs-api-go/session" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/rand" - "github.com/nspcc-dev/neofs-node/lib/transformer" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transformer" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/nspcc-dev/neofs-node/pkg/util/rand" "github.com/pkg/errors" "github.com/stretchr/testify/require" "go.uber.org/zap" @@ -121,7 +120,7 @@ func Test_objectService_Delete(t *testing.T) { req := &object.DeleteRequest{Address: testObjectAddress(t)} t.Run("handler error", func(t *testing.T) { - rhErr := internal.Error("test error for request handler") + rhErr := errors.New("test error for request handler") s := &objectService{ statusCalculator: newStatusCalculator(), @@ -182,7 +181,7 @@ func Test_coreObjRemover_delete(t *testing.T) { }) t.Run("prepare error", func(t *testing.T) { - dpErr := internal.Error("test error for delete preparer") + dpErr := errors.New("test error for delete preparer") dp := &testDeleteEntity{ f: func(items ...interface{}) { @@ -205,7 +204,7 @@ func Test_coreObjRemover_delete(t *testing.T) { // ascertain that error returns as expected require.EqualError(t, s.delete(ctx, req), dpErr.Error()) - dp.err = internal.Error("some other error") + dp.err = errors.New("some other error") // ascertain that error returns as expected require.EqualError(t, s.delete(ctx, req), errDeletePrepare.Error()) @@ -220,7 +219,7 @@ func Test_coreObjRemover_delete(t *testing.T) { dInfo, } - srErr := internal.Error("test error for straight remover") + srErr := errors.New("test error for straight remover") s := &coreObjRemover{ delPrep: &testDeleteEntity{ @@ -354,7 +353,7 @@ func Test_straightObjRemover_delete(t *testing.T) { req.setSessionToken(token) t.Run("correct result", func(t *testing.T) { - osErr := internal.Error("test error for object storer") + osErr := errors.New("test error for object storer") s := &straightObjRemover{ tombCreator: &testDeleteEntity{ diff --git a/pkg/network/transport/object/grpc/eacl/validator.go b/pkg/network/transport/object/grpc/eacl/validator.go new file mode 100644 index 00000000..d7ef4eb8 --- /dev/null +++ b/pkg/network/transport/object/grpc/eacl/validator.go @@ -0,0 +1,244 @@ +package eacl + +import ( + "bytes" + + eacl "github.com/nspcc-dev/neofs-api-go/acl/extended" + "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended" + "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage" + "github.com/nspcc-dev/neofs-node/pkg/util/logger" + "go.uber.org/zap" +) + +// RequestInfo represents the information +// about the request. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended.RequestInfo. +type RequestInfo = extended.RequestInfo + +// Action represents action on the request. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended.Action. +type Action = eacl.Action + +// Storage represents the eACL table storage. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage.Storage. +type Storage = storage.Storage + +// Target represents authorization group. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-api-go/acl/extended.Target. +type Target = eacl.Target + +// Table represents extended ACL rule table. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-api-go/acl/extended.ExtendedACLTable. +type Table = eacl.Table + +// HeaderFilter represents the header filter. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-api-go/acl/extended.HeaderFilter. +type HeaderFilter = eacl.HeaderFilter + +// Header represents the string +// key-value header. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended.Header. +type Header = extended.Header + +// MatchType represents value match type. +// +// It is a type alias of +// github.com/nspcc-dev/neofs-api-go/acl/extended.MatchType. +type MatchType = eacl.MatchType + +// Validator is a tool that calculates +// the action on a request according +// to the extended ACL rule table. +// +// Validator receives eACL table from +// the eACL storage. +type Validator struct { + logger *logger.Logger // logging component + + storage Storage // eACL table storage +} + +// NewValidator creates and initializes a new Validator using arguments. +// +// Returns an error if some of the arguments is nil. +// +// Using the Validator that has been created with new(Validator) +// expression (or just declaring a Validator variable) is unsafe +// and can lead to panic. +func NewValidator(st Storage, lg *logger.Logger) (*Validator, error) { + switch { + case st == nil: + return nil, storage.ErrNilStorage + case lg == nil: + return nil, logger.ErrNilLogger + } + + return &Validator{ + logger: lg, + storage: st, + }, nil +} + +// CalculateAction calculates action on the request according +// to its information. +// +// The action is calculated according to the application of +// eACL table of rules to the request. +// +// If request info argument is nil, eacl.ActionUnknown is +// returned immediately. +// +// If the eACL table is not available at the time of the call, +// eacl.ActionUnknown is returned. +// +// If no matching table entry is found, ActionAllow is returned. +func (v *Validator) CalculateAction(info RequestInfo) Action { + if info == nil { + return eacl.ActionUnknown + } + + // get container identifier from request + cid := info.CID() + + // get eACL table by container ID + table, err := v.storage.GetEACL(cid) + if err != nil { + v.logger.Error("could not get eACL table", + zap.Stringer("cid", cid), + zap.String("error", err.Error()), + ) + + return eacl.ActionUnknown + } + + return tableAction(info, table) +} + +// calculates action on the request based on the eACL rules. +func tableAction(info RequestInfo, table Table) Action { + requestOpType := info.OperationType() + + for _, record := range table.Records() { + // check type of operation + if record.OperationType() != requestOpType { + continue + } + + // check target + if !targetMatches(info, record.TargetList()) { + continue + } + + // check headers + switch val := matchFilters(info, record.HeaderFilters()); { + case val < 0: + // headers of some type could not be composed => allow + return eacl.ActionAllow + case val == 0: + return record.Action() + } + } + + return eacl.ActionAllow +} + +// returns: +// - positive value if no matching header is found for at least one filter; +// - zero if at least one suitable header is found for all filters; +// - negative value if the headers of at least one filter cannot be obtained. +func matchFilters(info extended.TypedHeaderSource, filters []HeaderFilter) int { + matched := 0 + + for _, filter := range filters { + // prevent NPE + if filter == nil { + continue + } + + headers, ok := info.HeadersOfType(filter.HeaderType()) + if !ok { + return -1 + } + + // get headers of filtering type + for _, header := range headers { + // prevent NPE + if header == nil { + continue + } + + // check header name + if header.Name() != filter.Name() { + continue + } + + // get match function + matchFn, ok := mMatchFns[filter.MatchType()] + if !ok { + continue + } + + // check match + if !matchFn(header, filter) { + continue + } + + // increment match counter + matched++ + + break + } + } + + return len(filters) - matched +} + +// returns true if one of ExtendedACLTarget has +// suitable target OR suitable public key. +func targetMatches(req RequestInfo, groups []Target) bool { + requestKey := req.Key() + + for _, target := range groups { + recordGroup := target.Group() + + // check public key match + for _, key := range target.KeyList() { + if bytes.Equal(key, requestKey) { + return true + } + } + + // check target group match + if req.Group() == recordGroup { + return true + } + } + + return false +} + +// Maps MatchType to corresponding function. +// 1st argument of function - header value, 2nd - header filter. +var mMatchFns = map[MatchType]func(Header, Header) bool{ + eacl.StringEqual: func(header Header, filter Header) bool { + return header.Value() == filter.Value() + }, + + eacl.StringNotEqual: func(header Header, filter Header) bool { + return header.Value() != filter.Value() + }, +} diff --git a/services/public/object/execution.go b/pkg/network/transport/object/grpc/execution.go similarity index 94% rename from services/public/object/execution.go rename to pkg/network/transport/object/grpc/execution.go index a8880930..46c5763b 100644 --- a/services/public/object/execution.go +++ b/pkg/network/transport/object/grpc/execution.go @@ -9,12 +9,11 @@ import ( "github.com/nspcc-dev/neofs-api-go/hash" "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/placement" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/replication/storage" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" "github.com/pkg/errors" "go.uber.org/zap" ) @@ -59,7 +58,7 @@ type ( prevPlacementBuilder placementBuilder interceptorPreparer interceptorPreparer workerPool WorkerPool - traverseExec implementations.ContainerTraverseExecutor + traverseExec transport.ContainerTraverseExecutor resLogger resultLogger log *zap.Logger } @@ -133,7 +132,7 @@ type ( coreInterceptorPreparer struct { localExec operationExecutor - addressStore implementations.AddressStore + addressStore storage.AddressStore } resultItems struct { @@ -155,11 +154,9 @@ type ( } ) -const ( - errIncompleteOperation = internal.Error("operation is not completed") +const emRangeReadFail = "could not read %d range data" - emRangeReadFail = "could not read %d range data" -) +var errIncompleteOperation = errors.New("operation is not completed") var ( _ resultTracker = (*idleResultTracker)(nil) @@ -276,7 +273,7 @@ func (s *coreOperationFinalizer) completeExecution(ctx context.Context, p operat ctx, cancel := context.WithCancel(ctx) defer cancel() - s.traverseExec.Execute(ctx, implementations.TraverseParams{ + s.traverseExec.Execute(ctx, transport.TraverseParams{ TransportInfo: p.metaInfo, Handler: handler, Traverser: traverser, @@ -446,7 +443,7 @@ func (s *localStoreExecutor) headObject(_ context.Context, addr Address) (*Objec m, err := s.localStore.Meta(addr) if err != nil { switch errors.Cause(err) { - case core.ErrNotFound: + case bucket.ErrNotFound: return nil, errIncompleteOperation default: return nil, err @@ -460,7 +457,7 @@ func (s *localStoreExecutor) getObject(_ context.Context, addr Address) (*Object obj, err := s.localStore.Get(addr) if err != nil { switch errors.Cause(err) { - case core.ErrNotFound: + case bucket.ErrNotFound: return nil, errIncompleteOperation default: return nil, err diff --git a/services/public/object/execution_test.go b/pkg/network/transport/object/grpc/execution_test.go similarity index 90% rename from services/public/object/execution_test.go rename to pkg/network/transport/object/grpc/execution_test.go index 81af16a6..4620c2f2 100644 --- a/services/public/object/execution_test.go +++ b/pkg/network/transport/object/grpc/execution_test.go @@ -11,11 +11,10 @@ import ( "github.com/nspcc-dev/neofs-api-go/hash" "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/replication/storage" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" "github.com/pkg/errors" "github.com/stretchr/testify/require" "go.uber.org/zap" @@ -47,26 +46,25 @@ func (s *testExecutionEntity) HandleResult(_ context.Context, n multiaddr.Multia } var ( - _ transport.ResultHandler = (*testExecutionEntity)(nil) - _ interceptorPreparer = (*testExecutionEntity)(nil) - _ implementations.ContainerTraverseExecutor = (*testExecutionEntity)(nil) - _ WorkerPool = (*testExecutionEntity)(nil) - _ operationExecutor = (*testExecutionEntity)(nil) - _ placementBuilder = (*testExecutionEntity)(nil) - _ implementations.AddressStore = (*testExecutionEntity)(nil) - _ executionParamsComputer = (*testExecutionEntity)(nil) - _ operationFinalizer = (*testExecutionEntity)(nil) - _ EpochReceiver = (*testExecutionEntity)(nil) - _ localstore.Localstore = (*testExecutionEntity)(nil) - _ containerTraverser = (*testExecutionEntity)(nil) - _ responseItemHandler = (*testExecutionEntity)(nil) - _ resultTracker = (*testExecutionEntity)(nil) - _ localObjectStorer = (*testExecutionEntity)(nil) - _ localFullObjectReceiver = (*testExecutionEntity)(nil) - _ localHeadReceiver = (*testExecutionEntity)(nil) - _ localQueryImposer = (*testExecutionEntity)(nil) - _ localRangeReader = (*testExecutionEntity)(nil) - _ localRangeHasher = (*testExecutionEntity)(nil) + _ transport.ResultHandler = (*testExecutionEntity)(nil) + _ interceptorPreparer = (*testExecutionEntity)(nil) + _ WorkerPool = (*testExecutionEntity)(nil) + _ operationExecutor = (*testExecutionEntity)(nil) + _ placementBuilder = (*testExecutionEntity)(nil) + _ storage.AddressStore = (*testExecutionEntity)(nil) + _ executionParamsComputer = (*testExecutionEntity)(nil) + _ operationFinalizer = (*testExecutionEntity)(nil) + _ EpochReceiver = (*testExecutionEntity)(nil) + _ localstore.Localstore = (*testExecutionEntity)(nil) + _ containerTraverser = (*testExecutionEntity)(nil) + _ responseItemHandler = (*testExecutionEntity)(nil) + _ resultTracker = (*testExecutionEntity)(nil) + _ localObjectStorer = (*testExecutionEntity)(nil) + _ localFullObjectReceiver = (*testExecutionEntity)(nil) + _ localHeadReceiver = (*testExecutionEntity)(nil) + _ localQueryImposer = (*testExecutionEntity)(nil) + _ localRangeReader = (*testExecutionEntity)(nil) + _ localRangeHasher = (*testExecutionEntity)(nil) ) func (s *testExecutionEntity) prepareInterceptor(p interceptorItems) (func(context.Context, multiaddr.Multiaddr) bool, error) { @@ -79,7 +77,7 @@ func (s *testExecutionEntity) prepareInterceptor(p interceptorItems) (func(conte return s.res.(func(context.Context, multiaddr.Multiaddr) bool), nil } -func (s *testExecutionEntity) Execute(_ context.Context, p implementations.TraverseParams) { +func (s *testExecutionEntity) Execute(_ context.Context, p transport.TraverseParams) { if s.f != nil { s.f(p) } @@ -418,7 +416,7 @@ func Test_coreOperationExecutor_executeOperation(t *testing.T) { p := new(testExecutionEntity) req := newRawPutInfo() req.setTTL(1) - finErr := internal.Error("test error for operation finalizer") + finErr := errors.New("test error for operation finalizer") s := &coreOperationExecutor{ pre: &testExecutionEntity{ @@ -449,7 +447,7 @@ func Test_coreOperationExecutor_executeOperation(t *testing.T) { t.Run("zero ttl", func(t *testing.T) { p := new(testExecutionEntity) req := newRawPutInfo() - finErr := internal.Error("test error for operation finalizer") + finErr := errors.New("test error for operation finalizer") s := &coreOperationExecutor{ loc: &testExecutionEntity{ @@ -476,7 +474,7 @@ func Test_localStoreExecutor(t *testing.T) { t.Run("put", func(t *testing.T) { epoch := uint64(100) obj := new(Object) - putErr := internal.Error("test error for put") + putErr := errors.New("test error for put") ls := &testExecutionEntity{ f: func(items ...interface{}) { @@ -509,7 +507,7 @@ func Test_localStoreExecutor(t *testing.T) { t.Run("get", func(t *testing.T) { t.Run("error", func(t *testing.T) { - getErr := internal.Error("test error for get") + getErr := errors.New("test error for get") ls := &testExecutionEntity{ f: func(items ...interface{}) { @@ -528,7 +526,7 @@ func Test_localStoreExecutor(t *testing.T) { require.EqualError(t, err, getErr.Error()) require.Nil(t, res) - ls.err = errors.Wrap(core.ErrNotFound, "wrap message") + ls.err = errors.Wrap(bucket.ErrNotFound, "wrap message") res, err = s.getObject(ctx, addr) require.EqualError(t, err, errIncompleteOperation.Error()) @@ -552,7 +550,7 @@ func Test_localStoreExecutor(t *testing.T) { t.Run("head", func(t *testing.T) { t.Run("error", func(t *testing.T) { - headErr := internal.Error("test error for head") + headErr := errors.New("test error for head") ls := &testExecutionEntity{ err: headErr, @@ -566,7 +564,7 @@ func Test_localStoreExecutor(t *testing.T) { require.EqualError(t, err, headErr.Error()) require.Nil(t, res) - ls.err = errors.Wrap(core.ErrNotFound, "wrap message") + ls.err = errors.Wrap(bucket.ErrNotFound, "wrap message") res, err = s.headObject(ctx, addr) require.EqualError(t, err, errIncompleteOperation.Error()) @@ -590,7 +588,7 @@ func Test_localStoreExecutor(t *testing.T) { t.Run("get range", func(t *testing.T) { t.Run("error", func(t *testing.T) { - rngErr := internal.Error("test error for range reader") + rngErr := errors.New("test error for range reader") s := &localStoreExecutor{ localStore: &testExecutionEntity{ @@ -638,7 +636,7 @@ func Test_localStoreExecutor(t *testing.T) { }) t.Run("error", func(t *testing.T) { - rhErr := internal.Error("test error for range hasher") + rhErr := errors.New("test error for range hasher") s := &localStoreExecutor{ localStore: &testExecutionEntity{ @@ -692,7 +690,7 @@ func Test_coreHandler_HandleResult(t *testing.T) { t.Run("error", func(t *testing.T) { handled := false - err := internal.Error("") + err := errors.New("") s := &coreHandler{ traverser: &testExecutionEntity{ @@ -776,7 +774,7 @@ func Test_localOperationExecutor_executeOperation(t *testing.T) { }} t.Run("error", func(t *testing.T) { - putErr := internal.Error("test error for put") + putErr := errors.New("test error for put") s := &localOperationExecutor{ objStore: &testExecutionEntity{ @@ -813,7 +811,7 @@ func Test_localOperationExecutor_executeOperation(t *testing.T) { req.setAddress(addr) t.Run("error", func(t *testing.T) { - getErr := internal.Error("test error for get") + getErr := errors.New("test error for get") s := &localOperationExecutor{ objRecv: &testExecutionEntity{ @@ -853,7 +851,7 @@ func Test_localOperationExecutor_executeOperation(t *testing.T) { }} t.Run("error", func(t *testing.T) { - headErr := internal.Error("test error for head") + headErr := errors.New("test error for head") s := &localOperationExecutor{ headRecv: &testExecutionEntity{ @@ -897,7 +895,7 @@ func Test_localOperationExecutor_executeOperation(t *testing.T) { }} t.Run("error", func(t *testing.T) { - searchErr := internal.Error("test error for search") + searchErr := errors.New("test error for search") s := &localOperationExecutor{ queryImp: &testExecutionEntity{ @@ -943,7 +941,7 @@ func Test_localOperationExecutor_executeOperation(t *testing.T) { req.setRange(rng) t.Run("error", func(t *testing.T) { - rrErr := internal.Error("test error for range reader") + rrErr := errors.New("test error for range reader") s := &localOperationExecutor{ rngReader: &testExecutionEntity{ @@ -996,7 +994,7 @@ func Test_localOperationExecutor_executeOperation(t *testing.T) { req.setSalt(salt) t.Run("error", func(t *testing.T) { - rhErr := internal.Error("test error for range hasher") + rhErr := errors.New("test error for range hasher") s := &localOperationExecutor{ rngHasher: &testExecutionEntity{ @@ -1042,7 +1040,7 @@ func Test_coreOperationFinalizer_completeExecution(t *testing.T) { ctx := context.TODO() t.Run("address store failure", func(t *testing.T) { - asErr := internal.Error("test error for address store") + asErr := errors.New("test error for address store") s := &coreOperationFinalizer{ interceptorPreparer: &testExecutionEntity{ @@ -1094,7 +1092,7 @@ func Test_coreOperationFinalizer_completeExecution(t *testing.T) { traverseExec: &testExecutionEntity{ f: func(items ...interface{}) { t.Run("correct traverse executor params", func(t *testing.T) { - p := items[0].(implementations.TraverseParams) + p := items[0].(transport.TraverseParams) require.True(t, p.ExecutionInterceptor(ctx, nil)) require.Equal(t, req, p.TransportInfo) @@ -1123,7 +1121,7 @@ func Test_coreOperationFinalizer_completeExecution(t *testing.T) { func Test_coreInterceptorPreparer_prepareInterceptor(t *testing.T) { t.Run("address store failure", func(t *testing.T) { - asErr := internal.Error("test error for address store") + asErr := errors.New("test error for address store") s := &coreInterceptorPreparer{ addressStore: &testExecutionEntity{ @@ -1144,7 +1142,7 @@ func Test_coreInterceptorPreparer_prepareInterceptor(t *testing.T) { req := new(transportRequest) itemHandler := new(testExecutionEntity) - localErr := internal.Error("test error for local executor") + localErr := errors.New("test error for local executor") p := interceptorItems{ selfForward: true, diff --git a/services/public/object/filter.go b/pkg/network/transport/object/grpc/filter.go similarity index 93% rename from services/public/object/filter.go rename to pkg/network/transport/object/grpc/filter.go index dc8ddc6c..809b4f58 100644 --- a/services/public/object/filter.go +++ b/pkg/network/transport/object/grpc/filter.go @@ -5,10 +5,9 @@ import ( "github.com/nspcc-dev/neofs-api-go/service" "github.com/nspcc-dev/neofs-api-go/storagegroup" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/objutil" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/verifier" "github.com/pkg/errors" ) @@ -20,7 +19,7 @@ type ( storageCap uint64 localStore localstore.Localstore epochRecv EpochReceiver - verifier objutil.Verifier + verifier verifier.Verifier maxPayloadSize uint64 } @@ -50,13 +49,7 @@ const ( payloadSizeFN = "PAYLOAD_SIZE" ) -const ( - errObjectFilter = internal.Error("incoming object has not passed filter") -) - -var ( - _ tombstonePresenceChecker = (*coreTSPresChecker)(nil) -) +var errObjectFilter = errors.New("incoming object has not passed filter") var mFilters = map[string]filterConstructor{ tombstoneOverwriteFN: tombstoneOverwriteFC, @@ -121,7 +114,7 @@ func newFilter(p *Params, name string, m map[string]filterConstructor) (Filter, func (s *coreTSPresChecker) hasLocalTombstone(addr Address) (bool, error) { m, err := s.localStore.Meta(addr) if err != nil { - if errors.Is(errors.Cause(err), core.ErrNotFound) { + if errors.Is(errors.Cause(err), bucket.ErrNotFound) { return false, nil } diff --git a/services/public/object/filter_test.go b/pkg/network/transport/object/grpc/filter_test.go similarity index 95% rename from services/public/object/filter_test.go rename to pkg/network/transport/object/grpc/filter_test.go index 1b4084f0..e3ce2781 100644 --- a/services/public/object/filter_test.go +++ b/pkg/network/transport/object/grpc/filter_test.go @@ -8,10 +8,9 @@ import ( "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/service" "github.com/nspcc-dev/neofs-api-go/storagegroup" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/objutil" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/verifier" "github.com/pkg/errors" "github.com/stretchr/testify/require" ) @@ -40,7 +39,7 @@ type ( var ( _ storagegroup.InfoReceiver = (*testFilterEntity)(nil) - _ objutil.Verifier = (*testFilterEntity)(nil) + _ verifier.Verifier = (*testFilterEntity)(nil) _ EpochReceiver = (*testFilterEntity)(nil) _ localstore.Localstore = (*testFilterEntity)(nil) _ tombstonePresenceChecker = (*testFilterEntity)(nil) @@ -161,7 +160,7 @@ func Test_objectIntegrityFC(t *testing.T) { if items[0].(*Object).SystemHeader.ID.Equal(valid.SystemHeader.ID) { ver.err = nil } else { - ver.err = internal.Error("") + ver.err = errors.New("") } } @@ -192,7 +191,7 @@ func Test_tombstoneOverwriteFC(t *testing.T) { ts.f = func(items ...interface{}) { addr := items[0].(Address) if addr.ObjectID.Equal(obj2.SystemHeader.ID) { - ts.res, ts.err = nil, internal.Error("") + ts.res, ts.err = nil, errors.New("") } else if addr.ObjectID.Equal(obj3.SystemHeader.ID) { ts.res, ts.err = true, nil } else { @@ -310,7 +309,7 @@ func Test_coreTSPresChecker(t *testing.T) { f: func(items ...interface{}) { require.Equal(t, addr, items[0]) }, - err: errors.Wrap(core.ErrNotFound, "some message"), + err: errors.Wrap(bucket.ErrNotFound, "some message"), } s := &coreTSPresChecker{localStore: ls} @@ -319,7 +318,7 @@ func Test_coreTSPresChecker(t *testing.T) { require.NoError(t, err) require.False(t, res) - lsErr := internal.Error("test error for local storage") + lsErr := errors.New("test error for local storage") ls.err = lsErr res, err = s.hasLocalTombstone(addr) diff --git a/services/public/object/get.go b/pkg/network/transport/object/grpc/get.go similarity index 100% rename from services/public/object/get.go rename to pkg/network/transport/object/grpc/get.go diff --git a/services/public/object/get_test.go b/pkg/network/transport/object/grpc/get_test.go similarity index 95% rename from services/public/object/get_test.go rename to pkg/network/transport/object/grpc/get_test.go index a78fde76..1cea57e7 100644 --- a/services/public/object/get_test.go +++ b/pkg/network/transport/object/grpc/get_test.go @@ -5,8 +5,7 @@ import ( "testing" "github.com/nspcc-dev/neofs-api-go/object" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/localstore" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" "github.com/pkg/errors" "github.com/stretchr/testify/require" ) @@ -106,7 +105,7 @@ func TestObjectService_Get(t *testing.T) { req := &object.GetRequest{Address: testObjectAddress(t)} t.Run("request handler failure", func(t *testing.T) { - hErr := internal.Error("test error for request handler") + hErr := errors.New("test error for request handler") s := &objectService{ statusCalculator: newStatusCalculator(), @@ -127,7 +126,7 @@ func TestObjectService_Get(t *testing.T) { }) t.Run("send object head failure", func(t *testing.T) { - srvErr := internal.Error("test error for get server") + srvErr := errors.New("test error for get server") obj := &Object{ SystemHeader: SystemHeader{ @@ -153,7 +152,7 @@ func TestObjectService_Get(t *testing.T) { }) t.Run("send chunk failure", func(t *testing.T) { - srvErr := internal.Error("test error for get server") + srvErr := errors.New("test error for get server") payload := testData(t, 10) obj := &Object{ diff --git a/services/public/object/handler.go b/pkg/network/transport/object/grpc/handler.go similarity index 100% rename from services/public/object/handler.go rename to pkg/network/transport/object/grpc/handler.go diff --git a/services/public/object/handler_test.go b/pkg/network/transport/object/grpc/handler_test.go similarity index 95% rename from services/public/object/handler_test.go rename to pkg/network/transport/object/grpc/handler_test.go index abc0c7ce..ce41776b 100644 --- a/services/public/object/handler_test.go +++ b/pkg/network/transport/object/grpc/handler_test.go @@ -10,8 +10,8 @@ import ( "time" "github.com/nspcc-dev/neofs-api-go/object" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/pkg/errors" "github.com/stretchr/testify/require" ) @@ -143,7 +143,7 @@ func TestCoreRequestHandler_HandleRequest(t *testing.T) { t.Run("pre processor error", func(t *testing.T) { // create custom error - pErr := internal.Error("test error for pre-processor") + pErr := errors.New("test error for pre-processor") s := &coreRequestHandler{ preProc: &testHandlerEntity{ @@ -167,7 +167,7 @@ func TestCoreRequestHandler_HandleRequest(t *testing.T) { t.Run("correct behavior", func(t *testing.T) { // create custom error - eErr := internal.Error("test error for request executor") + eErr := errors.New("test error for request executor") // create custom result eRes := testData(t, 10) @@ -250,7 +250,7 @@ func Test_objectService_executeRequest(t *testing.T) { t.Run("put request", func(t *testing.T) { t.Run("storer error", func(t *testing.T) { - sErr := internal.Error("test error for object storer") + sErr := errors.New("test error for object storer") req := &putRequest{ PutRequest: new(object.PutRequest), @@ -277,7 +277,7 @@ func Test_objectService_executeRequest(t *testing.T) { t.Run("correct result", func(t *testing.T) { addr := testObjectAddress(t) - srvErr := internal.Error("test error for stream server") + srvErr := errors.New("test error for stream server") resp := &object.PutResponse{Address: addr} @@ -315,7 +315,7 @@ func Test_objectService_executeRequest(t *testing.T) { t.Run("delete request", func(t *testing.T) { var ( timeout = 3 * time.Second - dErr = internal.Error("test error for object remover") + dErr = errors.New("test error for object remover") req = &object.DeleteRequest{Address: testObjectAddress(t)} ) @@ -365,7 +365,7 @@ func Test_objectService_executeRequest(t *testing.T) { t.Run("head request", func(t *testing.T) { var ( timeout = 3 * time.Second - hErr = internal.Error("test error for head receiver") + hErr = errors.New("test error for head receiver") req = &object.HeadRequest{Address: testObjectAddress(t)} ) @@ -418,7 +418,7 @@ func Test_objectService_executeRequest(t *testing.T) { t.Run("hashes", func(t *testing.T) { var ( timeout = 3 * time.Second - rErr = internal.Error("test error for range receiver") + rErr = errors.New("test error for range receiver") req = &object.GetRangeHashRequest{Address: testObjectAddress(t)} ) diff --git a/services/public/object/head.go b/pkg/network/transport/object/grpc/head.go similarity index 91% rename from services/public/object/head.go rename to pkg/network/transport/object/grpc/head.go index 2eb89ce4..c5aba326 100644 --- a/services/public/object/head.go +++ b/pkg/network/transport/object/grpc/head.go @@ -9,11 +9,10 @@ import ( "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/objio" - "github.com/nspcc-dev/neofs-node/lib/transformer" - "github.com/nspcc-dev/neofs-node/lib/transport" + _range "github.com/nspcc-dev/neofs-node/pkg/network/transport/object/grpc/range" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transformer" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport/storagegroup" "github.com/panjf2000/ants/v2" "github.com/pkg/errors" "go.uber.org/zap" @@ -117,8 +116,8 @@ type ( } corePayloadRangeReceiver struct { - chopTable objio.ChopperTable - relRecv objio.RelativeReceiver + chopTable _range.ChopperTable + relRecv _range.RelativeReceiver payloadRecv payloadPartReceiver // Set of errors that won't be converted to errPayloadRangeNotFound @@ -141,11 +140,11 @@ type ( emptyReader struct{} ) -const ( - emHeadRecvFail = "could not receive %d of %d object head" +const emHeadRecvFail = "could not receive %d of %d object head" - childrenNotFound = internal.Error("could not find child objects") - errNonAssembly = internal.Error("node is not capable to assemble the object") +var ( + childrenNotFound = errors.New("could not find child objects") + errNonAssembly = errors.New("node is not capable to assemble the object") ) var ( @@ -235,8 +234,8 @@ func (s *coreObjectReceiver) getObject(ctx context.Context, info ...transport.Ge ctx = contextWithValues(ctx, transformer.PublicSessionToken, info[0].GetSessionToken(), - implementations.BearerToken, info[0].GetBearerToken(), - implementations.ExtendedHeaders, info[0].ExtendedHeaders(), + storagegroup.BearerToken, info[0].GetBearerToken(), + storagegroup.ExtendedHeaders, info[0].ExtendedHeaders(), ) if childCount <= 0 { @@ -338,11 +337,11 @@ func (s *corePayloadRangeReceiver) getRangeData(ctx context.Context, info transp addr = info.GetAddress() ) - chopper, err = s.chopTable.GetChopper(addr, objio.RCCharybdis) + chopper, err = s.chopTable.GetChopper(addr, _range.RCCharybdis) if err != nil || !chopper.Closed() { if len(selection) == 0 { - if chopper, err = s.chopTable.GetChopper(addr, objio.RCScylla); err != nil { - if chopper, err = objio.NewScylla(&objio.ChopperParams{ + if chopper, err = s.chopTable.GetChopper(addr, _range.RCScylla); err != nil { + if chopper, err = _range.NewScylla(&_range.ChopperParams{ RelativeReceiver: s.relRecv, Addr: addr, }); err != nil { @@ -361,7 +360,7 @@ func (s *corePayloadRangeReceiver) getRangeData(ctx context.Context, info transp }) } - if chopper, err = objio.NewCharybdis(&objio.CharybdisParams{ + if chopper, err = _range.NewCharybdis(&_range.CharybdisParams{ Addr: addr, ReadySelection: rs, }); err != nil { @@ -376,8 +375,8 @@ func (s *corePayloadRangeReceiver) getRangeData(ctx context.Context, info transp ctx = contextWithValues(ctx, transformer.PublicSessionToken, info.GetSessionToken(), - implementations.BearerToken, info.GetBearerToken(), - implementations.ExtendedHeaders, info.ExtendedHeaders(), + storagegroup.BearerToken, info.GetBearerToken(), + storagegroup.ExtendedHeaders, info.ExtendedHeaders(), ) var rList []RangeDescriptor @@ -534,7 +533,7 @@ func tokenFromContext(ctx context.Context) service.SessionToken { } func bearerFromContext(ctx context.Context) service.BearerToken { - if v, ok := ctx.Value(implementations.BearerToken).(service.BearerToken); ok { + if v, ok := ctx.Value(storagegroup.BearerToken).(service.BearerToken); ok { return v } @@ -542,7 +541,7 @@ func bearerFromContext(ctx context.Context) service.BearerToken { } func extendedHeadersFromContext(ctx context.Context) []service.ExtendedHeader { - if v, ok := ctx.Value(implementations.ExtendedHeaders).([]service.ExtendedHeader); ok { + if v, ok := ctx.Value(storagegroup.ExtendedHeaders).([]service.ExtendedHeader); ok { return v } diff --git a/services/public/object/head_test.go b/pkg/network/transport/object/grpc/head_test.go similarity index 95% rename from services/public/object/head_test.go rename to pkg/network/transport/object/grpc/head_test.go index fcf2be7b..50da2434 100644 --- a/services/public/object/head_test.go +++ b/pkg/network/transport/object/grpc/head_test.go @@ -8,9 +8,9 @@ import ( "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/transformer" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transformer" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/pkg/errors" "github.com/stretchr/testify/require" "go.uber.org/zap" ) @@ -215,7 +215,7 @@ func Test_objectService_Head(t *testing.T) { t.Run("request handler error", func(t *testing.T) { // create custom error for test - rhErr := internal.Error("test error for request handler") + rhErr := errors.New("test error for request handler") // create custom request for test req := new(object.HeadRequest) @@ -277,7 +277,7 @@ func Test_coreHeadReceiver_head(t *testing.T) { hInfo.setRaw(true) // create custom error for test - srErr := internal.Error("test error for straight object receiver") + srErr := errors.New("test error for straight object receiver") s := &coreObjectReceiver{ straightObjRecv: &testHeadEntity{ @@ -343,7 +343,7 @@ func Test_coreHeadReceiver_head(t *testing.T) { s := &coreObjectReceiver{ straightObjRecv: &testHeadEntity{ - err: internal.Error(""), // force straightObjectReceiver to return non-empty error + err: errors.New(""), // force straightObjectReceiver to return non-empty error }, childLister: &testHeadEntity{ f: func(items ...interface{}) { @@ -368,7 +368,7 @@ func Test_coreHeadReceiver_head(t *testing.T) { t.Run("correct result", func(t *testing.T) { var ( childCount = 5 - rErr = internal.Error("test error for rewinding receiver") + rErr = errors.New("test error for rewinding receiver") children = make([]ID, 0, childCount) ) @@ -425,7 +425,7 @@ func Test_coreHeadReceiver_head(t *testing.T) { s := &coreObjectReceiver{ straightObjRecv: &testHeadEntity{ - err: internal.Error(""), // force straight receiver to return non-nil error + err: errors.New(""), // force straight receiver to return non-nil error }, ancestralRecv: &testHeadEntity{ f: func(items ...interface{}) { @@ -456,7 +456,7 @@ func Test_straightHeadReceiver_head(t *testing.T) { hInfo.setFullHeaders(true) t.Run("executor error", func(t *testing.T) { - exErr := internal.Error("test error for operation executor") + exErr := errors.New("test error for operation executor") s := &straightObjectReceiver{ executor: &testHeadEntity{ @@ -501,7 +501,7 @@ func Test_coreObjectRewinder_rewind(t *testing.T) { ctx := context.TODO() t.Run("transformer failure", func(t *testing.T) { - tErr := internal.Error("test error for object transformer") + tErr := errors.New("test error for object transformer") objs := []Object{*new(Object), *new(Object)} s := &coreObjectRewinder{ diff --git a/services/public/object/implementations.go b/pkg/network/transport/object/grpc/implementations.go similarity index 86% rename from services/public/object/implementations.go rename to pkg/network/transport/object/grpc/implementations.go index 2f4b3715..c63f3519 100644 --- a/services/public/object/implementations.go +++ b/pkg/network/transport/object/grpc/implementations.go @@ -5,7 +5,7 @@ import ( "github.com/multiformats/go-multiaddr" "github.com/nspcc-dev/neofs-api-go/object" - "github.com/nspcc-dev/neofs-node/lib/peers" + "github.com/nspcc-dev/neofs-node/pkg/network/peers" "github.com/pkg/errors" ) @@ -23,7 +23,7 @@ func NewRemoteService(ps peers.Interface) RemoteService { } func (rs remoteService) Remote(ctx context.Context, addr multiaddr.Multiaddr) (object.ServiceClient, error) { - con, err := rs.ps.GRPCConnection(ctx, addr, false) + con, err := rs.ps.GRPCConnection(ctx, addr) if err != nil { return nil, errors.Wrapf(err, "remoteService.Remote failed on GRPCConnection to %s", addr) } diff --git a/services/public/object/listing.go b/pkg/network/transport/object/grpc/listing.go similarity index 89% rename from services/public/object/listing.go rename to pkg/network/transport/object/grpc/listing.go index 9967fc54..8d3f7c86 100644 --- a/services/public/object/listing.go +++ b/pkg/network/transport/object/grpc/listing.go @@ -8,10 +8,8 @@ import ( "github.com/nspcc-dev/neofs-api-go/object" v1 "github.com/nspcc-dev/neofs-api-go/query" "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/objio" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/pkg/errors" "go.uber.org/zap" ) @@ -47,24 +45,16 @@ type ( } selectiveRangeRecv struct { - executor implementations.SelectiveContainerExecutor + executor transport.SelectiveContainerExecutor } ) const ( lmQueryMarshalFail = "marshal search query failure" lmListFail = "searching inside children listing failure" - - errRelationNotFound = internal.Error("relation not found") ) -var ( - _ relationQueryFunc = coreChildrenQueryFunc - _ transport.SearchInfo = (*rawSearchInfo)(nil) - _ objectChildrenLister = (*coreChildrenLister)(nil) - _ objio.RelativeReceiver = (*neighborReceiver)(nil) - _ selectiveRangeReceiver = (*selectiveRangeRecv)(nil) -) +var errRelationNotFound = errors.New("relation not found") func (s *neighborReceiver) Base(ctx context.Context, addr Address) (RangeDescriptor, error) { if res, err := s.rangeDescRecv.rangeDescriptor(ctx, addr, s.firstChildQueryFn); err == nil { @@ -87,9 +77,9 @@ func (s *neighborReceiver) Neighbor(ctx context.Context, addr Address, left bool func (s *selectiveRangeRecv) rangeDescriptor(ctx context.Context, addr Address, fn relationQueryFunc) (res RangeDescriptor, err error) { b := false - p := &implementations.HeadParams{ - GetParams: implementations.GetParams{ - SelectiveParams: implementations.SelectiveParams{ + p := &transport.HeadParams{ + GetParams: transport.GetParams{ + SelectiveParams: transport.SelectiveParams{ CID: addr.CID, ServeLocal: true, TTL: service.SingleForwardingTTL, diff --git a/services/public/object/listing_test.go b/pkg/network/transport/object/grpc/listing_test.go similarity index 91% rename from services/public/object/listing_test.go rename to pkg/network/transport/object/grpc/listing_test.go index 7c27ca79..614237af 100644 --- a/services/public/object/listing_test.go +++ b/pkg/network/transport/object/grpc/listing_test.go @@ -8,10 +8,9 @@ import ( "github.com/nspcc-dev/neofs-api-go/query" v1 "github.com/nspcc-dev/neofs-api-go/query" "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/test" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/nspcc-dev/neofs-node/pkg/util/logger/test" + "github.com/pkg/errors" "github.com/stretchr/testify/require" ) @@ -21,7 +20,7 @@ type ( // If err is not nil, it returns as it is. Otherwise, casted to needed type res returns w/o error. testListingEntity struct { // Set of interfaces which entity must implement, but some methods from those does not call. - implementations.SelectiveContainerExecutor + transport.SelectiveContainerExecutor // Argument interceptor. Used for ascertain of correct parameter passage between components. f func(...interface{}) @@ -36,7 +35,7 @@ var ( _ objectSearcher = (*testListingEntity)(nil) _ selectiveRangeReceiver = (*testListingEntity)(nil) - _ implementations.SelectiveContainerExecutor = (*testListingEntity)(nil) + _ transport.SelectiveContainerExecutor = (*testListingEntity)(nil) ) func (s *testListingEntity) rangeDescriptor(_ context.Context, a Address, f relationQueryFunc) (RangeDescriptor, error) { @@ -49,7 +48,7 @@ func (s *testListingEntity) rangeDescriptor(_ context.Context, a Address, f rela return s.res.(RangeDescriptor), nil } -func (s *testListingEntity) Head(_ context.Context, p *implementations.HeadParams) error { +func (s *testListingEntity) Head(_ context.Context, p *transport.HeadParams) error { if s.f != nil { s.f(p) } @@ -142,9 +141,9 @@ func Test_coreChildrenLister_children(t *testing.T) { t.Run("correct query function params", func(t *testing.T) { require.Equal(t, addr, v) }) - return nil, internal.Error("") // force relationQueryFunc to return some non-nil error + return nil, errors.New("") // force relationQueryFunc to return some non-nil error }, - log: test.NewTestLogger(false), + log: test.NewLogger(false), } require.Empty(t, s.children(ctx, addr)) @@ -152,7 +151,7 @@ func Test_coreChildrenLister_children(t *testing.T) { t.Run("object searcher failure", func(t *testing.T) { // create custom timeout for test - sErr := internal.Error("test error for object searcher") + sErr := errors.New("test error for object searcher") // create custom timeout for test timeout := 3 * time.Second // create custom query for test @@ -174,7 +173,7 @@ func Test_coreChildrenLister_children(t *testing.T) { }, err: sErr, // force objectSearcher to return sErr }, - log: test.NewTestLogger(false), + log: test.NewLogger(false), timeout: timeout, } @@ -295,7 +294,7 @@ func Test_selectiveRangeRecv(t *testing.T) { addr := testObjectAddress(t) t.Run("query function failure", func(t *testing.T) { - qfErr := internal.Error("test error for query function") + qfErr := errors.New("test error for query function") _, err := new(selectiveRangeRecv).rangeDescriptor(ctx, testObjectAddress(t), func(Address) ([]byte, error) { return nil, qfErr }) @@ -309,7 +308,7 @@ func Test_selectiveRangeRecv(t *testing.T) { s := &selectiveRangeRecv{ executor: &testListingEntity{ f: func(items ...interface{}) { - p := items[0].(*implementations.HeadParams) + p := items[0].(*transport.HeadParams) require.Equal(t, addr.CID, p.CID) require.True(t, p.ServeLocal) require.Equal(t, uint32(service.SingleForwardingTTL), p.TTL) @@ -327,7 +326,7 @@ func Test_selectiveRangeRecv(t *testing.T) { s := &selectiveRangeRecv{ executor: &testListingEntity{ f: func(items ...interface{}) { - p := items[0].(*implementations.HeadParams) + p := items[0].(*transport.HeadParams) require.Equal(t, addr.CID, p.CID) require.True(t, p.ServeLocal) require.Equal(t, uint32(service.SingleForwardingTTL), p.TTL) @@ -345,7 +344,7 @@ func Test_selectiveRangeRecv(t *testing.T) { t.Run("correct result", func(t *testing.T) { t.Run("failure", func(t *testing.T) { t.Run("executor failure", func(t *testing.T) { - exErr := internal.Error("test error for executor") + exErr := errors.New("test error for executor") s := &selectiveRangeRecv{ executor: &testListingEntity{ @@ -382,7 +381,7 @@ func Test_selectiveRangeRecv(t *testing.T) { executor: &testListingEntity{ SelectiveContainerExecutor: nil, f: func(items ...interface{}) { - p := items[0].(*implementations.HeadParams) + p := items[0].(*transport.HeadParams) p.Handler(nil, obj) }, }, @@ -426,7 +425,7 @@ func Test_neighborReceiver(t *testing.T) { require.Equal(t, addr, items[0]) _, _ = items[1].(relationQueryFunc)(addr) }, - err: internal.Error(""), + err: errors.New(""), }, } @@ -442,7 +441,7 @@ func Test_neighborReceiver(t *testing.T) { }) t.Run("correct result", func(t *testing.T) { - rErr := internal.Error("test error for range receiver") + rErr := errors.New("test error for range receiver") rngRecv := &testListingEntity{err: rErr} s := &neighborReceiver{rangeDescRecv: rngRecv} @@ -494,7 +493,7 @@ func Test_neighborReceiver(t *testing.T) { t.Run("first child doesn't exist", func(t *testing.T) { called := false - recv := &testListingEntity{err: internal.Error("")} + recv := &testListingEntity{err: errors.New("")} recv.f = func(...interface{}) { if called { diff --git a/services/public/object/postprocessor.go b/pkg/network/transport/object/grpc/postprocessor.go similarity index 100% rename from services/public/object/postprocessor.go rename to pkg/network/transport/object/grpc/postprocessor.go diff --git a/services/public/object/postprocessor_test.go b/pkg/network/transport/object/grpc/postprocessor_test.go similarity index 95% rename from services/public/object/postprocessor_test.go rename to pkg/network/transport/object/grpc/postprocessor_test.go index f114fc98..ad48fb85 100644 --- a/services/public/object/postprocessor_test.go +++ b/pkg/network/transport/object/grpc/postprocessor_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - "github.com/nspcc-dev/neofs-node/internal" + "github.com/pkg/errors" "github.com/stretchr/testify/require" ) @@ -48,7 +48,7 @@ func TestComplexPostProcessor_PostProcess(t *testing.T) { req := new(testPostProcessorEntity) // create custom error - pErr := internal.Error("test error for post processor") + pErr := errors.New("test error for post processor") // create list of post processors postProcCount := 10 diff --git a/services/public/object/preprocessor.go b/pkg/network/transport/object/grpc/preprocessor.go similarity index 97% rename from services/public/object/preprocessor.go rename to pkg/network/transport/object/grpc/preprocessor.go index 620cae5a..7b3291df 100644 --- a/services/public/object/preprocessor.go +++ b/pkg/network/transport/object/grpc/preprocessor.go @@ -94,8 +94,6 @@ func newPreProcessor(p *Params) requestPreProcessor { aclInfoReceiver: p.aclInfoReceiver, - basicChecker: p.BasicACLChecker, - reqActionCalc: p.requestActionCalculator, localStore: p.LocalStore, @@ -109,7 +107,7 @@ func newPreProcessor(p *Params) requestPreProcessor { }, new(bearerSignatureVerifier), &bearerOwnershipVerifier{ - cnrOwnerChecker: p.ACLHelper, + cnrStorage: p.ContainerStorage, }, }, }, @@ -145,7 +143,6 @@ func newPreProcessor(p *Params) requestPreProcessor { }, &tokenPreProcessor{ - keyVerifier: p.OwnerKeyVerifier, staticVerifier: newComplexTokenVerifier( &tokenEpochsVerifier{ epochRecv: p.EpochReceiver, diff --git a/services/public/object/preprocessor_test.go b/pkg/network/transport/object/grpc/preprocessor_test.go similarity index 90% rename from services/public/object/preprocessor_test.go rename to pkg/network/transport/object/grpc/preprocessor_test.go index 7a150928..bdadc95f 100644 --- a/services/public/object/preprocessor_test.go +++ b/pkg/network/transport/object/grpc/preprocessor_test.go @@ -5,10 +5,9 @@ import ( "testing" "github.com/nspcc-dev/neofs-api-go/object" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/test" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/replication/storage" + "github.com/nspcc-dev/neofs-node/pkg/util/test" + "github.com/pkg/errors" "github.com/stretchr/testify/require" ) @@ -20,9 +19,8 @@ type ( // Set of interfaces which testCommonEntity must implement, but some methods from those does not call. serviceRequest Placer - implementations.AddressStoreComponent + storage.AddressStoreComponent EpochReceiver - core.OwnerKeyVerifier // Argument interceptor. Used for ascertain of correct parameter passage between components. f func(...interface{}) @@ -48,7 +46,7 @@ func TestSigningPreProcessor_preProcess(t *testing.T) { req := new(object.SearchRequest) t.Run("internal pre-processor error", func(t *testing.T) { - ppErr := internal.Error("test error for pre-processor") + ppErr := errors.New("test error for pre-processor") s := &signingPreProcessor{ preProc: &testPreProcessorEntity{ @@ -109,7 +107,7 @@ func TestComplexPreProcessor_PreProcess(t *testing.T) { } // create custom error - pErr := internal.Error("pre processor error for test") + pErr := errors.New("pre processor error for test") p2 := &testPreProcessorEntity{ err: pErr, // force second requestPreProcessor to return created error } diff --git a/services/public/object/put.go b/pkg/network/transport/object/grpc/put.go similarity index 86% rename from services/public/object/put.go rename to pkg/network/transport/object/grpc/put.go index b1cc519c..404445f4 100644 --- a/services/public/object/put.go +++ b/pkg/network/transport/object/grpc/put.go @@ -10,12 +10,11 @@ import ( "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" "github.com/nspcc-dev/neofs-api-go/session" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/objutil" - "github.com/nspcc-dev/neofs-node/lib/transformer" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transformer" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport/storagegroup" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/verifier" "github.com/pkg/errors" "go.uber.org/zap" ) @@ -32,7 +31,7 @@ type ( receivingObjectStorer struct { straightStorer objectStorer - vPayload objutil.Verifier + vPayload verifier.Verifier } filteringObjectStorer struct { @@ -90,32 +89,20 @@ type transformerHandlerErr struct { error } -const ( - errObjectExpected = internal.Error("missing object") - errChunkExpected = internal.Error("empty chunk received") +var ( + errObjectExpected = errors.New("missing object") + errChunkExpected = errors.New("empty chunk received") ) -const ( - errMissingOwnerKeys = internal.Error("missing owner keys") - errBrokenToken = internal.Error("broken token structure") - errNilToken = internal.Error("missing session token") - errWrongTokenAddress = internal.Error("wrong object address in token") -) - -const errTransformer = internal.Error("could not transform the object") - var ( - _ transport.PutInfo = (*rawPutInfo)(nil) - _ addressAccumulator = (*coreAddrAccum)(nil) - _ objectStorer = (*straightObjectStorer)(nil) - _ transport.PutInfo = (*putRequest)(nil) - _ io.Reader = (*putStreamReader)(nil) - _ objectStorer = (*filteringObjectStorer)(nil) - _ objectStorer = (*transformingObjectStorer)(nil) - _ objectStorer = (*tokenObjectStorer)(nil) - _ objectStorer = (*receivingObjectStorer)(nil) + errMissingOwnerKeys = errors.New("missing owner keys") + errBrokenToken = errors.New("broken token structure") + errNilToken = errors.New("missing session token") + errWrongTokenAddress = errors.New("wrong object address in token") ) +var errTransformer = errors.New("could not transform the object") + func (s *objectService) Put(srv object.Service_PutServer) (err error) { defer func() { if r := recover(); r != nil { @@ -210,8 +197,8 @@ func (s *tokenObjectStorer) putObject(ctx context.Context, info transport.PutInf contextWithValues(ctx, transformer.PrivateSessionToken, pToken, transformer.PublicSessionToken, token, - implementations.BearerToken, info.GetBearerToken(), - implementations.ExtendedHeaders, info.ExtendedHeaders(), + storagegroup.BearerToken, info.GetBearerToken(), + storagegroup.ExtendedHeaders, info.ExtendedHeaders(), ), info, ) diff --git a/services/public/object/put_test.go b/pkg/network/transport/object/grpc/put_test.go similarity index 95% rename from services/public/object/put_test.go rename to pkg/network/transport/object/grpc/put_test.go index 80fe3383..1bdd5880 100644 --- a/services/public/object/put_test.go +++ b/pkg/network/transport/object/grpc/put_test.go @@ -11,12 +11,11 @@ import ( "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" "github.com/nspcc-dev/neofs-api-go/session" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/test" - "github.com/nspcc-dev/neofs-node/lib/transformer" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transformer" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/nspcc-dev/neofs-node/pkg/util/test" + "github.com/pkg/errors" "github.com/stretchr/testify/require" ) @@ -30,7 +29,7 @@ type ( transport.PutInfo Filter session.PrivateTokenStore - implementations.SelectiveContainerExecutor + transport.SelectiveContainerExecutor // Argument interceptor. Used for ascertain of correct parameter passage between components. f func(...interface{}) @@ -149,7 +148,7 @@ func Test_objectService_Put(t *testing.T) { t.Run("stream error", func(t *testing.T) { // create custom error for test - psErr := internal.Error("test error for put stream server") + psErr := errors.New("test error for put stream server") s := &testPutEntity{ err: psErr, // force server to return psErr @@ -175,7 +174,7 @@ func Test_objectService_Put(t *testing.T) { }} // create custom error for test - hErr := internal.Error("test error for request handler") + hErr := errors.New("test error for request handler") srv := &testPutEntity{ res: req, // force server to return req @@ -212,7 +211,7 @@ func Test_straightObjectStorer_putObject(t *testing.T) { t.Run("executor error", func(t *testing.T) { // create custom error for test - exErr := internal.Error("test error for operation executor") + exErr := errors.New("test error for operation executor") // create custom meta info for test info := new(testPutEntity) @@ -259,7 +258,7 @@ func Test_straightObjectStorer_putObject(t *testing.T) { func Test_recvPutHeaderMsg(t *testing.T) { t.Run("server error", func(t *testing.T) { // create custom error for test - srvErr := internal.Error("test error for put server") + srvErr := errors.New("test error for put server") srv := &testPutEntity{ err: srvErr, // force put server to return srvErr @@ -453,7 +452,7 @@ func Test_bifurcatingObjectStorer(t *testing.T) { ctx := context.TODO() // create custom error for test - sErr := internal.Error("test error for object storer") + sErr := errors.New("test error for object storer") t.Run("w/ token", func(t *testing.T) { // create custom request w/ token @@ -562,7 +561,7 @@ func Test_tokenObjectStorer(t *testing.T) { t.Run("token store failure", func(t *testing.T) { s := &tokenObjectStorer{ tokenStore: &testPutEntity{ - err: internal.Error(""), // force token store to return a non-nil error + err: errors.New(""), // force token store to return a non-nil error }, } @@ -676,7 +675,7 @@ func Test_receivingObjectStorer(t *testing.T) { }) t.Run("payload verification failure", func(t *testing.T) { - vErr := internal.Error("payload verification error for test") + vErr := errors.New("payload verification error for test") req := newRawPutInfo() req.setHead(&Object{ @@ -752,7 +751,7 @@ func Test_transformingObjectStorer(t *testing.T) { t.Run("correct behavior", func(t *testing.T) { var ( - tErr = internal.Error("test error for transformer") + tErr = errors.New("test error for transformer") addr = testObjectAddress(t) obj = &Object{ SystemHeader: SystemHeader{ @@ -804,7 +803,7 @@ func Test_transformingObjectStorer(t *testing.T) { require.Equal(t, exp, items[0]) }) }, - err: internal.Error(""), + err: errors.New(""), }, mErr: map[error]struct{}{ tErr: {}, @@ -820,13 +819,13 @@ func Test_transformingObjectStorer(t *testing.T) { _, err = s.putObject(ctx, req) require.EqualError(t, err, tErr.Error()) - tr.err = internal.Error("some other error") + tr.err = errors.New("some other error") _, err = s.putObject(ctx, req) require.EqualError(t, err, errTransformer.Error()) e := &transformerHandlerErr{ - error: internal.Error("transformer handler error"), + error: errors.New("transformer handler error"), } tr.err = e @@ -863,7 +862,7 @@ func Test_putStreamReader(t *testing.T) { t.Run("receive message failure", func(t *testing.T) { t.Run("stream problem", func(t *testing.T) { - srvErr := internal.Error("test error for stream server") + srvErr := errors.New("test error for stream server") s := &putStreamReader{ srv: &testPutEntity{ diff --git a/services/public/object/query.go b/pkg/network/transport/object/grpc/query.go similarity index 92% rename from services/public/object/query.go rename to pkg/network/transport/object/grpc/query.go index 79fddde4..12dea3ee 100644 --- a/services/public/object/query.go +++ b/pkg/network/transport/object/grpc/query.go @@ -7,9 +7,9 @@ import ( "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/query" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/pkg/errors" "go.uber.org/zap" ) @@ -36,13 +36,13 @@ const ( queryFilterName = "QUERY_FILTER" pmUndefinedFilterType = "undefined filter type %d" - - errUnsupportedQueryVersion = internal.Error("unsupported query version number") ) -const errSearchQueryUnmarshal = internal.Error("query unmarshal failure") +var errUnsupportedQueryVersion = errors.New("unsupported query version number") -const errLocalQueryImpose = internal.Error("local query imposing failure") +var errSearchQueryUnmarshal = errors.New("query unmarshal failure") + +var errLocalQueryImpose = errors.New("local query imposing failure") var ( _ filterCreator = (*coreFilterCreator)(nil) diff --git a/services/public/object/query_test.go b/pkg/network/transport/object/grpc/query_test.go similarity index 98% rename from services/public/object/query_test.go rename to pkg/network/transport/object/grpc/query_test.go index ee6a5dea..8e1a17dd 100644 --- a/services/public/object/query_test.go +++ b/pkg/network/transport/object/grpc/query_test.go @@ -9,9 +9,9 @@ import ( "github.com/nspcc-dev/neofs-api-go/query" "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/storagegroup" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/pkg/errors" "github.com/stretchr/testify/require" "go.uber.org/zap" ) @@ -96,7 +96,7 @@ func Test_queryVersionController_imposeQuery(t *testing.T) { require.Equal(t, 0, items[2].(int)) }) }, - err: internal.Error(""), // just to prevent panic + err: errors.New(""), // just to prevent panic } _, _ = qImp.imposeQuery(ctx, cid, qData, 0) @@ -107,7 +107,7 @@ func Test_queryVersionController_imposeQuery(t *testing.T) { m := make(map[int]localQueryImposer) qImp := &queryVersionController{m: m} - impErr := internal.Error("test error for query imposer") + impErr := errors.New("test error for query imposer") m[0] = &testQueryEntity{ err: impErr, // force localQueryImposer to return impErr @@ -208,7 +208,7 @@ func Test_coreQueryImposer_imposeQuery(t *testing.T) { t.Run("listing error", func(t *testing.T) { // create new error for test - lsErr := internal.Error("test error of local store listing") + lsErr := errors.New("test error of local store listing") // create test query imposer with mocked always failing lister qImposer := &coreQueryImposer{ @@ -241,7 +241,7 @@ func Test_coreQueryImposer_imposeQuery(t *testing.T) { // ascertain that argument is as expected require.Equal(t, fc, p[0].(Filter)) }, - err: internal.Error(""), + err: errors.New(""), }, log: log, } diff --git a/lib/objio/range.go b/pkg/network/transport/object/grpc/range/range.go similarity index 99% rename from lib/objio/range.go rename to pkg/network/transport/object/grpc/range/range.go index 183fb739..864888ff 100644 --- a/lib/objio/range.go +++ b/pkg/network/transport/object/grpc/range/range.go @@ -1,4 +1,4 @@ -package objio +package _range import ( "context" @@ -6,7 +6,7 @@ import ( "sync" "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-node/lib/localstore" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" "github.com/pkg/errors" ) diff --git a/lib/objio/range_test.go b/pkg/network/transport/object/grpc/range/range_test.go similarity index 99% rename from lib/objio/range_test.go rename to pkg/network/transport/object/grpc/range/range_test.go index 6d7290d9..82ec3d32 100644 --- a/lib/objio/range_test.go +++ b/pkg/network/transport/object/grpc/range/range_test.go @@ -1,4 +1,4 @@ -package objio +package _range import ( "context" diff --git a/services/public/object/ranges.go b/pkg/network/transport/object/grpc/ranges.go similarity index 89% rename from services/public/object/ranges.go rename to pkg/network/transport/object/grpc/ranges.go index acc05e0c..68196dfe 100644 --- a/services/public/object/ranges.go +++ b/pkg/network/transport/object/grpc/ranges.go @@ -8,9 +8,8 @@ import ( "github.com/nspcc-dev/neofs-api-go/hash" "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/objio" - "github.com/nspcc-dev/neofs-node/lib/transport" + _range "github.com/nspcc-dev/neofs-node/pkg/network/transport/object/grpc/range" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" "github.com/pkg/errors" "go.uber.org/zap" ) @@ -22,11 +21,11 @@ type ( // RangeDescriptor is a type alias of // RangeDescriptor from objio package. - RangeDescriptor = objio.RangeDescriptor + RangeDescriptor = _range.RangeDescriptor // RangeChopper is a type alias of // RangeChopper from objio package. - RangeChopper = objio.RangeChopper + RangeChopper = _range.RangeChopper // GetRangeRequest is a type alias of // GetRangeRequest from object package of neofs-api-go. @@ -99,8 +98,8 @@ type ( } coreRngRevealer struct { - relativeRecv objio.RelativeReceiver - chopTable objio.ChopperTable + relativeRecv _range.RelativeReceiver + chopTable _range.ChopperTable } getRangeServerWriter struct { @@ -116,22 +115,9 @@ const ( emGetRangeFail = "could get object range #%d part #%d" emRangeRevealFail = "could not reveal object range #%d" emRangeCollect = "could not collect result of object range #%d" - - errRangeReveal = internal.Error("could not reveal payload range") ) -var ( - _ transport.RangeInfo = (*rawRangeInfo)(nil) - _ rangeTool = (*rawRangeHashInfo)(nil) - _ rangeTool = (*transportRequest)(nil) - _ rangeItemAccumulator = (*rangeHashAccum)(nil) - _ rangeItemAccumulator = (*singleItemHandler)(nil) - _ rangeRevealer = (*coreRngRevealer)(nil) - _ objectRangeReceiver = (*coreRangeReceiver)(nil) - _ objectRangeReceiver = (*straightRangeReceiver)(nil) - _ io.Writer = (*getRangeServerWriter)(nil) - _ transport.RangeInfo = (*transportRequest)(nil) -) +var errRangeReveal = errors.New("could not reveal payload range") func (s *objectService) GetRange(req *GetRangeRequest, srv object.Service_GetRangeServer) (err error) { defer func() { @@ -286,11 +272,11 @@ func (s *coreRngRevealer) reveal(ctx context.Context, r *RangeDescriptor) ([]Ran } func (s *coreRngRevealer) getChopper(addr Address) (res RangeChopper, err error) { - if res, err = s.chopTable.GetChopper(addr, objio.RCCharybdis); err == nil && res.Closed() { + if res, err = s.chopTable.GetChopper(addr, _range.RCCharybdis); err == nil && res.Closed() { return - } else if res, err = s.chopTable.GetChopper(addr, objio.RCScylla); err == nil { + } else if res, err = s.chopTable.GetChopper(addr, _range.RCScylla); err == nil { return - } else if res, err = objio.NewScylla(&objio.ChopperParams{ + } else if res, err = _range.NewScylla(&_range.ChopperParams{ RelativeReceiver: s.relativeRecv, Addr: addr, }); err != nil { diff --git a/services/public/object/ranges_test.go b/pkg/network/transport/object/grpc/ranges_test.go similarity index 94% rename from services/public/object/ranges_test.go rename to pkg/network/transport/object/grpc/ranges_test.go index 57d6d2e8..065846be 100644 --- a/services/public/object/ranges_test.go +++ b/pkg/network/transport/object/grpc/ranges_test.go @@ -9,9 +9,9 @@ import ( "github.com/nspcc-dev/neofs-api-go/hash" "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/objio" - "github.com/nspcc-dev/neofs-node/lib/transport" + _range "github.com/nspcc-dev/neofs-node/pkg/network/transport/object/grpc/range" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/pkg/errors" "github.com/stretchr/testify/require" "go.uber.org/zap" ) @@ -35,7 +35,7 @@ type ( ) var ( - _ objio.RelativeReceiver = (*testRangeEntity)(nil) + _ _range.RelativeReceiver = (*testRangeEntity)(nil) _ RangeChopper = (*testRangeEntity)(nil) _ operationExecutor = (*testRangeEntity)(nil) _ requestHandler = (*testRangeEntity)(nil) @@ -117,7 +117,7 @@ func (s *testRangeEntity) PutChopper(addr Address, chopper RangeChopper) error { return s.err } -func (s *testRangeEntity) GetChopper(addr Address, rc objio.RCType) (RangeChopper, error) { +func (s *testRangeEntity) GetChopper(addr Address, rc _range.RCType) (RangeChopper, error) { if s.f != nil { s.f(addr, rc) } @@ -145,7 +145,7 @@ func Test_objectService_GetRange(t *testing.T) { req := &GetRangeRequest{Address: testObjectAddress(t)} t.Run("request handler error", func(t *testing.T) { - rhErr := internal.Error("test error for request handler") + rhErr := errors.New("test error for request handler") s := &objectService{ statusCalculator: newStatusCalculator(), @@ -202,7 +202,7 @@ func Test_objectService_GetRangeHash(t *testing.T) { req := &GetRangeHashRequest{Address: testObjectAddress(t)} t.Run("request handler error", func(t *testing.T) { - rhErr := internal.Error("test error for request handler") + rhErr := errors.New("test error for request handler") s := &objectService{ statusCalculator: newStatusCalculator(), @@ -259,7 +259,7 @@ func Test_coreRangeReceiver(t *testing.T) { log := zap.L() t.Run("range reveal failure", func(t *testing.T) { - revErr := internal.Error("test error for range revealer") + revErr := errors.New("test error for range revealer") rt := newRawRangeHashInfo() rt.setTTL(service.NonForwardingTTL) @@ -300,7 +300,7 @@ func Test_coreRangeReceiver(t *testing.T) { }) t.Run("get sub range failure", func(t *testing.T) { - gErr := internal.Error("test error for get range") + gErr := errors.New("test error for get range") rt := newRawRangeHashInfo() rt.setTTL(service.NonForwardingTTL) @@ -419,7 +419,7 @@ func Test_straightRangeReceiver_getRange(t *testing.T) { req := new(transportRequest) t.Run("executor error", func(t *testing.T) { - exErr := internal.Error("test error for executor") + exErr := errors.New("test error for executor") s := &straightRangeReceiver{ executor: &testRangeEntity{ @@ -466,14 +466,14 @@ func Test_coreRngRevealer_reveal(t *testing.T) { } t.Run("charybdis chopper presence", func(t *testing.T) { - cErr := internal.Error("test error for charybdis") + cErr := errors.New("test error for charybdis") s := &coreRngRevealer{ chopTable: &testRangeEntity{ f: func(items ...interface{}) { t.Run("correct chopper table params", func(t *testing.T) { require.Equal(t, rd.Addr, items[0]) - require.Equal(t, objio.RCCharybdis, items[1]) + require.Equal(t, _range.RCCharybdis, items[1]) }) }, res: &testRangeEntity{ @@ -496,7 +496,7 @@ func Test_coreRngRevealer_reveal(t *testing.T) { }) t.Run("scylla chopper presence", func(t *testing.T) { - scErr := internal.Error("test error for scylla") + scErr := errors.New("test error for scylla") scylla := &testRangeEntity{ err: scErr, // force RangeChopper to return scErr @@ -505,8 +505,8 @@ func Test_coreRngRevealer_reveal(t *testing.T) { ct := new(testRangeEntity) ct.f = func(items ...interface{}) { - if items[1].(objio.RCType) == objio.RCCharybdis { - ct.err = internal.Error("") + if items[1].(_range.RCType) == _range.RCCharybdis { + ct.err = errors.New("") } else { ct.res = scylla ct.err = nil @@ -527,7 +527,7 @@ func Test_coreRngRevealer_reveal(t *testing.T) { s := &coreRngRevealer{ relativeRecv: nil, // pass empty relation receiver to fail constructor chopTable: &testRangeEntity{ - err: internal.Error(""), // force ChopperTable to return non-nil error + err: errors.New(""), // force ChopperTable to return non-nil error }, } @@ -537,13 +537,13 @@ func Test_coreRngRevealer_reveal(t *testing.T) { }) t.Run("success", func(t *testing.T) { - rrErr := internal.Error("test error for relative receiver") + rrErr := errors.New("test error for relative receiver") relRecv := &testRangeEntity{ err: rrErr, // force relative receiver to return rrErr } - scylla, err := objio.NewScylla(&objio.ChopperParams{ + scylla, err := _range.NewScylla(&_range.ChopperParams{ RelativeReceiver: relRecv, Addr: rd.Addr, }) @@ -562,7 +562,7 @@ func Test_coreRngRevealer_reveal(t *testing.T) { } }) }, - err: internal.Error(""), // force ChopperTable to return non-nil error + err: errors.New(""), // force ChopperTable to return non-nil error }, } diff --git a/services/public/object/response.go b/pkg/network/transport/object/grpc/response.go similarity index 92% rename from services/public/object/response.go rename to pkg/network/transport/object/grpc/response.go index 37f08676..8e08941f 100644 --- a/services/public/object/response.go +++ b/pkg/network/transport/object/grpc/response.go @@ -3,9 +3,9 @@ package object import ( "context" - "github.com/nspcc-dev/neofs-api-go/acl" + eacl "github.com/nspcc-dev/neofs-api-go/acl/extended" "github.com/nspcc-dev/neofs-api-go/object" - libacl "github.com/nspcc-dev/neofs-node/lib/acl" + eaclstorage "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage" ) type ( @@ -27,7 +27,7 @@ type complexResponsePreparer struct { } type aclResponsePreparer struct { - eaclSrc libacl.ExtendedACLSource + eaclSrc eaclstorage.Storage aclInfoReceiver aclInfoReceiver @@ -95,7 +95,7 @@ func (s *aclResponsePreparer) prepareResponse(ctx context.Context, req serviceRe objHdrSrc: headersFromObject{ obj: obj, }, - target: aclInfo.target, + group: aclInfo.targetInfo.group, } if aclInfo.checkBearer { @@ -104,7 +104,7 @@ func (s *aclResponsePreparer) prepareResponse(ctx context.Context, req serviceRe } } - if action := s.reqActCalc.calculateRequestAction(ctx, p); action != acl.ActionAllow { + if action := s.reqActCalc.calculateRequestAction(ctx, p); action != eacl.ActionAllow { return errAccessDenied } diff --git a/services/public/object/response_test.go b/pkg/network/transport/object/grpc/response_test.go similarity index 100% rename from services/public/object/response_test.go rename to pkg/network/transport/object/grpc/response_test.go diff --git a/services/public/object/search.go b/pkg/network/transport/object/grpc/search.go similarity index 97% rename from services/public/object/search.go rename to pkg/network/transport/object/grpc/search.go index 39771ddd..cb6b35d2 100644 --- a/services/public/object/search.go +++ b/pkg/network/transport/object/grpc/search.go @@ -6,7 +6,7 @@ import ( "github.com/nspcc-dev/neofs-api-go/object" v1 "github.com/nspcc-dev/neofs-api-go/query" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" "go.uber.org/zap" ) diff --git a/services/public/object/search_test.go b/pkg/network/transport/object/grpc/search_test.go similarity index 95% rename from services/public/object/search_test.go rename to pkg/network/transport/object/grpc/search_test.go index dc65edef..bd1e7367 100644 --- a/services/public/object/search_test.go +++ b/pkg/network/transport/object/grpc/search_test.go @@ -6,8 +6,8 @@ import ( "github.com/nspcc-dev/neofs-api-go/object" v1 "github.com/nspcc-dev/neofs-api-go/query" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/pkg/errors" "github.com/stretchr/testify/require" ) @@ -140,7 +140,7 @@ func TestObjectService_Search(t *testing.T) { addrList := testAddrList(t, int(addrPerMsg)+5) t.Run("request handler failure", func(t *testing.T) { - rhErr := internal.Error("test error for request handler") + rhErr := errors.New("test error for request handler") s := &objectService{ statusCalculator: newStatusCalculator(), } @@ -158,7 +158,7 @@ func TestObjectService_Search(t *testing.T) { }) t.Run("server error", func(t *testing.T) { - srvErr := internal.Error("test error for search server") + srvErr := errors.New("test error for search server") resp := &object.SearchResponse{Addresses: addrList[:addrPerMsg]} @@ -230,7 +230,7 @@ func Test_coreObjectSearcher(t *testing.T) { req.setQuery(testData(t, 10)) t.Run("operation executor failure", func(t *testing.T) { - execErr := internal.Error("test error for operation executor") + execErr := errors.New("test error for operation executor") s := &coreObjectSearcher{ executor: &testSearchEntity{ diff --git a/services/public/object/service.go b/pkg/network/transport/object/grpc/service.go similarity index 79% rename from services/public/object/service.go rename to pkg/network/transport/object/grpc/service.go index 87e12007..142a9ee9 100644 --- a/services/public/object/service.go +++ b/pkg/network/transport/object/grpc/service.go @@ -13,16 +13,20 @@ import ( "github.com/nspcc-dev/neofs-api-go/service" "github.com/nspcc-dev/neofs-api-go/session" "github.com/nspcc-dev/neofs-api-go/storagegroup" - "github.com/nspcc-dev/neofs-node/internal" - libacl "github.com/nspcc-dev/neofs-node/lib/acl" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/ir" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/objio" - "github.com/nspcc-dev/neofs-node/lib/objutil" - "github.com/nspcc-dev/neofs-node/lib/transformer" - "github.com/nspcc-dev/neofs-node/modules/grpc" + eaclstorage "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/extended/storage" + "github.com/nspcc-dev/neofs-node/pkg/core/container/storage" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap/wrapper" + contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap/wrapper" + "github.com/nspcc-dev/neofs-node/pkg/network/transport/grpc" + libgrpc "github.com/nspcc-dev/neofs-node/pkg/network/transport/grpc" + _range "github.com/nspcc-dev/neofs-node/pkg/network/transport/object/grpc/range" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement" + storage2 "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/replication/storage" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transformer" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + storagegroup2 "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport/storagegroup" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/verifier" "github.com/panjf2000/ants/v2" "github.com/pkg/errors" "go.uber.org/zap" @@ -127,6 +131,8 @@ type ( SetHeaders([]service.RequestExtendedHeader_KV) } + NetmapClient = wrapper.Wrapper + // Params groups the parameters of Object service server's constructor. Params struct { CheckACL bool @@ -143,12 +149,12 @@ type ( Placer Placer ObjectRestorer transformer.ObjectRestorer RemoteService RemoteService - AddressStore implementations.AddressStoreComponent + AddressStore storage2.AddressStoreComponent Logger *zap.Logger TokenStore session.PrivateTokenStore EpochReceiver EpochReceiver - implementations.ContainerNodesLister + PlacementWrapper *placement.PlacementWrapper DialTimeout time.Duration @@ -164,25 +170,21 @@ type ( headRecv objectReceiver - Verifier objutil.Verifier + Verifier verifier.Verifier Transformer transformer.Transformer MaxPayloadSize uint64 // ACL pre-processor params - ACLHelper implementations.ACLHelper - BasicACLChecker libacl.BasicChecker - IRStorage ir.Storage - ContainerLister implementations.ContainerNodesLister + ContainerStorage storage.Storage + NetmapClient *NetmapClient SGInfoReceiver storagegroup.InfoReceiver - OwnerKeyVerifier core.OwnerKeyVerifier + ExtendedACLSource eaclstorage.Storage - ExtendedACLSource libacl.ExtendedACLSource - - requestActionCalculator + requestActionCalculator requestActionCalculator targetFinder RequestTargeter @@ -199,7 +201,7 @@ type ( ls localstore.Localstore storageCap uint64 - executor implementations.SelectiveContainerExecutor + executor transport.SelectiveContainerExecutor pPut OperationParams pGet OperationParams @@ -244,32 +246,30 @@ const ( readyObjectsCheckpointFilterName = "READY_OBJECTS_PUT_CHECKPOINT" allObjectsCheckpointFilterName = "ALL_OBJECTS_PUT_CHECKPOINT" +) - errEmptyTokenStore = internal.Error("objectService.New failed: key store not provided") - errEmptyPlacer = internal.Error("objectService.New failed: placer not provided") - errEmptyTransformer = internal.Error("objectService.New failed: transformer pipeline not provided") - errEmptyGRPC = internal.Error("objectService.New failed: gRPC connector not provided") - errEmptyAddress = internal.Error("objectService.New failed: address store not provided") - errEmptyLogger = internal.Error("objectService.New failed: logger not provided") - errEmptyEpochReceiver = internal.Error("objectService.New failed: epoch receiver not provided") - errEmptyLocalStore = internal.Error("new local client failed: localstore passed") - errEmptyPrivateKey = internal.Error("objectService.New failed: private key not provided") - errEmptyVerifier = internal.Error("objectService.New failed: object verifier not provided") - errEmptyACLHelper = internal.Error("objectService.New failed: ACL helper not provided") - errEmptyBasicACLChecker = internal.Error("objectService.New failed: basic ACL checker not provided") - errEmptyCnrLister = internal.Error("objectService.New failed: container lister not provided") - errEmptySGInfoRecv = internal.Error("objectService.New failed: SG info receiver not provided") - - errInvalidCIDFilter = internal.Error("invalid CID filter") - - errTokenRetrieval = internal.Error("objectService.Put failed on token retrieval") - - errHeaderExpected = internal.Error("expected header as a first message in stream") +var ( + errEmptyTokenStore = errors.New("objectService.New failed: key store not provided") + errEmptyPlacer = errors.New("objectService.New failed: placer not provided") + errEmptyTransformer = errors.New("objectService.New failed: transformer pipeline not provided") + errEmptyGRPC = errors.New("objectService.New failed: gRPC connector not provided") + errEmptyAddress = errors.New("objectService.New failed: address store not provided") + errEmptyLogger = errors.New("objectService.New failed: logger not provided") + errEmptyEpochReceiver = errors.New("objectService.New failed: epoch receiver not provided") + errEmptyLocalStore = errors.New("new local client failed: localstore passed") + errEmptyPrivateKey = errors.New("objectService.New failed: private key not provided") + errEmptyVerifier = errors.New("objectService.New failed: object verifier not provided") + errEmptyACLHelper = errors.New("objectService.New failed: ACL helper not provided") + errEmptyCnrLister = errors.New("objectService.New failed: container lister not provided") + errEmptySGInfoRecv = errors.New("objectService.New failed: SG info receiver not provided") + errInvalidCIDFilter = errors.New("invalid CID filter") + errTokenRetrieval = errors.New("objectService.Put failed on token retrieval") + errHeaderExpected = errors.New("expected header as a first message in stream") ) var requestSignFunc = service.SignRequestData -var requestVerifyFunc = core.VerifyRequestWithSignatures +var requestVerifyFunc = libgrpc.VerifyRequestWithSignatures // New is an Object service server's constructor. func New(p *Params) (Service, error) { @@ -330,20 +330,16 @@ func New(p *Params) (Service, error) { return nil, errEmptyPrivateKey case p.Verifier == nil: return nil, errEmptyVerifier - case p.IRStorage == nil: - return nil, ir.ErrNilStorage - case p.ContainerLister == nil: + case p.NetmapClient == nil: + return nil, contract.ErrNilWrapper + case p.PlacementWrapper == nil: return nil, errEmptyCnrLister - case p.ACLHelper == nil: - return nil, errEmptyACLHelper - case p.BasicACLChecker == nil: - return nil, errEmptyBasicACLChecker + case p.ContainerStorage == nil: + return nil, storage.ErrNilStorage case p.SGInfoReceiver == nil: return nil, errEmptySGInfoRecv - case p.OwnerKeyVerifier == nil: - return nil, core.ErrNilOwnerKeyVerifier case p.ExtendedACLSource == nil: - return nil, libacl.ErrNilBinaryExtendedACLStore + return nil, eaclstorage.ErrNilStorage } pool, err := ants.NewPool(p.PoolSize) @@ -364,22 +360,20 @@ func New(p *Params) (Service, error) { } p.targetFinder = &targetFinder{ - log: p.Logger, - irStorage: p.IRStorage, - cnrLister: p.ContainerLister, - cnrOwnerChecker: p.ACLHelper, + log: p.Logger, + irKeysRecv: p.NetmapClient, + cnrLister: p.PlacementWrapper, + cnrStorage: p.ContainerStorage, } p.requestActionCalculator = &reqActionCalc{ - extACLChecker: libacl.NewExtendedACLChecker(), + storage: p.ExtendedACLSource, log: p.Logger, } p.aclInfoReceiver = aclInfoReceiver{ - basicACLGetter: p.ACLHelper, - - basicChecker: p.BasicACLChecker, + cnrStorage: p.ContainerStorage, targetFinder: p.targetFinder, } @@ -440,13 +434,13 @@ func New(p *Params) (Service, error) { return nil, err } - exec, err := implementations.NewContainerTraverseExecutor(tr) + exec, err := transport.NewContainerTraverseExecutor(tr) if err != nil { return nil, err } - srv.executor, err = implementations.NewObjectContainerHandler(implementations.ObjectContainerHandlerParams{ - NodeLister: p.ContainerNodesLister, + srv.executor, err = transport.NewObjectContainerHandler(transport.ObjectContainerHandlerParams{ + NodeLister: p.PlacementWrapper, Executor: exec, Logger: p.Logger, }) @@ -522,7 +516,7 @@ func New(p *Params) (Service, error) { timeout: p.HeadParams.Timeout, } - chopperTable := objio.NewChopperTable() + chopperTable := _range.NewChopperTable() relRecv := &neighborReceiver{ firstChildQueryFn: firstChildQueryFunc, @@ -594,7 +588,7 @@ func New(p *Params) (Service, error) { mErr: map[error]struct{}{ transformer.ErrInvalidSGLinking: {}, - implementations.ErrIncompleteSGInfo: {}, + storagegroup2.ErrIncompleteSGInfo: {}, }, } @@ -605,7 +599,7 @@ func New(p *Params) (Service, error) { filter: filter, objStorer: &receivingObjectStorer{ straightStorer: straightStorer, - vPayload: implementations.NewPayloadVerifier(), + vPayload: storage2.NewPayloadVerifier(), }, }, tokenStorer: &tokenObjectStorer{ diff --git a/services/public/object/status.go b/pkg/network/transport/object/grpc/status.go similarity index 79% rename from services/public/object/status.go rename to pkg/network/transport/object/grpc/status.go index f8389c37..212ab27a 100644 --- a/services/public/object/status.go +++ b/pkg/network/transport/object/grpc/status.go @@ -7,10 +7,9 @@ import ( "github.com/golang/protobuf/proto" "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/session" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/transformer" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transformer" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport/storagegroup" "github.com/pkg/errors" "google.golang.org/genproto/googleapis/rpc/errdetails" "google.golang.org/grpc/codes" @@ -51,66 +50,57 @@ type statusCalculator struct { const panicLogMsg = "rpc handler caused panic" -const ( - msgServerPanic = "panic occurred during request processing" - errServerPanic = internal.Error("panic on call handler") -) +const msgServerPanic = "panic occurred during request processing" -const ( - msgUnauthenticated = "request does not have valid authentication credentials for the operation" - errUnauthenticated = internal.Error("unauthenticated request") -) +var errServerPanic = errors.New("panic on call handler") -const ( - msgReSigning = "server could not re-sign request" - errReSigning = internal.Error("could not re-sign request") -) +const msgUnauthenticated = "request does not have valid authentication credentials for the operation" -const ( - msgInvalidTTL = "invalid TTL value" - errInvalidTTL = internal.Error("invalid TTL value") -) +var errUnauthenticated = errors.New("unauthenticated request") + +const msgReSigning = "server could not re-sign request" + +var errReSigning = errors.New("could not re-sign request") + +const msgInvalidTTL = "invalid TTL value" + +var errInvalidTTL = errors.New("invalid TTL value") const ( msgNotLocalContainer = "server is not presented in container" - errNotLocalContainer = internal.Error("not local container") descNotLocalContainer = "server is outside container" ) -const ( - msgContainerAffiliationProblem = "server could not check container affiliation" - errContainerAffiliationProblem = internal.Error("could not check container affiliation") -) +var errNotLocalContainer = errors.New("not local container") + +const msgContainerAffiliationProblem = "server could not check container affiliation" + +var errContainerAffiliationProblem = errors.New("could not check container affiliation") const ( msgContainerNotFound = "container not found" - errContainerNotFound = internal.Error("container not found") descContainerNotFound = "handling a non-existent container" ) -const ( - msgPlacementProblem = "there were problems building the placement vector on the server" - errPlacementProblem = internal.Error("could not traverse over container") -) +var errContainerNotFound = errors.New("container not found") -const ( - msgOverloaded = "system resource overloaded" - errOverloaded = internal.Error("system resource overloaded") -) +const msgPlacementProblem = "there were problems building the placement vector on the server" -const ( - msgAccessDenied = "access to requested operation is denied" - errAccessDenied = internal.Error("access denied") -) +var errPlacementProblem = errors.New("could not traverse over container") -const ( - msgPutMessageProblem = "invalid message type" - msgPutNilObject = "object is null" -) +const msgOverloaded = "system resource overloaded" -const ( - msgCutObjectPayload = "lack of object payload data" -) +var errOverloaded = errors.New("system resource overloaded") + +const msgAccessDenied = "access to requested operation is denied" + +var errAccessDenied = errors.New("access denied") + +const msgPutMessageProblem = "invalid message type" + +var msgPutNilObject = "object is null" + +const msgCutObjectPayload = "lack of object payload data" const ( msgMissingTokenKeys = "missing public keys in token" @@ -118,116 +108,77 @@ const ( msgTokenObjectID = "missing object ID in token" ) -const ( - msgProcPayloadSize = "max payload size of processing object overflow" - errProcPayloadSize = internal.Error("max processing object payload size overflow") -) +const msgProcPayloadSize = "max payload size of processing object overflow" -const ( - msgObjectCreationEpoch = "invalid creation epoch of object" - errObjectFromTheFuture = internal.Error("object from the future") -) +var errProcPayloadSize = errors.New("max processing object payload size overflow") -const ( - msgObjectPayloadSize = "max object payload size overflow" - errObjectPayloadSize = internal.Error("max object payload size overflow") -) +const msgObjectCreationEpoch = "invalid creation epoch of object" -const ( - msgLocalStorageOverflow = "not enough space in local storage" - errLocalStorageOverflow = internal.Error("local storage overflow") -) +var errObjectFromTheFuture = errors.New("object from the future") -const ( - msgPayloadChecksum = "invalid payload checksum" - errPayloadChecksum = internal.Error("invalid payload checksum") -) +const msgObjectPayloadSize = "max object payload size overflow" -const ( - msgObjectHeadersVerification = "object headers failed verification" - errObjectHeadersVerification = internal.Error("object headers failed verification") -) +var errObjectPayloadSize = errors.New("max object payload size overflow") -const ( - msgForwardPutObject = "forward object failure" -) +const msgLocalStorageOverflow = "not enough space in local storage" -const ( - msgPutLocalFailure = "local object put failure" - errPutLocal = internal.Error("local object put failure") -) +var errLocalStorageOverflow = errors.New("local storage overflow") -const ( - msgPrivateTokenRecv = "private token receive failure" -) +const msgPayloadChecksum = "invalid payload checksum" -const ( - msgInvalidSGLinking = "invalid storage group headers" -) +var errPayloadChecksum = errors.New("invalid payload checksum") -const ( - msgIncompleteSGInfo = "collect storage group info failure" -) +const msgObjectHeadersVerification = "object headers failed verification" -const ( - msgTransformationFailure = "object preparation failure" -) +var errObjectHeadersVerification = errors.New("object headers failed verification") -const ( - msgWrongSGSize = "wrong storage group size" - errWrongSGSize = internal.Error("wrong storage group size") -) +const msgForwardPutObject = "forward object failure" -const ( - msgWrongSGHash = "wrong storage group homomorphic hash" - errWrongSGHash = internal.Error("wrong storage group homomorphic hash") -) +const msgPutLocalFailure = "local object put failure" -const ( - msgObjectNotFound = "object not found" -) +var errPutLocal = errors.New("local object put failure") -const ( - msgObjectHeaderNotFound = "object header not found" -) +const msgPrivateTokenRecv = "private token receive failure" -const ( - msgNonAssembly = "assembly option is not enabled on the server" -) +const msgInvalidSGLinking = "invalid storage group headers" -const ( - msgPayloadOutOfRange = "range is out of object payload bounds" -) +const msgIncompleteSGInfo = "collect storage group info failure" -const ( - msgPayloadRangeNotFound = "object payload range not found" - errPayloadRangeNotFound = internal.Error("object payload range not found") -) +const msgTransformationFailure = "object preparation failure" -const ( - msgMissingToken = "missing token in request" -) +const msgWrongSGSize = "wrong storage group size" -const ( - msgPutTombstone = "could not store tombstone" -) +var errWrongSGSize = errors.New("wrong storage group size") -const ( - msgDeletePrepare = "delete information preparation failure" - errDeletePrepare = internal.Error("delete information preparation failure") -) +const msgWrongSGHash = "wrong storage group homomorphic hash" -const ( - msgQueryVersion = "unsupported query version" -) +var errWrongSGHash = errors.New("wrong storage group homomorphic hash") -const ( - msgSearchQueryUnmarshal = "query unmarshal failure" -) +const msgObjectNotFound = "object not found" -const ( - msgLocalQueryImpose = "local query imposing failure" -) +const msgObjectHeaderNotFound = "object header not found" + +const msgNonAssembly = "assembly option is not enabled on the server" + +const msgPayloadOutOfRange = "range is out of object payload bounds" + +const msgPayloadRangeNotFound = "object payload range not found" + +var errPayloadRangeNotFound = errors.New("object payload range not found") + +const msgMissingToken = "missing token in request" + +const msgPutTombstone = "could not store tombstone" + +const msgDeletePrepare = "delete information preparation failure" + +var errDeletePrepare = errors.New("delete information preparation failure") + +const msgQueryVersion = "unsupported query version" + +const msgSearchQueryUnmarshal = "query unmarshal failure" + +const msgLocalQueryImpose = "local query imposing failure" var mStatusCommon = map[error]*statusInfo{ // RPC implementation recovered panic @@ -424,7 +375,7 @@ var mStatusCustom = map[requestError]*statusInfo{ }, { t: object.RequestPut, - e: implementations.ErrIncompleteSGInfo, + e: storagegroup.ErrIncompleteSGInfo, }: { c: codes.NotFound, m: msgIncompleteSGInfo, diff --git a/services/public/object/status_test.go b/pkg/network/transport/object/grpc/status_test.go similarity index 96% rename from services/public/object/status_test.go rename to pkg/network/transport/object/grpc/status_test.go index b076fec8..d6c32cdb 100644 --- a/services/public/object/status_test.go +++ b/pkg/network/transport/object/grpc/status_test.go @@ -7,11 +7,11 @@ import ( "github.com/golang/protobuf/proto" "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/session" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/test" - "github.com/nspcc-dev/neofs-node/lib/transformer" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transformer" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport/storagegroup" + testlogger "github.com/nspcc-dev/neofs-node/pkg/util/logger/test" + "github.com/pkg/errors" "github.com/stretchr/testify/require" "google.golang.org/genproto/googleapis/rpc/errdetails" "google.golang.org/grpc/codes" @@ -26,7 +26,7 @@ func (*testPanickingHandler) handleRequest(context.Context, handleRequestParams) func TestStatusCalculator(t *testing.T) { t.Run("unknown error", func(t *testing.T) { - e := internal.Error("error for test") + e := errors.New("error for test") s := newStatusCalculator() @@ -51,7 +51,7 @@ func TestStatusCalculator(t *testing.T) { s := newStatusCalculator() - e := internal.Error("error for test") + e := errors.New("error for test") s.addCommon(e, v) @@ -70,7 +70,7 @@ func TestStatusCalculator(t *testing.T) { var ( c1, c2 = codes.Aborted, codes.AlreadyExists t1, t2 = object.RequestPut, object.RequestGet - e1, e2 = internal.Error("test error 1"), internal.Error("test error 2") + e1, e2 = errors.New("test error 1"), errors.New("test error 2") m1, m2 = "message 1", "message 2" ) @@ -122,7 +122,7 @@ func testStatusCommon(t *testing.T, h requestHandler, c codes.Code, m string, d ctx := context.TODO() s := &objectService{ - log: test.NewTestLogger(false), + log: testlogger.NewLogger(false), requestHandler: h, statusCalculator: serviceStatusCalculator(), } @@ -329,7 +329,7 @@ func TestStatusCommon(t *testing.T) { func testStatusPut(t *testing.T, h requestHandler, srv object.Service_PutServer, info statusInfo, d []interface{}) { s := &objectService{ - log: test.NewTestLogger(false), + log: testlogger.NewLogger(false), requestHandler: h, statusCalculator: serviceStatusCalculator(), } @@ -574,7 +574,7 @@ func TestStatusPut(t *testing.T) { }) t.Run("invalid object header structure", func(t *testing.T) { - e := internal.Error("test error") + e := errors.New("test error") ds := make([]interface{}, 0) @@ -680,7 +680,7 @@ func TestStatusPut(t *testing.T) { } h := &testPutEntity{ - err: implementations.ErrIncompleteSGInfo, + err: storagegroup.ErrIncompleteSGInfo, } info := statusInfo{ @@ -769,7 +769,7 @@ func TestStatusPut(t *testing.T) { func testStatusGet(t *testing.T, h requestHandler, srv object.Service_GetServer, info statusInfo, d []interface{}) { s := &objectService{ - log: test.NewTestLogger(false), + log: testlogger.NewLogger(false), requestHandler: h, statusCalculator: serviceStatusCalculator(), } @@ -839,7 +839,7 @@ func TestStatusGet(t *testing.T) { func testStatusHead(t *testing.T, h requestHandler, info statusInfo, d []interface{}) { s := &objectService{ - log: test.NewTestLogger(false), + log: testlogger.NewLogger(false), requestHandler: h, statusCalculator: serviceStatusCalculator(), } @@ -903,7 +903,7 @@ func TestStatusHead(t *testing.T) { func testStatusGetRange(t *testing.T, h requestHandler, srv object.Service_GetRangeServer, info statusInfo, d []interface{}) { s := &objectService{ - log: test.NewTestLogger(false), + log: testlogger.NewLogger(false), requestHandler: h, statusCalculator: serviceStatusCalculator(), } @@ -956,7 +956,7 @@ func TestStatusGetRange(t *testing.T) { func testStatusDelete(t *testing.T, h requestHandler, info statusInfo, d []interface{}) { s := &objectService{ - log: test.NewTestLogger(false), + log: testlogger.NewLogger(false), requestHandler: h, statusCalculator: serviceStatusCalculator(), } @@ -1092,7 +1092,7 @@ func TestStatusDelete(t *testing.T) { func testStatusSearch(t *testing.T, h requestHandler, srv object.Service_SearchServer, info statusInfo, d []interface{}) { s := &objectService{ - log: test.NewTestLogger(false), + log: testlogger.NewLogger(false), requestHandler: h, statusCalculator: serviceStatusCalculator(), } @@ -1162,7 +1162,7 @@ func TestStatusSearch(t *testing.T) { func testStatusGetRangeHash(t *testing.T, h requestHandler, info statusInfo, d []interface{}) { s := &objectService{ - log: test.NewTestLogger(false), + log: testlogger.NewLogger(false), requestHandler: h, statusCalculator: serviceStatusCalculator(), } diff --git a/services/public/object/token.go b/pkg/network/transport/object/grpc/token.go similarity index 83% rename from services/public/object/token.go rename to pkg/network/transport/object/grpc/token.go index 81c54370..989a8cfd 100644 --- a/services/public/object/token.go +++ b/pkg/network/transport/object/grpc/token.go @@ -6,8 +6,8 @@ import ( "github.com/nspcc-dev/neofs-api-go/service" crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/core" + "github.com/nspcc-dev/neofs-node/pkg/services/id" + "github.com/pkg/errors" ) type sessionTokenVerifier interface { @@ -27,16 +27,14 @@ type tokenEpochsVerifier struct { } type tokenPreProcessor struct { - keyVerifier core.OwnerKeyVerifier - staticVerifier sessionTokenVerifier } -const errCreatedAfterExpiration = internal.Error("creation epoch number is greater than expired one") +var errCreatedAfterExpiration = errors.New("creation epoch number is greater than expired one") -const errTokenExpired = internal.Error("token is expired") +var errTokenExpired = errors.New("token is expired") -const errForbiddenSpawn = internal.Error("request spawn is forbidden") +var errForbiddenSpawn = errors.New("request spawn is forbidden") func (s tokenPreProcessor) preProcess(ctx context.Context, req serviceRequest) error { token := req.GetSessionToken() @@ -48,7 +46,7 @@ func (s tokenPreProcessor) preProcess(ctx context.Context, req serviceRequest) e return errForbiddenSpawn } - if err := s.keyVerifier.VerifyKey(ctx, token); err != nil { + if err := id.VerifyKey(token); err != nil { return err } diff --git a/services/public/object/token_test.go b/pkg/network/transport/object/grpc/token_test.go similarity index 82% rename from services/public/object/token_test.go rename to pkg/network/transport/object/grpc/token_test.go index e07ccb85..d26bf3fb 100644 --- a/services/public/object/token_test.go +++ b/pkg/network/transport/object/grpc/token_test.go @@ -6,8 +6,10 @@ import ( "testing" "github.com/nspcc-dev/neofs-api-go/object" + "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-node/lib/core" + crypto "github.com/nspcc-dev/neofs-crypto" + "github.com/nspcc-dev/neofs-node/pkg/util/test" "github.com/stretchr/testify/require" ) @@ -25,13 +27,6 @@ type testTokenEntity struct { err error } -func (s testTokenEntity) VerifyKey(_ context.Context, p core.OwnerKeyContainer) error { - if s.f != nil { - s.f(p) - } - return s.err -} - func (s testTokenEntity) Epoch() uint64 { return s.res.(uint64) } @@ -69,39 +64,42 @@ func TestTokenPreProcessor(t *testing.T) { }) t.Run("owner key verifier failure", func(t *testing.T) { - verifierErr := errors.New("test error for key verifier") + ownerKey := &test.DecodeKey(0).PublicKey + + ownerID, err := refs.NewOwnerID(ownerKey) + require.NoError(t, err) - owner := OwnerID{1, 2, 3} token := new(service.Token) - token.SetOwnerID(owner) + token.SetOwnerID(ownerID) + + ownerKeyBytes := crypto.MarshalPublicKey(ownerKey) + ownerKeyBytes[0]++ + token.SetOwnerKey(ownerKeyBytes) req := new(object.PutRequest) req.SetToken(token) - s := &tokenPreProcessor{ - keyVerifier: &testTokenEntity{ - f: func(items ...interface{}) { - require.Equal(t, token, items[0]) - }, - err: verifierErr, - }, - } + s := new(tokenPreProcessor) - require.EqualError(t, s.preProcess(ctx, req), verifierErr.Error()) + require.Error(t, s.preProcess(ctx, req)) }) t.Run("static verifier error", func(t *testing.T) { vErr := errors.New("test error for static verifier") - owner := OwnerID{1, 2, 3} + ownerKey := &test.DecodeKey(0).PublicKey + + ownerID, err := refs.NewOwnerID(ownerKey) + require.NoError(t, err) + token := new(service.Token) - token.SetOwnerID(owner) + token.SetOwnerID(ownerID) + token.SetOwnerKey(crypto.MarshalPublicKey(ownerKey)) req := new(object.PutRequest) req.SetToken(token) s := &tokenPreProcessor{ - keyVerifier: new(testTokenEntity), staticVerifier: &testTokenEntity{ f: func(items ...interface{}) { require.Equal(t, token, items[0]) diff --git a/services/public/object/transport_implementations.go b/pkg/network/transport/object/grpc/transport_implementations.go similarity index 98% rename from services/public/object/transport_implementations.go rename to pkg/network/transport/object/grpc/transport_implementations.go index 3c85ce05..6666c76c 100644 --- a/services/public/object/transport_implementations.go +++ b/pkg/network/transport/object/grpc/transport_implementations.go @@ -13,8 +13,8 @@ import ( "github.com/nspcc-dev/neofs-api-go/service" "github.com/nspcc-dev/neofs-api-go/session" crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/replication/storage" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" "github.com/pkg/errors" "go.uber.org/zap" ) @@ -22,7 +22,7 @@ import ( type ( // MultiTransportParams groups the parameters for object transport component's constructor. MultiTransportParams struct { - AddressStore implementations.AddressStoreComponent + AddressStore storage.AddressStoreComponent EpochReceiver EpochReceiver RemoteService RemoteService Logger *zap.Logger @@ -89,7 +89,7 @@ type ( coreRequestSender struct { requestPrep transportRequestPreparer - addressStore implementations.AddressStoreComponent + addressStore storage.AddressStoreComponent remoteService RemoteService putTimeout time.Duration diff --git a/services/public/object/transport_test.go b/pkg/network/transport/object/grpc/transport_test.go similarity index 100% rename from services/public/object/transport_test.go rename to pkg/network/transport/object/grpc/transport_test.go diff --git a/services/public/object/traverse.go b/pkg/network/transport/object/grpc/traverse.go similarity index 97% rename from services/public/object/traverse.go rename to pkg/network/transport/object/grpc/traverse.go index 38ec9d8b..1f4c272f 100644 --- a/services/public/object/traverse.go +++ b/pkg/network/transport/object/grpc/traverse.go @@ -5,7 +5,7 @@ import ( "sync" "github.com/nspcc-dev/neofs-api-go/container" - "github.com/nspcc-dev/neofs-node/lib/implementations" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" "github.com/multiformats/go-multiaddr" "github.com/pkg/errors" @@ -13,7 +13,7 @@ import ( type ( containerTraverser interface { - implementations.Traverser + transport.Traverser add(multiaddr.Multiaddr, bool) done(multiaddr.Multiaddr) bool finished() bool diff --git a/services/public/object/traverse_test.go b/pkg/network/transport/object/grpc/traverse_test.go similarity index 98% rename from services/public/object/traverse_test.go rename to pkg/network/transport/object/grpc/traverse_test.go index 93462b20..9a6a5cdb 100644 --- a/services/public/object/traverse_test.go +++ b/pkg/network/transport/object/grpc/traverse_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/multiformats/go-multiaddr" - "github.com/nspcc-dev/neofs-node/internal" + "github.com/pkg/errors" "github.com/stretchr/testify/require" "go.uber.org/zap" ) @@ -69,7 +69,7 @@ func Test_coreCnrAffChecker_buildPlacement(t *testing.T) { require.True(t, items[1].(bool)) require.Equal(t, nodes, items[2].([]multiaddr.Multiaddr)) }, - err: internal.Error(""), // just to prevent panic + err: errors.New(""), // just to prevent panic }, log: zap.L(), } @@ -81,7 +81,7 @@ func Test_coreCnrAffChecker_buildPlacement(t *testing.T) { t.Run("placer error", func(t *testing.T) { s := &corePlacementUtil{ placementBuilder: &testTraverseEntity{ - err: internal.Error(""), // force Placer to return some error + err: errors.New(""), // force Placer to return some error }, log: zap.L(), } diff --git a/services/public/object/ttl.go b/pkg/network/transport/object/grpc/ttl.go similarity index 97% rename from services/public/object/ttl.go rename to pkg/network/transport/object/grpc/ttl.go index cdc8a574..031b9028 100644 --- a/services/public/object/ttl.go +++ b/pkg/network/transport/object/grpc/ttl.go @@ -6,7 +6,7 @@ import ( "github.com/nspcc-dev/neofs-api-go/container" "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-node/lib/implementations" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/replication/storage" "github.com/pkg/errors" "go.uber.org/zap" ) @@ -50,7 +50,7 @@ type ( prevNetMap bool // Local node net address store. - localAddrStore implementations.AddressStore + localAddrStore storage.AddressStore // Container nodes membership maintainer. placementBuilder Placer @@ -122,7 +122,7 @@ func (s *coreTTLCondPreparer) prepareTTLCondition(ctx context.Context, req objec return nil } - // get target container ID from request body + // get group container ID from request body cid := req.CID() // check local node affiliation to container diff --git a/services/public/object/ttl_test.go b/pkg/network/transport/object/grpc/ttl_test.go similarity index 95% rename from services/public/object/ttl_test.go rename to pkg/network/transport/object/grpc/ttl_test.go index 073d8795..a9c040ed 100644 --- a/services/public/object/ttl_test.go +++ b/pkg/network/transport/object/grpc/ttl_test.go @@ -11,8 +11,8 @@ import ( "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/implementations" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/replication/storage" + "github.com/pkg/errors" "github.com/stretchr/testify/require" "go.uber.org/zap" ) @@ -36,10 +36,10 @@ type ( ) var ( - _ ttlConditionPreparer = (*testTTLEntity)(nil) - _ implementations.AddressStore = (*testTTLEntity)(nil) - _ containerAffiliationChecker = (*testTTLEntity)(nil) - _ Placer = (*testTTLEntity)(nil) + _ ttlConditionPreparer = (*testTTLEntity)(nil) + _ storage.AddressStore = (*testTTLEntity)(nil) + _ containerAffiliationChecker = (*testTTLEntity)(nil) + _ Placer = (*testTTLEntity)(nil) ) func (s *testTTLEntity) SelfAddr() (multiaddr.Multiaddr, error) { @@ -92,7 +92,7 @@ func Test_ttlPreProcessor_preProcess(t *testing.T) { t.Run("correct processing", func(t *testing.T) { // create custom error - pErr := internal.Error("test error for processing func") + pErr := errors.New("test error for processing func") // create custom ttlConditionPreparer condPreparer := &testTTLEntity{ @@ -278,7 +278,7 @@ func Test_coreCnrAffChecker_affiliated(t *testing.T) { t.Run("local network address store error", func(t *testing.T) { // create custom error for test - saErr := internal.Error("test error for self addr store") + saErr := errors.New("test error for self addr store") s := &corePlacementUtil{ localAddrStore: &testTTLEntity{ @@ -310,7 +310,7 @@ func Test_coreCnrAffChecker_affiliated(t *testing.T) { }, } - pb.err = internal.Error("") // force Placer to return some non-nil error + pb.err = errors.New("") // force Placer to return some non-nil error s := &corePlacementUtil{ prevNetMap: true, diff --git a/services/public/object/verb.go b/pkg/network/transport/object/grpc/verb.go similarity index 100% rename from services/public/object/verb.go rename to pkg/network/transport/object/grpc/verb.go diff --git a/services/public/object/verb_test.go b/pkg/network/transport/object/grpc/verb_test.go similarity index 100% rename from services/public/object/verb_test.go rename to pkg/network/transport/object/grpc/verb_test.go diff --git a/services/public/object/verification.go b/pkg/network/transport/object/grpc/verification.go similarity index 100% rename from services/public/object/verification.go rename to pkg/network/transport/object/grpc/verification.go diff --git a/services/public/object/verification_test.go b/pkg/network/transport/object/grpc/verification_test.go similarity index 94% rename from services/public/object/verification_test.go rename to pkg/network/transport/object/grpc/verification_test.go index b7c305c0..82adf0c1 100644 --- a/services/public/object/verification_test.go +++ b/pkg/network/transport/object/grpc/verification_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-node/internal" + "github.com/pkg/errors" "github.com/stretchr/testify/require" ) @@ -38,7 +38,7 @@ func Test_verifyPreProcessor_preProcess(t *testing.T) { t.Run("correct result", func(t *testing.T) { t.Run("failure", func(t *testing.T) { // create custom error - vErr := internal.Error("test error for verifying func") + vErr := errors.New("test error for verifying func") s := &verifyPreProcessor{ fVerify: func(service.RequestVerifyData) error { return vErr }, // force requestVerifyFunc to return vErr diff --git a/services/public/session/create.go b/pkg/network/transport/session/grpc/create.go similarity index 100% rename from services/public/session/create.go rename to pkg/network/transport/session/grpc/create.go diff --git a/services/public/session/service.go b/pkg/network/transport/session/grpc/service.go similarity index 92% rename from services/public/session/service.go rename to pkg/network/transport/session/grpc/service.go index 3accd079..5bf743b4 100644 --- a/services/public/session/service.go +++ b/pkg/network/transport/session/grpc/service.go @@ -2,8 +2,9 @@ package session import ( "github.com/nspcc-dev/neofs-api-go/session" - "github.com/nspcc-dev/neofs-node/modules/grpc" + libgrpc "github.com/nspcc-dev/neofs-node/pkg/network/transport/grpc" "go.uber.org/zap" + "google.golang.org/grpc" ) type ( @@ -16,7 +17,7 @@ type ( // Service is an interface of the server of Session service. Service interface { - grpc.Service + libgrpc.Service session.SessionServer } diff --git a/services/public/session/service_test.go b/pkg/network/transport/session/grpc/service_test.go similarity index 100% rename from services/public/session/service_test.go rename to pkg/network/transport/session/grpc/service_test.go diff --git a/services/public/state/service.go b/pkg/network/transport/state/grpc/service.go similarity index 91% rename from services/public/state/service.go rename to pkg/network/transport/state/grpc/service.go index 14d19c10..49d5af77 100644 --- a/services/public/state/service.go +++ b/pkg/network/transport/state/grpc/service.go @@ -11,10 +11,9 @@ import ( "github.com/nspcc-dev/neofs-api-go/service" "github.com/nspcc-dev/neofs-api-go/state" crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/modules/grpc" + contract "github.com/nspcc-dev/neofs-node/pkg/morph/client/netmap/wrapper" + "github.com/nspcc-dev/neofs-node/pkg/network/transport/grpc" + libgrpc "github.com/nspcc-dev/neofs-node/pkg/network/transport/grpc" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "github.com/spf13/viper" @@ -42,6 +41,8 @@ type ( NetworkState() *bootstrap.SpreadMap } + NetMapClient = contract.Wrapper + // Params groups the parameters of State service server's constructor. Params struct { Stater Stater @@ -54,7 +55,7 @@ type ( PrivateKey *ecdsa.PrivateKey - MorphNetmapContract *implementations.MorphNetmapContract + Client *NetMapClient } stateService struct { @@ -64,7 +65,7 @@ type ( private *ecdsa.PrivateKey owners map[refs.OwnerID]struct{} - stateUpdater *implementations.MorphNetmapContract + netMapClient *NetMapClient } // HealthRequest is a type alias of @@ -72,16 +73,16 @@ type ( HealthRequest = state.HealthRequest ) -const ( - errEmptyViper = internal.Error("empty config") - errEmptyLogger = internal.Error("empty logger") - errEmptyStater = internal.Error("empty stater") - errUnknownChangeState = internal.Error("received unknown state") +var ( + errEmptyViper = errors.New("empty config") + errEmptyLogger = errors.New("empty logger") + errEmptyStater = errors.New("empty stater") + errUnknownChangeState = errors.New("received unknown state") ) const msgMissingRequestInitiator = "missing request initiator" -var requestVerifyFunc = core.VerifyRequestWithSignatures +var requestVerifyFunc = libgrpc.VerifyRequestWithSignatures // New is an State service server's constructor. func New(p Params) (Service, error) { @@ -103,7 +104,7 @@ func New(p Params) (Service, error) { owners: fetchOwners(p.Logger, p.Viper), checkers: make([]HealthChecker, 0, len(p.Checkers)), - stateUpdater: p.MorphNetmapContract, + netMapClient: p.Client, } for i, checker := range p.Checkers { @@ -210,13 +211,10 @@ func (s *stateService) ChangeState(ctx context.Context, in *state.ChangeStateReq } // set update state parameters - p := implementations.UpdateStateParams{} - p.SetState(implementations.StateOffline) - p.SetKey( + if err := s.netMapClient.UpdatePeerState( crypto.MarshalPublicKey(&s.private.PublicKey), - ) - - if err := s.stateUpdater.UpdateState(p); err != nil { + contract.StateOffline, + ); err != nil { return nil, status.Error(codes.Aborted, err.Error()) } diff --git a/services/public/state/service_test.go b/pkg/network/transport/state/grpc/service_test.go similarity index 95% rename from services/public/state/service_test.go rename to pkg/network/transport/state/grpc/service_test.go index b3a27975..315fc84a 100644 --- a/services/public/state/service_test.go +++ b/pkg/network/transport/state/grpc/service_test.go @@ -14,7 +14,8 @@ import ( "github.com/nspcc-dev/neofs-api-go/service" "github.com/nspcc-dev/neofs-api-go/state" crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-node/lib/test" + testlogger "github.com/nspcc-dev/neofs-node/pkg/util/logger/test" + "github.com/nspcc-dev/neofs-node/pkg/util/test" "github.com/spf13/viper" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" @@ -61,7 +62,7 @@ func Test_nonForwarding(t *testing.T) { } func Test_fetchOwners(t *testing.T) { - l := test.NewTestLogger(false) + l := testlogger.NewLogger(false) t.Run("from config options", func(t *testing.T) { key0 := test.DecodeKey(0) @@ -213,7 +214,7 @@ func TestStateService_DumpVars(t *testing.T) { svc := stateService{owners: owners} expvar.NewString("test1").Set("test1") - expvar.NewString("test2").Set("test2") + expvar.NewString("test").Set("test") for i := range cases { tt := cases[i] @@ -238,8 +239,8 @@ func TestStateService_DumpVars(t *testing.T) { require.Contains(t, dump, "test1") require.Equal(t, dump["test1"], "test1") - require.Contains(t, dump, "test2") - require.Equal(t, dump["test2"], "test2") + require.Contains(t, dump, "test") + require.Equal(t, dump["test"], "test") default: require.EqualError(t, err, tt.err.Error()) require.Empty(t, res) diff --git a/lib/core/verify.go b/pkg/services/id/owner_key.go similarity index 58% rename from lib/core/verify.go rename to pkg/services/id/owner_key.go index 57b80663..ddb0789a 100644 --- a/lib/core/verify.go +++ b/pkg/services/id/owner_key.go @@ -1,41 +1,25 @@ -package core +package id import ( - "context" + "errors" "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-node/internal" + "github.com/nspcc-dev/neofs-node/pkg/core/container" ) +type OwnerID = container.OwnerID + // OwnerKeyContainer is an interface of the container of owner's ID and key pair with read access. type OwnerKeyContainer interface { - GetOwnerID() refs.OwnerID + GetOwnerID() OwnerID GetOwnerKey() []byte } -// OwnerKeyVerifier is an interface of OwnerKeyContainer validator. -type OwnerKeyVerifier interface { - // Must check if OwnerKeyContainer satisfies a certain criterion. - // Nil error is equivalent to matching the criterion. - VerifyKey(context.Context, OwnerKeyContainer) error -} - -type neoKeyVerifier struct{} - // ErrNilOwnerKeyContainer is returned by functions that expect a non-nil // OwnerKeyContainer, but received nil. -const ErrNilOwnerKeyContainer = internal.Error("owner-key container is nil") - -// ErrNilOwnerKeyVerifier is returned by functions that expect a non-nil -// OwnerKeyVerifier, but received nil. -const ErrNilOwnerKeyVerifier = internal.Error("owner-key verifier is nil") - -// NewNeoKeyVerifier creates a new Neo owner key verifier and return a OwnerKeyVerifier interface. -func NewNeoKeyVerifier() OwnerKeyVerifier { - return new(neoKeyVerifier) -} +var ErrNilOwnerKeyContainer = errors.New("owner-key container is nil") // VerifyKey checks if the public key converts to owner ID. // @@ -46,7 +30,7 @@ func NewNeoKeyVerifier() OwnerKeyVerifier { // case VerifyKey should call NeoFS.ID smart-contract to check whether public // key is bounded with owner ID. If there is no bound, then return // service.ErrWrongOwner. -func (s neoKeyVerifier) VerifyKey(_ context.Context, src OwnerKeyContainer) error { +func VerifyKey(src OwnerKeyContainer) error { if src == nil { return ErrNilOwnerKeyContainer } diff --git a/lib/metrics/meta.go b/pkg/services/metrics/meta.go similarity index 58% rename from lib/metrics/meta.go rename to pkg/services/metrics/meta.go index d11685a5..86f2a63d 100644 --- a/lib/metrics/meta.go +++ b/pkg/services/metrics/meta.go @@ -3,25 +3,25 @@ package metrics import ( "sync" - "github.com/nspcc-dev/neofs-node/lib/meta" + meta2 "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/meta" ) type metaWrapper struct { sync.Mutex - iter meta.Iterator + iter meta2.Iterator } func newMetaWrapper() *metaWrapper { return &metaWrapper{} } -func (m *metaWrapper) changeIter(iter meta.Iterator) { +func (m *metaWrapper) changeIter(iter meta2.Iterator) { m.Lock() m.iter = iter m.Unlock() } -func (m *metaWrapper) Iterate(h meta.IterateFunc) error { +func (m *metaWrapper) Iterate(h meta2.IterateFunc) error { m.Lock() defer m.Unlock() diff --git a/lib/metrics/metrics.go b/pkg/services/metrics/metrics.go similarity index 84% rename from lib/metrics/metrics.go rename to pkg/services/metrics/metrics.go index 143c66ac..ed9ce8bf 100644 --- a/lib/metrics/metrics.go +++ b/pkg/services/metrics/metrics.go @@ -2,14 +2,14 @@ package metrics import ( "context" + "errors" "sync" "time" "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/lib/meta" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket" + meta2 "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/meta" "go.uber.org/zap" ) @@ -20,7 +20,7 @@ type ( UpdateSpaceUsage() SetCounter(ObjectCounter) - SetIterator(iter meta.Iterator) + SetIterator(iter meta2.Iterator) UpdateContainer(cid refs.CID, size uint64, op SpaceOp) } @@ -41,7 +41,7 @@ type ( Options []string Logger *zap.Logger Interval time.Duration - MetricsStore core.Bucket + MetricsStore bucket.Bucket } // ObjectCounter is an interface of object number storage. @@ -60,15 +60,15 @@ type ( } ) -const ( - errEmptyCounter = internal.Error("empty object counter") - errEmptyLogger = internal.Error("empty logger") - errEmptyMetaStore = internal.Error("empty meta store") - errEmptyMetricsStore = internal.Error("empty metrics store") - - defaultMetricsInterval = 5 * time.Second +var ( + errEmptyCounter = errors.New("empty object counter") + errEmptyLogger = errors.New("empty logger") + errEmptyMetaStore = errors.New("empty meta store") + errEmptyMetricsStore = errors.New("empty metrics store") ) +const defaultMetricsInterval = 5 * time.Second + // New constructs metrics collector and returns Collector interface. func New(p Params) (Collector, error) { switch { @@ -122,7 +122,7 @@ func (c *collector) SetCounter(counter ObjectCounter) { c.counter.SetCounter(counter) } -func (c *collector) SetIterator(iter meta.Iterator) { +func (c *collector) SetIterator(iter meta2.Iterator) { c.metas.changeIter(iter) } diff --git a/lib/metrics/metrics_test.go b/pkg/services/metrics/metrics_test.go similarity index 95% rename from lib/metrics/metrics_test.go rename to pkg/services/metrics/metrics_test.go index 7e2b585d..2b9d017e 100644 --- a/lib/metrics/metrics_test.go +++ b/pkg/services/metrics/metrics_test.go @@ -9,7 +9,7 @@ import ( "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-node/lib/meta" + meta2 "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/meta" "github.com/pkg/errors" "github.com/stretchr/testify/require" "go.uber.org/zap" @@ -22,15 +22,15 @@ type ( ) var ( - _ ObjectCounter = (*fakeCounter)(nil) - _ meta.Iterator = (*fakeIterator)(nil) + _ ObjectCounter = (*fakeCounter)(nil) + _ meta2.Iterator = (*fakeIterator)(nil) ) func (f fakeCounter) ObjectsCount() (uint64, error) { return uint64(f), nil } -func (f fakeIterator) Iterate(_ meta.IterateFunc) error { +func (f fakeIterator) Iterate(_ meta2.IterateFunc) error { if f == "" { return nil } @@ -38,7 +38,7 @@ func (f fakeIterator) Iterate(_ meta.IterateFunc) error { return errors.New(string(f)) } -func (f fakeMetaStore) Iterate(cb meta.IterateFunc) error { +func (f fakeMetaStore) Iterate(cb meta2.IterateFunc) error { if cb == nil { return nil } diff --git a/lib/metrics/prometeus.go b/pkg/services/metrics/prometeus.go similarity index 100% rename from lib/metrics/prometeus.go rename to pkg/services/metrics/prometeus.go diff --git a/lib/metrics/store.go b/pkg/services/metrics/store.go similarity index 93% rename from lib/metrics/store.go rename to pkg/services/metrics/store.go index 85f72434..ef17ec2c 100644 --- a/lib/metrics/store.go +++ b/pkg/services/metrics/store.go @@ -6,14 +6,14 @@ import ( "sync" "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-node/lib/core" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket" "go.uber.org/zap" ) type ( syncStore struct { log *zap.Logger - store core.Bucket + store bucket.Bucket mutex sync.RWMutex items map[refs.CID]uint64 } @@ -32,7 +32,7 @@ const ( RemSpace ) -func newSyncStore(log *zap.Logger, store core.Bucket) *syncStore { +func newSyncStore(log *zap.Logger, store bucket.Bucket) *syncStore { return &syncStore{ log: log, store: store, diff --git a/lib/metrics/store_test.go b/pkg/services/metrics/store_test.go similarity index 94% rename from lib/metrics/store_test.go rename to pkg/services/metrics/store_test.go index 2827308e..dd4b760f 100644 --- a/lib/metrics/store_test.go +++ b/pkg/services/metrics/store_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-node/lib/core" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/bucket" "github.com/spaolacci/murmur3" "github.com/stretchr/testify/require" "go.uber.org/zap" @@ -24,8 +24,6 @@ type ( } ) -var _ core.Bucket = (*fakeBucket)(nil) - func keyFromBytes(b []byte) uint64 { return murmur3.Sum64(b) } @@ -85,7 +83,7 @@ func (f *fakeBucket) List() ([][]byte, error) { return items, nil } -func (f *fakeBucket) Iterate(handler core.FilterHandler) error { +func (f *fakeBucket) Iterate(handler bucket.FilterHandler) error { f.Lock() defer f.Unlock() diff --git a/lib/placement/graph.go b/pkg/services/object_manager/placement/graph.go similarity index 83% rename from lib/placement/graph.go rename to pkg/services/object_manager/placement/graph.go index 02efc3de..9f328ede 100644 --- a/lib/placement/graph.go +++ b/pkg/services/object_manager/placement/graph.go @@ -3,8 +3,8 @@ package placement import ( "github.com/gogo/protobuf/proto" "github.com/multiformats/go-multiaddr" - "github.com/nspcc-dev/neofs-api-go/bootstrap" - "github.com/nspcc-dev/neofs-node/lib/netmap" + netmapcore "github.com/nspcc-dev/neofs-node/pkg/core/netmap" + "github.com/nspcc-dev/netmap" "github.com/pkg/errors" ) @@ -13,7 +13,7 @@ func (g *graph) copy() *graph { var ( place *netmap.PlacementRule roots = make([]*netmap.Bucket, 0, len(g.roots)) - items = make([]bootstrap.NodeInfo, len(g.items)) + items = make([]netmapcore.Info, len(g.items)) ) copy(items, g.items) @@ -50,7 +50,7 @@ func (g *graph) Exclude(list []multiaddr.Multiaddr) Graph { for i := range list { for j := range sub.items { - if list[i].String() == sub.items[j].Address { + if list[i].String() == sub.items[j].Address() { ignore = append(ignore, uint32(j)) } } @@ -71,7 +71,7 @@ func (g *graph) Filter(rule FilterRule) Graph { var ( sub = g.copy() roots = make([]*netmap.Bucket, len(g.roots)) - items = make([]bootstrap.NodeInfo, len(g.items)) + items = make([]netmapcore.Info, len(g.items)) ) for i := range g.place.SFGroups { @@ -97,7 +97,7 @@ func (g *graph) NodeList() ([]multiaddr.Multiaddr, error) { var ( ln = uint32(len(g.items)) result = make([]multiaddr.Multiaddr, 0, ln) - items = make([]bootstrap.NodeInfo, len(g.items)) + items = make([]netmapcore.Info, len(g.items)) ) if ln == 0 { @@ -121,9 +121,9 @@ func (g *graph) NodeList() ([]multiaddr.Multiaddr, error) { return nil, errors.Errorf("could not find index(%d) in list(size: %d)", ln, idx) } - addr, err := multiaddr.NewMultiaddr(items[idx.N].Address) + addr, err := multiaddr.NewMultiaddr(items[idx.N].Address()) if err != nil { - return nil, errors.Wrapf(err, "could not convert multi address(%s)", g.items[idx.N].Address) + return nil, errors.Wrapf(err, "could not convert multi address(%s)", g.items[idx.N].Address()) } result = append(result, addr) @@ -138,11 +138,11 @@ func (g *graph) NodeList() ([]multiaddr.Multiaddr, error) { } // NodeInfo returns slice of NodeInfo for current graph. -func (g *graph) NodeInfo() ([]bootstrap.NodeInfo, error) { +func (g *graph) NodeInfo() ([]netmapcore.Info, error) { var ( ln = uint32(len(g.items)) - result = make([]bootstrap.NodeInfo, 0, ln) - items = make([]bootstrap.NodeInfo, len(g.items)) + result = make([]netmapcore.Info, 0, ln) + items = make([]netmapcore.Info, len(g.items)) ) if ln == 0 { diff --git a/lib/placement/interface.go b/pkg/services/object_manager/placement/interface.go similarity index 79% rename from lib/placement/interface.go rename to pkg/services/object_manager/placement/interface.go index 5c428f17..2f4e4547 100644 --- a/lib/placement/interface.go +++ b/pkg/services/object_manager/placement/interface.go @@ -6,9 +6,10 @@ import ( "github.com/multiformats/go-multiaddr" "github.com/nspcc-dev/neofs-api-go/bootstrap" "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-node/lib/container" - "github.com/nspcc-dev/neofs-node/lib/netmap" - "github.com/nspcc-dev/neofs-node/lib/peers" + "github.com/nspcc-dev/neofs-node/pkg/core/container/storage" + netmapcore "github.com/nspcc-dev/neofs-node/pkg/core/netmap" + "github.com/nspcc-dev/neofs-node/pkg/network/peers" + "github.com/nspcc-dev/netmap" "go.uber.org/atomic" "go.uber.org/zap" ) @@ -20,7 +21,7 @@ type ( NetworkState() *bootstrap.SpreadMap Neighbours(seed, epoch uint64, full bool) []peers.ID - Update(epoch uint64, nm *netmap.NetMap) error + Update(epoch uint64, nm *netmapcore.NetMap) error Query(ctx context.Context, opts ...QueryOption) (Graph, error) } @@ -42,7 +43,7 @@ type ( Filter(rule FilterRule) Graph Exclude(list []multiaddr.Multiaddr) Graph NodeList() ([]multiaddr.Multiaddr, error) - NodeInfo() ([]bootstrap.NodeInfo, error) + NodeInfo() ([]netmapcore.Info, error) } // Key to fetch node-list @@ -51,21 +52,21 @@ type ( // Params to create Placement component Params struct { Log *zap.Logger - Netmap *netmap.NetMap + Netmap *NetMap Peerstore peers.Store - Fetcher container.Storage + Fetcher storage.Storage ChronologyDuration uint64 // storing number of past epochs states } networkState struct { - nm *netmap.NetMap + nm *NetMap epoch uint64 } // placement is implementation of placement.Component placement struct { log *zap.Logger - cnr container.Storage + cnr storage.Storage chronologyDur uint64 nmStore *netMapStore @@ -78,19 +79,11 @@ type ( // graph is implementation of placement.Graph graph struct { roots []*netmap.Bucket - items []bootstrap.NodeInfo + items []netmapcore.Info place *netmap.PlacementRule } ) -// Copy network state. -func (ns networkState) Copy() *networkState { - return &networkState{ - nm: ns.nm.Copy(), - epoch: ns.epoch, - } -} - // ExcludeNodes to ignore some nodes. func ExcludeNodes(list []multiaddr.Multiaddr) QueryOption { return func(opt *QueryOptions) { diff --git a/lib/placement/neighbours.go b/pkg/services/object_manager/placement/neighbours.go similarity index 77% rename from lib/placement/neighbours.go rename to pkg/services/object_manager/placement/neighbours.go index 65154ee1..d483213c 100644 --- a/lib/placement/neighbours.go +++ b/pkg/services/object_manager/placement/neighbours.go @@ -4,8 +4,8 @@ import ( "math" "github.com/nspcc-dev/hrw" - "github.com/nspcc-dev/neofs-node/lib/netmap" - "github.com/nspcc-dev/neofs-node/lib/peers" + "github.com/nspcc-dev/neofs-node/pkg/core/netmap" + "github.com/nspcc-dev/neofs-node/pkg/network/peers" "go.uber.org/zap" ) @@ -30,7 +30,7 @@ func (p *placement) Neighbours(seed, epoch uint64, full bool) []peers.ID { return nil } - rPeers := p.listPeers(nm.ItemsCopy(), !full) + rPeers := p.listPeers(nm.Nodes(), !full) hrw.SortSliceByValue(rPeers, seed) @@ -50,14 +50,14 @@ func (p *placement) Neighbours(seed, epoch uint64, full bool) []peers.ID { return rPeers[:cut] } -func (p *placement) listPeers(nodes netmap.Nodes, exclSelf bool) []peers.ID { +func (p *placement) listPeers(nodes []netmap.Info, exclSelf bool) []peers.ID { var ( id = p.ps.SelfID() result = make([]peers.ID, 0, len(nodes)) ) for i := range nodes { - key := peers.IDFromBinary(nodes[i].PubKey) + key := peers.IDFromBinary(nodes[i].PublicKey()) if exclSelf && id.Equal(key) { continue } diff --git a/lib/placement/neighbours_test.go b/pkg/services/object_manager/placement/neighbours_test.go similarity index 79% rename from lib/placement/neighbours_test.go rename to pkg/services/object_manager/placement/neighbours_test.go index 8f9e43ac..799eecf2 100644 --- a/lib/placement/neighbours_test.go +++ b/pkg/services/object_manager/placement/neighbours_test.go @@ -9,9 +9,10 @@ import ( "github.com/multiformats/go-multiaddr" "github.com/nspcc-dev/neofs-api-go/bootstrap" crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-node/lib/netmap" - "github.com/nspcc-dev/neofs-node/lib/peers" - "github.com/nspcc-dev/neofs-node/lib/test" + "github.com/nspcc-dev/neofs-node/pkg/core/netmap" + "github.com/nspcc-dev/neofs-node/pkg/network/peers" + testlogger "github.com/nspcc-dev/neofs-node/pkg/util/logger/test" + "github.com/nspcc-dev/neofs-node/pkg/util/test" "github.com/stretchr/testify/require" ) @@ -26,7 +27,7 @@ func testAddress(t *testing.T) multiaddr.Multiaddr { func testPeerstore(t *testing.T) peers.Store { p, err := peers.NewStore(peers.StoreParams{ Key: test.DecodeKey(-1), - Logger: test.NewTestLogger(false), + Logger: testlogger.NewLogger(false), Addr: testAddress(t), }) require.NoError(t, err) @@ -48,7 +49,7 @@ func TestPlacement_Neighbours(t *testing.T) { nm := testNetmap(t, nodes) p := New(Params{ - Log: test.NewTestLogger(false), + Log: testlogger.NewLogger(false), Peerstore: ps, }) @@ -85,14 +86,14 @@ func TestPlacement_Neighbours(t *testing.T) { nm := testNetmap(t, nodes) p := New(Params{ - Log: test.NewTestLogger(false), + Log: testlogger.NewLogger(false), Netmap: nm, Peerstore: ps, }) t.Run("check, that items have expected length (< 30)", func(t *testing.T) { items := p.Neighbours(1, 0, false) - require.Len(t, items, len(nm.ItemsCopy())) + require.Len(t, items, len(nm.Nodes())) }) t.Run("check, that items have expected length ( > 30)", func(t *testing.T) { @@ -104,18 +105,24 @@ func TestPlacement_Neighbours(t *testing.T) { keyBytes := crypto.MarshalPublicKey(key) addr := address + idFromString(t, "NewRUS") - err = nm.Add(addr, keyBytes, 0, opts...) - require.NoError(t, err) + info := netmap.Info{} + info.SetAddress(addr) + info.SetPublicKey(keyBytes) + info.SetOptions(opts) + require.NoError(t, nm.AddNode(info)) for i := 0; i < 30; i++ { addr := address + idFromString(t, "RUS"+strconv.Itoa(i+2)) key := test.DecodeKey(i + len(nodes)) pub := crypto.MarshalPublicKey(&key.PublicKey) - err := nm.Add(addr, pub, 0, opts...) - require.NoError(t, err) + info := netmap.Info{} + info.SetAddress(addr) + info.SetPublicKey(pub) + info.SetOptions(opts) + require.NoError(t, nm.AddNode(info)) } - ln := calculateCount(len(nm.ItemsCopy())) + ln := calculateCount(len(nm.Nodes())) items := p.Neighbours(1, 0, false) require.Len(t, items, ln) }) @@ -142,7 +149,7 @@ func TestPlacement_Neighbours(t *testing.T) { t.Run("unknown epoch", func(t *testing.T) { s := &placement{ - log: test.NewTestLogger(false), + log: testlogger.NewLogger(false), nmStore: newNetMapStore(), ps: testPeerstore(t), } @@ -154,18 +161,21 @@ func TestPlacement_Neighbours(t *testing.T) { var ( n = 3 e uint64 = 5 - nm = netmap.NewNetmap() + nm = netmap.New() nms = newNetMapStore() ) for i := 0; i < n; i++ { - require.NoError(t, nm.Add("node"+strconv.Itoa(i), []byte{1}, 1)) + info := netmap.Info{} + info.SetAddress("node" + strconv.Itoa(i)) + info.SetPublicKey([]byte{byte(i)}) + require.NoError(t, nm.AddNode(info)) } nms.put(e, nm) s := &placement{ - log: test.NewTestLogger(false), + log: testlogger.NewLogger(false), nmStore: nms, ps: testPeerstore(t), } diff --git a/lib/placement/placement.go b/pkg/services/object_manager/placement/placement.go similarity index 73% rename from lib/placement/placement.go rename to pkg/services/object_manager/placement/placement.go index dd7e8dad..dcdb4bd9 100644 --- a/lib/placement/placement.go +++ b/pkg/services/object_manager/placement/placement.go @@ -8,10 +8,9 @@ import ( "github.com/nspcc-dev/neofs-api-go/bootstrap" "github.com/nspcc-dev/neofs-api-go/refs" crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/container" - "github.com/nspcc-dev/neofs-node/lib/netmap" - "github.com/nspcc-dev/neofs-node/lib/peers" + "github.com/nspcc-dev/neofs-node/pkg/core/netmap" + "github.com/nspcc-dev/neofs-node/pkg/network/peers" + libnetmap "github.com/nspcc-dev/netmap" "github.com/pkg/errors" "go.uber.org/atomic" "go.uber.org/zap" @@ -21,13 +20,13 @@ const defaultChronologyDuration = 1 var ( // ErrEmptyNodes when container doesn't contains any nodes - ErrEmptyNodes = internal.Error("container doesn't contains nodes") + ErrEmptyNodes = errors.New("container doesn't contains nodes") // ErrNodesBucketOmitted when in PlacementRule, Selector has not NodesBucket - ErrNodesBucketOmitted = internal.Error("nodes-bucket is omitted") + ErrNodesBucketOmitted = errors.New("nodes-bucket is omitted") // ErrEmptyContainer when GetMaxSelection or GetSelection returns empty result - ErrEmptyContainer = internal.Error("could not get container, it's empty") + ErrEmptyContainer = errors.New("could not get container, it's empty") ) var errNilNetMap = errors.New("network map is nil") @@ -35,7 +34,7 @@ var errNilNetMap = errors.New("network map is nil") // New is a placement component constructor. func New(p Params) Component { if p.Netmap == nil { - p.Netmap = netmap.NewNetmap() + p.Netmap = netmap.New() } if p.ChronologyDuration <= 0 { @@ -62,34 +61,31 @@ func New(p Params) Component { func (p *placement) Name() string { return "PresentInNetwork" } func (p *placement) Healthy() bool { return p.healthy.Load() } -type strNodes []bootstrap.NodeInfo +type strNodes []netmap.Info func (n strNodes) String() string { list := make([]string, 0, len(n)) for i := range n { - list = append(list, n[i].Address) + list = append(list, n[i].Address()) } return `[` + strings.Join(list, ",") + `]` } -func (p *placement) Update(epoch uint64, nm *netmap.NetMap) error { +func (p *placement) Update(epoch uint64, nm *NetMap) error { cnm := p.nmStore.get(p.nmStore.epoch()) if cnm == nil { return errNilNetMap } - cp := cnm.Copy() - cp.Update(nm) - - items := nm.ItemsCopy() + items := nm.Nodes() p.log.Debug("update to new netmap", zap.Stringer("nodes", strNodes(items))) p.log.Debug("update peerstore") - if err := p.ps.Update(cp); err != nil { + if err := p.ps.Update(nm); err != nil { return err } @@ -107,13 +103,13 @@ func (p *placement) Update(epoch uint64, nm *netmap.NetMap) error { pubkeyBinary = crypto.MarshalPublicKey(pubkey) for i := range items { - if bytes.Equal(pubkeyBinary, items[i].GetPubKey()) { + if bytes.Equal(pubkeyBinary, items[i].PublicKey()) { healthy = true } p.log.Debug("new peer for dht", - zap.Stringer("peer", peers.IDFromBinary(items[i].GetPubKey())), - zap.String("addr", items[i].GetAddress())) + zap.Stringer("peer", peers.IDFromBinary(items[i].PublicKey())), + zap.String("addr", items[i].Address())) } // make copy to previous @@ -124,7 +120,7 @@ func (p *placement) Update(epoch uint64, nm *netmap.NetMap) error { } p.log.Debug("update current netmap") - p.nmStore.put(epoch, cp) + p.nmStore.put(epoch, nm) p.log.Debug("update current epoch") @@ -137,13 +133,25 @@ func (p *placement) Update(epoch uint64, nm *netmap.NetMap) error { func (p *placement) NetworkState() *bootstrap.SpreadMap { ns := p.networkState(p.nmStore.epoch()) if ns == nil { - ns = &networkState{nm: netmap.NewNetmap()} + ns = &networkState{nm: netmap.New()} } - return &bootstrap.SpreadMap{ + nodes := ns.nm.Nodes() + + res := &bootstrap.SpreadMap{ Epoch: ns.epoch, - NetMap: ns.nm.Items(), + NetMap: make([]bootstrap.NodeInfo, 0, len(nodes)), } + + for i := range nodes { + res.NetMap = append(res.NetMap, bootstrap.NodeInfo{ + Address: nodes[i].Address(), + PubKey: nodes[i].PublicKey(), + Options: nodes[i].Options(), + }) + } + + return res } func (p *placement) networkState(epoch uint64) *networkState { @@ -153,7 +161,7 @@ func (p *placement) networkState(epoch uint64) *networkState { } return &networkState{ - nm: nm.Copy(), + nm: nm, epoch: epoch, } } @@ -161,7 +169,6 @@ func (p *placement) networkState(epoch uint64) *networkState { // Query returns graph based on container. func (p *placement) Query(ctx context.Context, opts ...QueryOption) (Graph, error) { var ( - items []bootstrap.NodeInfo query QueryOptions ignore []uint32 ) @@ -180,55 +187,51 @@ func (p *placement) Query(ctx context.Context, opts ...QueryOption) (Graph, erro return nil, errors.Errorf("could not get network state for epoch #%d", epoch) } - items = state.nm.Items() + items := state.nm.Nodes() - gp := container.GetParams{} - gp.SetContext(ctx) - gp.SetCID(query.CID) - - getRes, err := p.cnr.GetContainer(gp) + cnr, err := p.cnr.Get(query.CID) if err != nil { return nil, errors.Wrap(err, "could not fetch container") } for i := range query.Excludes { for j := range items { - if query.Excludes[i].String() == items[j].Address { + if query.Excludes[i].String() == items[j].Address() { ignore = append(ignore, uint32(j)) } } } - rule := getRes.Container().GetRules() + rule := cnr.PlacementRule() return ContainerGraph(state.nm, &rule, ignore, query.CID) } // ContainerGraph applies the placement rules to network map and returns container graph. -func ContainerGraph(nm *netmap.NetMap, rule *netmap.PlacementRule, ignore []uint32, cid refs.CID) (Graph, error) { +func ContainerGraph(nm *NetMap, rule *libnetmap.PlacementRule, ignore []uint32, cid refs.CID) (Graph, error) { root := nm.Root() roots := make([]*netmap.Bucket, 0, len(rule.SFGroups)) for i := range rule.SFGroups { rule.SFGroups[i].Exclude = ignore if ln := len(rule.SFGroups[i].Selectors); ln <= 0 || - rule.SFGroups[i].Selectors[ln-1].Key != netmap.NodesBucket { + rule.SFGroups[i].Selectors[ln-1].Key != libnetmap.NodesBucket { return nil, errors.Wrapf(ErrNodesBucketOmitted, "container (%s)", cid) } - bigSelectors := make([]netmap.Select, len(rule.SFGroups[i].Selectors)) + bigSelectors := make([]libnetmap.Select, len(rule.SFGroups[i].Selectors)) for j := range rule.SFGroups[i].Selectors { - bigSelectors[j] = netmap.Select{ + bigSelectors[j] = libnetmap.Select{ Key: rule.SFGroups[i].Selectors[j].Key, Count: rule.SFGroups[i].Selectors[j].Count, } - if rule.ReplFactor > 1 && rule.SFGroups[i].Selectors[j].Key == netmap.NodesBucket { + if rule.ReplFactor > 1 && rule.SFGroups[i].Selectors[j].Key == libnetmap.NodesBucket { bigSelectors[j].Count *= rule.ReplFactor } } - sf := netmap.SFGroup{ + sf := libnetmap.SFGroup{ Selectors: bigSelectors, Filters: rule.SFGroups[i].Filters, Exclude: ignore, @@ -251,7 +254,7 @@ func ContainerGraph(nm *netmap.NetMap, rule *netmap.PlacementRule, ignore []uint return &graph{ roots: roots, - items: nm.ItemsCopy(), + items: nm.Nodes(), place: rule, }, nil } diff --git a/lib/placement/placement_test.go b/pkg/services/object_manager/placement/placement_test.go similarity index 78% rename from lib/placement/placement_test.go rename to pkg/services/object_manager/placement/placement_test.go index 53ac8127..9605f195 100644 --- a/lib/placement/placement_test.go +++ b/pkg/services/object_manager/placement/placement_test.go @@ -13,12 +13,16 @@ import ( "github.com/multiformats/go-multiaddr" "github.com/multiformats/go-multihash" "github.com/nspcc-dev/neofs-api-go/bootstrap" - "github.com/nspcc-dev/neofs-api-go/container" "github.com/nspcc-dev/neofs-api-go/refs" - libcnr "github.com/nspcc-dev/neofs-node/lib/container" - "github.com/nspcc-dev/neofs-node/lib/netmap" - "github.com/nspcc-dev/neofs-node/lib/peers" - "github.com/nspcc-dev/neofs-node/lib/test" + crypto "github.com/nspcc-dev/neofs-crypto" + libcnr "github.com/nspcc-dev/neofs-node/pkg/core/container" + "github.com/nspcc-dev/neofs-node/pkg/core/container/acl/basic" + "github.com/nspcc-dev/neofs-node/pkg/core/container/storage" + netmapcore "github.com/nspcc-dev/neofs-node/pkg/core/netmap" + "github.com/nspcc-dev/neofs-node/pkg/network/peers" + testlogger "github.com/nspcc-dev/neofs-node/pkg/util/logger/test" + "github.com/nspcc-dev/neofs-node/pkg/util/test" + "github.com/nspcc-dev/netmap" "github.com/pkg/errors" "github.com/stretchr/testify/require" ) @@ -28,9 +32,9 @@ type ( } fakeContainerStorage struct { - libcnr.Storage + storage.Storage *sync.RWMutex - items map[refs.CID]*container.Container + items map[refs.CID]*storage.Container } ) @@ -43,35 +47,32 @@ var ( func testContainerStorage() *fakeContainerStorage { return &fakeContainerStorage{ RWMutex: new(sync.RWMutex), - items: make(map[refs.CID]*container.Container, testDHTCapacity), + items: make(map[refs.CID]*storage.Container, testDHTCapacity), } } -func (f *fakeContainerStorage) GetContainer(p libcnr.GetParams) (*libcnr.GetResult, error) { +func (f *fakeContainerStorage) Get(cid storage.CID) (*storage.Container, error) { f.RLock() - val, ok := f.items[p.CID()] + val, ok := f.items[cid] f.RUnlock() if !ok { return nil, errors.New("value for requested key not found in DHT") } - res := new(libcnr.GetResult) - res.SetContainer(val) - - return res, nil + return val, nil } -func (f *fakeContainerStorage) Put(c *container.Container) error { - id, err := c.ID() +func (f *fakeContainerStorage) Put(c *storage.Container) (*storage.CID, error) { + id, err := libcnr.CalculateID(c) if err != nil { - return err + return nil, err } f.Lock() - f.items[id] = c + f.items[*id] = c f.Unlock() - return nil + return id, nil } func (f *fakeDHT) UpdatePeers([]peers.ID) { @@ -96,11 +97,15 @@ func (f *fakeDHT) Put(ctx context.Context, key string, val []byte) error { // -- -- // -func testNetmap(t *testing.T, nodes []bootstrap.NodeInfo) *netmap.NetMap { - nm := netmap.NewNetmap() +func testNetmap(t *testing.T, nodes []bootstrap.NodeInfo) *NetMap { + nm := netmapcore.New() for i := range nodes { - err := nm.Add(nodes[i].Address, nil, 0, nodes[i].Options...) + info := netmapcore.Info{} + info.SetAddress(nodes[i].Address) + info.SetOptions(nodes[i].Options) + info.SetPublicKey(crypto.MarshalPublicKey(&test.DecodeKey(i).PublicKey)) + err := nm.AddNode(info) require.NoError(t, err) } @@ -178,8 +183,7 @@ func TestPlacement(t *testing.T) { cnrStorage := testContainerStorage() p := New(Params{ - Log: test.NewTestLogger(false), - Netmap: netmap.NewNetmap(), + Log: testlogger.NewLogger(false), Peerstore: testPeerstore(t), Fetcher: cnrStorage, }) @@ -196,7 +200,11 @@ func TestPlacement(t *testing.T) { owner, err := refs.NewOwnerID(&key.PublicKey) require.NoError(t, err) - res1, err := container.New(100, owner, 0, netmap.PlacementRule{ + + cnr1 := new(storage.Container) + cnr1.SetOwnerID(owner) + cnr1.SetBasicACL(basic.FromUint32(0)) + cnr1.SetPlacementRule(netmap.PlacementRule{ ReplFactor: 2, SFGroups: []netmap.SFGroup{ { @@ -211,12 +219,14 @@ func TestPlacement(t *testing.T) { }, }, }) + + cid1, err := cnrStorage.Put(cnr1) require.NoError(t, err) - err = cnrStorage.Put(res1) - require.NoError(t, err) - - res2, err := container.New(100, owner, 0, netmap.PlacementRule{ + cnr2 := new(storage.Container) + cnr2.SetOwnerID(owner) + cnr2.SetBasicACL(basic.FromUint32(0)) + cnr2.SetPlacementRule(netmap.PlacementRule{ ReplFactor: 2, SFGroups: []netmap.SFGroup{ { @@ -230,12 +240,14 @@ func TestPlacement(t *testing.T) { }, }, }) + + cid2, err := cnrStorage.Put(cnr2) require.NoError(t, err) - err = cnrStorage.Put(res2) - require.NoError(t, err) - - res3, err := container.New(100, owner, 0, netmap.PlacementRule{ + cnr3 := new(storage.Container) + cnr3.SetOwnerID(owner) + cnr3.SetBasicACL(basic.FromUint32(0)) + cnr3.SetPlacementRule(netmap.PlacementRule{ ReplFactor: 2, SFGroups: []netmap.SFGroup{ { @@ -248,22 +260,17 @@ func TestPlacement(t *testing.T) { }, }, }) - require.NoError(t, err) - err = cnrStorage.Put(res3) + cid3, err := cnrStorage.Put(cnr3) require.NoError(t, err) t.Run("Should fail on empty container", func(t *testing.T) { - id, err := res2.ID() - require.NoError(t, err) - _, err = p.Query(ctx, ContainerID(id)) + _, err = p.Query(ctx, ContainerID(*cid2)) require.EqualError(t, errors.Cause(err), ErrEmptyContainer.Error()) }) t.Run("Should fail on Nodes Bucket is omitted in container", func(t *testing.T) { - id, err := res3.ID() - require.NoError(t, err) - _, err = p.Query(ctx, ContainerID(id)) + _, err = p.Query(ctx, ContainerID(*cid3)) require.EqualError(t, errors.Cause(err), ErrNodesBucketOmitted.Error()) }) @@ -272,10 +279,7 @@ func TestPlacement(t *testing.T) { require.Error(t, err) }) - id1, err := res1.ID() - require.NoError(t, err) - - g, err := p.Query(ctx, ContainerID(id1)) + g, err := p.Query(ctx, ContainerID(*cid1)) require.NoError(t, err) t.Run("Should return error on empty items", func(t *testing.T) { @@ -286,7 +290,7 @@ func TestPlacement(t *testing.T) { }) t.Run("Should ignore some nodes", func(t *testing.T) { - g1, err := p.Query(ctx, ContainerID(id1)) + g1, err := p.Query(ctx, ContainerID(*cid1)) require.NoError(t, err) expect, err := g1. @@ -294,7 +298,7 @@ func TestPlacement(t *testing.T) { NodeList() require.NoError(t, err) - g2, err := p.Query(ctx, ContainerID(id1)) + g2, err := p.Query(ctx, ContainerID(*cid1)) require.NoError(t, err) actual, err := g2. @@ -304,7 +308,7 @@ func TestPlacement(t *testing.T) { require.Equal(t, expect, actual) - g3, err := p.Query(ctx, ContainerID(id1)) + g3, err := p.Query(ctx, ContainerID(*cid1)) require.NoError(t, err) actual, err = g3. @@ -318,7 +322,7 @@ func TestPlacement(t *testing.T) { } g4, err := p.Query(ctx, - ContainerID(id1), + ContainerID(*cid1), ExcludeNodes(expect)) require.NoError(t, err) @@ -367,7 +371,7 @@ func TestPlacement(t *testing.T) { }) for i := range cp.(*graph).items { - cp.(*graph).items[i].Address = "BadAddress" + cp.(*graph).items[i].SetAddress("BadAddress") } _, err := cp.NodeList() @@ -397,7 +401,7 @@ func TestContainerGraph(t *testing.T) { require.NotPanics(t, func() { _, _ = ContainerGraph( - netmap.NewNetmap(), + netmapcore.New(), rule, nil, refs.CID{}, diff --git a/lib/placement/store.go b/pkg/services/object_manager/placement/store.go similarity index 94% rename from lib/placement/store.go rename to pkg/services/object_manager/placement/store.go index 7d27bdf0..f991aca7 100644 --- a/lib/placement/store.go +++ b/pkg/services/object_manager/placement/store.go @@ -3,7 +3,7 @@ package placement import ( "sync" - "github.com/nspcc-dev/neofs-node/lib/netmap" + "github.com/nspcc-dev/neofs-node/pkg/core/netmap" ) type ( diff --git a/lib/implementations/placement.go b/pkg/services/object_manager/placement/wrapper.go similarity index 51% rename from lib/implementations/placement.go rename to pkg/services/object_manager/placement/wrapper.go index 4c7d95cf..2ca967ae 100644 --- a/lib/implementations/placement.go +++ b/pkg/services/object_manager/placement/wrapper.go @@ -1,16 +1,14 @@ -package implementations +package placement import ( "context" "github.com/multiformats/go-multiaddr" - "github.com/nspcc-dev/neofs-api-go/bootstrap" "github.com/nspcc-dev/neofs-api-go/container" "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/netmap" - "github.com/nspcc-dev/neofs-node/lib/placement" + netmapcore "github.com/nspcc-dev/neofs-node/pkg/core/netmap" + "github.com/nspcc-dev/netmap" "github.com/pkg/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -45,47 +43,24 @@ type ( // Address from refs package of neofs-api-go. Address = refs.Address - // Netmap is a type alias of - // NetMap from netmap package. - Netmap = netmap.NetMap - - // ObjectPlacer is an interface of placement utility. - ObjectPlacer interface { - ContainerNodesLister - ContainerInvolvementChecker - GetNodes(ctx context.Context, addr Address, usePreviousNetMap bool, excl ...multiaddr.Multiaddr) ([]multiaddr.Multiaddr, error) - Epoch() uint64 - } - - // ContainerNodesLister is an interface of container placement vector builder. - ContainerNodesLister interface { - ContainerNodes(ctx context.Context, cid CID) ([]multiaddr.Multiaddr, error) - ContainerNodesInfo(ctx context.Context, cid CID, prev int) ([]bootstrap.NodeInfo, error) - } - - // ContainerInvolvementChecker is an interface of container affiliation checker. - ContainerInvolvementChecker interface { - IsContainerNode(ctx context.Context, addr multiaddr.Multiaddr, cid CID, previousNetMap bool) (bool, error) - } - - objectPlacer struct { - pl placement.Component + PlacementWrapper struct { + pl Component } ) -const errEmptyPlacement = internal.Error("could not create storage lister: empty placement component") +var errEmptyPlacement = errors.New("could not create storage lister: empty placement component") -// NewObjectPlacer wraps placement.Component and returns ObjectPlacer interface. -func NewObjectPlacer(pl placement.Component) (ObjectPlacer, error) { +// NewObjectPlacer wraps Component and returns ObjectPlacer interface. +func NewObjectPlacer(pl Component) (*PlacementWrapper, error) { if pl == nil { return nil, errEmptyPlacement } - return &objectPlacer{pl}, nil + return &PlacementWrapper{pl}, nil } -func (v objectPlacer) ContainerNodes(ctx context.Context, cid CID) ([]multiaddr.Multiaddr, error) { - graph, err := v.pl.Query(ctx, placement.ContainerID(cid)) +func (v PlacementWrapper) ContainerNodes(ctx context.Context, cid CID) ([]multiaddr.Multiaddr, error) { + graph, err := v.pl.Query(ctx, ContainerID(cid)) if err != nil { return nil, errors.Wrap(err, "objectPlacer.ContainerNodes failed on graph query") } @@ -93,8 +68,8 @@ func (v objectPlacer) ContainerNodes(ctx context.Context, cid CID) ([]multiaddr. return graph.NodeList() } -func (v objectPlacer) ContainerNodesInfo(ctx context.Context, cid CID, prev int) ([]bootstrap.NodeInfo, error) { - graph, err := v.pl.Query(ctx, placement.ContainerID(cid), placement.UsePreviousNetmap(prev)) +func (v PlacementWrapper) ContainerNodesInfo(ctx context.Context, cid CID, prev int) ([]netmapcore.Info, error) { + graph, err := v.pl.Query(ctx, ContainerID(cid), UsePreviousNetmap(prev)) if err != nil { return nil, errors.Wrap(err, "objectPlacer.ContainerNodesInfo failed on graph query") } @@ -102,12 +77,12 @@ func (v objectPlacer) ContainerNodesInfo(ctx context.Context, cid CID, prev int) return graph.NodeInfo() } -func (v objectPlacer) GetNodes(ctx context.Context, addr Address, usePreviousNetMap bool, excl ...multiaddr.Multiaddr) ([]multiaddr.Multiaddr, error) { - queryOptions := make([]placement.QueryOption, 1, 2) - queryOptions[0] = placement.ContainerID(addr.CID) +func (v PlacementWrapper) GetNodes(ctx context.Context, addr Address, usePreviousNetMap bool, excl ...multiaddr.Multiaddr) ([]multiaddr.Multiaddr, error) { + queryOptions := make([]QueryOption, 1, 2) + queryOptions[0] = ContainerID(addr.CID) if usePreviousNetMap { - queryOptions = append(queryOptions, placement.UsePreviousNetmap(1)) + queryOptions = append(queryOptions, UsePreviousNetmap(1)) } graph, err := v.pl.Query(ctx, queryOptions...) @@ -132,7 +107,7 @@ func (v objectPlacer) GetNodes(ctx context.Context, addr Address, usePreviousNet return graph.Exclude(excl).Filter(filter).NodeList() } -func (v objectPlacer) IsContainerNode(ctx context.Context, addr multiaddr.Multiaddr, cid CID, previousNetMap bool) (bool, error) { +func (v PlacementWrapper) IsContainerNode(ctx context.Context, addr multiaddr.Multiaddr, cid CID, previousNetMap bool) (bool, error) { nodes, err := v.GetNodes(ctx, Address{ CID: cid, }, previousNetMap) @@ -149,4 +124,4 @@ func (v objectPlacer) IsContainerNode(ctx context.Context, addr multiaddr.Multia return false, nil } -func (v objectPlacer) Epoch() uint64 { return v.pl.NetworkState().Epoch } +func (v PlacementWrapper) Epoch() uint64 { return v.pl.NetworkState().Epoch } diff --git a/lib/replication/common.go b/pkg/services/object_manager/replication/common.go similarity index 100% rename from lib/replication/common.go rename to pkg/services/object_manager/replication/common.go diff --git a/lib/replication/garbage.go b/pkg/services/object_manager/replication/garbage.go similarity index 100% rename from lib/replication/garbage.go rename to pkg/services/object_manager/replication/garbage.go diff --git a/lib/replication/implementations.go b/pkg/services/object_manager/replication/implementations.go similarity index 92% rename from lib/replication/implementations.go rename to pkg/services/object_manager/replication/implementations.go index 708a8226..2a3cf81a 100644 --- a/lib/replication/implementations.go +++ b/pkg/services/object_manager/replication/implementations.go @@ -5,11 +5,10 @@ import ( "sync" "github.com/multiformats/go-multiaddr" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/netmap" - "github.com/nspcc-dev/neofs-node/lib/placement" - "github.com/nspcc-dev/neofs-node/lib/rand" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement" + "github.com/nspcc-dev/neofs-node/pkg/util/rand" + "github.com/nspcc-dev/netmap" "github.com/pkg/errors" ) @@ -43,18 +42,21 @@ type ( ) const ( - errPoolExhausted = internal.Error("object pool is exhausted") - objectPoolInstanceFailMsg = "could not create object pool" - errEmptyLister = internal.Error("empty local objects lister") - errEmptyContainerActual = internal.Error("empty container actuality checker") multiSolverInstanceFailMsg = "could not create multi solver" - errEmptyAddressStore = internal.Error("empty address store") - errEmptyPlacement = internal.Error("empty placement") + replicationSchedulerEntity = "replication scheduler" ) +var ( + errPoolExhausted = errors.New("object pool is exhausted") + errEmptyLister = errors.New("empty local objects lister") + errEmptyContainerActual = errors.New("empty container actuality checker") + errEmptyAddressStore = errors.New("empty address store") + errEmptyPlacement = errors.New("empty placement") +) + // NewObjectPool is an object pool constructor. func NewObjectPool() ObjectPool { return &objectPool{mu: new(sync.Mutex)} diff --git a/lib/replication/location_detector.go b/pkg/services/object_manager/replication/location_detector.go similarity index 100% rename from lib/replication/location_detector.go rename to pkg/services/object_manager/replication/location_detector.go diff --git a/lib/replication/manager.go b/pkg/services/object_manager/replication/manager.go similarity index 100% rename from lib/replication/manager.go rename to pkg/services/object_manager/replication/manager.go diff --git a/lib/replication/object_replicator.go b/pkg/services/object_manager/replication/object_replicator.go similarity index 100% rename from lib/replication/object_replicator.go rename to pkg/services/object_manager/replication/object_replicator.go diff --git a/lib/replication/object_restorer.go b/pkg/services/object_manager/replication/object_restorer.go similarity index 98% rename from lib/replication/object_restorer.go rename to pkg/services/object_manager/replication/object_restorer.go index 00e70d87..d64dbdb2 100644 --- a/lib/replication/object_restorer.go +++ b/pkg/services/object_manager/replication/object_restorer.go @@ -5,7 +5,7 @@ import ( "time" "github.com/multiformats/go-multiaddr" - "github.com/nspcc-dev/neofs-node/lib/localstore" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" "go.uber.org/zap" ) diff --git a/lib/replication/placement_honorer.go b/pkg/services/object_manager/replication/placement_honorer.go similarity index 100% rename from lib/replication/placement_honorer.go rename to pkg/services/object_manager/replication/placement_honorer.go diff --git a/lib/implementations/locator.go b/pkg/services/object_manager/replication/storage/locator.go similarity index 69% rename from lib/implementations/locator.go rename to pkg/services/object_manager/replication/storage/locator.go index 6cf19ce0..7d8fd08d 100644 --- a/lib/implementations/locator.go +++ b/pkg/services/object_manager/replication/storage/locator.go @@ -1,4 +1,4 @@ -package implementations +package storage import ( "context" @@ -7,21 +7,23 @@ import ( "github.com/nspcc-dev/neofs-api-go/query" "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-node/lib/replication" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/core/object" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/replication" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/nspcc-dev/neofs-node/pkg/util/logger" "github.com/pkg/errors" "go.uber.org/zap" ) type ( locator struct { - executor SelectiveContainerExecutor + executor transport.SelectiveContainerExecutor log *zap.Logger } // LocatorParams groups the parameters of ObjectLocator constructor. LocatorParams struct { - SelectiveContainerExecutor SelectiveContainerExecutor + SelectiveContainerExecutor transport.SelectiveContainerExecutor Logger *zap.Logger } ) @@ -30,7 +32,7 @@ const locatorInstanceFailMsg = "could not create object locator" var errEmptyObjectsContainerHandler = errors.New("empty container objects container handler") -func (s *locator) LocateObject(ctx context.Context, addr Address) (res []multiaddr.Multiaddr, err error) { +func (s *locator) LocateObject(ctx context.Context, addr refs.Address) (res []multiaddr.Multiaddr, err error) { queryBytes, err := (&query.Query{ Filters: []query.Filter{ { @@ -44,11 +46,11 @@ func (s *locator) LocateObject(ctx context.Context, addr Address) (res []multiad return nil, errors.Wrap(err, "locate object failed on query marshal") } - err = s.executor.Search(ctx, &SearchParams{ - SelectiveParams: SelectiveParams{ + err = s.executor.Search(ctx, &transport.SearchParams{ + SelectiveParams: transport.SelectiveParams{ CID: addr.CID, TTL: service.NonForwardingTTL, - IDList: make([]ObjectID, 1), + IDList: make([]object.ID, 1), }, SearchCID: addr.CID, SearchQuery: queryBytes, @@ -68,7 +70,7 @@ func NewObjectLocator(p LocatorParams) (replication.ObjectLocator, error) { case p.SelectiveContainerExecutor == nil: return nil, errors.Wrap(errEmptyObjectsContainerHandler, locatorInstanceFailMsg) case p.Logger == nil: - return nil, errors.Wrap(errEmptyLogger, locatorInstanceFailMsg) + return nil, errors.Wrap(logger.ErrNilLogger, locatorInstanceFailMsg) } return &locator{ diff --git a/lib/implementations/locator_test.go b/pkg/services/object_manager/replication/storage/locator_test.go similarity index 74% rename from lib/implementations/locator_test.go rename to pkg/services/object_manager/replication/storage/locator_test.go index 892b3883..9b8a5fa5 100644 --- a/lib/implementations/locator_test.go +++ b/pkg/services/object_manager/replication/storage/locator_test.go @@ -1,15 +1,17 @@ -package implementations +package storage import ( "testing" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/nspcc-dev/neofs-node/pkg/util/logger" "github.com/pkg/errors" "github.com/stretchr/testify/require" "go.uber.org/zap" ) type testExecutor struct { - SelectiveContainerExecutor + transport.SelectiveContainerExecutor } func TestNewObjectLocator(t *testing.T) { @@ -27,7 +29,7 @@ func TestNewObjectLocator(t *testing.T) { p := validParams p.Logger = nil _, err := NewObjectLocator(p) - require.EqualError(t, err, errors.Wrap(errEmptyLogger, locatorInstanceFailMsg).Error()) + require.EqualError(t, err, errors.Wrap(logger.ErrNilLogger, locatorInstanceFailMsg).Error()) }) t.Run("empty container handler", func(t *testing.T) { p := validParams diff --git a/lib/implementations/object.go b/pkg/services/object_manager/replication/storage/object.go similarity index 73% rename from lib/implementations/object.go rename to pkg/services/object_manager/replication/storage/object.go index ed260af1..122f76d7 100644 --- a/lib/implementations/object.go +++ b/pkg/services/object_manager/replication/storage/object.go @@ -1,4 +1,4 @@ -package implementations +package storage import ( "context" @@ -7,8 +7,10 @@ import ( "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/replication" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/replication" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/nspcc-dev/neofs-node/pkg/util/logger" "github.com/pkg/errors" "go.uber.org/zap" ) @@ -22,14 +24,14 @@ type ( objectStorage struct { ls localstore.Localstore - executor SelectiveContainerExecutor + executor transport.SelectiveContainerExecutor log *zap.Logger } // ObjectStorageParams groups the parameters of ObjectStorage constructor. ObjectStorageParams struct { Localstore localstore.Localstore - SelectiveContainerExecutor SelectiveContainerExecutor + SelectiveContainerExecutor transport.SelectiveContainerExecutor Logger *zap.Logger } ) @@ -55,12 +57,12 @@ func (s *objectStorage) Put(ctx context.Context, params replication.ObjectStoreP nodes[i] = params.Nodes[i].Node } - return s.executor.Put(ctx, &PutParams{ - SelectiveParams: SelectiveParams{ + return s.executor.Put(ctx, &transport.PutParams{ + SelectiveParams: transport.SelectiveParams{ CID: params.Object.SystemHeader.CID, Nodes: nodes, TTL: service.NonForwardingTTL, - IDList: make([]ObjectID, 1), + IDList: make([]object.ID, 1), }, Object: params.Object, Handler: func(node multiaddr.Multiaddr, valid bool) { @@ -77,7 +79,7 @@ func (s *objectStorage) Put(ctx context.Context, params replication.ObjectStoreP }) } -func (s *objectStorage) Get(ctx context.Context, addr Address) (res *Object, err error) { +func (s *objectStorage) Get(ctx context.Context, addr object.Address) (res *object.Object, err error) { if s.ls != nil { if has, err := s.ls.Has(addr); err == nil && has { if res, err = s.ls.Get(addr); err == nil { @@ -86,14 +88,14 @@ func (s *objectStorage) Get(ctx context.Context, addr Address) (res *Object, err } } - if err = s.executor.Get(ctx, &GetParams{ - SelectiveParams: SelectiveParams{ + if err = s.executor.Get(ctx, &transport.GetParams{ + SelectiveParams: transport.SelectiveParams{ CID: addr.CID, TTL: service.NonForwardingTTL, - IDList: []ObjectID{addr.ObjectID}, - Breaker: func(refs.Address) (cFlag ProgressControlFlag) { + IDList: []object.ID{addr.ObjectID}, + Breaker: func(refs.Address) (cFlag transport.ProgressControlFlag) { if res != nil { - cFlag = BreakProgress + cFlag = transport.BreakProgress } return }, @@ -112,7 +114,7 @@ func (s *objectStorage) Get(ctx context.Context, addr Address) (res *Object, err // and returns ObjectStorage interface. func NewObjectStorage(p ObjectStorageParams) (ObjectStorage, error) { if p.Logger == nil { - return nil, errors.Wrap(errEmptyLogger, objectSourceInstanceFailMsg) + return nil, errors.Wrap(logger.ErrNilLogger, objectSourceInstanceFailMsg) } if p.Localstore == nil { diff --git a/lib/implementations/peerstore.go b/pkg/services/object_manager/replication/storage/peerstore.go similarity index 79% rename from lib/implementations/peerstore.go rename to pkg/services/object_manager/replication/storage/peerstore.go index 6a7070f1..6890ed8b 100644 --- a/lib/implementations/peerstore.go +++ b/pkg/services/object_manager/replication/storage/peerstore.go @@ -1,11 +1,11 @@ -package implementations +package storage import ( "crypto/ecdsa" "github.com/multiformats/go-multiaddr" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/peers" + "github.com/nspcc-dev/neofs-node/pkg/network/peers" + "github.com/nspcc-dev/neofs-node/pkg/util/logger" "github.com/pkg/errors" "go.uber.org/zap" ) @@ -34,11 +34,12 @@ type ( } ) -const ( - addressStoreInstanceFailMsg = "could not create address store" - errEmptyPeerStore = internal.Error("empty peer store") +const addressStoreInstanceFailMsg = "could not create address store" - errEmptyAddressStore = internal.Error("empty address store") +var ( + errEmptyPeerStore = errors.New("empty peer store") + + errEmptyAddressStore = errors.New("empty address store") ) func (s addressStore) SelfAddr() (multiaddr.Multiaddr, error) { return s.ps.GetAddr(s.ps.SelfID()) } @@ -64,7 +65,7 @@ func NewAddressStore(ps peers.Store, log *zap.Logger) (AddressStoreComponent, er if ps == nil { return nil, errors.Wrap(errEmptyPeerStore, addressStoreInstanceFailMsg) } else if log == nil { - return nil, errors.Wrap(errEmptyLogger, addressStoreInstanceFailMsg) + return nil, errors.Wrap(logger.ErrNilLogger, addressStoreInstanceFailMsg) } return &addressStore{ diff --git a/lib/implementations/validation.go b/pkg/services/object_manager/replication/storage/validation.go similarity index 78% rename from lib/implementations/validation.go rename to pkg/services/object_manager/replication/storage/validation.go index 4ab858a3..0bd3a4a4 100644 --- a/lib/implementations/validation.go +++ b/pkg/services/object_manager/replication/storage/validation.go @@ -1,4 +1,4 @@ -package implementations +package storage import ( "bytes" @@ -12,12 +12,13 @@ import ( "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/objutil" - "github.com/nspcc-dev/neofs-node/lib/rand" - "github.com/nspcc-dev/neofs-node/lib/replication" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + "github.com/nspcc-dev/neofs-node/pkg/services/id" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/replication" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/verifier" + "github.com/nspcc-dev/neofs-node/pkg/util/logger" + "github.com/nspcc-dev/neofs-node/pkg/util/rand" "github.com/pkg/errors" "go.uber.org/zap" ) @@ -26,14 +27,14 @@ type ( objectValidator struct { as AddressStore ls localstore.Localstore - executor SelectiveContainerExecutor + executor transport.SelectiveContainerExecutor log *zap.Logger saltSize int maxRngSize uint64 rangeCount int sltr Salitor - verifier objutil.Verifier + verifier verifier.Verifier } // Salitor is a salting data function. @@ -43,7 +44,7 @@ type ( ObjectValidatorParams struct { AddressStore AddressStore Localstore localstore.Localstore - SelectiveContainerExecutor SelectiveContainerExecutor + SelectiveContainerExecutor transport.SelectiveContainerExecutor Logger *zap.Logger Salitor Salitor @@ -51,37 +52,39 @@ type ( MaxPayloadRangeSize uint64 PayloadRangeCount int - Verifier objutil.Verifier + Verifier verifier.Verifier } localHeadIntegrityVerifier struct { - keyVerifier core.OwnerKeyVerifier } payloadVerifier struct { } localIntegrityVerifier struct { - headVerifier objutil.Verifier - payloadVerifier objutil.Verifier + headVerifier verifier.Verifier + payloadVerifier verifier.Verifier } ) const ( objectValidatorInstanceFailMsg = "could not create object validator" - errEmptyLocalstore = internal.Error("empty local storage") - errEmptyObjectVerifier = internal.Error("empty object verifier") defaultSaltSize = 64 // bytes defaultPayloadRangeCount = 3 defaultMaxPayloadRangeSize = 64 ) -const ( - errBrokenHeaderStructure = internal.Error("broken header structure") +var ( + errEmptyLocalstore = errors.New("empty local storage") + errEmptyObjectVerifier = errors.New("empty object verifier") +) - errMissingPayloadChecksumHeader = internal.Error("missing payload checksum header") - errWrongPayloadChecksum = internal.Error("wrong payload checksum") +var ( + errBrokenHeaderStructure = errors.New("broken header structure") + + errMissingPayloadChecksumHeader = errors.New("missing payload checksum header") + errWrongPayloadChecksum = errors.New("wrong payload checksum") ) func (s *objectValidator) Verify(ctx context.Context, params *replication.ObjectVerificationParams) bool { @@ -98,10 +101,10 @@ func (s *objectValidator) Verify(ctx context.Context, params *replication.Object return s.verifyRemote(ctx, params) } -func (s *objectValidator) verifyLocal(ctx context.Context, addr Address) bool { +func (s *objectValidator) verifyLocal(ctx context.Context, addr refs.Address) bool { var ( err error - obj *Object + obj *object.Object ) if obj, err = s.ls.Get(addr); err != nil { @@ -116,7 +119,7 @@ func (s *objectValidator) verifyLocal(ctx context.Context, addr Address) bool { func (s *objectValidator) verifyRemote(ctx context.Context, params *replication.ObjectVerificationParams) bool { var ( - receivedObj *Object + receivedObj *object.Object valid bool ) @@ -126,13 +129,13 @@ func (s *objectValidator) verifyRemote(ctx context.Context, params *replication. } }() - p := &HeadParams{ - GetParams: GetParams{ - SelectiveParams: SelectiveParams{ + p := &transport.HeadParams{ + GetParams: transport.GetParams{ + SelectiveParams: transport.SelectiveParams{ CID: params.CID, Nodes: []multiaddr.Multiaddr{params.Node}, TTL: service.NonForwardingTTL, - IDList: []ObjectID{params.ObjectID}, + IDList: []object.ID{params.ObjectID}, Raw: true, }, Handler: func(_ multiaddr.Multiaddr, obj *object.Object) { @@ -165,18 +168,18 @@ func (s *objectValidator) verifyRemote(ctx context.Context, params *replication. return valid } -func (s *objectValidator) verifyThroughHashes(ctx context.Context, obj *Object, node multiaddr.Multiaddr) (valid bool) { +func (s *objectValidator) verifyThroughHashes(ctx context.Context, obj *object.Object, node multiaddr.Multiaddr) (valid bool) { var ( salt = generateSalt(s.saltSize) rngs = generateRanges(obj.SystemHeader.PayloadLength, s.maxRngSize, s.rangeCount) ) - _ = s.executor.RangeHash(ctx, &RangeHashParams{ - SelectiveParams: SelectiveParams{ + _ = s.executor.RangeHash(ctx, &transport.RangeHashParams{ + SelectiveParams: transport.SelectiveParams{ CID: obj.SystemHeader.CID, Nodes: []multiaddr.Multiaddr{node}, TTL: service.NonForwardingTTL, - IDList: []ObjectID{obj.SystemHeader.ID}, + IDList: []object.ID{obj.SystemHeader.ID}, }, Ranges: rngs, Salt: salt, @@ -237,7 +240,7 @@ func generateSalt(saltSize int) []byte { func NewObjectValidator(p *ObjectValidatorParams) (replication.ObjectVerifier, error) { switch { case p.Logger == nil: - return nil, errors.Wrap(errEmptyLogger, objectValidatorInstanceFailMsg) + return nil, errors.Wrap(logger.ErrNilLogger, objectValidatorInstanceFailMsg) case p.AddressStore == nil: return nil, errors.Wrap(errEmptyAddressStore, objectValidatorInstanceFailMsg) case p.Localstore == nil: @@ -272,32 +275,20 @@ func NewObjectValidator(p *ObjectValidatorParams) (replication.ObjectVerifier, e } // NewLocalHeadIntegrityVerifier constructs local object head verifier and returns objutil.Verifier interface. -func NewLocalHeadIntegrityVerifier(keyVerifier core.OwnerKeyVerifier) (objutil.Verifier, error) { - if keyVerifier == nil { - return nil, core.ErrNilOwnerKeyVerifier - } - - return &localHeadIntegrityVerifier{ - keyVerifier: keyVerifier, - }, nil +func NewLocalHeadIntegrityVerifier() (verifier.Verifier, error) { + return new(localHeadIntegrityVerifier), nil } // NewLocalIntegrityVerifier constructs local object verifier and returns objutil.Verifier interface. -func NewLocalIntegrityVerifier(keyVerifier core.OwnerKeyVerifier) (objutil.Verifier, error) { - if keyVerifier == nil { - return nil, core.ErrNilOwnerKeyVerifier - } - +func NewLocalIntegrityVerifier() (verifier.Verifier, error) { return &localIntegrityVerifier{ - headVerifier: &localHeadIntegrityVerifier{ - keyVerifier: keyVerifier, - }, + headVerifier: new(localHeadIntegrityVerifier), payloadVerifier: new(payloadVerifier), }, nil } // NewPayloadVerifier constructs object payload verifier and returns objutil.Verifier. -func NewPayloadVerifier() objutil.Verifier { +func NewPayloadVerifier() verifier.Verifier { return new(payloadVerifier) } @@ -314,10 +305,10 @@ func (s hdrOwnerKeyContainer) GetOwnerKey() []byte { return s.key } -func (s *localHeadIntegrityVerifier) Verify(ctx context.Context, obj *Object) error { +func (s *localHeadIntegrityVerifier) Verify(ctx context.Context, obj *object.Object) error { var ( checkKey *ecdsa.PublicKey - ownerKeyCnr core.OwnerKeyContainer + ownerKeyCnr id.OwnerKeyContainer ) if _, h := obj.LastHeader(object.HeaderType(object.TokenHdr)); h != nil { @@ -348,8 +339,8 @@ func (s *localHeadIntegrityVerifier) Verify(ctx context.Context, obj *Object) er } if ownerKeyCnr == nil { - return core.ErrNilOwnerKeyContainer - } else if err := s.keyVerifier.VerifyKey(ctx, ownerKeyCnr); err != nil { + return id.ErrNilOwnerKeyContainer + } else if err := id.VerifyKey(ownerKeyCnr); err != nil { return err } @@ -361,7 +352,7 @@ func (s *localHeadIntegrityVerifier) Verify(ctx context.Context, obj *Object) er // - does not contains integrity header; // - integrity header is not a last header in object; // - integrity header signature is broken. -func verifyObjectIntegrity(obj *Object, key *ecdsa.PublicKey) error { +func verifyObjectIntegrity(obj *object.Object, key *ecdsa.PublicKey) error { n, h := obj.LastHeader(object.HeaderType(object.IntegrityHdr)) if l := len(obj.Headers); l <= 0 || n != l-1 { @@ -373,7 +364,7 @@ func verifyObjectIntegrity(obj *Object, key *ecdsa.PublicKey) error { return errBrokenHeaderStructure } - data, err := objutil.MarshalHeaders(obj, n) + data, err := verifier.MarshalHeaders(obj, n) if err != nil { return err } @@ -383,7 +374,7 @@ func verifyObjectIntegrity(obj *Object, key *ecdsa.PublicKey) error { return crypto.Verify(key, hdrChecksum[:], integrityHdr.ChecksumSignature) } -func (s *payloadVerifier) Verify(_ context.Context, obj *Object) error { +func (s *payloadVerifier) Verify(_ context.Context, obj *object.Object) error { if _, h := obj.LastHeader(object.HeaderType(object.PayloadChecksumHdr)); h == nil { return errMissingPayloadChecksumHeader } else if checksum := sha256.Sum256(obj.Payload); !bytes.Equal( @@ -396,7 +387,7 @@ func (s *payloadVerifier) Verify(_ context.Context, obj *Object) error { return nil } -func (s *localIntegrityVerifier) Verify(ctx context.Context, obj *Object) error { +func (s *localIntegrityVerifier) Verify(ctx context.Context, obj *object.Object) error { if err := s.headVerifier.Verify(ctx, obj); err != nil { return err } diff --git a/lib/implementations/validation_test.go b/pkg/services/object_manager/replication/storage/validation_test.go similarity index 80% rename from lib/implementations/validation_test.go rename to pkg/services/object_manager/replication/storage/validation_test.go index f795ebd4..4e15df18 100644 --- a/lib/implementations/validation_test.go +++ b/pkg/services/object_manager/replication/storage/validation_test.go @@ -1,4 +1,4 @@ -package implementations +package storage import ( "context" @@ -12,11 +12,10 @@ import ( "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/objutil" - "github.com/nspcc-dev/neofs-node/lib/test" + "github.com/nspcc-dev/neofs-node/pkg/local_object_storage/localstore" + verifier2 "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/verifier" + "github.com/nspcc-dev/neofs-node/pkg/util/logger" + "github.com/nspcc-dev/neofs-node/pkg/util/test" "github.com/pkg/errors" "github.com/stretchr/testify/require" "go.uber.org/zap" @@ -44,10 +43,6 @@ func (s *testEntity) PRead(ctx context.Context, addr refs.Address, rng object.Ra panic("implement me") } -func (s *testEntity) VerifyKey(context.Context, core.OwnerKeyContainer) error { - return s.err -} - func TestNewObjectValidator(t *testing.T) { validParams := ObjectValidatorParams{ Logger: zap.L(), @@ -71,28 +66,21 @@ func TestNewObjectValidator(t *testing.T) { p := validParams p.Logger = nil _, err := NewObjectValidator(&p) - require.EqualError(t, err, errors.Wrap(errEmptyLogger, objectValidatorInstanceFailMsg).Error()) + require.EqualError(t, err, errors.Wrap(logger.ErrNilLogger, objectValidatorInstanceFailMsg).Error()) }) } func TestNewLocalIntegrityVerifier(t *testing.T) { var ( - err error - verifier objutil.Verifier - keyVerifier = new(testEntity) + err error + verifier verifier2.Verifier ) - _, err = NewLocalHeadIntegrityVerifier(nil) - require.EqualError(t, err, core.ErrNilOwnerKeyVerifier.Error()) - - _, err = NewLocalIntegrityVerifier(nil) - require.EqualError(t, err, core.ErrNilOwnerKeyVerifier.Error()) - - verifier, err = NewLocalHeadIntegrityVerifier(keyVerifier) + verifier, err = NewLocalHeadIntegrityVerifier() require.NoError(t, err) require.NotNil(t, verifier) - verifier, err = NewLocalIntegrityVerifier(keyVerifier) + verifier, err = NewLocalIntegrityVerifier() require.NoError(t, err) require.NotNil(t, verifier) } @@ -109,12 +97,12 @@ func TestLocalHeadIntegrityVerifier_Verify(t *testing.T) { ownerID, err := refs.NewOwnerID(ownerPublicKey) require.NoError(t, err) - s, err := NewLocalIntegrityVerifier(core.NewNeoKeyVerifier()) + s, err := NewLocalIntegrityVerifier() require.NoError(t, err) - okItems := []func() *Object{ + okItems := []func() *object.Object{ // correct object w/ session token - func() *Object { + func() *object.Object { token := new(service.Token) token.SetOwnerID(ownerID) token.SetSessionKey(crypto.MarshalPublicKey(sessionPublicKey)) @@ -126,7 +114,7 @@ func TestLocalHeadIntegrityVerifier_Verify(t *testing.T) { ), ) - obj := new(Object) + obj := new(object.Object) obj.AddHeader(&object.Header{ Value: &object.Header_Token{ Token: token, @@ -141,8 +129,8 @@ func TestLocalHeadIntegrityVerifier_Verify(t *testing.T) { return obj }, // correct object w/o session token - func() *Object { - obj := new(Object) + func() *object.Object { + obj := new(object.Object) obj.SystemHeader.OwnerID = ownerID obj.SetPayload([]byte{1, 2, 3}) @@ -162,7 +150,7 @@ func TestLocalHeadIntegrityVerifier_Verify(t *testing.T) { }, } - failItems := []func() *Object{} + failItems := []func() *object.Object{} for _, item := range okItems { require.NoError(t, s.Verify(ctx, item())) @@ -173,7 +161,7 @@ func TestLocalHeadIntegrityVerifier_Verify(t *testing.T) { } } -func addPayloadChecksum(obj *Object) { +func addPayloadChecksum(obj *object.Object) { payloadChecksum := sha256.Sum256(obj.GetPayload()) obj.AddHeader(&object.Header{ @@ -183,8 +171,8 @@ func addPayloadChecksum(obj *Object) { }) } -func addHeadersChecksum(t *testing.T, obj *Object, key *ecdsa.PrivateKey) { - headersData, err := objutil.MarshalHeaders(obj, len(obj.Headers)) +func addHeadersChecksum(t *testing.T, obj *object.Object, key *ecdsa.PrivateKey) { + headersData, err := verifier2.MarshalHeaders(obj, len(obj.Headers)) require.NoError(t, err) headersChecksum := sha256.Sum256(headersData) @@ -206,7 +194,7 @@ func TestPayloadVerifier_Verify(t *testing.T) { verifier := new(payloadVerifier) t.Run("missing header", func(t *testing.T) { - obj := new(Object) + obj := new(object.Object) require.EqualError(t, verifier.Verify(ctx, obj), errMissingPayloadChecksumHeader.Error()) }) @@ -216,7 +204,7 @@ func TestPayloadVerifier_Verify(t *testing.T) { cs := sha256.Sum256(payload) hdr := &object.Header_PayloadChecksum{PayloadChecksum: cs[:]} - obj := &Object{ + obj := &object.Object{ Headers: []object.Header{{Value: hdr}}, Payload: payload, } @@ -234,10 +222,10 @@ func TestPayloadVerifier_Verify(t *testing.T) { func TestLocalIntegrityVerifier_Verify(t *testing.T) { ctx := context.TODO() - obj := new(Object) + obj := new(object.Object) t.Run("head verification failure", func(t *testing.T) { - hErr := internal.Error("test error for head verifier") + hErr := errors.New("test error for head verifier") s := &localIntegrityVerifier{ headVerifier: &testEntity{ @@ -249,7 +237,7 @@ func TestLocalIntegrityVerifier_Verify(t *testing.T) { }) t.Run("correct result", func(t *testing.T) { - pErr := internal.Error("test error for payload verifier") + pErr := errors.New("test error for payload verifier") s := &localIntegrityVerifier{ headVerifier: new(testEntity), diff --git a/lib/replication/storage_validator.go b/pkg/services/object_manager/replication/storage_validator.go similarity index 100% rename from lib/replication/storage_validator.go rename to pkg/services/object_manager/replication/storage_validator.go diff --git a/lib/transformer/alias.go b/pkg/services/object_manager/transformer/alias.go similarity index 100% rename from lib/transformer/alias.go rename to pkg/services/object_manager/transformer/alias.go diff --git a/lib/transformer/put_test.go b/pkg/services/object_manager/transformer/put_test.go similarity index 94% rename from lib/transformer/put_test.go rename to pkg/services/object_manager/transformer/put_test.go index ddd7affd..73f0470f 100644 --- a/lib/transformer/put_test.go +++ b/pkg/services/object_manager/transformer/put_test.go @@ -16,11 +16,9 @@ import ( "github.com/nspcc-dev/neofs-api-go/session" "github.com/nspcc-dev/neofs-api-go/storagegroup" crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/core" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/objutil" - "github.com/nspcc-dev/neofs-node/lib/test" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/replication/storage" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/verifier" + "github.com/nspcc-dev/neofs-node/pkg/util/test" "github.com/pkg/errors" "github.com/stretchr/testify/require" ) @@ -46,7 +44,7 @@ var ( _ EpochReceiver = (*testPutEntity)(nil) _ Transformer = (*testPutEntity)(nil) _ storagegroup.InfoReceiver = (*testPutEntity)(nil) - _ objutil.Verifier = (*testPutEntity)(nil) + _ verifier.Verifier = (*testPutEntity)(nil) ) func (s *testPutEntity) Verify(_ context.Context, obj *Object) error { @@ -135,7 +133,7 @@ func Test_transformer(t *testing.T) { t.Run("preliminary transformation failure", func(t *testing.T) { // create custom error for test - pErr := internal.Error("test error for prelim transformer") + pErr := errors.New("test error for prelim transformer") s := &transformer{ tPrelim: &testPutEntity{ @@ -155,8 +153,8 @@ func Test_transformer(t *testing.T) { t.Run("size limiter error/correct sign processing", func(t *testing.T) { // create custom error for test - sErr := internal.Error("test error for signer") - lErr := internal.Error("test error for size limiter") + sErr := errors.New("test error for signer") + lErr := errors.New("test error for size limiter") s := &transformer{ tPrelim: new(testPutEntity), @@ -199,7 +197,7 @@ func Test_preliminaryTransformer(t *testing.T) { t.Run("field moulder failure", func(t *testing.T) { // create custom error for test - mErr := internal.Error("test error for field moulder") + mErr := errors.New("test error for field moulder") s := &preliminaryTransformer{ fMoulder: &testPutEntity{ @@ -219,7 +217,7 @@ func Test_preliminaryTransformer(t *testing.T) { t.Run("correct result", func(t *testing.T) { // create custom error for test - sgErr := internal.Error("test error for SG moulder") + sgErr := errors.New("test error for SG moulder") s := &preliminaryTransformer{ fMoulder: new(testPutEntity), @@ -301,7 +299,7 @@ func Test_readChunk(t *testing.T) { ) t.Run("failure", func(t *testing.T) { - hErr := internal.Error("test error for hash writer") + hErr := errors.New("test error for hash writer") b := testData(t, len(d)) require.EqualError(t, readChunk(EmptyPayloadUnit(new(Object)), b, &testPutEntity{ @@ -333,14 +331,14 @@ func Test_headSigner(t *testing.T) { t.Run("missing token", func(t *testing.T) { u := ProcUnit{Head: new(Object)} require.Error(t, u.Head.Verify()) - s := &headSigner{verifier: &testPutEntity{err: internal.Error("")}} + s := &headSigner{verifier: &testPutEntity{err: errors.New("")}} require.EqualError(t, s.Transform(ctx, u), errNoToken.Error()) }) t.Run("with token", func(t *testing.T) { u := ProcUnit{Head: new(Object)} - verifier, err := implementations.NewLocalHeadIntegrityVerifier(core.NewNeoKeyVerifier()) + v, err := storage.NewLocalHeadIntegrityVerifier() require.NoError(t, err) require.Error(t, u.Head.Verify()) @@ -351,7 +349,7 @@ func Test_headSigner(t *testing.T) { s := &headSigner{ verifier: &testPutEntity{ - err: internal.Error(""), + err: errors.New(""), }, } @@ -368,10 +366,10 @@ func Test_headSigner(t *testing.T) { }) require.NoError(t, s.Transform(ctx, u, func(_ context.Context, unit ProcUnit) error { - require.NoError(t, verifier.Verify(ctx, unit.Head)) + require.NoError(t, v.Verify(ctx, unit.Head)) _, h := unit.Head.LastHeader(object.HeaderType(object.IntegrityHdr)) require.NotNil(t, h) - d, err := objutil.MarshalHeaders(unit.Head, len(unit.Head.Headers)-1) + d, err := verifier.MarshalHeaders(unit.Head, len(unit.Head.Headers)-1) require.NoError(t, err) cs := sha256.Sum256(d) require.Equal(t, cs[:], h.Value.(*object.Header_Integrity).Integrity.GetHeadersChecksum()) @@ -459,7 +457,7 @@ func Test_sgMoulder(t *testing.T) { addLink(obj, object.Link_StorageGroup, group[i]) } - sgErr := internal.Error("test error for SG info receiver") + sgErr := errors.New("test error for SG info receiver") mSG := &sgMoulder{ sgInfoRecv: &testPutEntity{ @@ -597,7 +595,7 @@ func Test_sizeLimiter(t *testing.T) { objs := make([]Object, 0) t.Run("handler error", func(t *testing.T) { - hErr := internal.Error("test error for handler") + hErr := errors.New("test error for handler") require.EqualError(t, sl.Transform(ctx, ProcUnit{ Head: &Object{ @@ -697,7 +695,7 @@ func TestIntegration(t *testing.T) { EpochReceiver: &testPutEntity{res: uint64(1)}, SizeLimit: 13, Verifier: &testPutEntity{ - err: internal.Error(""), // force verifier to return non-nil error + err: errors.New(""), // force verifier to return non-nil error }, }) require.NoError(t, err) @@ -734,7 +732,7 @@ func TestIntegration(t *testing.T) { func testTransformer(t *testing.T, ctx context.Context, u ProcUnit, tr Transformer, src []byte) { objList := make([]Object, 0) - verifier, err := implementations.NewLocalHeadIntegrityVerifier(core.NewNeoKeyVerifier()) + verifier, err := storage.NewLocalHeadIntegrityVerifier() require.NoError(t, err) require.NoError(t, tr.Transform(ctx, u, func(_ context.Context, unit ProcUnit) error { @@ -748,7 +746,7 @@ func testTransformer(t *testing.T, ctx context.Context, u ProcUnit, tr Transform res, err := reverse.Restore(ctx, objList...) require.NoError(t, err) - integrityVerifier, err := implementations.NewLocalIntegrityVerifier(core.NewNeoKeyVerifier()) + integrityVerifier, err := storage.NewLocalIntegrityVerifier() require.NoError(t, err) require.NoError(t, integrityVerifier.Verify(ctx, &res[0])) diff --git a/lib/transformer/restore.go b/pkg/services/object_manager/transformer/restore.go similarity index 100% rename from lib/transformer/restore.go rename to pkg/services/object_manager/transformer/restore.go diff --git a/lib/transformer/transformer.go b/pkg/services/object_manager/transformer/transformer.go similarity index 92% rename from lib/transformer/transformer.go rename to pkg/services/object_manager/transformer/transformer.go index 0016035b..81f4de49 100644 --- a/lib/transformer/transformer.go +++ b/pkg/services/object_manager/transformer/transformer.go @@ -13,8 +13,7 @@ import ( "github.com/nspcc-dev/neofs-api-go/service" "github.com/nspcc-dev/neofs-api-go/session" "github.com/nspcc-dev/neofs-api-go/storagegroup" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/objutil" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/verifier" "github.com/pkg/errors" ) @@ -67,7 +66,7 @@ type ( } headSigner struct { - verifier objutil.Verifier + verifier verifier.Verifier } emptyReader struct{} @@ -77,44 +76,42 @@ type ( SGInfoReceiver storagegroup.InfoReceiver EpochReceiver EpochReceiver SizeLimit uint64 - Verifier objutil.Verifier + Verifier verifier.Verifier } ) // ErrPayloadEOF is returned by Transformer that // received unexpected end of object payload. -const ErrPayloadEOF = internal.Error("payload EOF") +var ErrPayloadEOF = errors.New("payload EOF") const ( verifyHeadersCount = 2 // payload checksum, integrity splitHeadersCount = 4 // flag, parent, left, right - errEmptyInput = internal.Error("empty input") - transformerInstanceFailMsg = "could not create transformer instance" - errEmptySGInfoRecv = internal.Error("empty storage group info receivers") - errInvalidSizeLimit = internal.Error("non-positive object size limit") - errEmptyEpochReceiver = internal.Error("empty epoch receiver") - errEmptyVerifier = internal.Error("empty object verifier") - - // ErrInvalidSGLinking is returned by Transformer that received - // an object with broken storage group links. - ErrInvalidSGLinking = internal.Error("invalid storage group linking") // PrivateSessionToken is a context key for session.PrivateToken. PrivateSessionToken = "private token" // PublicSessionToken is a context key for service.SessionToken. PublicSessionToken = "public token" - - errNoToken = internal.Error("no token provided") ) -var errChainNotFound = errors.New("chain not found") +// ErrInvalidSGLinking is returned by Transformer that received +// an object with broken storage group links. +var ErrInvalidSGLinking = errors.New("invalid storage group linking") -var errCutChain = errors.New("GetChain failed: chain is not full") - -var errMissingTransformHdr = errors.New("cannot find transformer header") +var ( + errNoToken = errors.New("no token provided") + errEmptyInput = errors.New("empty input") + errChainNotFound = errors.New("chain not found") + errCutChain = errors.New("GetChain failed: chain is not full") + errMissingTransformHdr = errors.New("cannot find transformer header") + errEmptySGInfoRecv = errors.New("empty storage group info receivers") + errInvalidSizeLimit = errors.New("non-positive object size limit") + errEmptyEpochReceiver = errors.New("empty epoch receiver") + errEmptyVerifier = errors.New("empty object verifier") +) // NewTransformer is an object transformer's constructor. func NewTransformer(p Params) (Transformer, error) { @@ -348,7 +345,7 @@ func signWithToken(ctx context.Context, obj *Object) error { if pToken, ok := ctx.Value(PrivateSessionToken).(session.PrivateToken); !ok { return errNoToken - } else if hdrData, err := objutil.MarshalHeaders(obj, len(obj.Headers)); err != nil { + } else if hdrData, err := verifier.MarshalHeaders(obj, len(obj.Headers)); err != nil { return err } else { cs := sha256.Sum256(hdrData) diff --git a/lib/transport/object.go b/pkg/services/object_manager/transport/object.go similarity index 100% rename from lib/transport/object.go rename to pkg/services/object_manager/transport/object.go diff --git a/lib/implementations/sg.go b/pkg/services/object_manager/transport/storagegroup/sg.go similarity index 76% rename from lib/implementations/sg.go rename to pkg/services/object_manager/transport/storagegroup/sg.go index ef0f95e8..5d394906 100644 --- a/lib/implementations/sg.go +++ b/pkg/services/object_manager/transport/storagegroup/sg.go @@ -1,4 +1,4 @@ -package implementations +package storagegroup import ( "context" @@ -9,7 +9,9 @@ import ( "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" "github.com/nspcc-dev/neofs-api-go/storagegroup" - "github.com/nspcc-dev/neofs-node/internal" + "github.com/nspcc-dev/neofs-node/pkg/core/container" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/transport" + "github.com/nspcc-dev/neofs-node/pkg/util/logger" "github.com/pkg/errors" "go.uber.org/zap" ) @@ -18,12 +20,12 @@ type ( // StorageGroupInfoReceiverParams groups the parameters of // storage group information receiver. StorageGroupInfoReceiverParams struct { - SelectiveContainerExecutor SelectiveContainerExecutor + SelectiveContainerExecutor transport.SelectiveContainerExecutor Logger *zap.Logger } sgInfoRecv struct { - executor SelectiveContainerExecutor + executor transport.SelectiveContainerExecutor log *zap.Logger } ) @@ -32,7 +34,7 @@ const locationFinderInstanceFailMsg = "could not create object location finder" // ErrIncompleteSGInfo is returned by storage group information receiver // that could not receive full information. -const ErrIncompleteSGInfo = internal.Error("could not receive full storage group info") +var ErrIncompleteSGInfo = errors.New("could not receive full storage group info") // PublicSessionToken is a context key for SessionToken. // FIXME: temp solution for cycle import fix. @@ -45,7 +47,7 @@ const BearerToken = "bearer token" // ExtendedHeaders is a context key for X-headers. const ExtendedHeaders = "extended headers" -func (s *sgInfoRecv) GetSGInfo(ctx context.Context, cid CID, group []ObjectID) (*storagegroup.StorageGroup, error) { +func (s *sgInfoRecv) GetSGInfo(ctx context.Context, cid container.ID, group []object.ID) (*storagegroup.StorageGroup, error) { var ( err error res = new(storagegroup.StorageGroup) @@ -77,17 +79,17 @@ func (s *sgInfoRecv) GetSGInfo(ctx context.Context, cid CID, group []ObjectID) ( extHdrs = v } - if err = s.executor.Head(ctx, &HeadParams{ - GetParams: GetParams{ - SelectiveParams: SelectiveParams{ + if err = s.executor.Head(ctx, &transport.HeadParams{ + GetParams: transport.GetParams{ + SelectiveParams: transport.SelectiveParams{ CID: cid, TTL: service.SingleForwardingTTL, IDList: group, - Breaker: func(addr refs.Address) (cFlag ProgressControlFlag) { + Breaker: func(addr refs.Address) (cFlag transport.ProgressControlFlag) { if len(m) == 0 { - cFlag = BreakProgress + cFlag = transport.BreakProgress } else if _, ok := m[addr.ObjectID.String()]; !ok { - cFlag = NextAddress + cFlag = transport.NextAddress } return }, @@ -124,9 +126,9 @@ func (s *sgInfoRecv) GetSGInfo(ctx context.Context, cid CID, group []ObjectID) ( func NewStorageGroupInfoReceiver(p StorageGroupInfoReceiverParams) (storagegroup.InfoReceiver, error) { switch { case p.Logger == nil: - return nil, errors.Wrap(errEmptyLogger, locationFinderInstanceFailMsg) + return nil, errors.Wrap(logger.ErrNilLogger, locationFinderInstanceFailMsg) case p.SelectiveContainerExecutor == nil: - return nil, errors.Wrap(errEmptyObjectsContainerHandler, locationFinderInstanceFailMsg) + return nil, errors.Wrap(errors.New("empty container handler"), locationFinderInstanceFailMsg) } return &sgInfoRecv{ diff --git a/lib/implementations/transport.go b/pkg/services/object_manager/transport/transport.go similarity index 90% rename from lib/implementations/transport.go rename to pkg/services/object_manager/transport/transport.go index b409be83..63ccce1f 100644 --- a/lib/implementations/transport.go +++ b/pkg/services/object_manager/transport/transport.go @@ -1,4 +1,4 @@ -package implementations +package transport import ( "context" @@ -11,8 +11,8 @@ import ( "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" - "github.com/nspcc-dev/neofs-node/internal" - "github.com/nspcc-dev/neofs-node/lib/transport" + "github.com/nspcc-dev/neofs-node/pkg/core/container" + "github.com/nspcc-dev/neofs-node/pkg/services/object_manager/placement" "github.com/pkg/errors" "go.uber.org/zap" ) @@ -91,7 +91,7 @@ type ( TTL uint32 /* Required ID of processing container. If empty or not set, an error is returned. */ - CID + CID container.ID /* List of nodes selected for processing. If not specified => nodes will be selected during. */ Nodes []multiaddr.Multiaddr @@ -127,7 +127,7 @@ type ( // ObjectContainerHandlerParams grops the parameters of SelectiveContainerExecutor constructor. ObjectContainerHandlerParams struct { - NodeLister ContainerNodesLister + NodeLister *placement.PlacementWrapper Executor ContainerTraverseExecutor *zap.Logger } @@ -138,7 +138,7 @@ type ( } selectiveCnrExec struct { - cnl ContainerNodesLister + cnl *placement.PlacementWrapper Executor ContainerTraverseExecutor log *zap.Logger } @@ -163,7 +163,7 @@ type ( getInfo struct { metaInfo - addr Address + addr object.Address raw bool } @@ -174,21 +174,21 @@ type ( searchInfo struct { metaInfo - cid CID + cid container.ID query []byte } rangeHashInfo struct { metaInfo - addr Address + addr object.Address ranges []object.Range salt []byte } execItems struct { params SelectiveParams - metaConstructor func(addr Address) transport.MetaInfo - handler transport.ResultHandler + metaConstructor func(addr object.Address) MetaInfo + handler ResultHandler } searchTarget struct { @@ -203,8 +203,8 @@ type ( // TraverseParams groups the parameters of container traversing. TraverseParams struct { - TransportInfo transport.MetaInfo - Handler transport.ResultHandler + TransportInfo MetaInfo + Handler ResultHandler Traverser Traverser WorkerPool WorkerPool ExecutionInterceptor func(context.Context, multiaddr.Multiaddr) bool @@ -221,7 +221,7 @@ type ( } cnrTraverseExec struct { - transport transport.ObjectTransport + transport ObjectTransport } singleRoutinePool struct{} @@ -243,15 +243,16 @@ const ( ) const ( - instanceFailMsg = "could not create container objects collector" - errEmptyLogger = internal.Error("empty logger") - errEmptyNodeLister = internal.Error("empty container node lister") - errEmptyTraverseExecutor = internal.Error("empty container traverse executor") - - errSelectiveParams = internal.Error("neither ID list nor query provided") + instanceFailMsg = "could not create container objects collector" ) -var errNilObjectTransport = errors.New("object transport is nil") +var ( + errNilObjectTransport = errors.New("object transport is nil") + errEmptyLogger = errors.New("empty logger") + errEmptyNodeLister = errors.New("empty container node lister") + errEmptyTraverseExecutor = errors.New("empty container traverse executor") + errSelectiveParams = errors.New("neither ID list nor query provided") +) func (s *selectiveCnrExec) Put(ctx context.Context, p *PutParams) error { meta := &putInfo{ @@ -272,7 +273,7 @@ func (s *selectiveCnrExec) Put(ctx context.Context, p *PutParams) error { return s.exec(ctx, &execItems{ params: p.SelectiveParams, - metaConstructor: func(Address) transport.MetaInfo { return meta }, + metaConstructor: func(object.Address) MetaInfo { return meta }, handler: p, }) } @@ -280,7 +281,7 @@ func (s *selectiveCnrExec) Put(ctx context.Context, p *PutParams) error { func (s *selectiveCnrExec) Get(ctx context.Context, p *GetParams) error { return s.exec(ctx, &execItems{ params: p.SelectiveParams, - metaConstructor: func(addr Address) transport.MetaInfo { + metaConstructor: func(addr object.Address) MetaInfo { return &getInfo{ metaInfo: metaInfo{ ttl: p.TTL, @@ -304,7 +305,7 @@ func (s *selectiveCnrExec) Get(ctx context.Context, p *GetParams) error { func (s *selectiveCnrExec) Head(ctx context.Context, p *HeadParams) error { return s.exec(ctx, &execItems{ params: p.SelectiveParams, - metaConstructor: func(addr Address) transport.MetaInfo { + metaConstructor: func(addr object.Address) MetaInfo { return &headInfo{ getInfo: getInfo{ metaInfo: metaInfo{ @@ -331,7 +332,7 @@ func (s *selectiveCnrExec) Head(ctx context.Context, p *HeadParams) error { func (s *selectiveCnrExec) Search(ctx context.Context, p *SearchParams) error { return s.exec(ctx, &execItems{ params: p.SelectiveParams, - metaConstructor: func(Address) transport.MetaInfo { + metaConstructor: func(object.Address) MetaInfo { return &searchInfo{ metaInfo: metaInfo{ ttl: p.TTL, @@ -355,7 +356,7 @@ func (s *selectiveCnrExec) Search(ctx context.Context, p *SearchParams) error { func (s *selectiveCnrExec) RangeHash(ctx context.Context, p *RangeHashParams) error { return s.exec(ctx, &execItems{ params: p.SelectiveParams, - metaConstructor: func(addr Address) transport.MetaInfo { + metaConstructor: func(addr object.Address) MetaInfo { return &rangeHashInfo{ metaInfo: metaInfo{ ttl: p.TTL, @@ -443,14 +444,14 @@ func (s *selectiveCnrExec) prepareNodes(ctx context.Context, p *SelectiveParams) func (s *selectiveCnrExec) prepareAddrList(ctx context.Context, p *SelectiveParams, node multiaddr.Multiaddr) []refs.Address { var ( - addrList []Address + addrList []object.Address l = len(p.IDList) ) if l > 0 { - addrList = make([]Address, 0, l) + addrList = make([]object.Address, 0, l) for i := range p.IDList { - addrList = append(addrList, Address{CID: p.CID, ObjectID: p.IDList[i]}) + addrList = append(addrList, object.Address{CID: p.CID, ObjectID: p.IDList[i]}) } return addrList @@ -610,7 +611,7 @@ func (s *cnrTraverseExec) Execute(ctx context.Context, p TraverseParams) { return } - s.transport.Transport(ctx, transport.ObjectTransportParams{ + s.transport.Transport(ctx, ObjectTransportParams{ TransportInfo: p.TransportInfo, TargetNode: node, ResultHandler: p.Handler, @@ -648,7 +649,7 @@ func NewObjectContainerHandler(p ObjectContainerHandlerParams) (SelectiveContain } // NewContainerTraverseExecutor is a ContainerTraverseExecutor executor. -func NewContainerTraverseExecutor(t transport.ObjectTransport) (ContainerTraverseExecutor, error) { +func NewContainerTraverseExecutor(t ObjectTransport) (ContainerTraverseExecutor, error) { if t == nil { return nil, errNilObjectTransport } diff --git a/lib/objutil/verifier.go b/pkg/services/object_manager/verifier/verifier.go similarity index 98% rename from lib/objutil/verifier.go rename to pkg/services/object_manager/verifier/verifier.go index a31dfff4..46c83ce7 100644 --- a/lib/objutil/verifier.go +++ b/pkg/services/object_manager/verifier/verifier.go @@ -1,4 +1,4 @@ -package objutil +package verifier import ( "bytes" diff --git a/lib/fix/grace.go b/pkg/util/grace/grace.go similarity index 79% rename from lib/fix/grace.go rename to pkg/util/grace/grace.go index 3343b8ea..8f48d3f0 100644 --- a/lib/fix/grace.go +++ b/pkg/util/grace/grace.go @@ -1,4 +1,4 @@ -package fix +package grace import ( "context" @@ -9,7 +9,8 @@ import ( "go.uber.org/zap" ) -// NewGracefulContext returns graceful context. +// NewGracefulContext returns grace context that cancelled by sigint, +// sigterm and sighup. func NewGracefulContext(l *zap.Logger) context.Context { ctx, cancel := context.WithCancel(context.Background()) diff --git a/lib/fix/logger/logger.go b/pkg/util/logger/logger.go similarity index 84% rename from lib/fix/logger/logger.go rename to pkg/util/logger/logger.go index 4f10ee11..da0de1b0 100644 --- a/lib/fix/logger/logger.go +++ b/pkg/util/logger/logger.go @@ -1,6 +1,7 @@ package logger import ( + "errors" "strings" "github.com/spf13/viper" @@ -8,6 +9,13 @@ import ( "go.uber.org/zap/zapcore" ) +// Logger represents the component +// for writing messages to log. +// +// It is a type alias of +// go.uber.org/zap.Logger. +type Logger = zap.Logger + const ( formatJSON = "json" formatConsole = "console" @@ -16,25 +24,12 @@ const ( defaultSamplingThereafter = 100 ) -func safeLevel(lvl string) zap.AtomicLevel { - switch strings.ToLower(lvl) { - case "debug": - return zap.NewAtomicLevelAt(zap.DebugLevel) - case "warn": - return zap.NewAtomicLevelAt(zap.WarnLevel) - case "error": - return zap.NewAtomicLevelAt(zap.ErrorLevel) - case "fatal": - return zap.NewAtomicLevelAt(zap.FatalLevel) - case "panic": - return zap.NewAtomicLevelAt(zap.PanicLevel) - default: - return zap.NewAtomicLevelAt(zap.InfoLevel) - } -} +// ErrNilLogger is returned by functions that +// expect a non-nil Logger, but received nil. +var ErrNilLogger = errors.New("logger is nil") // NewLogger is a logger's constructor. -func NewLogger(v *viper.Viper) (*zap.Logger, error) { +func NewLogger(v *viper.Viper) (*Logger, error) { c := zap.NewProductionConfig() c.OutputPaths = []string{"stdout"} @@ -88,3 +83,20 @@ func NewLogger(v *viper.Viper) (*zap.Logger, error) { zap.String("app_name", name), zap.String("app_version", version)), nil } + +func safeLevel(lvl string) zap.AtomicLevel { + switch strings.ToLower(lvl) { + case "debug": + return zap.NewAtomicLevelAt(zap.DebugLevel) + case "warn": + return zap.NewAtomicLevelAt(zap.WarnLevel) + case "error": + return zap.NewAtomicLevelAt(zap.ErrorLevel) + case "fatal": + return zap.NewAtomicLevelAt(zap.FatalLevel) + case "panic": + return zap.NewAtomicLevelAt(zap.PanicLevel) + default: + return zap.NewAtomicLevelAt(zap.InfoLevel) + } +} diff --git a/lib/test/logger.go b/pkg/util/logger/test/logger.go similarity index 71% rename from lib/test/logger.go rename to pkg/util/logger/test/logger.go index 1ba43137..d2dda19d 100644 --- a/lib/test/logger.go +++ b/pkg/util/logger/test/logger.go @@ -1,14 +1,17 @@ package test import ( + "github.com/nspcc-dev/neofs-node/pkg/util/logger" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) const sampling = 1000 -// NewTestLogger creates test logger. -func NewTestLogger(debug bool) *zap.Logger { +// NewLogger creates a new logger. +// +// If debug, development logger is created. +func NewLogger(debug bool) *logger.Logger { if debug { cfg := zap.NewDevelopmentConfig() cfg.Sampling = &zap.SamplingConfig{ diff --git a/lib/fix/web/http.go b/pkg/util/profiler/http.go similarity index 99% rename from lib/fix/web/http.go rename to pkg/util/profiler/http.go index 19941eb6..30264d66 100644 --- a/lib/fix/web/http.go +++ b/pkg/util/profiler/http.go @@ -1,4 +1,4 @@ -package web +package profiler import ( "context" diff --git a/lib/fix/web/metrics.go b/pkg/util/profiler/metrics.go similarity index 97% rename from lib/fix/web/metrics.go rename to pkg/util/profiler/metrics.go index 951b17f2..e165d6dc 100644 --- a/lib/fix/web/metrics.go +++ b/pkg/util/profiler/metrics.go @@ -1,4 +1,4 @@ -package web +package profiler import ( "context" diff --git a/lib/fix/web/pprof.go b/pkg/util/profiler/pprof.go similarity index 98% rename from lib/fix/web/pprof.go rename to pkg/util/profiler/pprof.go index da5a331b..e863a353 100644 --- a/lib/fix/web/pprof.go +++ b/pkg/util/profiler/pprof.go @@ -1,4 +1,4 @@ -package web +package profiler import ( "context" diff --git a/lib/fix/web/server.go b/pkg/util/profiler/server.go similarity index 98% rename from lib/fix/web/server.go rename to pkg/util/profiler/server.go index e4fcb845..ab5549a3 100644 --- a/lib/fix/web/server.go +++ b/pkg/util/profiler/server.go @@ -1,4 +1,4 @@ -package web +package profiler import ( "context" diff --git a/lib/rand/rand.go b/pkg/util/rand/rand.go similarity index 100% rename from lib/rand/rand.go rename to pkg/util/rand/rand.go diff --git a/lib/test/keys.go b/pkg/util/test/keys.go similarity index 100% rename from lib/test/keys.go rename to pkg/util/test/keys.go diff --git a/services/public/container/alias.go b/services/public/container/alias.go deleted file mode 100644 index 138f09a5..00000000 --- a/services/public/container/alias.go +++ /dev/null @@ -1,15 +0,0 @@ -package container - -import ( - "github.com/nspcc-dev/neofs-api-go/container" - "github.com/nspcc-dev/neofs-api-go/refs" -) - -// CID is a type alias of CID. -type CID = refs.CID - -// OwnerID is a type alias of OwnerID. -type OwnerID = refs.OwnerID - -// Container is a type alias of Container. -type Container = container.Container diff --git a/services/public/container/get.go b/services/public/container/get.go deleted file mode 100644 index 8e48bae2..00000000 --- a/services/public/container/get.go +++ /dev/null @@ -1,38 +0,0 @@ -package container - -import ( - "context" - - "github.com/nspcc-dev/neofs-api-go/container" - libcnr "github.com/nspcc-dev/neofs-node/lib/container" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -func (s cnrService) Get(ctx context.Context, req *container.GetRequest) (*container.GetResponse, error) { - // check healthiness - if err := s.healthy.Healthy(); err != nil { - return nil, status.Error(codes.Unavailable, err.Error()) - } - - // verify request structure - if err := requestVerifyFunc(req); err != nil { - return nil, status.Error(codes.InvalidArgument, err.Error()) - } - - // get container - p := libcnr.GetParams{} - p.SetContext(ctx) - p.SetCID(req.GetCID()) - - gRes, err := s.cnrStore.GetContainer(p) - if err != nil { - return nil, status.Error(codes.NotFound, err.Error()) - } - - // fill the response - res := new(container.GetResponse) - res.Container = gRes.Container() - - return res, nil -} diff --git a/services/public/container/list_test.go b/services/public/container/list_test.go deleted file mode 100644 index 38123ece..00000000 --- a/services/public/container/list_test.go +++ /dev/null @@ -1,124 +0,0 @@ -package container - -import ( - "context" - "errors" - "testing" - - "github.com/nspcc-dev/neofs-api-go/container" - libcnr "github.com/nspcc-dev/neofs-node/lib/container" - "github.com/nspcc-dev/neofs-node/lib/test" - "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -// Entity for mocking interfaces. -// Implementation of any interface intercepts arguments via f (if not nil). -// If err is not nil, it returns as it is. Otherwise, casted to needed type res returns w/o error. -type testListEntity struct { - // Set of interfaces which entity must implement, but some methods from those does not call. - libcnr.Storage - - // Argument interceptor. Used for ascertain of correct parameter passage between components. - f func(...interface{}) - // Mocked result of any interface. - res interface{} - // Mocked error of any interface. - err error -} - -func (s testListEntity) ListContainers(p libcnr.ListParams) (*libcnr.ListResult, error) { - if s.f != nil { - s.f(p) - } - - if s.err != nil { - return nil, s.err - } - - return s.res.(*libcnr.ListResult), nil -} - -func TestCnrService_List(t *testing.T) { - ctx := context.TODO() - - t.Run("unhealthy", func(t *testing.T) { - s := cnrService{ - healthy: &testCommonEntity{ - err: errors.New("some error"), - }, - } - - _, err := s.List(ctx, new(container.ListRequest)) - require.Error(t, err) - }) - - t.Run("invalid request structure", func(t *testing.T) { - s := cnrService{ - healthy: new(testCommonEntity), - } - - // create unsigned request - req := new(container.ListRequest) - require.Error(t, requestVerifyFunc(req)) - - _, err := s.List(ctx, req) - require.Error(t, err) - - st, ok := status.FromError(err) - require.True(t, ok) - require.Equal(t, codes.InvalidArgument, st.Code()) - }) - - t.Run("container storage failure", func(t *testing.T) { - req := new(container.ListRequest) - req.SetOwnerID(OwnerID{1, 2, 3}) - - require.NoError(t, requestSignFunc(test.DecodeKey(0), req)) - - s := cnrService{ - healthy: new(testCommonEntity), - cnrStore: &testListEntity{ - f: func(items ...interface{}) { - p := items[0].(libcnr.ListParams) - require.Equal(t, ctx, p.Context()) - require.Equal(t, req.GetOwnerID(), p.OwnerIDList()[0]) - }, - err: errors.New("storage error"), - }, - } - - _, err := s.List(ctx, req) - require.Error(t, err) - - st, ok := status.FromError(err) - require.True(t, ok) - require.Equal(t, codes.NotFound, st.Code()) - }) - - t.Run("correct result", func(t *testing.T) { - req := new(container.ListRequest) - - require.NoError(t, requestSignFunc(test.DecodeKey(0), req)) - - cidList := []CID{ - {1, 2, 3}, - {4, 5, 6}, - } - - listRes := new(libcnr.ListResult) - listRes.SetCIDList(cidList) - - s := cnrService{ - healthy: new(testCommonEntity), - cnrStore: &testListEntity{ - res: listRes, - }, - } - - res, err := s.List(ctx, req) - require.NoError(t, err) - require.Equal(t, cidList, res.CID) - }) -} diff --git a/services/public/container/put_test.go b/services/public/container/put_test.go deleted file mode 100644 index a777c23a..00000000 --- a/services/public/container/put_test.go +++ /dev/null @@ -1,132 +0,0 @@ -package container - -import ( - "context" - "errors" - "testing" - - "github.com/nspcc-dev/neofs-api-go/container" - libcnr "github.com/nspcc-dev/neofs-node/lib/container" - "github.com/nspcc-dev/neofs-node/lib/netmap" - "github.com/nspcc-dev/neofs-node/lib/test" - "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -// Entity for mocking interfaces. -// Implementation of any interface intercepts arguments via f (if not nil). -// If err is not nil, it returns as it is. Otherwise, casted to needed type res returns w/o error. -type testPutEntity struct { - // Set of interfaces which entity must implement, but some methods from those does not call. - libcnr.Storage - - // Argument interceptor. Used for ascertain of correct parameter passage between components. - f func(...interface{}) - // Mocked result of any interface. - res interface{} - // Mocked error of any interface. - err error -} - -func (s testPutEntity) PutContainer(p libcnr.PutParams) (*libcnr.PutResult, error) { - if s.f != nil { - s.f(p) - } - - if s.err != nil { - return nil, s.err - } - - return s.res.(*libcnr.PutResult), nil -} - -func TestCnrService_Put(t *testing.T) { - ctx := context.TODO() - - t.Run("unhealthy", func(t *testing.T) { - s := cnrService{ - healthy: &testCommonEntity{ - err: errors.New("some error"), - }, - } - - _, err := s.Put(ctx, new(container.PutRequest)) - require.Error(t, err) - }) - - t.Run("invalid request structure", func(t *testing.T) { - s := cnrService{ - healthy: new(testCommonEntity), - } - - // create unsigned request - req := new(container.PutRequest) - require.Error(t, requestVerifyFunc(req)) - - _, err := s.Put(ctx, req) - require.Error(t, err) - - st, ok := status.FromError(err) - require.True(t, ok) - require.Equal(t, codes.InvalidArgument, st.Code()) - }) - - t.Run("container storage failure", func(t *testing.T) { - req := new(container.PutRequest) - req.SetCapacity(1) - req.SetBasicACL(2) - req.SetOwnerID(OwnerID{1, 2, 3}) - req.SetRules(netmap.PlacementRule{ - ReplFactor: 3, - }) - - require.NoError(t, requestSignFunc(test.DecodeKey(0), req)) - - s := cnrService{ - healthy: new(testCommonEntity), - cnrStore: &testPutEntity{ - f: func(items ...interface{}) { - p := items[0].(libcnr.PutParams) - require.Equal(t, ctx, p.Context()) - - cnr := p.Container() - require.Equal(t, req.GetCapacity(), cnr.GetCapacity()) - require.Equal(t, req.GetBasicACL(), cnr.GetBasicACL()) - require.Equal(t, req.GetRules(), cnr.GetRules()) - require.Equal(t, req.GetOwnerID(), cnr.OwnerID) - }, - err: errors.New("storage error"), - }, - } - - _, err := s.Put(ctx, req) - require.Error(t, err) - - st, ok := status.FromError(err) - require.True(t, ok) - require.Equal(t, codes.Aborted, st.Code()) - }) - - t.Run("correct result", func(t *testing.T) { - req := new(container.PutRequest) - - require.NoError(t, requestSignFunc(test.DecodeKey(0), req)) - - cid := CID{1, 2, 3} - - putRes := new(libcnr.PutResult) - putRes.SetCID(cid) - - s := cnrService{ - healthy: new(testCommonEntity), - cnrStore: &testPutEntity{ - res: putRes, - }, - } - - res, err := s.Put(ctx, req) - require.NoError(t, err) - require.Equal(t, cid, res.CID) - }) -} diff --git a/services/public/object/acl.go b/services/public/object/acl.go deleted file mode 100644 index 26264f28..00000000 --- a/services/public/object/acl.go +++ /dev/null @@ -1,428 +0,0 @@ -package object - -import ( - "bytes" - "context" - "crypto/ecdsa" - - "github.com/nspcc-dev/neofs-api-go/acl" - "github.com/nspcc-dev/neofs-api-go/object" - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-api-go/service" - crypto "github.com/nspcc-dev/neofs-crypto" - libacl "github.com/nspcc-dev/neofs-node/lib/acl" - "github.com/nspcc-dev/neofs-node/lib/implementations" - "github.com/nspcc-dev/neofs-node/lib/ir" - "github.com/nspcc-dev/neofs-node/lib/localstore" - "github.com/nspcc-dev/neofs-node/lib/transport" - "github.com/pkg/errors" - "go.uber.org/zap" -) - -type ( - // RequestTargeter is an interface of request's ACL target calculator. - RequestTargeter interface { - Target(context.Context, serviceRequest) acl.Target - } - - // aclPreProcessor is an implementation of requestPreProcessor interface. - aclPreProcessor struct { - log *zap.Logger - - aclInfoReceiver aclInfoReceiver - - basicChecker libacl.BasicChecker - - reqActionCalc requestActionCalculator - - localStore localstore.Localstore - - extACLSource libacl.ExtendedACLSource - - bearerVerifier bearerTokenVerifier - } - - targetFinder struct { - log *zap.Logger - - irStorage ir.Storage - cnrLister implementations.ContainerNodesLister - cnrOwnerChecker implementations.ContainerOwnerChecker - } -) - -type objectHeadersSource interface { - getHeaders() (*Object, bool) -} - -type requestActionCalculator interface { - calculateRequestAction(context.Context, requestActionParams) acl.ExtendedACLAction -} - -type aclInfoReceiver struct { - basicACLGetter implementations.BasicACLGetter - - basicChecker libacl.BasicChecker - - targetFinder RequestTargeter -} - -type aclInfo struct { - rule uint32 - - checkExtended bool - - checkBearer bool - - target acl.Target -} - -type reqActionCalc struct { - extACLChecker libacl.ExtendedACLChecker - - log *zap.Logger -} - -type serviceRequestInfo struct { - target acl.Target - - req serviceRequest - - objHdrSrc objectHeadersSource -} - -type requestObjHdrSrc struct { - req serviceRequest - - ls localstore.Localstore -} - -type eaclFromBearer struct { - bearer service.BearerToken -} - -var _ requestPreProcessor = (*aclPreProcessor)(nil) - -var errMissingSignatures = errors.New("empty signature list") - -func (p *aclPreProcessor) preProcess(ctx context.Context, req serviceRequest) error { - if req == nil { - panic(pmEmptyServiceRequest) - } - - // fetch ACL info - aclInfo, err := p.aclInfoReceiver.getACLInfo(ctx, req) - if err != nil { - p.log.Warn("can't get acl of the container", zap.Stringer("cid", req.CID())) - return errAccessDenied - } - - // check basic ACL permissions - allow, err := p.basicChecker.Action(aclInfo.rule, req.Type(), aclInfo.target) - if err != nil || !allow { - return errAccessDenied - } - - if aclInfo.target != acl.Target_System && - p.basicChecker.Sticky(aclInfo.rule) && - !checkObjectRequestOwnerMatch(req) { - return errAccessDenied - } - - if !aclInfo.checkBearer && !aclInfo.checkExtended { - return nil - } - - actionParams := requestActionParams{ - eaclSrc: p.extACLSource, - request: req, - objHdrSrc: &requestObjHdrSrc{ - req: req, - ls: p.localStore, - }, - target: aclInfo.target, - } - - if aclInfo.checkBearer { - bearer := req.GetBearerToken() - - if err := p.bearerVerifier.verifyBearerToken(ctx, req.CID(), bearer); err != nil { - p.log.Warn("bearer token verification failure", - zap.String("error", err.Error()), - ) - - return errAccessDenied - } - - actionParams.eaclSrc = eaclFromBearer{ - bearer: bearer, - } - } - - if p.reqActionCalc.calculateRequestAction(ctx, actionParams) != acl.ActionAllow { - return errAccessDenied - } - - return nil -} - -func (t *targetFinder) Target(ctx context.Context, req serviceRequest) acl.Target { - ownerID, ownerKey, err := requestOwner(req) - if err != nil { - t.log.Warn("could not get request owner", - zap.String("error", err.Error()), - ) - - return acl.Target_Unknown - } else if ownerKey == nil { - t.log.Warn("signature with nil public key detected") - return acl.Target_Unknown - } - - // if request from container owner then return Target_User - isOwner, err := t.cnrOwnerChecker.IsContainerOwner(ctx, req.CID(), ownerID) - if err != nil { - t.log.Warn("can't check container owner", zap.String("err", err.Error())) - return acl.Target_Unknown - } else if isOwner { - return acl.Target_User - } - - ownerKeyBytes := crypto.MarshalPublicKey(ownerKey) - - // if request from inner ring then return Target_System - isIRKey, err := ir.IsInnerRingKey(t.irStorage, ownerKeyBytes) - if err != nil { - t.log.Warn("could not verify the key belongs to the node", zap.String("err", err.Error())) - return acl.Target_Unknown - } else if isIRKey { - return acl.Target_System - } - - // if request from current container node then return Target_System - cnr, err := t.cnrLister.ContainerNodesInfo(ctx, req.CID(), 0) - if err != nil { - t.log.Warn("can't get current container list", zap.String("err", err.Error())) - return acl.Target_Unknown - } - - for i := range cnr { - if bytes.Equal(cnr[i].PubKey, ownerKeyBytes) { - return acl.Target_System - } - } - - // if request from previous container node then return Target_System - cnr, err = t.cnrLister.ContainerNodesInfo(ctx, req.CID(), 1) - if err != nil { - t.log.Warn("can't get previous container list", zap.String("err", err.Error())) - return acl.Target_Unknown - } - - for i := range cnr { - if bytes.Equal(cnr[i].PubKey, ownerKeyBytes) { - return acl.Target_System - } - } - - // if none of the above return Target_Others - return acl.Target_Others -} - -func checkObjectRequestOwnerMatch(req serviceRequest) bool { - rt := req.Type() - - // ignore all request types except Put and Delete - if rt != object.RequestPut && rt != object.RequestDelete { - return true - } - - // get request owner - reqOwner, _, err := requestOwner(req) - if err != nil { - return false - } - - var payloadOwner OwnerID - - // get owner from request payload - if rt == object.RequestPut { - obj := req.(transport.PutInfo).GetHead() - if obj == nil { - return false - } - - payloadOwner = obj.GetSystemHeader().OwnerID - } else { - payloadOwner = req.(*object.DeleteRequest).OwnerID - } - - return reqOwner.Equal(payloadOwner) -} - -// FIXME: this solution only works with healthy key-to-owner conversion. -func requestOwner(req serviceRequest) (OwnerID, *ecdsa.PublicKey, error) { - // if session token exists => return its owner - if token := req.GetSessionToken(); token != nil { - return token.GetOwnerID(), crypto.UnmarshalPublicKey(token.GetOwnerKey()), nil - } - - signKeys := req.GetSignKeyPairs() - if len(signKeys) == 0 { - return OwnerID{}, nil, errMissingSignatures - } - - firstKey := signKeys[0].GetPublicKey() - if firstKey == nil { - return OwnerID{}, nil, crypto.ErrEmptyPublicKey - } - - owner, err := refs.NewOwnerID(firstKey) - - return owner, firstKey, err -} - -// HeadersOfType returns request or object headers. -func (s serviceRequestInfo) HeadersOfType(typ acl.HeaderType) ([]acl.Header, bool) { - switch typ { - default: - return nil, true - case acl.HdrTypeRequest: - return libacl.TypedHeaderSourceFromExtendedHeaders(s.req).HeadersOfType(typ) - case acl.HdrTypeObjSys, acl.HdrTypeObjUsr: - obj, ok := s.objHdrSrc.getHeaders() - if !ok { - return nil, false - } - - return libacl.TypedHeaderSourceFromObject(obj).HeadersOfType(typ) - } -} - -// Key returns a binary representation of sender public key. -func (s serviceRequestInfo) Key() []byte { - _, key, err := requestOwner(s.req) - if err != nil { - return nil - } - - return crypto.MarshalPublicKey(key) -} - -// TypeOf returns true of object request type corresponds to passed OperationType. -func (s serviceRequestInfo) TypeOf(opType acl.OperationType) bool { - switch s.req.Type() { - case object.RequestGet: - return opType == acl.OpTypeGet - case object.RequestPut: - return opType == acl.OpTypePut - case object.RequestHead: - return opType == acl.OpTypeHead - case object.RequestSearch: - return opType == acl.OpTypeSearch - case object.RequestDelete: - return opType == acl.OpTypeDelete - case object.RequestRange: - return opType == acl.OpTypeRange - case object.RequestRangeHash: - return opType == acl.OpTypeRangeHash - default: - return false - } -} - -// TargetOf return true if target field is equal to passed ACL target. -func (s serviceRequestInfo) TargetOf(target acl.Target) bool { - return s.target == target -} - -func (s requestObjHdrSrc) getHeaders() (*Object, bool) { - switch s.req.Type() { - case object.RequestSearch: - // object header filters is not supported in Search request now - return nil, true - case object.RequestPut: - // for Put we get object headers from request - return s.req.(transport.PutInfo).GetHead(), true - default: - tReq := &transportRequest{ - serviceRequest: s.req, - } - - // for other requests we get object headers from local storage - m, err := s.ls.Meta(tReq.GetAddress()) - if err == nil { - return m.GetObject(), true - } - - return nil, false - } -} - -type requestActionParams struct { - eaclSrc libacl.ExtendedACLSource - - request serviceRequest - - objHdrSrc objectHeadersSource - - target acl.Target -} - -func (s reqActionCalc) calculateRequestAction(ctx context.Context, p requestActionParams) acl.ExtendedACLAction { - // get EACL table - table, err := p.eaclSrc.GetExtendedACLTable(ctx, p.request.CID()) - if err != nil { - s.log.Warn("could not get extended acl of the container", - zap.Stringer("cid", p.request.CID()), - zap.String("error", err.Error()), - ) - - return acl.ActionUndefined - } - - // create RequestInfo instance - reqInfo := &serviceRequestInfo{ - target: p.target, - req: p.request, - objHdrSrc: p.objHdrSrc, - } - - // calculate ACL action - return s.extACLChecker.Action(table, reqInfo) -} - -func (s aclInfoReceiver) getACLInfo(ctx context.Context, req serviceRequest) (*aclInfo, error) { - rule, err := s.basicACLGetter.GetBasicACL(ctx, req.CID()) - if err != nil { - return nil, err - } - - isBearer, err := s.basicChecker.Bearer(rule, req.Type()) - if err != nil { - return nil, err - } - - // fetch target from the request - target := s.targetFinder.Target(ctx, req) - - return &aclInfo{ - rule: rule, - - checkExtended: target != acl.Target_System && s.basicChecker.Extended(rule), - - target: target, - - checkBearer: target != acl.Target_System && isBearer && req.GetBearerToken() != nil, - }, nil -} - -func (s eaclFromBearer) GetExtendedACLTable(ctx context.Context, cid CID) (acl.ExtendedACLTable, error) { - table := acl.WrapEACLTable(nil) - - if err := table.UnmarshalBinary(s.bearer.GetACLRules()); err != nil { - return nil, err - } - - return table, nil -}