From b532f3daad9d8bb19f9bd90e370b7d2d556f858e Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Thu, 28 Sep 2023 14:03:16 +0400 Subject: [PATCH 1/6] go.mod: Upgrade to `github.com/nspcc-dev/neofs-contract@v0.18.0` Signed-off-by: Leonard Lyubich --- CHANGELOG.md | 1 + go.mod | 4 +- go.sum | 145 ++------------------------------------------------- 3 files changed, 7 insertions(+), 143 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d48950dd6..7214c2c7d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ Changelog for NeoFS Node - `neofs-lens` `inspect` object commands to `get` with `inspect` deprecation (#2548) - Update `tzhash` to `v1.7.1` - `github.com/panjf2000/ants/v2` to `v2.8.2` +- `github.com/nspcc-dev/neofs-contract` to `v0.18.0` (#2580) ### Updating from v0.38.1 diff --git a/go.mod b/go.mod index f509cbf336..06300dfae0 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/nspcc-dev/hrw v1.0.9 github.com/nspcc-dev/neo-go v0.102.0 github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 - github.com/nspcc-dev/neofs-contract v0.16.0 + github.com/nspcc-dev/neofs-contract v0.18.0 github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11 github.com/nspcc-dev/tzhash v1.7.1 github.com/olekukonko/tablewriter v0.0.5 @@ -75,7 +75,7 @@ require ( github.com/nats-io/nuid v1.0.1 // indirect github.com/nspcc-dev/dbft v0.0.0-20230515113611-25db6ba61d5c // indirect github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 // indirect - github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230810094130-11bb733f1a2c // indirect + github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230919154019-f66abd020d6a // indirect github.com/nspcc-dev/neofs-crypto v0.4.0 // indirect github.com/nspcc-dev/rfc6979 v0.2.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect diff --git a/go.sum b/go.sum index 30f20da532..b8ada780f1 100644 --- a/go.sum +++ b/go.sum @@ -38,24 +38,11 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/CityOfZion/neo-go v0.62.1-pre.0.20191114145240-e740fbe708f8/go.mod h1:MJCkWUBhi9pn/CrYO1Q3P687y2KeahrOPS9BD9LDGb0= -github.com/CityOfZion/neo-go v0.70.1-pre.0.20191209120015-fccb0085941e/go.mod h1:0enZl0az8xA6PVkwzEOwPWVJGqlt/GO4hA4kmQ5Xzig= -github.com/CityOfZion/neo-go v0.70.1-pre.0.20191212173117-32ac01130d4c/go.mod h1:JtlHfeqLywZLswKIKFnAp+yzezY4Dji9qlfQKB2OD/I= -github.com/CityOfZion/neo-go v0.71.1-pre.0.20200129171427-f773ec69fb84/go.mod h1:FLI526IrRWHmcsO+mHsCbj64pJZhwQFTLJZu+A4PGOA= -github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= -github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg= -github.com/abiosoft/ishell/v2 v2.0.2/go.mod h1:E4oTCXfo6QjoCart0QYa5m9w4S+deXs/P/9jA77A9Bs= -github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521073959-f0d4d129b7f1/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12 h1:npHgfD4Tl2WJS3AJaMUi5ynGDPUBfkg3U3fCzDyXZ+4= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20221202181307-76fa05c21b12/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= @@ -64,20 +51,7 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.22.0-beta/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= @@ -97,45 +71,34 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.9.1 h1:mru55qKdWl3E035hAoh1jj9d7hVnYY5pfb6tmovSmII= github.com/consensys/gnark-crypto v0.9.1/go.mod h1:a2DQL4+5ywF6safEeZFEPGRiiGbjzGFRUN2sg06VuU4= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BMXYYRWTLOJKlh+lOBt6nUQgXAfB7oVIQt5cNreqSLI= github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:rZfgFAXFS/z/lEd6LJmf9HVZ1LkgYiHx5pHhV5DR16M= -github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -147,9 +110,7 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-redis/redis v6.10.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -163,7 +124,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -182,11 +142,9 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -200,7 +158,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-github/v39 v39.2.0 h1:rNNM311XtPOz5rDdsJXAp2o8F67X9FnROXTvto3aSnQ= github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE= @@ -223,7 +180,6 @@ github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -231,10 +187,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5twqnfBdU= @@ -250,12 +204,8 @@ github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7P github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc= github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= @@ -264,10 +214,8 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -280,23 +228,16 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= @@ -318,7 +259,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= @@ -344,43 +284,24 @@ github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/nspcc-dev/dbft v0.0.0-20191205084618-dacb1a30c254/go.mod h1:w1Ln2aT+dBlPhLnuZhBV+DfPEdS2CHWWLp5JTScY3bw= -github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA51EGfb5GS/HOv7VdmngNRTssSeQ729dvGY= -github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk= -github.com/nspcc-dev/dbft v0.0.0-20200219114139-199d286ed6c1/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ= -github.com/nspcc-dev/dbft v0.0.0-20210721160347-1b03241391ac/go.mod h1:U8MSnEShH+o5hexfWJdze6uMFJteP0ko7J2frO7Yu1Y= -github.com/nspcc-dev/dbft v0.0.0-20220629112714-fd49ca59d354/go.mod h1:U8MSnEShH+o5hexfWJdze6uMFJteP0ko7J2frO7Yu1Y= github.com/nspcc-dev/dbft v0.0.0-20230515113611-25db6ba61d5c h1:uyK5aLbAhrnZtnvobJLN24gGUrlxIJAAFqiWl+liZuo= github.com/nspcc-dev/dbft v0.0.0-20230515113611-25db6ba61d5c/go.mod h1:kjBC9F8L25GR+kIHy/1KgG/KfcoGnVwIiyovgq1uszk= -github.com/nspcc-dev/go-ordered-json v0.0.0-20210915112629-e1b6cce73d02/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U= github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22 h1:n4ZaFCKt1pQJd7PXoMJabZWK9ejjbLOVrkl/lOUmshg= github.com/nspcc-dev/go-ordered-json v0.0.0-20220111165707-25110be27d22/go.mod h1:79bEUDEviBHJMFV6Iq6in57FEOCMcRhfQnfaf0ETA5U= github.com/nspcc-dev/hrw v1.0.9 h1:17VcAuTtrstmFppBjfRiia4K2wA/ukXZhLFS8Y8rz5Y= github.com/nspcc-dev/hrw v1.0.9/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkPG06MU= -github.com/nspcc-dev/neo-go v0.73.1-pre.0.20200303142215-f5a1b928ce09/go.mod h1:pPYwPZ2ks+uMnlRLUyXOpLieaDQSEaf4NM3zHVbRjmg= -github.com/nspcc-dev/neo-go v0.98.0/go.mod h1:E3cc1x6RXSXrJb2nDWXTXjnXk3rIqVN8YdFyWv+FrqM= -github.com/nspcc-dev/neo-go v0.99.2/go.mod h1:9P0yWqhZX7i/ChJ+zjtiStO1uPTolPFUM+L5oNznU8E= github.com/nspcc-dev/neo-go v0.102.0 h1:O2Gt4JPOWmp0c+PnPWwd2wPI74BKSwkaNCEyvyQTWJw= github.com/nspcc-dev/neo-go v0.102.0/go.mod h1:QXxpZxJT2KedwM0Nlj8UO0/fZN2WIe4h/i03uBHKbnc= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20220809123759-3094d3e0c14b/go.mod h1:23bBw0v6pBYcrWs8CBEEDIEDJNbcFoIh8pGGcf2Vv8s= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230810094130-11bb733f1a2c h1:upj+XyoI0dS7CYnZOTAeyuznhvDIJurQXL4FvHNKyl4= -github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230810094130-11bb733f1a2c/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= -github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211201134523-3604d96f3fe1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs= -github.com/nspcc-dev/neofs-api-go/v2 v2.11.1/go.mod h1:oS8dycEh8PPf2Jjp6+8dlwWyEv2Dy77h/XhhcdxYEFs= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230919154019-f66abd020d6a h1:ZF1T2PfRJh0LbQcMQAPMmp94B27o/6DKZV+zcMIUNHM= +github.com/nspcc-dev/neo-go/pkg/interop v0.0.0-20230919154019-f66abd020d6a/go.mod h1:ZUuXOkdtHZgaC13za/zMgXfQFncZ0jLzfQTe+OsDOtg= github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 h1:jhuN8Ldqz7WApvUJRFY0bjRXE1R3iCkboMX5QVZhHVk= github.com/nspcc-dev/neofs-api-go/v2 v2.14.0/go.mod h1:DRIr0Ic1s+6QgdqmNFNLIqMqd7lNMJfYwkczlm1hDtM= -github.com/nspcc-dev/neofs-contract v0.16.0 h1:/K5IMwZOlUCooxpe//73RW20HTfHvwRL088gppU5DM8= -github.com/nspcc-dev/neofs-contract v0.16.0/go.mod h1:gN5bo2TlMvLbySImmg76DVj3jVmYgti2VVlQ+h/tcr0= -github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA= -github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= -github.com/nspcc-dev/neofs-crypto v0.3.0/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= +github.com/nspcc-dev/neofs-contract v0.18.0 h1:9g50b16s0mQFFskG93yRSWh4KL7yYOW+xjEvA0WGM/s= +github.com/nspcc-dev/neofs-contract v0.18.0/go.mod h1:UQr1rUjg0eibLwJd6vfsJJEUBnmRysCg8XQd1HYiS2w= github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4= github.com/nspcc-dev/neofs-crypto v0.4.0/go.mod h1:6XJ8kbXgOfevbI2WMruOtI+qUJXNwSGM/E9eClXxPHs= -github.com/nspcc-dev/neofs-sdk-go v0.0.0-20211201182451-a5b61c4f6477/go.mod h1:dfMtQWmBHYpl9Dez23TGtIUKiFvCIxUZq/CkSIhEpz4= -github.com/nspcc-dev/neofs-sdk-go v0.0.0-20220113123743-7f3162110659/go.mod h1:/jay1lr3w7NQd/VDBkEhkJmDmyPNsu4W+QV2obsUV40= github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11 h1:QOc8ZRN5DXlAeRPh5QG9u8rMLgoeRNiZF5/vL7QupWg= github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11/go.mod h1:W+ImTNRnSNMH8w43H1knCcIqwu7dLHePXtlJNZ7EFIs= -github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE= github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= github.com/nspcc-dev/tzhash v1.7.1 h1:6zmexLqdTF/ssbUAh7XJS7RxgKWaw28kdNpE/4UFdEU= @@ -390,14 +311,9 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= @@ -421,7 +337,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= @@ -433,7 +348,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= @@ -441,16 +355,13 @@ github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8 github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -488,12 +399,10 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/syndtr/goleveldb v0.0.0-20180307113352-169b1b37be73/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs= github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM= github.com/twmb/murmur3 v1.1.5 h1:i9OLS9fkuLzBXjt6dptlAEyk58fJsSTXbRg3SgVyqgk= github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/virtuald/go-ordered-json v0.0.0-20170621173500-b18e6e673d74 h1:JwtAtbp7r/7QSyGz8mKUbYJBg2+6Cd7OjM8o/GNOcVo= @@ -502,11 +411,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= -github.com/yuin/gopher-lua v0.0.0-20191128022950-c6266f4fe8d7/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -515,35 +419,21 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= @@ -583,10 +473,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -624,7 +512,6 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= @@ -651,14 +538,12 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -669,19 +554,15 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -699,7 +580,6 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -711,17 +591,14 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210429154555-c04ba851c2a4/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= @@ -740,7 +617,6 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180318012157-96caea41033d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -756,7 +632,6 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -784,7 +659,6 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -792,7 +666,6 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.11.1 h1:ojD5zOW8+7dOGzdnNgersm8aPfcDjhMp12UfG93NIMc= golang.org/x/tools v0.11.1/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -849,7 +722,6 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -878,12 +750,9 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -898,11 +767,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/abiosoft/ishell.v2 v2.0.0/go.mod h1:sFp+cGtH6o4s1FtpVPTMcHq2yue+c4DGOVohJCPUzwY= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -916,15 +782,12 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From 319434e07506094e2aa1ad10ffbe807de0a745ef Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Thu, 28 Sep 2023 14:05:35 +0400 Subject: [PATCH 2/6] ir: Support verified NNS domains of the storage nodes From now, the Inner Ring checks any incoming node for permission to associate itself with optional private node group (kind of subnet). Access lists are stored in the NeoFS NNS. Closes #2280. Signed-off-by: Leonard Lyubich --- CHANGELOG.md | 1 + cmd/neofs-node/config/node/config_test.go | 3 +- config/example/node.env | 1 + config/example/node.json | 1 + config/example/node.yaml | 4 + docs/storage-node-configuration.md | 2 +- docs/verified-node-domains.md | 40 +++ go.mod | 2 +- go.sum | 4 +- pkg/innerring/innerring.go | 9 + pkg/innerring/nns.go | 190 +++++++++++ pkg/innerring/nns_test.go | 310 ++++++++++++++++++ .../privatedomains/validator.go | 90 +++++ .../privatedomains/validator_test.go | 150 +++++++++ pkg/morph/client/client.go | 70 +++- 15 files changed, 860 insertions(+), 17 deletions(-) create mode 100644 docs/verified-node-domains.md create mode 100644 pkg/innerring/nns.go create mode 100644 pkg/innerring/nns_test.go create mode 100644 pkg/innerring/processors/netmap/nodevalidation/privatedomains/validator.go create mode 100644 pkg/innerring/processors/netmap/nodevalidation/privatedomains/validator_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 7214c2c7d9..c97614fec4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Changelog for NeoFS Node ### Added - Policer's setting to the SN's application configuration (#2600) +- Support of verified domains for the storage nodes (#2280) ### Fixed - `neofs-cli netmap netinfo` documentation (#2555) diff --git a/cmd/neofs-node/config/node/config_test.go b/cmd/neofs-node/config/node/config_test.go index d12d703774..f3b1dc89fc 100644 --- a/cmd/neofs-node/config/node/config_test.go +++ b/cmd/neofs-node/config/node/config_test.go @@ -111,9 +111,10 @@ func TestNodeSection(t *testing.T) { require.Equal(t, true, relay) - require.Len(t, attributes, 2) + require.Len(t, attributes, 3) require.Equal(t, "Price:11", attributes[0]) require.Equal(t, "UN-LOCODE:RU MSK", attributes[1]) + require.Equal(t, "VerifiedNodesDomain:nodes.some-org.neofs", attributes[2]) require.NotNil(t, wKey) require.Equal(t, diff --git a/config/example/node.env b/config/example/node.env index f0f651394f..573a4e096f 100644 --- a/config/example/node.env +++ b/config/example/node.env @@ -16,6 +16,7 @@ NEOFS_NODE_WALLET_PASSWORD=password NEOFS_NODE_ADDRESSES="s01.neofs.devenv:8080 /dns4/s02.neofs.devenv/tcp/8081 grpc://127.0.0.1:8082 grpcs://localhost:8083" NEOFS_NODE_ATTRIBUTE_0=Price:11 NEOFS_NODE_ATTRIBUTE_1="UN-LOCODE:RU MSK" +NEOFS_NODE_ATTRIBUTE_2="VerifiedNodesDomain:nodes.some-org.neofs" NEOFS_NODE_RELAY=true NEOFS_NODE_PERSISTENT_SESSIONS_PATH=/sessions NEOFS_NODE_PERSISTENT_STATE_PATH=/state diff --git a/config/example/node.json b/config/example/node.json index cbb685a95f..c0fae0c013 100644 --- a/config/example/node.json +++ b/config/example/node.json @@ -27,6 +27,7 @@ ], "attribute_0": "Price:11", "attribute_1": "UN-LOCODE:RU MSK", + "attribute_2": "VerifiedNodesDomain:nodes.some-org.neofs", "relay": true, "persistent_sessions": { "path": "/sessions" diff --git a/config/example/node.yaml b/config/example/node.yaml index 18eea8fe84..14a145fc8f 100644 --- a/config/example/node.yaml +++ b/config/example/node.yaml @@ -22,8 +22,12 @@ node: - /dns4/s02.neofs.devenv/tcp/8081 - grpc://127.0.0.1:8082 - grpcs://localhost:8083 + # List of colon-separated key-value attributes. attribute_0: "Price:11" attribute_1: UN-LOCODE:RU MSK + # Next attribute specifies optional NeoFS NNS domain in order to enter the storage node into a private node group + # (kind of subnet). The node must have public key from the corresponding access list. See docs for more detailed information. + attribute_2: VerifiedNodesDomain:nodes.some-org.neofs relay: true # start Storage node in relay mode without bootstrapping into the Network map persistent_sessions: path: /sessions # path to persistent session tokens file of Storage node (default: in-memory sessions) diff --git a/docs/storage-node-configuration.md b/docs/storage-node-configuration.md index 414595fedc..532021713c 100644 --- a/docs/storage-node-configuration.md +++ b/docs/storage-node-configuration.md @@ -311,7 +311,7 @@ node: | `key` | `string` | | Path to the binary-encoded private key. | | `wallet` | [Wallet config](#wallet-subsection) | | Wallet configuration. Has no effect if `key` is provided. | | `addresses` | `[]string` | | Addresses advertised in the netmap. | -| `attribute` | `[]string` | | Node attributes as a list of key-value pairs in `:` format. | +| `attribute` | `[]string` | | Node attributes as a list of key-value pairs in `:` format. See also docs about verified nodes' domains.| | `relay` | `bool` | | Enable relay mode. | | `persistent_sessions` | [Persistent sessions config](#persistent_sessions-subsection) | | Persistent session token store configuration. | | `persistent_state` | [Persistent state config](#persistent_state-subsection) | | Persistent state configuration. | diff --git a/docs/verified-node-domains.md b/docs/verified-node-domains.md new file mode 100644 index 0000000000..be70543332 --- /dev/null +++ b/docs/verified-node-domains.md @@ -0,0 +1,40 @@ +# Verified domains of the NeoFS storage nodes + +Storage nodes declare information flexibly via key-value string attributes when +applying to enter the NeoFS network map. In general, any attributes can be +declared, however, some of them may be subject to restrictions. In particular, +some parties may need to limit the relationship to them of any nodes of their +public network. For example, an organization may need to deploy its storage +nodes as a subnet of a public network to implement specific data storage +strategies. In this example, the organization’s nodes will be “normal” for 3rd +parties, while other nodes will not be able to enter the subnet without special +permission at the system level. + +NeoFS implements solution of the described task through access lists managed +within NeoFS NNS. + +## Access lists + +These lists are stored in the NeoFS NNS. Each party may register any available +NNS domain and set records of `TXT` type with Neo addresses of the storage +nodes. After the domain is registered, it becomes an alias to the subnet composed +only from specified storage nodes. Any storage node trying to associate itself +with this subnet while trying to enter the network must have public key +presented in the access list. The Inner Ring will deny everyone else access to +the network map. + +### Domain record format + +For each public key, a record is created - a structure with at least 3 fields: +1. `ByteString` with name of the corresponding domain +2. `Integer` that is `16` for TXT records (other record types are allowed but left unprocessed) +3. `ByteString` with Neo address of the storage node's public key + +## Private subnet entrance + +By default, storage nodes do not belong to private groups. Any node wishing to +enter the private subnet of storage nodes must first find out the corresponding +domain name. To request a binding to a given subnet, a node needs to set +related domain name in its information about when registering in the network +map. The domain is set via `VerifiedNodesDomain` attribute. To be admitted to +the network, a node must be present in the access list. diff --git a/go.mod b/go.mod index 06300dfae0..7490aa04d0 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/nspcc-dev/neo-go v0.102.0 github.com/nspcc-dev/neofs-api-go/v2 v2.14.0 github.com/nspcc-dev/neofs-contract v0.18.0 - github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11 + github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11.0.20230926161529-a5cb78a74aed github.com/nspcc-dev/tzhash v1.7.1 github.com/olekukonko/tablewriter v0.0.5 github.com/panjf2000/ants/v2 v2.8.2 diff --git a/go.sum b/go.sum index b8ada780f1..550abfcae2 100644 --- a/go.sum +++ b/go.sum @@ -300,8 +300,8 @@ github.com/nspcc-dev/neofs-contract v0.18.0 h1:9g50b16s0mQFFskG93yRSWh4KL7yYOW+x github.com/nspcc-dev/neofs-contract v0.18.0/go.mod h1:UQr1rUjg0eibLwJd6vfsJJEUBnmRysCg8XQd1HYiS2w= github.com/nspcc-dev/neofs-crypto v0.4.0 h1:5LlrUAM5O0k1+sH/sktBtrgfWtq1pgpDs09fZo+KYi4= github.com/nspcc-dev/neofs-crypto v0.4.0/go.mod h1:6XJ8kbXgOfevbI2WMruOtI+qUJXNwSGM/E9eClXxPHs= -github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11 h1:QOc8ZRN5DXlAeRPh5QG9u8rMLgoeRNiZF5/vL7QupWg= -github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11/go.mod h1:W+ImTNRnSNMH8w43H1knCcIqwu7dLHePXtlJNZ7EFIs= +github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11.0.20230926161529-a5cb78a74aed h1:ySOlpzLNU3djblNtjZFiTrfoE9zZ5fd1bwjxbbI3gvM= +github.com/nspcc-dev/neofs-sdk-go v1.0.0-rc.11.0.20230926161529-a5cb78a74aed/go.mod h1:W+ImTNRnSNMH8w43H1knCcIqwu7dLHePXtlJNZ7EFIs= github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE= github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= github.com/nspcc-dev/tzhash v1.7.1 h1:6zmexLqdTF/ssbUAh7XJS7RxgKWaw28kdNpE/4UFdEU= diff --git a/pkg/innerring/innerring.go b/pkg/innerring/innerring.go index 1bd2d8b0b3..caa3341fa4 100644 --- a/pkg/innerring/innerring.go +++ b/pkg/innerring/innerring.go @@ -25,6 +25,7 @@ import ( "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/netmap" nodevalidator "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/netmap/nodevalidation" availabilityvalidator "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/netmap/nodevalidation/availability" + "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/netmap/nodevalidation/privatedomains" statevalidation "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/netmap/nodevalidation/state" addrvalidator "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/netmap/nodevalidation/structure" "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/reputation" @@ -724,6 +725,13 @@ func New(ctx context.Context, log *zap.Logger, cfg *viper.Viper, errChan chan<- var netMapCandidateStateValidator statevalidation.NetMapCandidateValidator netMapCandidateStateValidator.SetNetworkSettings(netSettings) + nnsContractAddr, err := server.morphClient.NNSHash() + if err != nil { + return nil, fmt.Errorf("get NeoFS NNS contract address: %w", err) + } + + nnsService := newNeoFSNNS(nnsContractAddr, server.morphClient) + // create netmap processor server.netmapProcessor, err = netmap.New(&netmap.Params{ Log: log, @@ -749,6 +757,7 @@ func New(ctx context.Context, log *zap.Logger, cfg *viper.Viper, errChan chan<- &netMapCandidateStateValidator, addrvalidator.New(), availabilityvalidator.New(), + privatedomains.New(nnsService), locodeValidator, ), NodeStateSettings: netSettings, diff --git a/pkg/innerring/nns.go b/pkg/innerring/nns.go new file mode 100644 index 0000000000..1cd205c934 --- /dev/null +++ b/pkg/innerring/nns.go @@ -0,0 +1,190 @@ +package innerring + +import ( + "errors" + "fmt" + "strings" + + "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + nnsrpc "github.com/nspcc-dev/neofs-contract/rpc/nns" + "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/netmap/nodevalidation/privatedomains" +) + +// provides services of the NeoFS Name Service consumed by the Inner Ring node. +type neoFSNNS struct { + invoker nep11.Invoker + contract *nnsrpc.ContractReader +} + +// creates NeoFS Name Service provider working with the Neo smart contract +// deployed in the Neo network accessed through the specified [nep11.Invoker]. +func newNeoFSNNS(contractAddress util.Uint160, contractCaller nep11.Invoker) *neoFSNNS { + return &neoFSNNS{ + invoker: contractCaller, + contract: nnsrpc.NewReader(contractCaller, contractAddress), + } +} + +var errDomainNotFound = errors.New("domain not found") + +type errWrongStackItemType struct { + expected, got stackitem.Type +} + +func wrongStackItemTypeError(expected, got stackitem.Type) errWrongStackItemType { + return errWrongStackItemType{ + expected: expected, + got: got, + } +} + +func (x errWrongStackItemType) Error() string { + return fmt.Sprintf("wrong type: expected %s, got %s", x.expected, x.got) +} + +type errInvalidNumberOfStructFields struct { + expected, got int +} + +func invalidNumberOfStructFieldsError(expected, got int) errInvalidNumberOfStructFields { + return errInvalidNumberOfStructFields{ + expected: expected, + got: got, + } +} + +func (x errInvalidNumberOfStructFields) Error() string { + return fmt.Sprintf("invalid number of struct fields: expected %d, got %d", x.expected, x.got) +} + +type errInvalidStructField struct { + index uint + cause error +} + +func invalidStructFieldError(index uint, cause error) errInvalidStructField { + return errInvalidStructField{ + index: index, + cause: cause, + } +} + +func (x errInvalidStructField) Error() string { + return fmt.Sprintf("invalid struct field #%d: %v", x.index, x.cause) +} + +func (x errInvalidStructField) Unwrap() error { + return x.cause +} + +type errInvalidNNSDomainRecord struct { + domain string + record string + cause error +} + +func invalidNNSDomainRecordError(domain, record string, cause error) errInvalidNNSDomainRecord { + return errInvalidNNSDomainRecord{ + domain: domain, + record: record, + cause: cause, + } +} + +func (x errInvalidNNSDomainRecord) Error() string { + if x.record != "" { + return fmt.Sprintf("invalid record %q of the NNS domain %q: %v", x.record, x.domain, x.cause) + } + + return fmt.Sprintf("invalid record of the NNS domain %q: %v", x.domain, x.cause) +} + +func (x errInvalidNNSDomainRecord) Unwrap() error { + return x.cause +} + +// CheckDomainRecord calls iterating 'getAllRecords' method of the parameterized +// Neo smart contract passing the given domain name. If contract throws 'token +// not found' exception, CheckDomainRecord returns errDomainNotFound. Each value +// in the resulting iterator is expected to be structure with at least 3 fields. +// If any value has the 2nd field is a number equal to 16 (TXT record type in +// the NNS) and the 3rd one is a string equal to the specified record, +// CheckDomainRecord returns nil. Otherwise, +// [privatedomains.ErrMissingDomainRecord] is returned. +func (x *neoFSNNS) CheckDomainRecord(domain string, record string) error { + sessionID, iter, err := x.contract.GetAllRecords(domain) + if err != nil { + // Track https://github.com/nspcc-dev/neofs-node/issues/2583. + if strings.Contains(err.Error(), "token not found") { + return errDomainNotFound + } + + return fmt.Errorf("get iterator over all records of the NNS domain %q: %w", domain, err) + } + + defer func() { + _ = x.invoker.TerminateSession(sessionID) + }() + + hasRecords := false + + for { + items, err := x.invoker.TraverseIterator(sessionID, &iter, 10) + if err != nil { + return fmt.Errorf("traverse iterator over all records of the NNS domain %q: %w", domain, err) + } + + if len(items) == 0 { + break + } + + hasRecords = true + + for i := range items { + fields, ok := items[i].Value().([]stackitem.Item) + if !ok { + return invalidNNSDomainRecordError(domain, "", + wrongStackItemTypeError(stackitem.StructT, items[i].Type())) + } + + if len(fields) < 3 { + return invalidNNSDomainRecordError(domain, "", + invalidNumberOfStructFieldsError(3, len(fields))) + } + + _, err = fields[0].TryBytes() + if err != nil { + return invalidNNSDomainRecordError(domain, "", + invalidStructFieldError(0, wrongStackItemTypeError(stackitem.ByteArrayT, fields[0].Type()))) + } + + typ, err := fields[1].TryInteger() + if err != nil { + return invalidNNSDomainRecordError(domain, "", + invalidStructFieldError(1, wrongStackItemTypeError(stackitem.IntegerT, fields[1].Type()))) + } + + if typ.Cmp(nnsrpc.TXT) != 0 { + continue + } + + data, err := fields[2].TryBytes() + if err != nil { + return invalidNNSDomainRecordError(domain, "", + invalidStructFieldError(2, wrongStackItemTypeError(stackitem.ByteArrayT, fields[2].Type()))) + } + + if string(data) == record { + return nil + } + } + } + + if hasRecords { + return privatedomains.ErrMissingDomainRecord + } + + return nil +} diff --git a/pkg/innerring/nns_test.go b/pkg/innerring/nns_test.go new file mode 100644 index 0000000000..e664c84a20 --- /dev/null +++ b/pkg/innerring/nns_test.go @@ -0,0 +1,310 @@ +package innerring + +import ( + "crypto/rand" + "encoding/hex" + "errors" + "strings" + "testing" + + "github.com/google/uuid" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" + nnsrpc "github.com/nspcc-dev/neofs-contract/rpc/nns" + "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/netmap/nodevalidation/privatedomains" + "github.com/stretchr/testify/require" +) + +type testInvoker struct { + tb testing.TB + contractAddr util.Uint160 + + domain string + + callRes *result.Invoke + callErr error + + traverseErr error + + items []stackitem.Item + readItems int +} + +func newTestInvoker(tb testing.TB, contractAddr util.Uint160, domain string) *testInvoker { + return &testInvoker{ + tb: tb, + contractAddr: contractAddr, + domain: domain, + } +} + +func (x *testInvoker) Call(contract util.Uint160, method string, args ...any) (*result.Invoke, error) { + require.Equal(x.tb, x.contractAddr, contract) + require.Equal(x.tb, "getAllRecords", method) + require.Len(x.tb, args, 1) + require.Equal(x.tb, x.domain, args[0]) + + if x.callErr != nil { + return nil, x.callErr + } + + return x.callRes, nil +} + +func (x *testInvoker) CallAndExpandIterator(contract util.Uint160, method string, maxItems int, args ...any) (*result.Invoke, error) { + panic("not expected to be called") +} + +func (x *testInvoker) TerminateSession(sessionID uuid.UUID) error { + require.Equal(x.tb, x.callRes.Session, sessionID) + return nil +} + +func (x *testInvoker) TraverseIterator(sessionID uuid.UUID, iterator *result.Iterator, num int) ([]stackitem.Item, error) { + require.Equal(x.tb, x.callRes.Session, sessionID) + + if x.traverseErr != nil { + return nil, x.traverseErr + } + + if num > len(x.items)-x.readItems { + num = len(x.items) - x.readItems + } + + defer func() { + x.readItems += num + }() + + return x.items[x.readItems : x.readItems+num], nil +} + +func TestNeoFSNNS_CheckDomainRecord(t *testing.T) { + var contractAddr util.Uint160 + rand.Read(contractAddr[:]) + const domain = "l2.l1.tld" + searchedRecord := "abcdef" + + t.Run("call failure", func(t *testing.T) { + inv := newTestInvoker(t, contractAddr, domain) + inv.callErr = errors.New("any error") + + nnsService := newNeoFSNNS(contractAddr, inv) + + err := nnsService.CheckDomainRecord(domain, searchedRecord) + require.ErrorIs(t, err, inv.callErr) + }) + + t.Run("fault exception", func(t *testing.T) { + inv := newTestInvoker(t, contractAddr, domain) + inv.callRes = &result.Invoke{ + State: vmstate.Fault.String(), + FaultException: "any fault exception", + } + + nnsService := newNeoFSNNS(contractAddr, inv) + + err := nnsService.CheckDomainRecord(domain, searchedRecord) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), inv.callRes.FaultException)) + + inv.callRes.FaultException = "token not found" + + err = nnsService.CheckDomainRecord(domain, searchedRecord) + require.ErrorIs(t, err, errDomainNotFound) + }) + + t.Run("invalid stack", func(t *testing.T) { + inv := newTestInvoker(t, contractAddr, domain) + inv.callRes = &result.Invoke{ + State: vmstate.Halt.String(), + Stack: make([]stackitem.Item, 0), + } + + nnsService := newNeoFSNNS(contractAddr, inv) + + err := nnsService.CheckDomainRecord(domain, searchedRecord) + require.Error(t, err) + + inv.callRes.Stack = make([]stackitem.Item, 2) + + err = nnsService.CheckDomainRecord(domain, searchedRecord) + require.Error(t, err) + + nonIteratorItem := stackitem.NewBool(true) + inv.callRes.Stack = []stackitem.Item{nonIteratorItem} + + err = nnsService.CheckDomainRecord(domain, searchedRecord) + require.Error(t, err) + }) + + newWithRecords := func(items ...stackitem.Item) *neoFSNNS { + var iter result.Iterator + inv := newTestInvoker(t, contractAddr, domain) + inv.callRes = &result.Invoke{ + State: vmstate.Halt.String(), + Stack: []stackitem.Item{stackitem.NewInterop(iter)}, + } + inv.items = items + + return newNeoFSNNS(contractAddr, inv) + } + + t.Run("no domain records", func(t *testing.T) { + nnsService := newWithRecords() + + err := nnsService.CheckDomainRecord(domain, searchedRecord) + require.NoError(t, err) + }) + + checkInvalidRecordError := func(record string, err error) { + var e errInvalidNNSDomainRecord + require.ErrorAs(t, err, &e) + require.Equal(t, domain, e.domain) + require.Equal(t, record, e.record) + } + + checkInvalidStructFieldError := func(index uint, err error) { + var e errInvalidStructField + require.ErrorAs(t, err, &e) + require.Equal(t, index, e.index) + } + + t.Run("with invalid record", func(t *testing.T) { + t.Run("non-struct element", func(t *testing.T) { + nnsService := newWithRecords(stackitem.NewBool(true)) + + err := nnsService.CheckDomainRecord(domain, searchedRecord) + + checkInvalidRecordError("", err) + + var wrongTypeErr errWrongStackItemType + require.ErrorAs(t, err, &wrongTypeErr) + require.Equal(t, stackitem.StructT, wrongTypeErr.expected) + require.Equal(t, stackitem.BooleanT, wrongTypeErr.got) + }) + + t.Run("invalid number of fields", func(t *testing.T) { + for i := 0; i < 3; i++ { + nnsService := newWithRecords(stackitem.NewStruct(make([]stackitem.Item, i))) + + err := nnsService.CheckDomainRecord(domain, searchedRecord) + + checkInvalidRecordError("", err) + + var invalidFieldNumErr errInvalidNumberOfStructFields + require.ErrorAs(t, err, &invalidFieldNumErr) + require.Equal(t, 3, invalidFieldNumErr.expected, i) + require.Equal(t, i, invalidFieldNumErr.got, i) + } + }) + + t.Run("invalid 1st field", func(t *testing.T) { + nnsService := newWithRecords(stackitem.NewStruct([]stackitem.Item{ + stackitem.NewMap(), + stackitem.NewBigInteger(nnsrpc.TXT), + stackitem.NewByteArray([]byte("any")), + })) + + err := nnsService.CheckDomainRecord(domain, searchedRecord) + + checkInvalidRecordError("", err) + checkInvalidStructFieldError(0, err) + + var wrongTypeErr errWrongStackItemType + require.ErrorAs(t, err, &wrongTypeErr) + require.Equal(t, stackitem.ByteArrayT, wrongTypeErr.expected) + require.Equal(t, stackitem.MapT, wrongTypeErr.got) + }) + + t.Run("invalid 2nd field", func(t *testing.T) { + t.Run("non-integer", func(t *testing.T) { + nnsService := newWithRecords(stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray([]byte("any")), + stackitem.NewMap(), + stackitem.NewByteArray([]byte("any")), + })) + + err := nnsService.CheckDomainRecord(domain, searchedRecord) + + checkInvalidRecordError("", err) + checkInvalidStructFieldError(1, err) + + var wrongTypeErr errWrongStackItemType + require.ErrorAs(t, err, &wrongTypeErr) + require.Equal(t, stackitem.IntegerT, wrongTypeErr.expected) + require.Equal(t, stackitem.MapT, wrongTypeErr.got) + }) + + t.Run("non-TXT record", func(t *testing.T) { + nnsService := newWithRecords(stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray([]byte("any")), + stackitem.NewBigInteger(nnsrpc.CNAME), + stackitem.NewByteArray([]byte("any")), + })) + + err := nnsService.CheckDomainRecord(domain, searchedRecord) + require.ErrorIs(t, err, privatedomains.ErrMissingDomainRecord) + }) + }) + + t.Run("invalid 3rd field", func(t *testing.T) { + t.Run("invalid type", func(t *testing.T) { + nnsService := newWithRecords(stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray([]byte("any")), + stackitem.NewBigInteger(nnsrpc.TXT), + stackitem.NewMap(), + })) + + err := nnsService.CheckDomainRecord(domain, searchedRecord) + + checkInvalidRecordError("", err) + checkInvalidStructFieldError(2, err) + + var wrongTypeErr errWrongStackItemType + require.ErrorAs(t, err, &wrongTypeErr) + require.Equal(t, stackitem.ByteArrayT, wrongTypeErr.expected) + require.Equal(t, stackitem.MapT, wrongTypeErr.got) + }) + }) + }) + + t.Run("without searched record", func(t *testing.T) { + records := make([]stackitem.Item, 100) + for i := range records { + records[i] = stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray([]byte("any")), + stackitem.NewBigInteger(nnsrpc.TXT), + stackitem.NewByteArray([]byte(searchedRecord + hex.EncodeToString([]byte{byte(i)}))), + }) + } + + nnsService := newWithRecords(records...) + + err := nnsService.CheckDomainRecord(domain, searchedRecord) + require.ErrorIs(t, err, privatedomains.ErrMissingDomainRecord) + }) + + t.Run("with searched record", func(t *testing.T) { + records := make([]stackitem.Item, 100) + for i := range records { + records[i] = stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray([]byte("any")), + stackitem.NewBigInteger(nnsrpc.TXT), + stackitem.NewByteArray([]byte(searchedRecord + hex.EncodeToString([]byte{byte(i)}))), + }) + } + + records = append(records, stackitem.NewStruct([]stackitem.Item{ + stackitem.NewByteArray([]byte("any")), + stackitem.NewBigInteger(nnsrpc.TXT), + stackitem.NewByteArray([]byte(searchedRecord)), + })) + + nnsService := newWithRecords(records...) + + err := nnsService.CheckDomainRecord(domain, searchedRecord) + require.NoError(t, err) + }) +} diff --git a/pkg/innerring/processors/netmap/nodevalidation/privatedomains/validator.go b/pkg/innerring/processors/netmap/nodevalidation/privatedomains/validator.go new file mode 100644 index 0000000000..c8ab674f32 --- /dev/null +++ b/pkg/innerring/processors/netmap/nodevalidation/privatedomains/validator.go @@ -0,0 +1,90 @@ +package privatedomains + +import ( + "errors" + "fmt" + + "github.com/nspcc-dev/neo-go/pkg/crypto/hash" + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/vm/emit" + "github.com/nspcc-dev/neofs-sdk-go/netmap" +) + +// ErrMissingDomainRecord is returned when some record is missing in the +// particular domain. +var ErrMissingDomainRecord = errors.New("missing domain record") + +// NNS provides services of the NeoFS NNS consumed by [Validator] to process. +type NNS interface { + // CheckDomainRecord checks whether NNS domain with the specified name exists + // and has given TXT record. Returns [ErrMissingDomainRecord] if domain exists + // but has no given record, or any other error encountered prevented the check. + // + // Both domain name and record are non-empty. + CheckDomainRecord(domainName string, record string) error +} + +// Validator validates NNS domains declared by the storage nodes on their +// attempts to enter the NeoFS network map. +// +// There is an option to specify name of the verified nodes' domain. Such +// domains allow to combine several nodes into a private group (kind of subnet). +// Access is controlled using access lists: Validator checks that any incoming +// node declaring private node domain is presented in the corresponding access +// list. Access lists are stored in the NeoFS NNS: for each private node group, +// there is a registered NNS domain. TXT records of each such domain are Neo +// addresses of the nodes' public keys. To be allowed to use a specific verified +// domain value, the storage node must have a Neo address from this list. +// Otherwise, the storage node will be denied access to the network map. Note +// that if domain exists but has no records, then access is forbidden for +// anyone. +type Validator struct { + nns NNS +} + +// New returns new Validator that uses provided [NNS] as a source of node access +// records. +func New(nns NNS) *Validator { + return &Validator{ + nns: nns, + } +} + +// various errors useful for testing. +var ( + errMissingNodeBinaryKey = errors.New("missing node binary key") + errAccessDenied = errors.New("access denied") +) + +// VerifyAndUpdate checks allowance of the storage node represented by the given +// descriptor to enter the private node group (if any). Returns an error if on +// access denial or the check cannot be done at the moment. +// +// VerifyAndUpdate does not mutate the argument. +func (x *Validator) VerifyAndUpdate(info *netmap.NodeInfo) error { + verifiedNodesDomain := info.VerifiedNodesDomain() + if verifiedNodesDomain == "" { + return nil + } + + bNodeKey := info.PublicKey() + if len(bNodeKey) == 0 { + return errMissingNodeBinaryKey + } + + buf := io.NewBufBinWriter() + emit.CheckSig(buf.BinWriter, bNodeKey) + nodeNeoAddress := address.Uint160ToString(hash.Hash160(buf.Bytes())) + + err := x.nns.CheckDomainRecord(verifiedNodesDomain, nodeNeoAddress) + if err != nil { + if errors.Is(err, ErrMissingDomainRecord) { + return errAccessDenied + } + + return fmt.Errorf("check records of the domain %q: %w", verifiedNodesDomain, err) + } + + return nil +} diff --git a/pkg/innerring/processors/netmap/nodevalidation/privatedomains/validator_test.go b/pkg/innerring/processors/netmap/nodevalidation/privatedomains/validator_test.go new file mode 100644 index 0000000000..74d7d5bcb8 --- /dev/null +++ b/pkg/innerring/processors/netmap/nodevalidation/privatedomains/validator_test.go @@ -0,0 +1,150 @@ +package privatedomains + +import ( + "encoding/hex" + "errors" + "testing" + + "github.com/nspcc-dev/neofs-sdk-go/netmap" + "github.com/stretchr/testify/require" +) + +type testNNS struct { + mDomains map[string][]string + + staticErr error +} + +// creates test NNS provider without any domains. +func newTestNNS() *testNNS { + return &testNNS{ + mDomains: make(map[string][]string), + } +} + +// creates test NNS provider that always fails with the given error. +func newTestNNSWithStaticErr(err error) *testNNS { + return &testNNS{ + staticErr: err, + } +} + +func (x *testNNS) registerDomain(domain string) { + x.mDomains[domain] = nil +} + +func (x *testNNS) addDomainRecord(domain string, record string) { + x.mDomains[domain] = append(x.mDomains[domain], record) +} + +func (x *testNNS) removeDomainRecord(domain string, record string) { + recs, ok := x.mDomains[domain] + if !ok { + return + } + + for i := 0; i < len(recs); i++ { // do not use range, slice is mutated inside + if recs[i] == record { + recs = append(recs[:i], recs[i+1:]...) + i-- + } + } + + x.mDomains[domain] = recs +} + +func (x *testNNS) CheckDomainRecord(domainName string, record string) error { + if x.staticErr != nil { + return x.staticErr + } + + recs, ok := x.mDomains[domainName] + if !ok { + return errors.New("missing domain") + } + + for i := range recs { + if recs[i] == record { + return nil + } + } + + return ErrMissingDomainRecord +} + +func TestValidator_VerifyAndUpdate(t *testing.T) { + const verifiedDomain = "nodes.some-org.neofs" + const hNodeKey = "02a70577a832b338772c8cd07e7eaf526cae8d9b025a51b41671de5a4363eafe07" + const nodeNeoAddress = "NZ1czz5gkEDamTg6Tiw6cxqp9Me1KLs8ae" + const anyOtherNeoAddress = "NfMvD6WmBiCr4erfEnFFLs7jdj4Y5CM7nN" + + bNodeKey, err := hex.DecodeString(hNodeKey) + require.NoError(t, err) + + var node netmap.NodeInfo + node.SetPublicKey(bNodeKey) + node.SetVerifiedNodesDomain(verifiedDomain) + + t.Run("unspecified verified nodes domain", func(t *testing.T) { + var node netmap.NodeInfo + require.Zero(t, node.VerifiedNodesDomain()) + + v := New(newTestNNS()) + + err := v.VerifyAndUpdate(&node) + require.NoError(t, err) + }) + + t.Run("invalid node key", func(t *testing.T) { + var node netmap.NodeInfo + node.SetVerifiedNodesDomain(verifiedDomain) + + v := New(newTestNNS()) + + node.SetPublicKey(nil) + err := v.VerifyAndUpdate(&node) + require.ErrorIs(t, err, errMissingNodeBinaryKey) + + node.SetPublicKey([]byte{}) + err = v.VerifyAndUpdate(&node) + require.ErrorIs(t, err, errMissingNodeBinaryKey) + }) + + t.Run("other failure", func(t *testing.T) { + anyErr := errors.New("any error") + v := New(newTestNNSWithStaticErr(anyErr)) + + err := v.VerifyAndUpdate(&node) + require.ErrorIs(t, err, anyErr) + }) + + t.Run("missing domain", func(t *testing.T) { + v := New(newTestNNS()) + + err := v.VerifyAndUpdate(&node) + require.Error(t, err) + require.NotErrorIs(t, err, errAccessDenied) + }) + + t.Run("existing domain", func(t *testing.T) { + nns := newTestNNS() + v := New(nns) + + nns.registerDomain(verifiedDomain) + + err := v.VerifyAndUpdate(&node) + require.ErrorIs(t, err, errAccessDenied) + + nns.addDomainRecord(verifiedDomain, anyOtherNeoAddress) + err = v.VerifyAndUpdate(&node) + require.ErrorIs(t, err, errAccessDenied) + + nns.addDomainRecord(verifiedDomain, nodeNeoAddress) + err = v.VerifyAndUpdate(&node) + require.NoError(t, err) + + nns.removeDomainRecord(verifiedDomain, nodeNeoAddress) + err = v.VerifyAndUpdate(&node) + require.ErrorIs(t, err, errAccessDenied) + }) +} diff --git a/pkg/morph/client/client.go b/pkg/morph/client/client.go index 1c95932291..268d971d31 100644 --- a/pkg/morph/client/client.go +++ b/pkg/morph/client/client.go @@ -8,12 +8,14 @@ import ( "sync" "time" + "github.com/google/uuid" lru "github.com/hashicorp/golang-lru/v2" "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" + "github.com/nspcc-dev/neo-go/pkg/neorpc/result" "github.com/nspcc-dev/neo-go/pkg/rpcclient" "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/rpcclient/gas" @@ -79,6 +81,57 @@ type Client struct { inactive bool } +// Call calls specified method of the Neo smart contract with provided arguments. +func (c *Client) Call(contract util.Uint160, method string, args ...any) (*result.Invoke, error) { + c.switchLock.RLock() + defer c.switchLock.RUnlock() + + if c.inactive { + return nil, ErrConnectionLost + } + + return c.rpcActor.Call(contract, method, args...) +} + +// CallAndExpandIterator calls specified iterating method of the Neo smart +// contract with provided arguments, and fetches iterator from the response +// carrying up to limited number of items. +func (c *Client) CallAndExpandIterator(contract util.Uint160, method string, maxItems int, args ...any) (*result.Invoke, error) { + c.switchLock.RLock() + defer c.switchLock.RUnlock() + + if c.inactive { + return nil, ErrConnectionLost + } + + return c.rpcActor.CallAndExpandIterator(contract, method, maxItems, args...) +} + +// TerminateSession closes opened session by its ID. +func (c *Client) TerminateSession(sessionID uuid.UUID) error { + c.switchLock.RLock() + defer c.switchLock.RUnlock() + + if c.inactive { + return ErrConnectionLost + } + + return c.rpcActor.TerminateSession(sessionID) +} + +// TraverseIterator reads specified number of items from the provided iterator +// initialized within given session. +func (c *Client) TraverseIterator(sessionID uuid.UUID, iterator *result.Iterator, num int) ([]stackitem.Item, error) { + c.switchLock.RLock() + defer c.switchLock.RUnlock() + + if c.inactive { + return nil, ErrConnectionLost + } + + return c.rpcActor.TraverseIterator(sessionID, iterator, num) +} + type cache struct { m *sync.RWMutex @@ -174,24 +227,17 @@ func (c *Client) Invoke(contract util.Uint160, fee fixedn.Fixed8, method string, // TestInvoke invokes contract method locally in neo-go node. This method should // be used to read data from smart-contract. -func (c *Client) TestInvoke(contract util.Uint160, method string, args ...any) (res []stackitem.Item, err error) { - c.switchLock.RLock() - defer c.switchLock.RUnlock() - - if c.inactive { - return nil, ErrConnectionLost - } - - val, err := c.rpcActor.Call(contract, method, args...) +func (c *Client) TestInvoke(contract util.Uint160, method string, args ...any) ([]stackitem.Item, error) { + resInvoke, err := c.Call(contract, method, args...) if err != nil { return nil, err } - if val.State != HaltState { - return nil, ¬HaltStateError{state: val.State, exception: val.FaultException} + if resInvoke.State != HaltState { + return nil, ¬HaltStateError{state: resInvoke.State, exception: resInvoke.FaultException} } - return val.Stack, nil + return resInvoke.Stack, nil } // TestInvokeIterator is the same [Client.TestInvoke] but expands an iterator placed From 8e017fa902c596ec286be4d33037b111c2a10f61 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 25 Sep 2023 14:10:47 +0400 Subject: [PATCH 3/6] adm: Add commands to work with verified nodes' domains Add commands to get and set list of the storage nodes allowed to use domain of the private node group. Refs #2280. Signed-off-by: Leonard Lyubich --- .../internal/modules/morph/n3client.go | 2 +- cmd/neofs-adm/internal/modules/morph/nns.go | 107 +++++++++ cmd/neofs-adm/internal/modules/morph/root.go | 64 +++++ .../modules/morph/verified_domains.go | 225 ++++++++++++++++++ cmd/neofs-cli/modules/storagegroup/put.go | 3 +- docs/verified-node-domains.md | 54 +++++ 6 files changed, 452 insertions(+), 3 deletions(-) create mode 100644 cmd/neofs-adm/internal/modules/morph/nns.go create mode 100644 cmd/neofs-adm/internal/modules/morph/verified_domains.go diff --git a/cmd/neofs-adm/internal/modules/morph/n3client.go b/cmd/neofs-adm/internal/modules/morph/n3client.go index 138943b6e4..720744a0ce 100644 --- a/cmd/neofs-adm/internal/modules/morph/n3client.go +++ b/cmd/neofs-adm/internal/modules/morph/n3client.go @@ -58,7 +58,7 @@ type clientContext struct { SentTxs []hashVUBPair } -func getN3Client(v *viper.Viper) (Client, error) { +func getN3Client(v *viper.Viper) (*rpcclient.Client, error) { // number of opened connections // by neo-go client per one host const ( diff --git a/cmd/neofs-adm/internal/modules/morph/nns.go b/cmd/neofs-adm/internal/modules/morph/nns.go new file mode 100644 index 0000000000..815c6fb65a --- /dev/null +++ b/cmd/neofs-adm/internal/modules/morph/nns.go @@ -0,0 +1,107 @@ +package morph + +import ( + "errors" + "fmt" + "strings" + + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" + nnsrpc "github.com/nspcc-dev/neofs-contract/rpc/nns" +) + +// Various NeoFS NNS errors. +var ( + errDomainNotFound = errors.New("domain not found") + errMissingDomainRecords = errors.New("missing domain records") +) + +func invalidNNSDomainRecordError(cause error) error { + return fmt.Errorf("invalid domain record: %w", cause) +} + +var errBreakIterator = errors.New("break iterator") + +// iterates over text records of the specified NeoFS NNS domain and passes them +// into f. Breaks on any f's error and returns it (if f returns +// errBreakIterator, iterateNNSDomainTextRecords returns no error). Returns +// errDomainNotFound if domain is missing in the NNS. Returns +// errMissingDomainRecords if domain exists but has no records. +func iterateNNSDomainTextRecords(inv nnsrpc.Invoker, nnsContractAddr util.Uint160, domain string, f func(string) error) error { + nnsContract := nnsrpc.NewReader(inv, nnsContractAddr) + + sessionID, iter, err := nnsContract.GetAllRecords(domain) + if err != nil { + // Track https://github.com/nspcc-dev/neofs-node/issues/2583. + if strings.Contains(err.Error(), "token not found") { + return errDomainNotFound + } + + return fmt.Errorf("get all records of the NNS domain: %w", err) + } + + defer func() { + _ = inv.TerminateSession(sessionID) + }() + + hasRecords := false + + for { + items, err := inv.TraverseIterator(sessionID, &iter, 10) + if err != nil { + return fmt.Errorf("NNS domain records' iterator break: %w", err) + } + + if len(items) == 0 { + if hasRecords { + return nil + } + + return errMissingDomainRecords + } + + hasRecords = true + + for i := range items { + fields, ok := items[i].Value().([]stackitem.Item) + if !ok { + return invalidNNSDomainRecordError( + fmt.Errorf("unexpected type %s instead of %s", stackitem.StructT, items[i].Type())) + } + + if len(fields) < 3 { + return invalidNNSDomainRecordError( + fmt.Errorf("unsupported number of struct fields: expected at least 3, got %d", len(fields))) + } + + _, err = fields[0].TryBytes() + if err != nil { + return invalidNNSDomainRecordError( + fmt.Errorf("1st field is not a byte array: got %v", fields[0].Type())) + } + + typ, err := fields[1].TryInteger() + if err != nil { + return invalidNNSDomainRecordError(fmt.Errorf("2nd field is not an integer: got %v", fields[1].Type())) + } + + if typ.Cmp(nnsrpc.TXT) != 0 { + continue + } + + data, err := fields[2].TryBytes() + if err != nil { + return invalidNNSDomainRecordError( + fmt.Errorf("3rd field is not a byte array: got %v", fields[2].Type())) + } + + if err = f(string(data)); err != nil { + if errors.Is(err, errBreakIterator) { + return nil + } + + return err + } + } + } +} diff --git a/cmd/neofs-adm/internal/modules/morph/root.go b/cmd/neofs-adm/internal/modules/morph/root.go index 9d092f118c..1fcb5a6550 100644 --- a/cmd/neofs-adm/internal/modules/morph/root.go +++ b/cmd/neofs-adm/internal/modules/morph/root.go @@ -42,6 +42,10 @@ const ( localDumpFlag = "local-dump" protoConfigPath = "protocol" walletAddressFlag = "wallet-address" + domainFlag = "domain" + neoAddressesFlag = "neo-addresses" + publicKeysFlag = "public-keys" + walletFlag = "wallet" ) var ( @@ -257,6 +261,39 @@ Values for unknown keys are added exactly the way they're provided, no conversio }, RunE: listNetmapCandidatesNodes, } + + verifiedNodesDomainCmd = &cobra.Command{ + Use: "verified-nodes-domain", + Short: "Group of commands to work with verified domains for the storage nodes", + Args: cobra.NoArgs, + PersistentPreRun: func(cmd *cobra.Command, _ []string) { + _ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag)) + _ = viper.BindPFlag(domainFlag, cmd.Flags().Lookup(domainFlag)) + }, + } + + verifiedNodesDomainAccessListCmd = &cobra.Command{ + Use: "access-list", + Short: "Get access list for the verified nodes' domain", + Long: "List Neo addresses of the storage nodes that have access to use the specified verified domain.", + Args: cobra.NoArgs, + RunE: verifiedNodesDomainAccessList, + } + + verifiedNodesDomainSetAccessListCmd = &cobra.Command{ + Use: "set-access-list", + Short: "Set access list for the verified nodes' domain", + Long: "Set list of the storage nodes that have access to use the specified verified domain. " + + "The list may be either Neo addresses or HEX-encoded public keys of the nodes.", + Args: cobra.NoArgs, + PreRun: func(cmd *cobra.Command, _ []string) { + _ = viper.BindPFlag(walletFlag, cmd.Flags().Lookup(walletFlag)) + _ = viper.BindPFlag(walletAccountFlag, cmd.Flags().Lookup(walletAccountFlag)) + _ = viper.BindPFlag(publicKeysFlag, cmd.Flags().Lookup(publicKeysFlag)) + _ = viper.BindPFlag(neoAddressesFlag, cmd.Flags().Lookup(neoAddressesFlag)) + }, + RunE: verifiedNodesDomainSetAccessList, + } ) func init() { @@ -365,4 +402,31 @@ func init() { RootCmd.AddCommand(netmapCandidatesCmd) netmapCandidatesCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint") + + cmd := verifiedNodesDomainAccessListCmd + fs := cmd.Flags() + fs.StringP(endpointFlag, "r", "", "NeoFS Sidechain RPC endpoint") + _ = cmd.MarkFlagRequired(endpointFlag) + fs.StringP(domainFlag, "d", "", "Verified domain of the storage nodes. Must be a valid NeoFS NNS domain (e.g. 'nodes.some-org.neofs')") + _ = cmd.MarkFlagRequired(domainFlag) + + verifiedNodesDomainCmd.AddCommand(cmd) + + cmd = verifiedNodesDomainSetAccessListCmd + fs = cmd.Flags() + fs.StringP(walletFlag, "w", "", "Path to the Neo wallet file") + _ = cmd.MarkFlagRequired(walletFlag) + fs.StringP(walletAccountFlag, "a", "", "Optional Neo address of the wallet account for signing transactions. "+ + "If omitted, default change address from the wallet is used") + fs.StringP(endpointFlag, "r", "", "NeoFS Sidechain RPC endpoint") + _ = cmd.MarkFlagRequired(endpointFlag) + fs.StringP(domainFlag, "d", "", "Verified domain of the storage nodes. Must be a valid NeoFS NNS domain (e.g. 'nodes.some-org.neofs')") + _ = cmd.MarkFlagRequired(domainFlag) + fs.StringSlice(neoAddressesFlag, nil, "Neo addresses resolved from public keys of the storage nodes") + fs.StringSlice(publicKeysFlag, nil, "HEX-encoded public keys of the storage nodes") + cmd.MarkFlagsMutuallyExclusive(publicKeysFlag, neoAddressesFlag) + + verifiedNodesDomainCmd.AddCommand(cmd) + + RootCmd.AddCommand(verifiedNodesDomainCmd) } diff --git a/cmd/neofs-adm/internal/modules/morph/verified_domains.go b/cmd/neofs-adm/internal/modules/morph/verified_domains.go new file mode 100644 index 0000000000..63e874ab43 --- /dev/null +++ b/cmd/neofs-adm/internal/modules/morph/verified_domains.go @@ -0,0 +1,225 @@ +package morph + +import ( + "errors" + "fmt" + "strings" + + "github.com/nspcc-dev/neo-go/cli/input" + "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/wallet" + nnsrpc "github.com/nspcc-dev/neofs-contract/rpc/nns" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +func verifiedNodesDomainAccessList(cmd *cobra.Command, _ []string) error { + vpr := viper.GetViper() + + n3Client, err := getN3Client(vpr) + if err != nil { + return fmt.Errorf("open connection: %w", err) + } + + nnsContractAddr, err := nnsrpc.InferHash(n3Client) + if err != nil { + return fmt.Errorf("get NeoFS NNS contract address: %w", err) + } + + domain := vpr.GetString(domainFlag) + + err = iterateNNSDomainTextRecords(invoker.New(n3Client, nil), nnsContractAddr, domain, func(rec string) error { + cmd.Println(rec) + return nil + }) + if err != nil { + switch { + default: + return fmt.Errorf("handle domain %q records: %w", domain, err) + case errors.Is(err, errDomainNotFound): + cmd.Println("Domain not found.") + return nil + case errors.Is(err, errMissingDomainRecords): + cmd.Println("List is empty.") + return nil + } + } + + return nil +} + +func verifiedNodesDomainSetAccessList(cmd *cobra.Command, _ []string) error { + vpr := viper.GetViper() + + strNeoAddresses := vpr.GetStringSlice(neoAddressesFlag) + strPublicKeys := vpr.GetStringSlice(publicKeysFlag) + if len(strNeoAddresses)+len(strPublicKeys) == 0 { + // Track https://github.com/nspcc-dev/neofs-node/issues/2595. + return errors.New("neither Neo addresses nor public keys are set") + } + + if len(strNeoAddresses)*len(strPublicKeys) != 0 { + // just to make sure + panic("mutually exclusive flags bypassed Cobra") + } + + var err error + var additionalRecords []string + + if len(strNeoAddresses) > 0 { + for i := range strNeoAddresses { + for j := i + 1; j < len(strNeoAddresses); j++ { + if strNeoAddresses[i] == strNeoAddresses[j] { + return fmt.Errorf("duplicated Neo address %s", strNeoAddresses[i]) + } + } + + _, err = address.StringToUint160(strNeoAddresses[i]) + if err != nil { + return fmt.Errorf("address #%d is invalid: %w", i, err) + } + } + + additionalRecords = strNeoAddresses + } else { + additionalRecords = make([]string, len(strPublicKeys)) + + for i := range strPublicKeys { + for j := i + 1; j < len(strPublicKeys); j++ { + if strPublicKeys[i] == strPublicKeys[j] { + return fmt.Errorf("duplicated public key %s", strPublicKeys[i]) + } + } + + pubKey, err := keys.NewPublicKeyFromString(strPublicKeys[i]) + if err != nil { + return fmt.Errorf("public key #%d is not a HEX-encoded public key: %w", i, err) + } + + additionalRecords[i] = address.Uint160ToString(pubKey.GetScriptHash()) + } + } + + w, err := wallet.NewWalletFromFile(viper.GetString(walletFlag)) + if err != nil { + return fmt.Errorf("decode Neo wallet from file: %v", err) + } + + var accAddr util.Uint160 + if strAccAddr := viper.GetString(walletAccountFlag); strAccAddr != "" { + accAddr, err = address.StringToUint160(strAccAddr) + if err != nil { + return fmt.Errorf("invalid Neo account address in flag --%s: %q", walletAccountFlag, strAccAddr) + } + } else { + accAddr = w.GetChangeAddress() + } + + acc := w.GetAccount(accAddr) + if acc == nil { + return fmt.Errorf("account %s not found in the wallet", address.Uint160ToString(accAddr)) + } + + prompt := fmt.Sprintf("Enter password for %s >", address.Uint160ToString(accAddr)) + pass, err := input.ReadPassword(prompt) + if err != nil { + return fmt.Errorf("failed to read account password: %v", err) + } + + err = acc.Decrypt(pass, keys.NEP2ScryptParams()) + if err != nil { + return fmt.Errorf("failed to unlock the account with password: %v", err) + } + + n3Client, err := getN3Client(vpr) + if err != nil { + return fmt.Errorf("open connection: %w", err) + } + + nnsContractAddr, err := nnsrpc.InferHash(n3Client) + if err != nil { + return fmt.Errorf("get NeoFS NNS contract address: %w", err) + } + + actr, err := actor.NewSimple(n3Client, acc) + if err != nil { + return fmt.Errorf("init committee actor: %w", err) + } + + domain := vpr.GetString(domainFlag) + scriptBuilder := smartcontract.NewBuilder() + hasOtherRecord := false + mAlreadySetIndices := make(map[int]struct{}, len(additionalRecords)) + + err = iterateNNSDomainTextRecords(actr, nnsContractAddr, domain, func(rec string) error { + for i := range additionalRecords { + if additionalRecords[i] == rec { + mAlreadySetIndices[i] = struct{}{} + return nil + } + } + + hasOtherRecord = true + + return errBreakIterator + }) + if err != nil { + switch { + default: + return fmt.Errorf("handle domain %q records: %w", domain, err) + case errors.Is(err, errMissingDomainRecords): + // domain exists but has no records, that's ok: just going to add new ones + case errors.Is(err, errDomainNotFound): + domainToRegister := domain + if labels := strings.Split(domainToRegister, "."); len(labels) > 2 { + // we need explicitly register L2 domain like 'some-org.neofs' + // and then just add records to inferior domains + domainToRegister = labels[len(labels)-2] + "." + labels[len(labels)-1] + } + + scriptBuilder.InvokeMethod(nnsContractAddr, "register", + domainToRegister, acc.ScriptHash(), "ops@nspcc.ru", int64(3600), int64(600), int64(defaultExpirationTime), int64(3600)) + } + } + + if !hasOtherRecord && len(mAlreadySetIndices) == len(additionalRecords) { + cmd.Println("Current list is already the same, skip.") + return nil + } + + if hasOtherRecord { + // there is no way to delete particular record, so clean all first + scriptBuilder.InvokeMethod(nnsContractAddr, "deleteRecords", + domain, nnsrpc.TXT.Int64()) + } + + for i := range additionalRecords { + if !hasOtherRecord { + if _, ok := mAlreadySetIndices[i]; ok { + continue + } + } + + scriptBuilder.InvokeMethod(nnsContractAddr, "addRecord", + domain, nnsrpc.TXT.Int64(), additionalRecords[i]) + } + + txScript, err := scriptBuilder.Script() + if err != nil { + return fmt.Errorf("build transaction script: %w", err) + } + + _, err = actr.Wait(actr.SendRun(txScript)) + if err != nil { + return fmt.Errorf("send transction with built script and wait for it to be accepted: %w", err) + } + + cmd.Println("Access list has been successfully updated.") + + return nil +} diff --git a/cmd/neofs-cli/modules/storagegroup/put.go b/cmd/neofs-cli/modules/storagegroup/put.go index 161052a1ba..836daef646 100644 --- a/cmd/neofs-cli/modules/storagegroup/put.go +++ b/cmd/neofs-cli/modules/storagegroup/put.go @@ -49,8 +49,7 @@ func initSGPutCmd() { } func putSG(cmd *cobra.Command, _ []string) { - // with 1.8.0 cobra release we can use this instead of below - // sgPutCmd.MarkFlagsOneRequired("expire-at", "lifetime") + // Track https://github.com/nspcc-dev/neofs-node/issues/2595. exp, _ := cmd.Flags().GetUint64(commonflags.ExpireAt) lifetime, _ := cmd.Flags().GetUint64(commonflags.Lifetime) if exp == 0 && lifetime == 0 { // mutual exclusion is ensured by cobra diff --git a/docs/verified-node-domains.md b/docs/verified-node-domains.md index be70543332..01d6919422 100644 --- a/docs/verified-node-domains.md +++ b/docs/verified-node-domains.md @@ -30,6 +30,60 @@ For each public key, a record is created - a structure with at least 3 fields: 2. `Integer` that is `16` for TXT records (other record types are allowed but left unprocessed) 3. `ByteString` with Neo address of the storage node's public key +### Management + +NeoFS ADM tool may be used to work with verified nodes' domains from command line. +``` +$ neofs-adm morph verified-nodes-domain +``` + +#### Get access list + +List allowed storage nodes: +``` +$ neofs-adm morph verified-nodes-domain access-list -r https://rpc1.morph.t5.fs.neo.org:51331 \ +-d nodes.some-org.neofs +NZ1czz5gkEDamTg6Tiw6cxqp9Me1KLs8ae +NfMvD6WmBiCr4erfEnFFLs7jdj4Y5CM7nN +``` +where `-r` is the NeoFS Sidechain network endpoint. + +See command help for details +``` +$ neofs-adm morph verified-nodes-domain access-list -h +``` + +#### Set access list + +Set list of Neo addresses of the allowed storage nodes: +``` +$ neofs-adm morph verified-nodes-domain set-access-list -r https://rpc1.morph.t5.fs.neo.org:51331 \ +-d nodes.some-org.neofs --alphabet-wallets ./ \ +--neo-addresses NZ1czz5gkEDamTg6Tiw6cxqp9Me1KLs8ae \ +--neo-addresses NfMvD6WmBiCr4erfEnFFLs7jdj4Y5CM7nN +$ Password for az wallet > +$ Waiting for transactions to persist... +$ Access list has been successfully updated. +``` +where `--alphabet-wallets` should lead to directory with NeoFS Alphabet wallet +files `az.json`, `buky.json`, etc. + +Auxiliary flag `--public-keys` allows you to specify public keys instead of addresses: +``` +$ neofs-adm morph verified-nodes-domain set-access-list -r https://rpc1.morph.t5.fs.neo.org:51331 \ +-d nodes.some-org.neofs --alphabet-wallets ./ \ +--public-keys 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2 \ +--public-keys 02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e +$ Password for az wallet > +$ Waiting for transactions to persist... +$ Access list has been successfully updated. +``` + +See command help for details: +``` +$ neofs-adm morph verified-nodes-domain set-access-list -h +``` + ## Private subnet entrance By default, storage nodes do not belong to private groups. Any node wishing to From 56ba4bb003ccc15c65ff94f783dc5cbf5bd10f3d Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 10 Oct 2023 20:11:06 +0400 Subject: [PATCH 4/6] ir: Call `resolve` method of the NNS smart contract to check the record Previously, Inner Ring called `getAllRecords` method to lookup for the particular entry. In particular, this method was used during validation of verified nodes' domains. Implementation was pretty complex due to low-levelness. The `resolve` method is much simpler, but it returns all records on each call. Taking into account that each domain can have no more than 255 records, this drawback is considered insignificant. From now, Inner Ring calls `resolve` method to check domain record existence. This is done as simple as possible through RPC interface provided by NeoFS Contracts lib. Refs #2280. Signed-off-by: Leonard Lyubich --- pkg/innerring/nns.go | 145 ++------------------------------ pkg/innerring/nns_test.go | 170 ++++---------------------------------- 2 files changed, 23 insertions(+), 292 deletions(-) diff --git a/pkg/innerring/nns.go b/pkg/innerring/nns.go index 1cd205c934..c82fdb50b3 100644 --- a/pkg/innerring/nns.go +++ b/pkg/innerring/nns.go @@ -7,7 +7,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11" "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" nnsrpc "github.com/nspcc-dev/neofs-contract/rpc/nns" "github.com/nspcc-dev/neofs-node/pkg/innerring/processors/netmap/nodevalidation/privatedomains" ) @@ -29,82 +28,6 @@ func newNeoFSNNS(contractAddress util.Uint160, contractCaller nep11.Invoker) *ne var errDomainNotFound = errors.New("domain not found") -type errWrongStackItemType struct { - expected, got stackitem.Type -} - -func wrongStackItemTypeError(expected, got stackitem.Type) errWrongStackItemType { - return errWrongStackItemType{ - expected: expected, - got: got, - } -} - -func (x errWrongStackItemType) Error() string { - return fmt.Sprintf("wrong type: expected %s, got %s", x.expected, x.got) -} - -type errInvalidNumberOfStructFields struct { - expected, got int -} - -func invalidNumberOfStructFieldsError(expected, got int) errInvalidNumberOfStructFields { - return errInvalidNumberOfStructFields{ - expected: expected, - got: got, - } -} - -func (x errInvalidNumberOfStructFields) Error() string { - return fmt.Sprintf("invalid number of struct fields: expected %d, got %d", x.expected, x.got) -} - -type errInvalidStructField struct { - index uint - cause error -} - -func invalidStructFieldError(index uint, cause error) errInvalidStructField { - return errInvalidStructField{ - index: index, - cause: cause, - } -} - -func (x errInvalidStructField) Error() string { - return fmt.Sprintf("invalid struct field #%d: %v", x.index, x.cause) -} - -func (x errInvalidStructField) Unwrap() error { - return x.cause -} - -type errInvalidNNSDomainRecord struct { - domain string - record string - cause error -} - -func invalidNNSDomainRecordError(domain, record string, cause error) errInvalidNNSDomainRecord { - return errInvalidNNSDomainRecord{ - domain: domain, - record: record, - cause: cause, - } -} - -func (x errInvalidNNSDomainRecord) Error() string { - if x.record != "" { - return fmt.Sprintf("invalid record %q of the NNS domain %q: %v", x.record, x.domain, x.cause) - } - - return fmt.Sprintf("invalid record of the NNS domain %q: %v", x.domain, x.cause) -} - -func (x errInvalidNNSDomainRecord) Unwrap() error { - return x.cause -} - // CheckDomainRecord calls iterating 'getAllRecords' method of the parameterized // Neo smart contract passing the given domain name. If contract throws 'token // not found' exception, CheckDomainRecord returns errDomainNotFound. Each value @@ -114,77 +37,21 @@ func (x errInvalidNNSDomainRecord) Unwrap() error { // CheckDomainRecord returns nil. Otherwise, // [privatedomains.ErrMissingDomainRecord] is returned. func (x *neoFSNNS) CheckDomainRecord(domain string, record string) error { - sessionID, iter, err := x.contract.GetAllRecords(domain) + records, err := x.contract.Resolve(domain, nnsrpc.TXT) if err != nil { // Track https://github.com/nspcc-dev/neofs-node/issues/2583. if strings.Contains(err.Error(), "token not found") { return errDomainNotFound } - return fmt.Errorf("get iterator over all records of the NNS domain %q: %w", domain, err) + return fmt.Errorf("get all text records of the NNS domain %q: %w", domain, err) } - defer func() { - _ = x.invoker.TerminateSession(sessionID) - }() - - hasRecords := false - - for { - items, err := x.invoker.TraverseIterator(sessionID, &iter, 10) - if err != nil { - return fmt.Errorf("traverse iterator over all records of the NNS domain %q: %w", domain, err) - } - - if len(items) == 0 { - break - } - - hasRecords = true - - for i := range items { - fields, ok := items[i].Value().([]stackitem.Item) - if !ok { - return invalidNNSDomainRecordError(domain, "", - wrongStackItemTypeError(stackitem.StructT, items[i].Type())) - } - - if len(fields) < 3 { - return invalidNNSDomainRecordError(domain, "", - invalidNumberOfStructFieldsError(3, len(fields))) - } - - _, err = fields[0].TryBytes() - if err != nil { - return invalidNNSDomainRecordError(domain, "", - invalidStructFieldError(0, wrongStackItemTypeError(stackitem.ByteArrayT, fields[0].Type()))) - } - - typ, err := fields[1].TryInteger() - if err != nil { - return invalidNNSDomainRecordError(domain, "", - invalidStructFieldError(1, wrongStackItemTypeError(stackitem.IntegerT, fields[1].Type()))) - } - - if typ.Cmp(nnsrpc.TXT) != 0 { - continue - } - - data, err := fields[2].TryBytes() - if err != nil { - return invalidNNSDomainRecordError(domain, "", - invalidStructFieldError(2, wrongStackItemTypeError(stackitem.ByteArrayT, fields[2].Type()))) - } - - if string(data) == record { - return nil - } + for i := range records { + if records[i] == record { + return nil } } - if hasRecords { - return privatedomains.ErrMissingDomainRecord - } - - return nil + return privatedomains.ErrMissingDomainRecord } diff --git a/pkg/innerring/nns_test.go b/pkg/innerring/nns_test.go index e664c84a20..d5fa15533c 100644 --- a/pkg/innerring/nns_test.go +++ b/pkg/innerring/nns_test.go @@ -25,11 +25,6 @@ type testInvoker struct { callRes *result.Invoke callErr error - - traverseErr error - - items []stackitem.Item - readItems int } func newTestInvoker(tb testing.TB, contractAddr util.Uint160, domain string) *testInvoker { @@ -42,9 +37,10 @@ func newTestInvoker(tb testing.TB, contractAddr util.Uint160, domain string) *te func (x *testInvoker) Call(contract util.Uint160, method string, args ...any) (*result.Invoke, error) { require.Equal(x.tb, x.contractAddr, contract) - require.Equal(x.tb, "getAllRecords", method) - require.Len(x.tb, args, 1) + require.Equal(x.tb, "resolve", method) + require.Len(x.tb, args, 2) require.Equal(x.tb, x.domain, args[0]) + require.Equal(x.tb, nnsrpc.TXT, args[1]) if x.callErr != nil { return nil, x.callErr @@ -58,26 +54,11 @@ func (x *testInvoker) CallAndExpandIterator(contract util.Uint160, method string } func (x *testInvoker) TerminateSession(sessionID uuid.UUID) error { - require.Equal(x.tb, x.callRes.Session, sessionID) - return nil + panic("not expected to be called") } func (x *testInvoker) TraverseIterator(sessionID uuid.UUID, iterator *result.Iterator, num int) ([]stackitem.Item, error) { - require.Equal(x.tb, x.callRes.Session, sessionID) - - if x.traverseErr != nil { - return nil, x.traverseErr - } - - if num > len(x.items)-x.readItems { - num = len(x.items) - x.readItems - } - - defer func() { - x.readItems += num - }() - - return x.items[x.readItems : x.readItems+num], nil + panic("not expected to be called") } func TestNeoFSNNS_CheckDomainRecord(t *testing.T) { @@ -125,28 +106,26 @@ func TestNeoFSNNS_CheckDomainRecord(t *testing.T) { nnsService := newNeoFSNNS(contractAddr, inv) err := nnsService.CheckDomainRecord(domain, searchedRecord) - require.Error(t, err) + require.ErrorContains(t, err, "result stack is empty") inv.callRes.Stack = make([]stackitem.Item, 2) err = nnsService.CheckDomainRecord(domain, searchedRecord) require.Error(t, err) - nonIteratorItem := stackitem.NewBool(true) - inv.callRes.Stack = []stackitem.Item{nonIteratorItem} + nonArrayItem := stackitem.NewBool(true) + inv.callRes.Stack = []stackitem.Item{nonArrayItem} err = nnsService.CheckDomainRecord(domain, searchedRecord) - require.Error(t, err) + require.ErrorContains(t, err, "not an array") }) newWithRecords := func(items ...stackitem.Item) *neoFSNNS { - var iter result.Iterator inv := newTestInvoker(t, contractAddr, domain) inv.callRes = &result.Invoke{ State: vmstate.Halt.String(), - Stack: []stackitem.Item{stackitem.NewInterop(iter)}, + Stack: []stackitem.Item{stackitem.NewArray(items)}, } - inv.items = items return newNeoFSNNS(contractAddr, inv) } @@ -155,129 +134,22 @@ func TestNeoFSNNS_CheckDomainRecord(t *testing.T) { nnsService := newWithRecords() err := nnsService.CheckDomainRecord(domain, searchedRecord) - require.NoError(t, err) + require.ErrorIs(t, err, privatedomains.ErrMissingDomainRecord) }) - checkInvalidRecordError := func(record string, err error) { - var e errInvalidNNSDomainRecord - require.ErrorAs(t, err, &e) - require.Equal(t, domain, e.domain) - require.Equal(t, record, e.record) - } - - checkInvalidStructFieldError := func(index uint, err error) { - var e errInvalidStructField - require.ErrorAs(t, err, &e) - require.Equal(t, index, e.index) - } - t.Run("with invalid record", func(t *testing.T) { - t.Run("non-struct element", func(t *testing.T) { - nnsService := newWithRecords(stackitem.NewBool(true)) - - err := nnsService.CheckDomainRecord(domain, searchedRecord) - - checkInvalidRecordError("", err) - - var wrongTypeErr errWrongStackItemType - require.ErrorAs(t, err, &wrongTypeErr) - require.Equal(t, stackitem.StructT, wrongTypeErr.expected) - require.Equal(t, stackitem.BooleanT, wrongTypeErr.got) - }) - - t.Run("invalid number of fields", func(t *testing.T) { - for i := 0; i < 3; i++ { - nnsService := newWithRecords(stackitem.NewStruct(make([]stackitem.Item, i))) - - err := nnsService.CheckDomainRecord(domain, searchedRecord) - - checkInvalidRecordError("", err) - - var invalidFieldNumErr errInvalidNumberOfStructFields - require.ErrorAs(t, err, &invalidFieldNumErr) - require.Equal(t, 3, invalidFieldNumErr.expected, i) - require.Equal(t, i, invalidFieldNumErr.got, i) - } - }) - - t.Run("invalid 1st field", func(t *testing.T) { - nnsService := newWithRecords(stackitem.NewStruct([]stackitem.Item{ - stackitem.NewMap(), - stackitem.NewBigInteger(nnsrpc.TXT), - stackitem.NewByteArray([]byte("any")), - })) + t.Run("non-string element", func(t *testing.T) { + nnsService := newWithRecords(stackitem.NewMap()) err := nnsService.CheckDomainRecord(domain, searchedRecord) - - checkInvalidRecordError("", err) - checkInvalidStructFieldError(0, err) - - var wrongTypeErr errWrongStackItemType - require.ErrorAs(t, err, &wrongTypeErr) - require.Equal(t, stackitem.ByteArrayT, wrongTypeErr.expected) - require.Equal(t, stackitem.MapT, wrongTypeErr.got) - }) - - t.Run("invalid 2nd field", func(t *testing.T) { - t.Run("non-integer", func(t *testing.T) { - nnsService := newWithRecords(stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray([]byte("any")), - stackitem.NewMap(), - stackitem.NewByteArray([]byte("any")), - })) - - err := nnsService.CheckDomainRecord(domain, searchedRecord) - - checkInvalidRecordError("", err) - checkInvalidStructFieldError(1, err) - - var wrongTypeErr errWrongStackItemType - require.ErrorAs(t, err, &wrongTypeErr) - require.Equal(t, stackitem.IntegerT, wrongTypeErr.expected) - require.Equal(t, stackitem.MapT, wrongTypeErr.got) - }) - - t.Run("non-TXT record", func(t *testing.T) { - nnsService := newWithRecords(stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray([]byte("any")), - stackitem.NewBigInteger(nnsrpc.CNAME), - stackitem.NewByteArray([]byte("any")), - })) - - err := nnsService.CheckDomainRecord(domain, searchedRecord) - require.ErrorIs(t, err, privatedomains.ErrMissingDomainRecord) - }) - }) - - t.Run("invalid 3rd field", func(t *testing.T) { - t.Run("invalid type", func(t *testing.T) { - nnsService := newWithRecords(stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray([]byte("any")), - stackitem.NewBigInteger(nnsrpc.TXT), - stackitem.NewMap(), - })) - - err := nnsService.CheckDomainRecord(domain, searchedRecord) - - checkInvalidRecordError("", err) - checkInvalidStructFieldError(2, err) - - var wrongTypeErr errWrongStackItemType - require.ErrorAs(t, err, &wrongTypeErr) - require.Equal(t, stackitem.ByteArrayT, wrongTypeErr.expected) - require.Equal(t, stackitem.MapT, wrongTypeErr.got) - }) + require.ErrorIs(t, err, stackitem.ErrInvalidConversion) }) }) t.Run("without searched record", func(t *testing.T) { records := make([]stackitem.Item, 100) for i := range records { - records[i] = stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray([]byte("any")), - stackitem.NewBigInteger(nnsrpc.TXT), - stackitem.NewByteArray([]byte(searchedRecord + hex.EncodeToString([]byte{byte(i)}))), - }) + records[i] = stackitem.NewByteArray([]byte(searchedRecord + hex.EncodeToString([]byte{byte(i)}))) } nnsService := newWithRecords(records...) @@ -289,18 +161,10 @@ func TestNeoFSNNS_CheckDomainRecord(t *testing.T) { t.Run("with searched record", func(t *testing.T) { records := make([]stackitem.Item, 100) for i := range records { - records[i] = stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray([]byte("any")), - stackitem.NewBigInteger(nnsrpc.TXT), - stackitem.NewByteArray([]byte(searchedRecord + hex.EncodeToString([]byte{byte(i)}))), - }) + records[i] = stackitem.NewByteArray([]byte(searchedRecord + hex.EncodeToString([]byte{byte(i)}))) } - records = append(records, stackitem.NewStruct([]stackitem.Item{ - stackitem.NewByteArray([]byte("any")), - stackitem.NewBigInteger(nnsrpc.TXT), - stackitem.NewByteArray([]byte(searchedRecord)), - })) + records = append(records, stackitem.NewByteArray([]byte(searchedRecord))) nnsService := newWithRecords(records...) From 9c98351ccda904222285e90bf7fbc4be3e4a8a88 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 10 Oct 2023 20:22:17 +0400 Subject: [PATCH 5/6] adm: Call `resolve` method of the NNS smart contract to check the record Similar motivation as in 8b0e5ed5ca34e03985e02315cd9d7c3644d48661. Refs #2280. Signed-off-by: Leonard Lyubich --- cmd/neofs-adm/internal/modules/morph/nns.go | 107 ------------------ .../modules/morph/verified_domains.go | 75 ++++++------ 2 files changed, 41 insertions(+), 141 deletions(-) delete mode 100644 cmd/neofs-adm/internal/modules/morph/nns.go diff --git a/cmd/neofs-adm/internal/modules/morph/nns.go b/cmd/neofs-adm/internal/modules/morph/nns.go deleted file mode 100644 index 815c6fb65a..0000000000 --- a/cmd/neofs-adm/internal/modules/morph/nns.go +++ /dev/null @@ -1,107 +0,0 @@ -package morph - -import ( - "errors" - "fmt" - "strings" - - "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" - nnsrpc "github.com/nspcc-dev/neofs-contract/rpc/nns" -) - -// Various NeoFS NNS errors. -var ( - errDomainNotFound = errors.New("domain not found") - errMissingDomainRecords = errors.New("missing domain records") -) - -func invalidNNSDomainRecordError(cause error) error { - return fmt.Errorf("invalid domain record: %w", cause) -} - -var errBreakIterator = errors.New("break iterator") - -// iterates over text records of the specified NeoFS NNS domain and passes them -// into f. Breaks on any f's error and returns it (if f returns -// errBreakIterator, iterateNNSDomainTextRecords returns no error). Returns -// errDomainNotFound if domain is missing in the NNS. Returns -// errMissingDomainRecords if domain exists but has no records. -func iterateNNSDomainTextRecords(inv nnsrpc.Invoker, nnsContractAddr util.Uint160, domain string, f func(string) error) error { - nnsContract := nnsrpc.NewReader(inv, nnsContractAddr) - - sessionID, iter, err := nnsContract.GetAllRecords(domain) - if err != nil { - // Track https://github.com/nspcc-dev/neofs-node/issues/2583. - if strings.Contains(err.Error(), "token not found") { - return errDomainNotFound - } - - return fmt.Errorf("get all records of the NNS domain: %w", err) - } - - defer func() { - _ = inv.TerminateSession(sessionID) - }() - - hasRecords := false - - for { - items, err := inv.TraverseIterator(sessionID, &iter, 10) - if err != nil { - return fmt.Errorf("NNS domain records' iterator break: %w", err) - } - - if len(items) == 0 { - if hasRecords { - return nil - } - - return errMissingDomainRecords - } - - hasRecords = true - - for i := range items { - fields, ok := items[i].Value().([]stackitem.Item) - if !ok { - return invalidNNSDomainRecordError( - fmt.Errorf("unexpected type %s instead of %s", stackitem.StructT, items[i].Type())) - } - - if len(fields) < 3 { - return invalidNNSDomainRecordError( - fmt.Errorf("unsupported number of struct fields: expected at least 3, got %d", len(fields))) - } - - _, err = fields[0].TryBytes() - if err != nil { - return invalidNNSDomainRecordError( - fmt.Errorf("1st field is not a byte array: got %v", fields[0].Type())) - } - - typ, err := fields[1].TryInteger() - if err != nil { - return invalidNNSDomainRecordError(fmt.Errorf("2nd field is not an integer: got %v", fields[1].Type())) - } - - if typ.Cmp(nnsrpc.TXT) != 0 { - continue - } - - data, err := fields[2].TryBytes() - if err != nil { - return invalidNNSDomainRecordError( - fmt.Errorf("3rd field is not a byte array: got %v", fields[2].Type())) - } - - if err = f(string(data)); err != nil { - if errors.Is(err, errBreakIterator) { - return nil - } - - return err - } - } - } -} diff --git a/cmd/neofs-adm/internal/modules/morph/verified_domains.go b/cmd/neofs-adm/internal/modules/morph/verified_domains.go index 63e874ab43..efd2a53ebf 100644 --- a/cmd/neofs-adm/internal/modules/morph/verified_domains.go +++ b/cmd/neofs-adm/internal/modules/morph/verified_domains.go @@ -32,22 +32,26 @@ func verifiedNodesDomainAccessList(cmd *cobra.Command, _ []string) error { } domain := vpr.GetString(domainFlag) + nnsContract := nnsrpc.NewReader(invoker.New(n3Client, nil), nnsContractAddr) - err = iterateNNSDomainTextRecords(invoker.New(n3Client, nil), nnsContractAddr, domain, func(rec string) error { - cmd.Println(rec) - return nil - }) + records, err := nnsContract.Resolve(domain, nnsrpc.TXT) if err != nil { - switch { - default: - return fmt.Errorf("handle domain %q records: %w", domain, err) - case errors.Is(err, errDomainNotFound): + // Track https://github.com/nspcc-dev/neofs-node/issues/2583. + if strings.Contains(err.Error(), "token not found") { cmd.Println("Domain not found.") return nil - case errors.Is(err, errMissingDomainRecords): - cmd.Println("List is empty.") - return nil } + + return fmt.Errorf("get all text records of the NNS domain %q: %w", domain, err) + } + + if len(records) == 0 { + cmd.Println("List is empty.") + return nil + } + + for i := range records { + cmd.Println(records[i]) } return nil @@ -151,40 +155,43 @@ func verifiedNodesDomainSetAccessList(cmd *cobra.Command, _ []string) error { return fmt.Errorf("init committee actor: %w", err) } + nnsContract := nnsrpc.New(actr, nnsContractAddr) domain := vpr.GetString(domainFlag) scriptBuilder := smartcontract.NewBuilder() + + records, err := nnsContract.Resolve(domain, nnsrpc.TXT) + if err != nil { + // Track https://github.com/nspcc-dev/neofs-node/issues/2583. + if !strings.Contains(err.Error(), "token not found") { + return fmt.Errorf("get all text records of the NNS domain %q: %w", domain, err) + } + + domainToRegister := domain + if labels := strings.Split(domainToRegister, "."); len(labels) > 2 { + // we need explicitly register L2 domain like 'some-org.neofs' + // and then just add records to inferior domains + domainToRegister = labels[len(labels)-2] + "." + labels[len(labels)-1] + } + + scriptBuilder.InvokeMethod(nnsContractAddr, "register", + domainToRegister, acc.ScriptHash(), "ops@nspcc.ru", int64(3600), int64(600), int64(defaultExpirationTime), int64(3600)) + } + hasOtherRecord := false mAlreadySetIndices := make(map[int]struct{}, len(additionalRecords)) - err = iterateNNSDomainTextRecords(actr, nnsContractAddr, domain, func(rec string) error { - for i := range additionalRecords { - if additionalRecords[i] == rec { +loop: + for i := range records { + for j := range additionalRecords { + if additionalRecords[j] == records[i] { mAlreadySetIndices[i] = struct{}{} - return nil + continue loop } } hasOtherRecord = true - return errBreakIterator - }) - if err != nil { - switch { - default: - return fmt.Errorf("handle domain %q records: %w", domain, err) - case errors.Is(err, errMissingDomainRecords): - // domain exists but has no records, that's ok: just going to add new ones - case errors.Is(err, errDomainNotFound): - domainToRegister := domain - if labels := strings.Split(domainToRegister, "."); len(labels) > 2 { - // we need explicitly register L2 domain like 'some-org.neofs' - // and then just add records to inferior domains - domainToRegister = labels[len(labels)-2] + "." + labels[len(labels)-1] - } - - scriptBuilder.InvokeMethod(nnsContractAddr, "register", - domainToRegister, acc.ScriptHash(), "ops@nspcc.ru", int64(3600), int64(600), int64(defaultExpirationTime), int64(3600)) - } + break } if !hasOtherRecord && len(mAlreadySetIndices) == len(additionalRecords) { From b87545a38f40ca2f32aa89580739359efb219c91 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 10 Oct 2023 20:43:50 +0400 Subject: [PATCH 6/6] nns: Use format of Neo address records specified in NEP-18 standard From now verified nodes' domain records are prefixed with `address=` in order to comply the Neo specification. Signed-off-by: Leonard Lyubich --- .../internal/modules/morph/verified_domains.go | 17 +++++++++++++---- docs/verified-node-domains.md | 2 +- .../nodevalidation/privatedomains/validator.go | 3 ++- .../privatedomains/validator_test.go | 4 ++-- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/cmd/neofs-adm/internal/modules/morph/verified_domains.go b/cmd/neofs-adm/internal/modules/morph/verified_domains.go index efd2a53ebf..49586e683a 100644 --- a/cmd/neofs-adm/internal/modules/morph/verified_domains.go +++ b/cmd/neofs-adm/internal/modules/morph/verified_domains.go @@ -18,6 +18,9 @@ import ( "github.com/spf13/viper" ) +// as described in NEP-18 Specification https://github.com/neo-project/proposals/pull/133 +const nnsNeoAddressTextRecordPrefix = "address=" + func verifiedNodesDomainAccessList(cmd *cobra.Command, _ []string) error { vpr := viper.GetViper() @@ -51,7 +54,13 @@ func verifiedNodesDomainAccessList(cmd *cobra.Command, _ []string) error { } for i := range records { - cmd.Println(records[i]) + neoAddr := strings.TrimPrefix(records[i], nnsNeoAddressTextRecordPrefix) + if len(neoAddr) == len(records[i]) { + cmd.Printf("%s (not a Neo address)\n", records[i]) + continue + } + + cmd.Println(neoAddr) } return nil @@ -87,9 +96,9 @@ func verifiedNodesDomainSetAccessList(cmd *cobra.Command, _ []string) error { if err != nil { return fmt.Errorf("address #%d is invalid: %w", i, err) } - } - additionalRecords = strNeoAddresses + additionalRecords = append(additionalRecords, nnsNeoAddressTextRecordPrefix+strNeoAddresses[i]) + } } else { additionalRecords = make([]string, len(strPublicKeys)) @@ -105,7 +114,7 @@ func verifiedNodesDomainSetAccessList(cmd *cobra.Command, _ []string) error { return fmt.Errorf("public key #%d is not a HEX-encoded public key: %w", i, err) } - additionalRecords[i] = address.Uint160ToString(pubKey.GetScriptHash()) + additionalRecords[i] = nnsNeoAddressTextRecordPrefix + address.Uint160ToString(pubKey.GetScriptHash()) } } diff --git a/docs/verified-node-domains.md b/docs/verified-node-domains.md index 01d6919422..447a20015a 100644 --- a/docs/verified-node-domains.md +++ b/docs/verified-node-domains.md @@ -28,7 +28,7 @@ the network map. For each public key, a record is created - a structure with at least 3 fields: 1. `ByteString` with name of the corresponding domain 2. `Integer` that is `16` for TXT records (other record types are allowed but left unprocessed) -3. `ByteString` with Neo address of the storage node's public key +3. `ByteString` with `address=` value described in [NEP-18 Specification](https://github.com/neo-project/proposals/pull/133) ### Management diff --git a/pkg/innerring/processors/netmap/nodevalidation/privatedomains/validator.go b/pkg/innerring/processors/netmap/nodevalidation/privatedomains/validator.go index c8ab674f32..2e6ef92693 100644 --- a/pkg/innerring/processors/netmap/nodevalidation/privatedomains/validator.go +++ b/pkg/innerring/processors/netmap/nodevalidation/privatedomains/validator.go @@ -77,7 +77,8 @@ func (x *Validator) VerifyAndUpdate(info *netmap.NodeInfo) error { emit.CheckSig(buf.BinWriter, bNodeKey) nodeNeoAddress := address.Uint160ToString(hash.Hash160(buf.Bytes())) - err := x.nns.CheckDomainRecord(verifiedNodesDomain, nodeNeoAddress) + // record format is described in NEP-18 Specification https://github.com/neo-project/proposals/pull/133 + err := x.nns.CheckDomainRecord(verifiedNodesDomain, "address="+nodeNeoAddress) if err != nil { if errors.Is(err, ErrMissingDomainRecord) { return errAccessDenied diff --git a/pkg/innerring/processors/netmap/nodevalidation/privatedomains/validator_test.go b/pkg/innerring/processors/netmap/nodevalidation/privatedomains/validator_test.go index 74d7d5bcb8..a3d70b2204 100644 --- a/pkg/innerring/processors/netmap/nodevalidation/privatedomains/validator_test.go +++ b/pkg/innerring/processors/netmap/nodevalidation/privatedomains/validator_test.go @@ -75,8 +75,8 @@ func (x *testNNS) CheckDomainRecord(domainName string, record string) error { func TestValidator_VerifyAndUpdate(t *testing.T) { const verifiedDomain = "nodes.some-org.neofs" const hNodeKey = "02a70577a832b338772c8cd07e7eaf526cae8d9b025a51b41671de5a4363eafe07" - const nodeNeoAddress = "NZ1czz5gkEDamTg6Tiw6cxqp9Me1KLs8ae" - const anyOtherNeoAddress = "NfMvD6WmBiCr4erfEnFFLs7jdj4Y5CM7nN" + const nodeNeoAddress = "address=NZ1czz5gkEDamTg6Tiw6cxqp9Me1KLs8ae" + const anyOtherNeoAddress = "address=NfMvD6WmBiCr4erfEnFFLs7jdj4Y5CM7nN" bNodeKey, err := hex.DecodeString(hNodeKey) require.NoError(t, err)