From 8645aca8600408f9d9066460b756bc1c7e8b5c81 Mon Sep 17 00:00:00 2001 From: Calin Martinconi Date: Mon, 20 May 2024 21:47:51 +0300 Subject: [PATCH 01/41] chore: bump protobuf version to 1.33.0 (#4613) (#4688) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b3d4a7b6623..89d065de8d7 100644 --- a/go.mod +++ b/go.mod @@ -169,7 +169,7 @@ require ( golang.org/x/mod v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.18.0 // indirect - google.golang.org/protobuf v1.32.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.57.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.2.1 // indirect diff --git a/go.sum b/go.sum index f5e9509bbea..fb8b5a46104 100644 --- a/go.sum +++ b/go.sum @@ -1384,8 +1384,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 5fe82a6be225225735872d70695d95ab16196b10 Mon Sep 17 00:00:00 2001 From: istae <14264581+istae@users.noreply.github.com> Date: Mon, 27 May 2024 20:10:49 +0300 Subject: [PATCH 02/41] fix: save stamp issuer after failed uploads (#4684) --- pkg/api/api.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 72100cc0d9b..4e356a14d69 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -791,15 +791,11 @@ func (p *putterSessionWrapper) Put(ctx context.Context, chunk swarm.Chunk) error } func (p *putterSessionWrapper) Done(ref swarm.Address) error { - err := p.PutterSession.Done(ref) - if err != nil { - return err - } - return p.save() + return errors.Join(p.PutterSession.Done(ref), p.save()) } func (p *putterSessionWrapper) Cleanup() error { - return p.PutterSession.Cleanup() + return errors.Join(p.PutterSession.Cleanup(), p.save()) } func (s *Service) getStamper(batchID []byte) (postage.Stamper, func() error, error) { From de7eccc12f00840327745b4cf7df018d93073264 Mon Sep 17 00:00:00 2001 From: istae <14264581+istae@users.noreply.github.com> Date: Tue, 28 May 2024 11:27:45 +0300 Subject: [PATCH 03/41] chore: pre-release updates (#4675) --- go.mod | 2 +- go.sum | 4 ++-- openapi/SwarmCommon.yaml | 2 +- openapi/SwarmDebug.yaml | 2 +- pkg/api/router.go | 2 +- pkg/retrieval/retrieval.go | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 89d065de8d7..ff25bea2ef6 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/coreos/go-semver v0.3.0 github.com/ethereum/go-ethereum v1.13.4 github.com/ethersphere/go-price-oracle-abi v0.2.0 - github.com/ethersphere/go-storage-incentives-abi v0.8.6-rc5 + github.com/ethersphere/go-storage-incentives-abi v0.8.6 github.com/ethersphere/go-sw3-abi v0.6.5 github.com/ethersphere/langos v1.0.0 github.com/go-playground/validator/v10 v10.11.1 diff --git a/go.sum b/go.sum index fb8b5a46104..e692609478e 100644 --- a/go.sum +++ b/go.sum @@ -240,8 +240,8 @@ github.com/ethereum/go-ethereum v1.13.4 h1:25HJnaWVg3q1O7Z62LaaI6S9wVq8QCw3K88g8 github.com/ethereum/go-ethereum v1.13.4/go.mod h1:I0U5VewuuTzvBtVzKo7b3hJzDhXOUtn9mJW7SsIPB0Q= github.com/ethersphere/go-price-oracle-abi v0.2.0 h1:wtIcYLgNZHY4BjYwJCnu93SvJdVAZVvBaKinspyyHvQ= github.com/ethersphere/go-price-oracle-abi v0.2.0/go.mod h1:sI/Qj4/zJ23/b1enzwMMv0/hLTpPNVNacEwCWjo6yBk= -github.com/ethersphere/go-storage-incentives-abi v0.8.6-rc5 h1:lW7p+KwAkCZbhoZIxYHfAJSqXHL02FXs1Su5WtevVrI= -github.com/ethersphere/go-storage-incentives-abi v0.8.6-rc5/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= +github.com/ethersphere/go-storage-incentives-abi v0.8.6 h1:M9WwEtWoxVHKehBAoPMzQhXlzlBetuXsrGgoFvM5I8Y= +github.com/ethersphere/go-storage-incentives-abi v0.8.6/go.mod h1:SXvJVtM4sEsaSKD0jc1ClpDLw8ErPoROZDme4Wrc/Nc= github.com/ethersphere/go-sw3-abi v0.6.5 h1:M5dcIe1zQYvGpY2K07UNkNU9Obc4U+A1fz68Ho/Q+XE= github.com/ethersphere/go-sw3-abi v0.6.5/go.mod h1:BmpsvJ8idQZdYEtWnvxA8POYQ8Rl/NhyCdF0zLMOOJU= github.com/ethersphere/langos v1.0.0 h1:NBtNKzXTTRSue95uOlzPN4py7Aofs0xWPzyj4AI1Vcc= diff --git a/openapi/SwarmCommon.yaml b/openapi/SwarmCommon.yaml index 84d690fc64f..42e3f79d46f 100644 --- a/openapi/SwarmCommon.yaml +++ b/openapi/SwarmCommon.yaml @@ -1,6 +1,6 @@ openapi: 3.0.3 info: - version: 3.2.7 + version: 3.2.8 title: Common Data Types description: | \*****bzzz***** diff --git a/openapi/SwarmDebug.yaml b/openapi/SwarmDebug.yaml index e2091171729..986456cb527 100644 --- a/openapi/SwarmDebug.yaml +++ b/openapi/SwarmDebug.yaml @@ -1,6 +1,6 @@ openapi: 3.0.3 info: - version: 4.1.1 + version: 4.1.2 title: Bee Debug API description: "A list of the currently provided debug interfaces to interact with the bee node" diff --git a/pkg/api/router.go b/pkg/api/router.go index c0c6d02782c..0396dd2b2de 100644 --- a/pkg/api/router.go +++ b/pkg/api/router.go @@ -370,7 +370,7 @@ func (s *Service) mountAPI() { func (s *Service) mountBusinessDebug() { handle := func(path string, handler http.Handler) { - s.logger.Warning("DEPRECATION NOTICE: This endpoint is now part of the main Bee API. The Debug API will be removed in the next release, version [2.2.0]. Update your integrations to use the main Bee API to avoid service disruptions.") + s.logger.Warning(fmt.Sprintf("DEPRECATION NOTICE: %s endpoint is now part of the main Bee API. The Debug API will be removed in the next release, version [2.2.0]. Update your integrations to use the main Bee API to avoid service disruptions.", path)) if s.Restricted { handler = web.ChainHandlers(auth.PermissionCheckHandler(s.auth), web.FinalHandler(handler)) } diff --git a/pkg/retrieval/retrieval.go b/pkg/retrieval/retrieval.go index 0bb0668f700..53452cec757 100644 --- a/pkg/retrieval/retrieval.go +++ b/pkg/retrieval/retrieval.go @@ -263,7 +263,7 @@ func (s *Service) RetrieveChunk(ctx context.Context, chunkAddr, sourcePeerAddr s go func() { span, _, ctx := s.tracer.FollowSpanFromContext(spanCtx, "retrieve-chunk", s.logger, opentracing.Tag{Key: "address", Value: chunkAddr.String()}) defer span.Finish() - s.retrieveChunk(ctx, quit, chunkAddr, peer, resultC, action, origin, span) + s.retrieveChunk(ctx, quit, chunkAddr, peer, resultC, action, span) }() case res := <-resultC: @@ -297,7 +297,7 @@ func (s *Service) RetrieveChunk(ctx context.Context, chunkAddr, sourcePeerAddr s return v, nil } -func (s *Service) retrieveChunk(ctx context.Context, quit chan struct{}, chunkAddr, peer swarm.Address, result chan retrievalResult, action accounting.Action, isOrigin bool, span opentracing.Span) { +func (s *Service) retrieveChunk(ctx context.Context, quit chan struct{}, chunkAddr, peer swarm.Address, result chan retrievalResult, action accounting.Action, span opentracing.Span) { var ( startTime = time.Now() From ca9e74a0efcc5c2f6d98d9185ddc5fb23568945e Mon Sep 17 00:00:00 2001 From: Calin Martinconi Date: Tue, 28 May 2024 14:38:50 +0300 Subject: [PATCH 04/41] chore: bump go ethereum version to 1.14.3 (#4661) (#4689) --- go.mod | 27 +++++++++-------- go.sum | 91 +++++++++++++++++++++++++++++++--------------------------- 2 files changed, 62 insertions(+), 56 deletions(-) diff --git a/go.mod b/go.mod index ff25bea2ef6..f85bdfb247c 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.3.2 github.com/casbin/casbin/v2 v2.35.0 github.com/coreos/go-semver v0.3.0 - github.com/ethereum/go-ethereum v1.13.4 + github.com/ethereum/go-ethereum v1.14.3 github.com/ethersphere/go-price-oracle-abi v0.2.0 github.com/ethersphere/go-storage-incentives-abi v0.8.6 github.com/ethersphere/go-sw3-abi v0.6.5 @@ -43,12 +43,12 @@ require ( gitlab.com/nolash/go-mockbytes v0.0.7 go.uber.org/atomic v1.11.0 go.uber.org/goleak v1.3.0 - golang.org/x/crypto v0.19.0 + golang.org/x/crypto v0.22.0 golang.org/x/exp v0.0.0-20240213143201-ec583247a57a - golang.org/x/net v0.21.0 - golang.org/x/sync v0.6.0 - golang.org/x/sys v0.17.0 - golang.org/x/term v0.17.0 + golang.org/x/net v0.24.0 + golang.org/x/sync v0.7.0 + golang.org/x/sys v0.19.0 + golang.org/x/term v0.19.0 golang.org/x/time v0.5.0 gopkg.in/yaml.v2 v2.4.0 resenje.org/multex v0.1.0 @@ -63,7 +63,7 @@ require ( github.com/StackExchange/wmi v1.2.1 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.7.0 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/codahale/hdrhistogram v0.0.0-00010101000000-000000000000 // indirect @@ -71,23 +71,22 @@ require ( github.com/consensys/gnark-crypto v0.12.1 // indirect github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect - github.com/crate-crypto/go-kzg-4844 v0.3.0 // indirect + github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/elastic/gosigar v0.14.2 // indirect - github.com/ethereum/c-kzg-4844 v0.3.1 // indirect + github.com/ethereum/c-kzg-4844 v1.0.0 // indirect github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect - github.com/go-ole/go-ole v1.2.5 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect - github.com/go-stack/stack v1.8.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -98,7 +97,7 @@ require ( github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/holiman/uint256 v1.2.3 // indirect + github.com/holiman/uint256 v1.2.4 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect @@ -166,9 +165,9 @@ require ( go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/mod v0.15.0 // indirect + golang.org/x/mod v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.18.0 // indirect + golang.org/x/tools v0.20.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.57.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index e692609478e..80862fcb763 100644 --- a/go.sum +++ b/go.sum @@ -116,8 +116,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= -github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= @@ -160,16 +160,14 @@ github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y= -github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= -github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= -github.com/cockroachdb/redact v1.0.8 h1:8QG/764wK+vmEYoOlfobpe12EQcS81ukx/a4hdVMxNw= -github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 h1:IKgmqgMQlVJIZj19CdocBeSfSaiCbEBZGKODaixqtHM= -github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= +github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= +github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.0 h1:pcFh8CdCIt2kmEpK0OIatq67Ln9uGDYY3d5XnE0LJG4= +github.com/cockroachdb/pebble v1.1.0/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= @@ -195,8 +193,10 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.0/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/crate-crypto/go-kzg-4844 v0.3.0 h1:UBlWE0CgyFqqzTI+IFyCzA7A3Zw4iip6uzRv5NIXG0A= -github.com/crate-crypto/go-kzg-4844 v0.3.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= +github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -233,11 +233,11 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF 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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/c-kzg-4844 v0.3.1 h1:sR65+68+WdnMKxseNWxSJuAv2tsUrihTpVBTfM/U5Zg= -github.com/ethereum/c-kzg-4844 v0.3.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= +github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/ethereum/go-ethereum v1.10.4/go.mod h1:nEE0TP5MtxGzOMd7egIrbPJMQBnhVU3ELNxhBglIzhg= -github.com/ethereum/go-ethereum v1.13.4 h1:25HJnaWVg3q1O7Z62LaaI6S9wVq8QCw3K88g8wEzrcM= -github.com/ethereum/go-ethereum v1.13.4/go.mod h1:I0U5VewuuTzvBtVzKo7b3hJzDhXOUtn9mJW7SsIPB0Q= +github.com/ethereum/go-ethereum v1.14.3 h1:5zvnAqLtnCZrU9uod1JCvHWJbPMURzYFHfc2eHz4PHA= +github.com/ethereum/go-ethereum v1.14.3/go.mod h1:1STrq471D0BQbCX9He0hUj4bHxX2k6mt5nOQJhDNOJ8= github.com/ethersphere/go-price-oracle-abi v0.2.0 h1:wtIcYLgNZHY4BjYwJCnu93SvJdVAZVvBaKinspyyHvQ= github.com/ethersphere/go-price-oracle-abi v0.2.0/go.mod h1:sI/Qj4/zJ23/b1enzwMMv0/hLTpPNVNacEwCWjo6yBk= github.com/ethersphere/go-storage-incentives-abi v0.8.6 h1:M9WwEtWoxVHKehBAoPMzQhXlzlBetuXsrGgoFvM5I8Y= @@ -247,8 +247,9 @@ github.com/ethersphere/go-sw3-abi v0.6.5/go.mod h1:BmpsvJ8idQZdYEtWnvxA8POYQ8Rl/ github.com/ethersphere/langos v1.0.0 h1:NBtNKzXTTRSue95uOlzPN4py7Aofs0xWPzyj4AI1Vcc= github.com/ethersphere/langos v1.0.0/go.mod h1:dlcN2j4O8sQ+BlCaxeBu43bgr4RQ+inJ+pHwLeZg5Tw= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= +github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= @@ -262,6 +263,10 @@ github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbS github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= +github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= +github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= @@ -284,8 +289,9 @@ github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KE github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= -github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= @@ -297,8 +303,6 @@ github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4 github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -350,8 +354,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 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/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -379,6 +383,8 @@ github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+u github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -448,13 +454,13 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= -github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= github.com/holiman/uint256 v1.2.0/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw= -github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= -github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.0.1-0.20210310174557-0ca763054c88/go.mod h1:nNs7wvRfN1eKaMknBydLNQU6146XQim8t4h+q90biWo= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= @@ -1003,8 +1009,8 @@ golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1043,8 +1049,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB 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.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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= @@ -1097,8 +1103,8 @@ golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/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.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1121,8 +1127,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ 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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1198,17 +1204,18 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1280,8 +1287,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= -golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 68cde4a951d9469b07da156b518dbd0f6a825a62 Mon Sep 17 00:00:00 2001 From: ldeffenb Date: Tue, 28 May 2024 07:39:03 -0400 Subject: [PATCH 05/41] fix: allow maxMultiplexForwards to be set to zero (#4682) --- pkg/pushsync/pushsync.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/pushsync/pushsync.go b/pkg/pushsync/pushsync.go index 90af7cfe8eb..3feeed67bf4 100644 --- a/pkg/pushsync/pushsync.go +++ b/pkg/pushsync/pushsync.go @@ -339,7 +339,7 @@ func (ps *PushSync) pushToClosest(ctx context.Context, ch swarm.Chunk, origin bo resultChan := make(chan receiptResult) - retryC := make(chan struct{}, parallelForwards) + retryC := make(chan struct{}, max(1, parallelForwards)) retry := func() { select { From 97e7ee699be3b4325a233b1ca2dc177cd88f17e1 Mon Sep 17 00:00:00 2001 From: Calin Martinconi Date: Tue, 28 May 2024 18:29:14 +0300 Subject: [PATCH 06/41] chore: bump go version to 1.22 (#4590) (#4687) --- Dockerfile | 2 +- go.mod | 14 ++++++++------ go.sum | 20 ++++++++++---------- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5aa35de1e8c..38b13602cb1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.21 AS build +FROM golang:1.22 AS build WORKDIR /src # enable modules caching in separate layer diff --git a/go.mod b/go.mod index f85bdfb247c..705c161b9f3 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/ethersphere/bee/v2 -go 1.21 +go 1.22 + +toolchain go1.22.0 require ( contrib.go.opencensus.io/exporter/prometheus v0.4.2 @@ -43,12 +45,12 @@ require ( gitlab.com/nolash/go-mockbytes v0.0.7 go.uber.org/atomic v1.11.0 go.uber.org/goleak v1.3.0 - golang.org/x/crypto v0.22.0 + golang.org/x/crypto v0.23.0 golang.org/x/exp v0.0.0-20240213143201-ec583247a57a - golang.org/x/net v0.24.0 + golang.org/x/net v0.25.0 golang.org/x/sync v0.7.0 - golang.org/x/sys v0.19.0 - golang.org/x/term v0.19.0 + golang.org/x/sys v0.20.0 + golang.org/x/term v0.20.0 golang.org/x/time v0.5.0 gopkg.in/yaml.v2 v2.4.0 resenje.org/multex v0.1.0 @@ -166,7 +168,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/text v0.15.0 // indirect golang.org/x/tools v0.20.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.57.0 // indirect diff --git a/go.sum b/go.sum index 80862fcb763..2e18ee9e929 100644 --- a/go.sum +++ b/go.sum @@ -1009,8 +1009,8 @@ golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1103,8 +1103,8 @@ golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1209,13 +1209,13 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1224,8 +1224,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 79a7991e0c1848cebc9e1a1d9d840c73d48812fd Mon Sep 17 00:00:00 2001 From: Acha Bill <57879913+acha-bill@users.noreply.github.com> Date: Thu, 30 May 2024 00:51:59 +0100 Subject: [PATCH 07/41] feat: remove debug api (#4674) --- .github/workflows/docs.yaml | 2 +- .github/workflows/release.yaml | 4 +- .goreleaser.yml | 5 - CODING.md | 4 +- CONTRIBUTING.md | 2 +- Dockerfile | 2 +- Dockerfile.ci | 2 +- Dockerfile.goreleaser | 2 +- Dockerfile.scratch | 2 +- Makefile | 2 - README.md | 3 +- cmd/bee/cmd/cmd.go | 4 - cmd/bee/cmd/start.go | 7 - cmd/bee/cmd/start_dev.go | 8 - openapi/SwarmCommon.yaml | 4 - openapi/SwarmDebug.yaml | 1170 --------------------------- packaging/bee.yaml | 4 - packaging/docker/README.md | 2 +- packaging/docker/docker-compose.yml | 3 - packaging/docker/env | 4 - packaging/homebrew-amd64/bee.yaml | 4 - packaging/homebrew-arm64/bee.yaml | 4 - packaging/scoop/bee.yaml | 4 - pkg/api/accounting_test.go | 2 - pkg/api/api.go | 1 - pkg/api/api_test.go | 30 +- pkg/api/balances_test.go | 14 +- pkg/api/chequebook_test.go | 17 +- pkg/api/debugstorage_test.go | 3 +- pkg/api/health.go | 14 +- pkg/api/health_test.go | 28 +- pkg/api/logger_test.go | 8 +- pkg/api/p2p_test.go | 2 - pkg/api/peer_test.go | 10 +- pkg/api/pingpong_test.go | 3 +- pkg/api/postage_test.go | 43 +- pkg/api/redistribution_test.go | 2 - pkg/api/router.go | 11 +- pkg/api/settlements_test.go | 8 +- pkg/api/staking_test.go | 22 +- pkg/api/status_test.go | 4 +- pkg/api/topology_test.go | 2 +- pkg/api/transaction_test.go | 10 - pkg/api/version.go | 1 - pkg/api/wallet_test.go | 15 +- pkg/api/welcome_message_test.go | 5 +- pkg/node/devnode.go | 58 +- pkg/node/node.go | 131 +-- pkg/topology/kademlia/doc.go | 2 +- 49 files changed, 84 insertions(+), 1610 deletions(-) delete mode 100644 openapi/SwarmDebug.yaml diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 4008e3a7b4e..83097774128 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -40,7 +40,7 @@ jobs: if: steps.checkdocs.outputs.build_docs == 'true' uses: acud/openapi-dockerized@v1 with: - build-roots: 'openapi/Swarm.yaml openapi/SwarmDebug.yaml' + build-roots: 'openapi/Swarm.yaml' env: AWS_ACCESS_KEY_ID: ${{ secrets.DO_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.DO_AWS_SECRET_ACCESS_KEY }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 7d7edc9e0b4..75eec69aab7 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -33,10 +33,9 @@ jobs: env: GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - - name: Set the API and debug API versions + - name: Set the API version run: | echo "BEE_API_VERSION=$(grep '^ version:' openapi/Swarm.yaml | awk '{print $2}')" >> $GITHUB_ENV - echo "BEE_DEBUG_API_VERSION=$(grep '^ version:' openapi/SwarmDebug.yaml | awk '{print $2}')" >> $GITHUB_ENV - name: Run GoReleaser uses: goreleaser/goreleaser-action@v5 with: @@ -50,4 +49,3 @@ jobs: GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} GPG_FINGERPRINT: ${{ secrets.GPG_FINGERPRINT }} BEE_API_VERSION: ${{ env.BEE_API_VERSION }} - BEE_DEBUG_API_VERSION: ${{ env.BEE_DEBUG_API_VERSION }} diff --git a/.goreleaser.yml b/.goreleaser.yml index 0f8844cbfdc..d84f3b1e8cd 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -19,7 +19,6 @@ builds: - -X github.com/ethersphere/bee/v2.commitHash={{ .ShortCommit }} - -X github.com/ethersphere/bee/v2.commitTime={{ .CommitTimestamp }} - -X github.com/ethersphere/bee/v2/pkg/api.Version={{ .Env.BEE_API_VERSION }} - - -X github.com/ethersphere/bee/v2/pkg/debugapi.Version={{ .Env.BEE_DEBUG_API_VERSION }} env: - CGO_ENABLED=0 goos: @@ -43,7 +42,6 @@ builds: - -X github.com/ethersphere/bee/v2.commitHash={{ .ShortCommit }} - -X github.com/ethersphere/bee/v2.commitTime={{ .CommitTimestamp }} - -X github.com/ethersphere/bee/v2/pkg/api.Version={{ .Env.BEE_API_VERSION }} - - -X github.com/ethersphere/bee/v2/pkg/debugapi.Version={{ .Env.BEE_DEBUG_API_VERSION }} env: - CGO_ENABLED=0 goos: @@ -69,7 +67,6 @@ builds: - -X github.com/ethersphere/bee/v2.commitHash={{ .ShortCommit }} - -X github.com/ethersphere/bee/v2.commitTime={{ .CommitTimestamp }} - -X github.com/ethersphere/bee/v2/pkg/api.Version={{ .Env.BEE_API_VERSION }} - - -X github.com/ethersphere/bee/v2/pkg/debugapi.Version={{ .Env.BEE_DEBUG_API_VERSION }} env: - CGO_ENABLED=0 goos: @@ -89,7 +86,6 @@ builds: - -X github.com/ethersphere/bee/v2.commitHash={{ .ShortCommit }} - -X github.com/ethersphere/bee/v2.commitTime={{ .CommitTimestamp }} - -X github.com/ethersphere/bee/v2/pkg/api.Version={{ .Env.BEE_API_VERSION }} - - -X github.com/ethersphere/bee/v2/pkg/debugapi.Version={{ .Env.BEE_DEBUG_API_VERSION }} env: - CGO_ENABLED=0 goos: @@ -108,7 +104,6 @@ builds: - -X github.com/ethersphere/bee/v2.commitHash={{ .ShortCommit }} - -X github.com/ethersphere/bee/v2.commitTime={{ .CommitTimestamp }} - -X github.com/ethersphere/bee/v2/pkg/api.Version={{ .Env.BEE_API_VERSION }} - - -X github.com/ethersphere/bee/v2/pkg/debugapi.Version={{ .Env.BEE_DEBUG_API_VERSION }} env: - CGO_ENABLED=0 goos: diff --git a/CODING.md b/CODING.md index 98efe16e63f..f72712ee8fe 100644 --- a/CODING.md +++ b/CODING.md @@ -206,9 +206,9 @@ A value of `all` will enable the highest verbosity of V-level. Examples: -`curl -XPUT http://localhost:1635/loggers/bm9kZS8q/none` - will disable all loggers; `bm9kZS8q` is base64 encoded `node/*` regular expression. +`curl -XPUT http://localhost:1633/loggers/bm9kZS8q/none` - will disable all loggers; `bm9kZS8q` is base64 encoded `node/*` regular expression. -`curl -XPUT http://localhost:1635/loggers/bm9kZS9hcGlbMV1bXT4-ODI0NjM0OTMzMjU2/error` - will set the verbosity of the logger with the subsystem `node/api[1][]>>824634933256` to `error`. +`curl -XPUT http://localhost:1633/loggers/bm9kZS9hcGlbMV1bXT4-ODI0NjM0OTMzMjU2/error` - will set the verbosity of the logger with the subsystem `node/api[1][]>>824634933256` to `error`. ## Commit Messages diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ba690740ec1..88973b5ffe2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,7 +35,7 @@ These are work items that are good if you're contributing to the codebase for th - Performance optimizations - The best way to propose any optimizations would be to provide the relevant data to describe the problem and then also the same data after the optimizations are done. Keep in mind, Bee nodes work in a distributed system, so changes that would seem good locally may not hold in some cases. The Bee client in debug mode can show you metrics as well as pprof information. This can be used to demonstrate the optimizations. + The best way to propose any optimizations would be to provide the relevant data to describe the problem and then also the same data after the optimizations are done. Keep in mind, Bee nodes work in a distributed system, so changes that would seem good locally may not hold in some cases. The Bee client can show you metrics as well as pprof information. This can be used to demonstrate the optimizations. - Concurrency related optimizations diff --git a/Dockerfile b/Dockerfile index 38b13602cb1..e0f0738de6c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ RUN mkdir -p /home/bee/.bee && chown 999:999 /home/bee/.bee COPY --from=build /src/dist/bee /usr/local/bin/bee -EXPOSE 1633 1634 1635 +EXPOSE 1633 1634 USER bee WORKDIR /home/bee VOLUME /home/bee/.bee diff --git a/Dockerfile.ci b/Dockerfile.ci index 2c12b44ad4a..b2f962ddc13 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -8,7 +8,7 @@ RUN addgroup --system bee --gid 998; \ COPY bee /bee -EXPOSE 1633 1634 1635 +EXPOSE 1633 1634 USER bee WORKDIR /home/bee diff --git a/Dockerfile.goreleaser b/Dockerfile.goreleaser index 907c29ed0ab..cd1fe90dc0d 100644 --- a/Dockerfile.goreleaser +++ b/Dockerfile.goreleaser @@ -14,7 +14,7 @@ RUN mkdir -p /home/bee/.bee && chown 999:999 /home/bee/.bee COPY bee /usr/local/bin/bee -EXPOSE 1633 1634 1635 +EXPOSE 1633 1634 USER bee WORKDIR /home/bee VOLUME /home/bee/.bee diff --git a/Dockerfile.scratch b/Dockerfile.scratch index 85905fd691b..40b0bec6c94 100644 --- a/Dockerfile.scratch +++ b/Dockerfile.scratch @@ -17,7 +17,7 @@ COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs COPY --from=0 /etc/passwd /etc/passwd COPY --from=0 /home /home -EXPOSE 1633 1634 1635 +EXPOSE 1633 1634 USER bee WORKDIR /home/bee VOLUME /home/bee/.bee diff --git a/Makefile b/Makefile index 46da4995b14..9aa79e4f9cd 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,6 @@ REACHABILITY_OVERRIDE_PUBLIC ?= false BATCHFACTOR_OVERRIDE_PUBLIC ?= 5 BEE_API_VERSION ?= "$(shell grep '^ version:' openapi/Swarm.yaml | awk '{print $$2}')" -BEE_DEBUG_API_VERSION ?= "$(shell grep '^ version:' openapi/SwarmDebug.yaml | awk '{print $$2}')" VERSION ?= "$(shell git describe --tags --abbrev=0 | cut -c2-)" COMMIT_HASH ?= "$(shell git describe --long --dirty --always --match "" || true)" @@ -24,7 +23,6 @@ LDFLAGS ?= -s -w \ -X github.com/ethersphere/bee/v2.commitHash="$(COMMIT_HASH)" \ -X github.com/ethersphere/bee/v2.commitTime="$(COMMIT_TIME)" \ -X github.com/ethersphere/bee/v2/pkg/api.Version="$(BEE_API_VERSION)" \ --X github.com/ethersphere/bee/v2/pkg/api.DebugVersion="$(BEE_DEBUG_API_VERSION)" \ -X github.com/ethersphere/bee/v2/pkg/p2p/libp2p.reachabilityOverridePublic="$(REACHABILITY_OVERRIDE_PUBLIC)" \ -X github.com/ethersphere/bee/v2/pkg/postage/listener.batchFactorOverridePublic="$(BATCHFACTOR_OVERRIDE_PUBLIC)" diff --git a/README.md b/README.md index c5fb6fdf04c..c310e4fea89 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ [![codecov](https://codecov.io/gh/ethersphere/bee/branch/master/graph/badge.svg?token=63RNRLO3RU)](https://codecov.io/gh/ethersphere/bee) [![Go Report Card](https://goreportcard.com/badge/github.com/ethersphere/bee)](https://goreportcard.com/report/github.com/ethersphere/bee) [![API OpenAPI Specs](https://img.shields.io/badge/openapi-api-blue)](https://docs.ethswarm.org/api/) -[![Debug API OpenAPI Specs](https://img.shields.io/badge/openapi-debugapi-lightblue)](https://docs.ethswarm.org/debug-api/) ![Docker Pulls](https://img.shields.io/docker/pulls/ethersphere/bee) ![GitHub all releases](https://img.shields.io/github/downloads/ethersphere/bee/total) ![GitHub](https://img.shields.io/github/license/ethersphere/bee) @@ -27,7 +26,7 @@ There are two versioning schemes used in Bee that you should be aware of. The ma strict Semantic Versioning. Bee hosts different peer-to-peer wire protocol implementations and individual protocol breaking changes would necessitate a bump in the major part of the version. Breaking changes are expected with bumps of the minor version component. New (backward-compatible) features and bug fixes are expected with a bump of the patch component. Major version bumps are reserved for significant changes in Swarm's incentive structure. -The second set of versions that are important are the Bee's API versions (denoted in our [Bee](https://github.com/ethersphere/bee/blob/master/openapi/Swarm.yaml) and [Bee Debug](https://github.com/ethersphere/bee/blob/master/openapi/SwarmDebug.yaml) OpenAPI specifications). These versions **do follow** +The second is the Bee's API version (denoted in our [Bee](https://github.com/ethersphere/bee/blob/master/openapi/Swarm.yaml) OpenAPI specifications). This version **follows** Semantic Versioning and hence you should follow these for breaking changes. ## Contributing diff --git a/cmd/bee/cmd/cmd.go b/cmd/bee/cmd/cmd.go index e02e3ff6f3f..964bb0754c1 100644 --- a/cmd/bee/cmd/cmd.go +++ b/cmd/bee/cmd/cmd.go @@ -34,8 +34,6 @@ const ( optionNameP2PAddr = "p2p-addr" optionNameNATAddr = "nat-addr" optionNameP2PWSEnable = "p2p-ws-enable" - optionNameDebugAPIEnable = "debug-api-enable" - optionNameDebugAPIAddr = "debug-api-addr" optionNameBootnodes = "bootnode" optionNameNetworkID = "network-id" optionWelcomeMessage = "welcome-message" @@ -256,8 +254,6 @@ func (c *command) setAllFlags(cmd *cobra.Command) { cmd.Flags().String(optionNameNATAddr, "", "NAT exposed address") cmd.Flags().Bool(optionNameP2PWSEnable, false, "enable P2P WebSocket transport") cmd.Flags().StringSlice(optionNameBootnodes, []string{""}, "initial nodes to connect to") - cmd.Flags().Bool(optionNameDebugAPIEnable, false, "enable debug HTTP API") - cmd.Flags().String(optionNameDebugAPIAddr, ":1635", "debug HTTP API listen address") cmd.Flags().Uint64(optionNameNetworkID, chaincfg.Mainnet.NetworkID, "ID of the Swarm network") cmd.Flags().StringSlice(optionCORSAllowedOrigins, []string{}, "origins with CORS headers enabled") cmd.Flags().Bool(optionNameTracingEnabled, false, "enable tracing") diff --git a/cmd/bee/cmd/start.go b/cmd/bee/cmd/start.go index e659d125116..d69be019c05 100644 --- a/cmd/bee/cmd/start.go +++ b/cmd/bee/cmd/start.go @@ -73,7 +73,6 @@ func (c *command) initStartCmd() (err error) { fmt.Print(beeWelcomeMessage) fmt.Printf("\n\nversion: %v - planned to be supported until %v, please follow https://ethswarm.org/\n\n", bee.Version, endSupportDate()) - fmt.Printf("DEPRECATION NOTICE:\nThe Debug API is deprecated and will be removed in the next release, version [2.2.0].\nPlease update your integrations to use the main Bee API to avoid service disruptions.\n\n") logger.Info("bee version", "version", bee.Version) go startTimeBomb(logger) @@ -214,11 +213,6 @@ func buildBeeNode(ctx context.Context, c *command, cmd *cobra.Command, logger lo } } - debugAPIAddr := c.config.GetString(optionNameDebugAPIAddr) - if !c.config.GetBool(optionNameDebugAPIEnable) { - debugAPIAddr = "" - } - signerConfig, err := c.configureSigner(cmd, logger) if err != nil { return nil, err @@ -301,7 +295,6 @@ func buildBeeNode(ctx context.Context, c *command, cmd *cobra.Command, logger lo DBWriteBufferSize: c.config.GetUint64(optionNameDBWriteBufferSize), DBDisableSeeksCompaction: c.config.GetBool(optionNameDBDisableSeeksCompaction), APIAddr: c.config.GetString(optionNameAPIAddr), - DebugAPIAddr: debugAPIAddr, Addr: c.config.GetString(optionNameP2PAddr), NATAddr: c.config.GetString(optionNameNATAddr), EnableWS: c.config.GetBool(optionNameP2PWSEnable), diff --git a/cmd/bee/cmd/start_dev.go b/cmd/bee/cmd/start_dev.go index a372094bf20..189363a455d 100644 --- a/cmd/bee/cmd/start_dev.go +++ b/cmd/bee/cmd/start_dev.go @@ -57,15 +57,9 @@ func (c *command) initStartDevCmd() (err error) { fmt.Println("Starting in development mode") fmt.Println() - debugAPIAddr := c.config.GetString(optionNameDebugAPIAddr) - if !c.config.GetBool(optionNameDebugAPIEnable) { - debugAPIAddr = "" - } - // generate signer in here b, err := node.NewDevBee(logger, &node.DevOptions{ APIAddr: c.config.GetString(optionNameAPIAddr), - DebugAPIAddr: debugAPIAddr, Logger: logger, DBOpenFilesLimit: c.config.GetUint64(optionNameDBOpenFilesLimit), DBBlockCacheCapacity: c.config.GetUint64(optionNameDBBlockCacheCapacity), @@ -140,9 +134,7 @@ func (c *command) initStartDevCmd() (err error) { }, } - cmd.Flags().Bool(optionNameDebugAPIEnable, true, "enable debug HTTP API") cmd.Flags().String(optionNameAPIAddr, ":1633", "HTTP API listen address") - cmd.Flags().String(optionNameDebugAPIAddr, ":1635", "debug HTTP API listen address") cmd.Flags().String(optionNameVerbosity, "info", "log verbosity level 0=silent, 1=error, 2=warn, 3=info, 4=debug, 5=trace") cmd.Flags().Uint64(optionNameDevReserveCapacity, 4194304, "cache reserve capacity") cmd.Flags().StringSlice(optionCORSAllowedOrigins, []string{}, "origins with CORS headers enabled") diff --git a/openapi/SwarmCommon.yaml b/openapi/SwarmCommon.yaml index 42e3f79d46f..b32fa88c8a4 100644 --- a/openapi/SwarmCommon.yaml +++ b/openapi/SwarmCommon.yaml @@ -439,10 +439,6 @@ components: type: string default: "0.0.0" description: The default value is set in case the bee binary was not build correctly. - debugApiVersion: - type: string - default: "0.0.0" - description: The default value is set in case the bee binary was not build correctly. PostageBatch: type: object diff --git a/openapi/SwarmDebug.yaml b/openapi/SwarmDebug.yaml deleted file mode 100644 index 986456cb527..00000000000 --- a/openapi/SwarmDebug.yaml +++ /dev/null @@ -1,1170 +0,0 @@ -openapi: 3.0.3 -info: - version: 4.1.2 - title: Bee Debug API - description: "A list of the currently provided debug interfaces to interact with the bee node" - -security: - - {} - -externalDocs: - description: Browse the documentation @ the Swarm Docs - url: "https://docs.ethswarm.org" - -servers: - - url: "http://{apiRoot}:{port}" - variables: - apiRoot: - default: "localhost" - description: Base address of the local bee node debug API - port: - default: "1635" - description: Service port provided in bee node config - -paths: - "/addresses": - get: - summary: Get overlay and underlay addresses of the node - tags: - - Connectivity - responses: - "200": - description: Own node underlay and overlay addresses - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/Addresses" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/accounting": - get: - summary: Get all accounting associated values with all known peers - tags: - - Balance - responses: - "200": - description: Own accounting associated values with all known peers - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/PeerAccountingData" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/balances": - get: - summary: Get the balances with all known peers including prepaid services - tags: - - Balance - responses: - "200": - description: Own balances with all known peers - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/Balances" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/balances/{address}": - get: - summary: Get the balances with a specific peer including prepaid services - tags: - - Balance - parameters: - - in: path - name: address - schema: - $ref: "SwarmCommon.yaml#/components/schemas/SwarmAddress" - required: true - description: Swarm address of peer - responses: - "200": - description: Balance with the specific peer - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/Balance" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/blocklist": - get: - summary: Get a list of blocklisted peers - tags: - - Connectivity - responses: - "200": - description: Returns overlay addresses of blocklisted peers - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/Peers" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/consumed": - get: - summary: Get the past due consumption balances with all known peers - tags: - - Balance - responses: - "200": - description: Own past due consumption balances with all known peers - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/Balances" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/consumed/{address}": - get: - summary: Get the past due consumption balance with a specific peer - tags: - - Balance - parameters: - - in: path - name: address - schema: - $ref: "SwarmCommon.yaml#/components/schemas/SwarmAddress" - required: true - description: Swarm address of peer - responses: - "200": - description: Past-due consumption balance with the specific peer - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/Balance" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/chequebook/address": - get: - summary: Get the address of the chequebook contract used - tags: - - Chequebook - responses: - "200": - description: Ethereum address of chequebook contract - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/ChequebookAddress" - - "/chequebook/balance": - get: - summary: Get the balance of the chequebook - tags: - - Chequebook - responses: - "200": - description: Balance of the chequebook - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/ChequebookBalance" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/chunks/{address}": - get: - summary: Check if chunk at address exists locally - tags: - - Chunk - parameters: - - in: path - name: address - schema: - $ref: "SwarmCommon.yaml#/components/schemas/SwarmAddress" - required: true - description: Swarm address of chunk - responses: - "200": - description: Chunk exists - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/Response" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - "404": - $ref: "SwarmCommon.yaml#/components/responses/404" - default: - description: Default response - - "/connect/{multiAddress}": - post: - summary: Connect to address - tags: - - Connectivity - parameters: - - in: path - allowReserved: true - name: multiAddress - schema: - $ref: "SwarmCommon.yaml#/components/schemas/MultiAddress" - required: true - description: Underlay address of peer - responses: - "200": - description: Returns overlay address of connected peer - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/Address" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/reservestate": - get: - summary: Get reserve state - tags: - - Status - responses: - "200": - description: Reserve State - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/ReserveState" - default: - description: Default response - - "/chainstate": - get: - summary: Get chain state - tags: - - Status - responses: - "200": - description: Chain State - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/ChainState" - default: - description: Default response - - "/node": - get: - summary: Get information about the node - tags: - - Status - responses: - "200": - description: Information about the node - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/Node" - default: - description: Default response - - "/peers": - get: - summary: Get a list of peers - tags: - - Connectivity - responses: - "200": - description: Returns overlay addresses of connected peers - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/Peers" - default: - description: Default response - - "/peers/{address}": - delete: - summary: Remove peer - tags: - - Connectivity - parameters: - - in: path - name: address - schema: - $ref: "SwarmCommon.yaml#/components/schemas/SwarmAddress" - required: true - description: Swarm address of peer - responses: - "200": - description: Disconnected peer - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/Response" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/pingpong/{address}": - post: - summary: Try connection to node - tags: - - Connectivity - parameters: - - in: path - name: address - schema: - $ref: "SwarmCommon.yaml#/components/schemas/SwarmAddress" - required: true - description: Swarm address of peer - responses: - "200": - description: Returns round trip time for given peer - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/RttMs" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - "404": - $ref: "SwarmCommon.yaml#/components/responses/404" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/health": - get: - summary: Get node overall health Status - description: | - Health Status will indicate node healthiness. - - If node is unhealthy please check node logs for errors. - tags: - - Status - responses: - "200": - description: Health Status of node - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/HealthStatus" - default: - description: Default response - - "/readiness": - get: - summary: Readiness endpoint indicates if node is ready to start accepting traffic - tags: - - Status - responses: - "200": - description: Indicates that node is ready - $ref: "SwarmCommon.yaml#/components/responses/200" - "400": - description: Indicates that node is not ready - $ref: "SwarmCommon.yaml#/components/responses/400" - default: - description: Default response - - "/settlements/{address}": - get: - summary: Get amount of sent and received from settlements with a peer - tags: - - Settlements - parameters: - - in: path - name: address - schema: - $ref: "SwarmCommon.yaml#/components/schemas/SwarmAddress" - required: true - description: Swarm address of peer - responses: - "200": - description: Amount of sent or received from settlements with a peer - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/Settlement" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/settlements": - get: - summary: Get settlements with all known peers and total amount sent or received - tags: - - Settlements - responses: - "200": - description: Settlements with all known peers and total amount sent or received - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/Settlements" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/timesettlements": - get: - summary: Get time based settlements with all known peers and total amount sent or received - tags: - - Settlements - responses: - "200": - description: Time based settlements with all known peers and total amount sent or received - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/Settlements" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/topology": - get: - description: Get topology of known network - tags: - - Connectivity - responses: - "200": - description: Swarm topology of the bee node - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/BzzTopology" - - "/welcome-message": - get: - summary: Get configured P2P welcome message - tags: - - Connectivity - responses: - "200": - description: Welcome message - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/WelcomeMessage" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - post: - summary: Set P2P welcome message - tags: - - Connectivity - requestBody: - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/WelcomeMessage" - responses: - "200": - description: OK - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/HealthStatus" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/chequebook/cashout/{peer-id}": - get: - summary: Get last cashout action for the peer - parameters: - - in: path - name: peer-id - schema: - $ref: "SwarmCommon.yaml#/components/schemas/SwarmAddress" - required: true - description: Swarm address of peer - tags: - - Chequebook - responses: - "200": - description: Cashout status - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/SwapCashoutStatus" - "404": - $ref: "SwarmCommon.yaml#/components/responses/404" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - post: - summary: Cashout the last cheque for the peer - parameters: - - in: path - name: peer-id - schema: - $ref: "SwarmCommon.yaml#/components/schemas/SwarmAddress" - required: true - description: Swarm address of peer - - $ref: "SwarmCommon.yaml#/components/parameters/GasPriceParameter" - - $ref: "SwarmCommon.yaml#/components/parameters/GasLimitParameter" - tags: - - Chequebook - responses: - "201": - description: OK - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/TransactionResponse" - "404": - $ref: "SwarmCommon.yaml#/components/responses/404" - "429": - $ref: "SwarmCommon.yaml#/components/responses/429" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/chequebook/cheque/{peer-id}": - get: - summary: Get last cheques for the peer - parameters: - - in: path - name: peer-id - schema: - $ref: "SwarmCommon.yaml#/components/schemas/SwarmAddress" - required: true - description: Swarm address of peer - tags: - - Chequebook - responses: - "200": - description: Last cheques - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/ChequePeerResponse" - "404": - $ref: "SwarmCommon.yaml#/components/responses/404" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/chequebook/cheque": - get: - summary: Get last cheques for all peers - tags: - - Chequebook - responses: - "200": - description: Last cheques - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/ChequeAllPeersResponse" - "404": - $ref: "SwarmCommon.yaml#/components/responses/404" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/chequebook/deposit": - post: - summary: Deposit tokens from overlay address into chequebook - parameters: - - in: query - name: amount - schema: - type: integer - required: true - description: amount of tokens to deposit - - $ref: "SwarmCommon.yaml#/components/parameters/GasPriceParameter" - tags: - - Chequebook - responses: - "201": - description: Transaction hash of the deposit transaction - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/TransactionResponse" - "400": - $ref: "SwarmCommon.yaml#/components/responses/404" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/chequebook/withdraw": - post: - summary: Withdraw tokens from the chequebook to the overlay address - parameters: - - in: query - name: amount - schema: - type: integer - required: true - description: amount of tokens to withdraw - - $ref: "SwarmCommon.yaml#/components/parameters/GasPriceParameter" - tags: - - Chequebook - responses: - "201": - description: Transaction hash of the withdraw transaction - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/TransactionResponse" - "400": - $ref: "SwarmCommon.yaml#/components/responses/404" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/redistributionstate": - get: - summary: Get current status of node in redistribution game - tags: - - RedistributionState - responses: - "200": - description: Redistribution status info - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/RedistributionStatusResponse" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - "/wallet": - get: - summary: Get wallet balance for BZZ and xDai - tags: - - Wallet - responses: - "200": - description: Wallet balance info - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/WalletResponse" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - "/wallet/withdraw/{coin}": - post: - summary: Allows withdrawals of BZZ or xDAI to provided (whitelisted) address - tags: - - Wallet - parameters: - - in: query - name: amount - required: true - schema: - $ref: "SwarmCommon.yaml#/components/schemas/BigInt" - - in: query - name: address - required: true - schema: - $ref: "SwarmCommon.yaml#/components/schemas/EthereumAddress" - - in: path - name: coin - required: true - schema: - $ref: "SwarmCommon.yaml#/components/schemas/SwarmAddress" - responses: - "200": - content: - application/json: - schema: - $ref: 'SwarmCommon.yaml#/components/schemas/WalletTxResponse' - description: OK - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - description: Amount greater than ballance or coin is other than BZZ/xDAI - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/transactions": - get: - summary: Get list of pending transactions - tags: - - Transaction - responses: - "200": - description: List of pending transactions - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/PendingTransactionsResponse" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/transactions/{txHash}": - get: - summary: Get information about a sent transaction - parameters: - - in: path - name: txHash - schema: - $ref: "SwarmCommon.yaml#/components/schemas/TransactionHash" - required: true - description: Hash of the transaction - tags: - - Transaction - responses: - "200": - description: Get info about transaction - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/TransactionInfo" - "404": - $ref: "SwarmCommon.yaml#/components/responses/404" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - post: - summary: Rebroadcast existing transaction - parameters: - - in: path - name: txHash - schema: - $ref: "SwarmCommon.yaml#/components/schemas/TransactionHash" - required: true - description: Hash of the transaction - tags: - - Transaction - responses: - "200": - description: Hash of the transaction - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/TransactionResponse" - "404": - $ref: "SwarmCommon.yaml#/components/responses/404" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - delete: - summary: Cancel existing transaction - parameters: - - in: path - name: txHash - schema: - $ref: "SwarmCommon.yaml#/components/schemas/TransactionHash" - required: true - description: Hash of the transaction - - $ref: "SwarmCommon.yaml#/components/parameters/GasPriceParameter" - tags: - - Transaction - responses: - "200": - description: Hash of the transaction - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/TransactionResponse" - "404": - $ref: "SwarmCommon.yaml#/components/responses/404" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/stamps": - get: - summary: Get stamps for this node - tags: - - Postage Stamps - responses: - "200": - description: Returns an array of postage batches. - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/DebugPostageBatchesResponse" - "404": - $ref: "SwarmCommon.yaml#/components/responses/404" - - default: - description: Default response - - "/stamps/{batch_id}": - parameters: - - in: path - name: batch_id - schema: - $ref: "SwarmCommon.yaml#/components/schemas/BatchID" - required: true - description: Swarm address of the stamp - get: - summary: Get an individual postage batch status - tags: - - Postage Stamps - responses: - "200": - description: Returns an individual postage batch state - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/DebugPostageBatch" - "404": - $ref: "SwarmCommon.yaml#/components/responses/404" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - default: - description: Default response - - "/stamps/{batch_id}/buckets": - parameters: - - in: path - name: batch_id - schema: - $ref: "SwarmCommon.yaml#/components/schemas/BatchID" - required: true - description: Swarm address of the stamp - get: - summary: Get extended bucket data of a batch - tags: - - Postage Stamps - responses: - "200": - description: Returns extended bucket data of the provided batch ID - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/PostageStampBuckets" - "404": - $ref: "SwarmCommon.yaml#/components/responses/404" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - default: - description: Default response - - "/stamps/{amount}/{depth}": - post: - summary: Buy a new postage batch. - description: Be aware, this endpoint creates an on-chain transactions and transfers BZZ from the node's Ethereum account and hence directly manipulates the wallet balance! - tags: - - Postage Stamps - parameters: - - in: path - name: amount - schema: - $ref: "SwarmCommon.yaml#/components/schemas/BigInt" - required: true - description: Amount of BZZ added that the postage batch will have. - - in: path - name: depth - schema: - type: integer - required: true - description: Batch depth which specifies how many chunks can be signed with the batch. It is a logarithm. Must be higher than default bucket depth (16) - - in: query - name: label - schema: - type: string - required: false - description: An optional label for this batch - - in: header - name: immutable - schema: - type: boolean - required: false - responses: - "201": - description: Returns the newly created postage batch ID - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/BatchIDResponse" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - "429": - $ref: "SwarmCommon.yaml#/components/responses/429" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/stamps/topup/{batch_id}/{amount}": - patch: - summary: Top up an existing postage batch. - description: Be aware, this endpoint creates on-chain transactions and transfers BZZ from the node's Ethereum account and hence directly manipulates the wallet balance! - tags: - - Postage Stamps - parameters: - - in: path - name: batch_id - schema: - $ref: "SwarmCommon.yaml#/components/schemas/BatchID" - required: true - description: Batch ID to top up - - in: path - name: amount - schema: - type: integer - required: true - description: Amount of BZZ per chunk to top up to an existing postage batch. - responses: - "202": - description: Returns the postage batch ID that was topped up - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/BatchIDResponse" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - "429": - $ref: "SwarmCommon.yaml#/components/responses/429" - "402": - $ref: "SwarmCommon.yaml#/components/responses/402" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/stamps/dilute/{batch_id}/{depth}": - patch: - summary: Dilute an existing postage batch. - description: Be aware, this endpoint creates on-chain transactions and transfers BZZ from the node's Ethereum account and hence directly manipulates the wallet balance! - tags: - - Postage Stamps - parameters: - - in: path - name: batch_id - schema: - $ref: "SwarmCommon.yaml#/components/schemas/BatchID" - required: true - description: Batch ID to dilute - - in: path - name: depth - schema: - type: integer - required: true - description: New batch depth. Must be higher than the previous depth. - responses: - "202": - description: Returns the postage batch ID that was diluted. - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/BatchIDResponse" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - "429": - $ref: "SwarmCommon.yaml#/components/responses/429" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/batches": - get: - summary: Get all globally available batches that were purchased by all nodes. - tags: - - Postage Stamps - responses: - "200": - description: Returns an array of all available and currently valid postage batches. - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/DebugPostageAllBatchesResponse" - - default: - description: Default response - - "/stake/{amount}": - post: - summary: Deposit some amount for staking. - description: Be aware, this endpoint creates an on-chain transactions and transfers BZZ from the node's Ethereum account and hence directly manipulates the wallet balance. - tags: - - Staking - parameters: - - in: path - name: amount - schema: - type: string - description: Amount of BZZ added that will be deposited for staking. - - $ref: "SwarmCommon.yaml#/components/parameters/GasPriceParameter" - - $ref: "SwarmCommon.yaml#/components/parameters/GasLimitParameter" - responses: - "200": - $ref: "SwarmCommon.yaml#/components/schemas/StakeDepositResponse" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/stake": - get: - summary: Get the staked amount. - description: This endpoint fetches the staked amount from the blockchain. - tags: - - Staking - responses: - "200": - $ref: "SwarmCommon.yaml#/components/schemas/GetStakeResponse" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - delete: - summary: Withdraw all staked amount. - description: Be aware, this endpoint creates an on-chain transactions and transfers BZZ from the node's Ethereum account and hence directly manipulates the wallet balance. - tags: - - Staking - parameters: - - $ref: "SwarmCommon.yaml#/components/parameters/GasPriceParameter" - - $ref: "SwarmCommon.yaml#/components/parameters/GasLimitParameter" - responses: - "200": - $ref: "SwarmCommon.yaml#/components/schemas/WithdrawAllStakeResponse" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/loggers": - get: - summary: Get all available loggers. - tags: - - Logging - responses: - "200": - description: Returns an array of all available loggers, also represented in short form in a tree. - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/LoggerResponse" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - default: - description: Default response - - "/loggers/{exp}": - get: - summary: Get all available loggers that match the specified expression. - parameters: - - in: path - name: exp - schema: - $ref: "SwarmCommon.yaml#/components/schemas/LoggerExp" - required: true - description: Regular expression or a subsystem that matches the logger(s). - tags: - - Logging - responses: - "200": - description: Returns an array of all available loggers that matches given expression, also represented in short form in a tree. - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/LoggerResponse" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - default: - description: Default response - put: - summary: Set logger(s) verbosity level. - parameters: - - in: path - name: exp - schema: - $ref: "SwarmCommon.yaml#/components/schemas/LoggerExp" - required: true - description: Regular expression or a subsystem that matches the logger(s). - tags: - - Logging - responses: - "200": - description: The verbosity was changed successfully. - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - default: - description: Default response - - "/status": - get: - summary: Get the current status snapshot of this node. - tags: - - Node Status - responses: - "200": - description: Returns the current node status snapshot. - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/StatusSnapshotResponse" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - default: - description: Default response. - - "/status/peers": - get: - summary: Get the current status snapshot of this node connected peers. - tags: - - Node Status - responses: - "200": - description: Returns the status snapshot of this node connected peers - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/StatusResponse" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - default: - description: Default response. diff --git a/packaging/bee.yaml b/packaging/bee.yaml index 560352926e1..6db6944b84c 100644 --- a/packaging/bee.yaml +++ b/packaging/bee.yaml @@ -24,10 +24,6 @@ data-dir: /var/lib/bee # db-write-buffer-size: 33554432 ## disables db compactions triggered by seeks # db-disable-seeks-compaction: false -## debug HTTP API listen address (default ":1635") -debug-api-addr: 127.0.0.1:1635 -## enable debug HTTP API -debug-api-enable: true ## cause the node to start in full mode # full-node: false ## NAT exposed address diff --git a/packaging/docker/README.md b/packaging/docker/README.md index cd95f1d131f..bce886018ae 100644 --- a/packaging/docker/README.md +++ b/packaging/docker/README.md @@ -43,7 +43,7 @@ docker-compose pull && docker-compose up -d ## Running multiple Bee nodes It is easy to run multiple bee nodes with docker compose by adding more services to `docker-compose.yaml` To do so, open `docker-compose.yaml`, copy lines 4-54 and past this after line 54 (whole bee-1 section). -In the copied lines, replace all occurrences of `bee-1` with `bee-2` and adjust the `API_ADDR` and `P2P_ADDR` and `DEBUG_API_ADDR` to respectively `1733`, `1734` and `127.0.0.1:1735` +In the copied lines, replace all occurrences of `bee-1` with `bee-2` and adjust the `API_ADDR` and `P2P_ADDR` to respectively `1733`, `1734.` Lastly, add your newly configured services under `volumes` (last lines), such that it looks like: ```yaml volumes: diff --git a/packaging/docker/docker-compose.yml b/packaging/docker/docker-compose.yml index f0b21be7701..56b6581d574 100644 --- a/packaging/docker/docker-compose.yml +++ b/packaging/docker/docker-compose.yml @@ -17,8 +17,6 @@ services: - BEE_DB_BLOCK_CACHE_CAPACITY - BEE_DB_WRITE_BUFFER_SIZE - BEE_DB_DISABLE_SEEKS_COMPACTION - - BEE_DEBUG_API_ADDR - - BEE_DEBUG_API_ENABLE - BEE_FULL_NODE - BEE_NAT_ADDR - BEE_NETWORK_ID @@ -48,7 +46,6 @@ services: ports: - "${API_ADDR:-1633}${BEE_API_ADDR:-:1633}" - "${P2P_ADDR:-1634}${BEE_P2P_ADDR:-:1634}" - - "${DEBUG_API_ADDR:-127.0.0.1:1635}${BEE_DEBUG_API_ADDR:-:1635}" volumes: - bee-1:/home/bee/.bee command: start diff --git a/packaging/docker/env b/packaging/docker/env index a9da56ae85e..f578f1ea9c9 100644 --- a/packaging/docker/env +++ b/packaging/docker/env @@ -26,10 +26,6 @@ # BEE_DB_WRITE_BUFFER_SIZE=33554432 ## disables db compactions triggered by seeks # BEE_DB_DISABLE_SEEKS_COMPACTION=false -## debug HTTP API listen address (default :1635) -# BEE_DEBUG_API_ADDR=:1635 -## enable debug HTTP API -# BEE_DEBUG_API_ENABLE=false ## enable global pinning ## cause the node to start in full mode # BEE_FULL_NODE=false diff --git a/packaging/homebrew-amd64/bee.yaml b/packaging/homebrew-amd64/bee.yaml index 235ad42e781..79470f1b7a4 100644 --- a/packaging/homebrew-amd64/bee.yaml +++ b/packaging/homebrew-amd64/bee.yaml @@ -24,10 +24,6 @@ data-dir: /usr/local/var/lib/swarm-bee # db-write-buffer-size: 33554432 ## disables db compactions triggered by seeks # db-disable-seeks-compaction: false -## debug HTTP API listen address (default ":1635") -debug-api-addr: 127.0.0.1:1635 -## enable debug HTTP API -debug-api-enable: true ## cause the node to start in full mode # full-node: false ## NAT exposed address diff --git a/packaging/homebrew-arm64/bee.yaml b/packaging/homebrew-arm64/bee.yaml index 212fadca581..5b52b272d31 100644 --- a/packaging/homebrew-arm64/bee.yaml +++ b/packaging/homebrew-arm64/bee.yaml @@ -24,10 +24,6 @@ data-dir: /opt/homebrew/var/lib/swarm-bee # db-write-buffer-size: 33554432 ## disables db compactions triggered by seeks # db-disable-seeks-compaction: false -## debug HTTP API listen address (default ":1635") -debug-api-addr: 127.0.0.1:1635 -## enable debug HTTP API -debug-api-enable: true ## cause the node to start in full mode # full-node: false ## NAT exposed address diff --git a/packaging/scoop/bee.yaml b/packaging/scoop/bee.yaml index 18c01229b2c..6efdc90bb20 100644 --- a/packaging/scoop/bee.yaml +++ b/packaging/scoop/bee.yaml @@ -14,10 +14,6 @@ config: ./bee.yaml data-dir: ./data ## cache capacity in chunks, multiply by 4096 to get approximate capacity in bytes # cache-capacity: 1000000 -## debug HTTP API listen address (default ":1635") -# debug-api-addr: 127.0.0.1:1635 -## enable debug HTTP API -# debug-api-enable: false ## cause the node to start in full mode # full-node: false ## NAT exposed address diff --git a/pkg/api/accounting_test.go b/pkg/api/accounting_test.go index 7a807aa811e..c2db87d2cdb 100644 --- a/pkg/api/accounting_test.go +++ b/pkg/api/accounting_test.go @@ -56,7 +56,6 @@ func TestAccountingInfo(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, AccountingOpts: []mock.Option{mock.WithPeerAccountingFunc(accountingFunc)}, }) @@ -112,7 +111,6 @@ func TestAccountingInfoError(t *testing.T) { return nil, wantErr } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, AccountingOpts: []mock.Option{mock.WithPeerAccountingFunc(accountingFunc)}, }) diff --git a/pkg/api/api.go b/pkg/api/api.go index 4e356a14d69..fec8ab44afe 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -161,7 +161,6 @@ type Service struct { wsWg sync.WaitGroup // wait for all websockets to close on exit quit chan struct{} - // from debug API overlay *swarm.Address publicKey ecdsa.PublicKey pssPublicKey ecdsa.PublicKey diff --git a/pkg/api/api_test.go b/pkg/api/api_test.go index 6a96812a908..bf2c5288c5a 100644 --- a/pkg/api/api_test.go +++ b/pkg/api/api_test.go @@ -105,7 +105,6 @@ type testServerOptions struct { Steward steward.Interface WsHeaders http.Header Authenticator auth.Authenticator - DebugAPI bool Restricted bool DirectUpload bool Probe *api.Probe @@ -235,12 +234,10 @@ func newTestServer(t *testing.T, o testServerOptions) (*http.Client, *websocket. WsPingPeriod: o.WsPingPeriod, Restricted: o.Restricted, }, extraOpts, 1, erc20) - if o.DebugAPI { - s.MountTechnicalDebug() - s.MountDebug() - } else { - s.MountAPI() - } + + s.MountTechnicalDebug() + s.MountDebug() + s.MountAPI() if o.DirectUpload { chanStore = newChanStore(o.Storer.PusherFeed()) @@ -251,21 +248,6 @@ func newTestServer(t *testing.T, o testServerOptions) (*http.Client, *websocket. t.Cleanup(ts.Close) var ( - httpClient = &http.Client{ - Transport: web.RoundTripperFunc(func(r *http.Request) (*http.Response, error) { - u, err := url.Parse(ts.URL + r.URL.String()) - if err != nil { - return nil, err - } - r.URL = u - return ts.Client().Transport.RoundTrip(r) - }), - } - conn *websocket.Conn - err error - ) - - if !o.DebugAPI { httpClient = &http.Client{ Transport: web.RoundTripperFunc(func(r *http.Request) (*http.Response, error) { requestURL := r.URL.String() @@ -287,7 +269,9 @@ func newTestServer(t *testing.T, o testServerOptions) (*http.Client, *websocket. return transport.RoundTrip(r) }), } - } + conn *websocket.Conn + err error + ) if o.WsPath != "" { u := url.URL{Scheme: "ws", Host: ts.Listener.Addr().String(), Path: o.WsPath} diff --git a/pkg/api/balances_test.go b/pkg/api/balances_test.go index 174196d253a..d5434c3d3c8 100644 --- a/pkg/api/balances_test.go +++ b/pkg/api/balances_test.go @@ -32,7 +32,6 @@ func TestBalances(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, AccountingOpts: []mock.Option{mock.WithCompensatedBalancesFunc(compensatedBalancesFunc)}, }) @@ -72,7 +71,6 @@ func TestBalancesError(t *testing.T) { return nil, wantErr } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, AccountingOpts: []mock.Option{mock.WithCompensatedBalancesFunc(compensatedBalancesFunc)}, }) @@ -92,7 +90,6 @@ func TestBalancesPeers(t *testing.T) { return big.NewInt(100000000000000000), nil } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, AccountingOpts: []mock.Option{mock.WithCompensatedBalanceFunc(compensatedBalanceFunc)}, }) @@ -113,7 +110,6 @@ func TestBalancesPeersError(t *testing.T) { return nil, wantErr } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, AccountingOpts: []mock.Option{mock.WithCompensatedBalanceFunc(compensatedBalanceFunc)}, }) @@ -133,7 +129,6 @@ func TestBalancesPeersNoBalance(t *testing.T) { return nil, accounting.ErrPeerNoBalance } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, AccountingOpts: []mock.Option{mock.WithCompensatedBalanceFunc(compensatedBalanceFunc)}, }) @@ -186,7 +181,6 @@ func TestConsumedBalances(t *testing.T) { return ret, err } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, AccountingOpts: []mock.Option{mock.WithBalancesFunc(balancesFunc)}, }) @@ -227,7 +221,6 @@ func TestConsumedError(t *testing.T) { return nil, wantErr } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, AccountingOpts: []mock.Option{mock.WithBalancesFunc(balancesFunc)}, }) @@ -247,7 +240,6 @@ func TestConsumedPeers(t *testing.T) { return big.NewInt(1000000000000000000), nil } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, AccountingOpts: []mock.Option{mock.WithBalanceFunc(balanceFunc)}, }) @@ -268,7 +260,6 @@ func TestConsumedPeersError(t *testing.T) { return nil, wantErr } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, AccountingOpts: []mock.Option{mock.WithBalanceFunc(balanceFunc)}, }) @@ -288,7 +279,6 @@ func TestConsumedPeersNoBalance(t *testing.T) { return nil, accounting.ErrPeerNoBalance } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, AccountingOpts: []mock.Option{mock.WithBalanceFunc(balanceFunc)}, }) @@ -303,7 +293,7 @@ func TestConsumedPeersNoBalance(t *testing.T) { func Test_peerBalanceHandler_invalidInputs(t *testing.T) { t.Parallel() - client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + client, _, _, _ := newTestServer(t, testServerOptions{}) tests := []struct { name string @@ -352,7 +342,7 @@ func Test_peerBalanceHandler_invalidInputs(t *testing.T) { func Test_compensatedPeerBalanceHandler_invalidInputs(t *testing.T) { t.Parallel() - client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + client, _, _, _ := newTestServer(t, testServerOptions{}) tests := []struct { name string diff --git a/pkg/api/chequebook_test.go b/pkg/api/chequebook_test.go index b9b9cbdef32..e0276654d45 100644 --- a/pkg/api/chequebook_test.go +++ b/pkg/api/chequebook_test.go @@ -40,7 +40,6 @@ func TestChequebookBalance(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, ChequebookOpts: []mock.Option{ mock.WithChequebookBalanceFunc(chequebookBalanceFunc), mock.WithChequebookAvailableBalanceFunc(chequebookAvailableBalanceFunc), @@ -71,7 +70,6 @@ func TestChequebookBalanceError(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, ChequebookOpts: []mock.Option{mock.WithChequebookBalanceFunc(chequebookBalanceFunc)}, }) @@ -95,7 +93,6 @@ func TestChequebookAvailableBalanceError(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, ChequebookOpts: []mock.Option{ mock.WithChequebookBalanceFunc(chequebookBalanceFunc), mock.WithChequebookAvailableBalanceFunc(chequebookAvailableBalanceFunc), @@ -118,7 +115,6 @@ func TestChequebookAddress(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, ChequebookOpts: []mock.Option{mock.WithChequebookAddressFunc(chequebookAddressFunc)}, }) @@ -154,7 +150,6 @@ func TestChequebookWithdraw(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, ChequebookOpts: []mock.Option{mock.WithChequebookWithdrawFunc(chequebookWithdrawFunc)}, }) @@ -184,7 +179,6 @@ func TestChequebookWithdraw(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, ChequebookOpts: []mock.Option{mock.WithChequebookWithdrawFunc(chequebookWithdrawFunc)}, }) @@ -218,7 +212,6 @@ func TestChequebookDeposit(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, ChequebookOpts: []mock.Option{mock.WithChequebookDepositFunc(chequebookDepositFunc)}, }) @@ -249,7 +242,6 @@ func TestChequebookDeposit(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, ChequebookOpts: []mock.Option{mock.WithChequebookDepositFunc(chequebookDepositFunc)}, }) @@ -358,7 +350,6 @@ func TestChequebookLastCheques(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, SwapOpts: []swapmock.Option{swapmock.WithLastReceivedChequesFunc(lastReceivedChequesFunc), swapmock.WithLastSentChequesFunc(lastSentChequesFunc)}, }) @@ -472,7 +463,6 @@ func TestChequebookLastChequesPeer(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, SwapOpts: []swapmock.Option{swapmock.WithLastReceivedChequeFunc(lastReceivedChequeFunc), swapmock.WithLastSentChequeFunc(lastSentChequeFunc)}, }) @@ -512,7 +502,6 @@ func TestChequebookCashout(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, SwapOpts: []swapmock.Option{swapmock.WithCashChequeFunc(cashChequeFunc)}, }) @@ -543,7 +532,6 @@ func TestChequebookCashout_CustomGas(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, SwapOpts: []swapmock.Option{swapmock.WithCashChequeFunc(cashChequeFunc)}, }) @@ -619,7 +607,6 @@ func TestChequebookCashoutStatus(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, SwapOpts: []swapmock.Option{swapmock.WithCashoutStatusFunc(cashoutStatusFunc)}, }) @@ -667,7 +654,6 @@ func TestChequebookCashoutStatus(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, SwapOpts: []swapmock.Option{swapmock.WithCashoutStatusFunc(cashoutStatusFunc)}, }) @@ -706,7 +692,6 @@ func TestChequebookCashoutStatus(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, SwapOpts: []swapmock.Option{swapmock.WithCashoutStatusFunc(cashoutStatusFunc)}, }) @@ -733,7 +718,7 @@ func TestChequebookCashoutStatus(t *testing.T) { func Test_chequebookLastPeerHandler_invalidInputs(t *testing.T) { t.Parallel() - client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + client, _, _, _ := newTestServer(t, testServerOptions{}) tests := []struct { name string diff --git a/pkg/api/debugstorage_test.go b/pkg/api/debugstorage_test.go index bb12a5585fd..14a669f6b10 100644 --- a/pkg/api/debugstorage_test.go +++ b/pkg/api/debugstorage_test.go @@ -35,8 +35,7 @@ func TestDebugStorage(t *testing.T) { } ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, - Storer: mockstorer.NewWithDebugInfo(want), + Storer: mockstorer.NewWithDebugInfo(want), }) jsonhttptest.Request(t, ts, http.MethodGet, "/debugstore", http.StatusOK, diff --git a/pkg/api/health.go b/pkg/api/health.go index 33b6481b6ee..e153b9114f0 100644 --- a/pkg/api/health.go +++ b/pkg/api/health.go @@ -12,18 +12,16 @@ import ( ) type healthStatusResponse struct { - Status string `json:"status"` - Version string `json:"version"` - APIVersion string `json:"apiVersion"` - DebugAPIVersion string `json:"debugApiVersion"` + Status string `json:"status"` + Version string `json:"version"` + APIVersion string `json:"apiVersion"` } func (s *Service) healthHandler(w http.ResponseWriter, _ *http.Request) { status := s.probe.Healthy() jsonhttp.OK(w, healthStatusResponse{ - Status: status.String(), - Version: bee.Version, - APIVersion: Version, - DebugAPIVersion: DebugVersion, + Status: status.String(), + Version: bee.Version, + APIVersion: Version, }) } diff --git a/pkg/api/health_test.go b/pkg/api/health_test.go index 3446e167895..11c79f5366c 100644 --- a/pkg/api/health_test.go +++ b/pkg/api/health_test.go @@ -23,10 +23,9 @@ func TestHealth(t *testing.T) { // When probe is not set health endpoint should indicate that node is not healthy jsonhttptest.Request(t, testServer, http.MethodGet, "/health", http.StatusOK, jsonhttptest.WithExpectedJSONResponse(api.HealthStatusResponse{ - Status: "nok", - Version: bee.Version, - APIVersion: api.Version, - DebugAPIVersion: api.Version, + Status: "nok", + Version: bee.Version, + APIVersion: api.Version, })) }) @@ -40,28 +39,25 @@ func TestHealth(t *testing.T) { // Current health probe is pending which should indicate that API is not healthy jsonhttptest.Request(t, testServer, http.MethodGet, "/health", http.StatusOK, jsonhttptest.WithExpectedJSONResponse(api.HealthStatusResponse{ - Status: "nok", - Version: bee.Version, - APIVersion: api.Version, - DebugAPIVersion: api.Version, + Status: "nok", + Version: bee.Version, + APIVersion: api.Version, })) // When we set health probe to OK it should indicate that node is healthy probe.SetHealthy(api.ProbeStatusOK) jsonhttptest.Request(t, testServer, http.MethodGet, "/health", http.StatusOK, jsonhttptest.WithExpectedJSONResponse(api.HealthStatusResponse{ - Status: "ok", - Version: bee.Version, - APIVersion: api.Version, - DebugAPIVersion: api.Version, + Status: "ok", + Version: bee.Version, + APIVersion: api.Version, })) // When we set health probe to NOK it should indicate that node is not healthy probe.SetHealthy(api.ProbeStatusNOK) jsonhttptest.Request(t, testServer, http.MethodGet, "/health", http.StatusOK, jsonhttptest.WithExpectedJSONResponse(api.HealthStatusResponse{ - Status: "nok", - Version: bee.Version, - APIVersion: api.Version, - DebugAPIVersion: api.Version, + Status: "nok", + Version: bee.Version, + APIVersion: api.Version, })) }) } diff --git a/pkg/api/logger_test.go b/pkg/api/logger_test.go index b8420b79067..4a3349f6681 100644 --- a/pkg/api/logger_test.go +++ b/pkg/api/logger_test.go @@ -72,7 +72,7 @@ func TestGetLoggers(t *testing.T) { t.Fatalf("unexpected error: %v", err) } - client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + client, _, _, _ := newTestServer(t, testServerOptions{}) jsonhttptest.Request(t, client, http.MethodGet, "/loggers", http.StatusOK, jsonhttptest.WithUnmarshalJSONResponse(&have), ) @@ -88,7 +88,7 @@ func TestSetLoggerVerbosity(t *testing.T) { api.ReplaceLogSetVerbosityByExp(fn) }(api.LogSetVerbosityByExp) - client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + client, _, _, _ := newTestServer(t, testServerOptions{}) type data struct { exp string @@ -143,7 +143,7 @@ func TestSetLoggerVerbosity(t *testing.T) { func Test_loggerGetHandler_invalidInputs(t *testing.T) { t.Parallel() - client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + client, _, _, _ := newTestServer(t, testServerOptions{}) tests := []struct { name string @@ -192,7 +192,7 @@ func Test_loggerGetHandler_invalidInputs(t *testing.T) { func Test_loggerSetVerbosityHandler_invalidInputs(t *testing.T) { t.Parallel() - client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + client, _, _, _ := newTestServer(t, testServerOptions{}) tests := []struct { name string diff --git a/pkg/api/p2p_test.go b/pkg/api/p2p_test.go index 5cf9de6124c..933596afff7 100644 --- a/pkg/api/p2p_test.go +++ b/pkg/api/p2p_test.go @@ -40,7 +40,6 @@ func TestAddresses(t *testing.T) { ethereumAddress := common.HexToAddress("abcd") testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, PublicKey: privateKey.PublicKey, PSSPublicKey: pssPrivateKey.PublicKey, Overlay: overlay, @@ -79,7 +78,6 @@ func TestAddresses_error(t *testing.T) { testErr := errors.New("test error") testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, P2P: mock.New(mock.WithAddressesFunc(func() ([]multiaddr.Multiaddr, error) { return nil, testErr })), diff --git a/pkg/api/peer_test.go b/pkg/api/peer_test.go index 0697a6277d2..fdda7b8edf4 100644 --- a/pkg/api/peer_test.go +++ b/pkg/api/peer_test.go @@ -51,7 +51,6 @@ func TestConnect(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, P2P: mock.New(mock.WithConnectFunc(func(ctx context.Context, addr ma.Multiaddr) (*bzz.Address, error) { if addr.String() == errorUnderlay { return nil, testErr @@ -82,7 +81,6 @@ func TestConnect(t *testing.T) { t.Run("error - add peer", func(t *testing.T) { t.Parallel() testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, P2P: mock.New(mock.WithConnectFunc(func(ctx context.Context, addr ma.Multiaddr) (*bzz.Address, error) { if addr.String() == errorUnderlay { return nil, testErr @@ -109,7 +107,6 @@ func TestDisconnect(t *testing.T) { testErr := errors.New("test error") testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, P2P: mock.New(mock.WithDisconnectFunc(func(addr swarm.Address, reason string) error { if reason != "user requested disconnect" { return testErr @@ -166,7 +163,6 @@ func TestPeer(t *testing.T) { overlay := swarm.MustParseHexAddress("ca1e9f3938cc1425c6061b96ad9eb93e134dfe8734ad490164ef20af9d1cf59c") testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, P2P: mock.New(mock.WithPeersFunc(func() []p2p.Peer { return []p2p.Peer{{Address: overlay}} })), @@ -188,7 +184,6 @@ func TestBlocklistedPeers(t *testing.T) { overlay := swarm.MustParseHexAddress("ca1e9f3938cc1425c6061b96ad9eb93e134dfe8734ad490164ef20af9d1cf59c") testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, P2P: mock.New(mock.WithBlocklistedPeersFunc(func() ([]p2p.BlockListedPeer, error) { return []p2p.BlockListedPeer{{Peer: p2p.Peer{Address: overlay}}}, nil })), @@ -206,7 +201,6 @@ func TestBlocklistedPeersErr(t *testing.T) { overlay := swarm.MustParseHexAddress("ca1e9f3938cc1425c6061b96ad9eb93e134dfe8734ad490164ef20af9d1cf59c") testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, P2P: mock.New(mock.WithBlocklistedPeersFunc(func() ([]p2p.BlockListedPeer, error) { return []p2p.BlockListedPeer{{Peer: p2p.Peer{Address: overlay}}}, errors.New("some error") })), @@ -224,7 +218,7 @@ func TestBlocklistedPeersErr(t *testing.T) { func Test_peerConnectHandler_invalidInputs(t *testing.T) { t.Parallel() - client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + client, _, _, _ := newTestServer(t, testServerOptions{}) tests := []struct { name string @@ -260,7 +254,7 @@ func Test_peerConnectHandler_invalidInputs(t *testing.T) { func Test_peerDisconnectHandler_invalidInputs(t *testing.T) { t.Parallel() - client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + client, _, _, _ := newTestServer(t, testServerOptions{}) tests := []struct { name string diff --git a/pkg/api/pingpong_test.go b/pkg/api/pingpong_test.go index 4dffae5b0e6..9de99103619 100644 --- a/pkg/api/pingpong_test.go +++ b/pkg/api/pingpong_test.go @@ -39,7 +39,6 @@ func TestPingpong(t *testing.T) { }) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, Pingpong: pingpongService, }) @@ -79,7 +78,7 @@ func TestPingpong(t *testing.T) { func Test_pingpongHandler_invalidInputs(t *testing.T) { t.Parallel() - client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + client, _, _, _ := newTestServer(t, testServerOptions{}) tests := []struct { name string diff --git a/pkg/api/postage_test.go b/pkg/api/postage_test.go index ebf808c1c7a..13e3dda13d6 100644 --- a/pkg/api/postage_test.go +++ b/pkg/api/postage_test.go @@ -65,7 +65,6 @@ func TestPostageCreateStamp(t *testing.T) { }), ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, PostageContract: contract, }) @@ -90,7 +89,6 @@ func TestPostageCreateStamp(t *testing.T) { }), ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, PostageContract: contract, }) @@ -111,7 +109,6 @@ func TestPostageCreateStamp(t *testing.T) { }), ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, PostageContract: contract, }) @@ -132,7 +129,6 @@ func TestPostageCreateStamp(t *testing.T) { }), ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, PostageContract: contract, }) @@ -161,7 +157,6 @@ func TestPostageCreateStamp(t *testing.T) { }), ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, PostageContract: contract, }) @@ -182,7 +177,6 @@ func TestPostageCreateStamp(t *testing.T) { t.Parallel() ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, SyncStatus: func() (bool, error) { return false, nil }, }) @@ -197,7 +191,6 @@ func TestPostageCreateStamp(t *testing.T) { t.Parallel() ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, SyncStatus: func() (bool, error) { return true, errors.New("oops") }, }) @@ -223,7 +216,7 @@ func TestPostageGetStamps(t *testing.T) { t.Parallel() bs := mock.New(mock.WithChainState(cs), mock.WithBatch(b)) - ts, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true, Post: mp, BatchStore: bs, BlockTime: 2 * time.Second}) + ts, _, _, _ := newTestServer(t, testServerOptions{Post: mp, BatchStore: bs, BlockTime: 2 * time.Second}) jsonhttptest.Request(t, ts, http.MethodGet, "/stamps", http.StatusOK, jsonhttptest.WithExpectedJSONResponse(&api.PostageStampsResponse{ @@ -259,7 +252,7 @@ func TestPostageGetStamps(t *testing.T) { } ecs := &postage.ChainState{Block: 10, TotalAmount: big.NewInt(15), CurrentPrice: big.NewInt(12)} ebs := mock.New(mock.WithChainState(ecs)) - ts, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true, Post: emp, BatchStore: ebs, BlockTime: 2 * time.Second}) + ts, _, _, _ := newTestServer(t, testServerOptions{Post: emp, BatchStore: ebs, BlockTime: 2 * time.Second}) jsonhttptest.Request(t, ts, http.MethodGet, "/stamps/"+hex.EncodeToString(eb.ID), http.StatusNotFound, jsonhttptest.WithExpectedJSONResponse(&jsonhttp.StatusResponse{ @@ -281,7 +274,7 @@ func TestGetAllBatches(t *testing.T) { mp := mockpost.New(mockpost.WithIssuer(si)) cs := &postage.ChainState{Block: 10, TotalAmount: big.NewInt(5), CurrentPrice: big.NewInt(2)} bs := mock.New(mock.WithChainState(cs), mock.WithBatch(b)) - ts, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true, Post: mp, BatchStore: bs, BlockTime: 2 * time.Second}) + ts, _, _, _ := newTestServer(t, testServerOptions{Post: mp, BatchStore: bs, BlockTime: 2 * time.Second}) oneBatch := struct { Batches []api.PostageBatchResponse `json:"batches"` @@ -318,7 +311,7 @@ func TestPostageGetStamp(t *testing.T) { mp := mockpost.New(mockpost.WithIssuer(si)) cs := &postage.ChainState{Block: 10, TotalAmount: big.NewInt(5), CurrentPrice: big.NewInt(2)} bs := mock.New(mock.WithChainState(cs), mock.WithBatch(b)) - ts, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true, Post: mp, BatchStore: bs, BlockTime: 2 * time.Second}) + ts, _, _, _ := newTestServer(t, testServerOptions{Post: mp, BatchStore: bs, BlockTime: 2 * time.Second}) t.Run("ok", func(t *testing.T) { t.Parallel() @@ -346,7 +339,7 @@ func TestPostageGetBuckets(t *testing.T) { si := postage.NewStampIssuer("", "", batchOk, big.NewInt(3), 11, 10, 1000, true) mp := mockpost.New(mockpost.WithIssuer(si)) - ts, _, _, _ := newTestServer(t, testServerOptions{Post: mp, DebugAPI: true}) + ts, _, _, _ := newTestServer(t, testServerOptions{Post: mp}) buckets := make([]api.BucketData, 1024) for i := range buckets { buckets[i] = api.BucketData{BucketID: uint32(i)} @@ -369,7 +362,7 @@ func TestPostageGetBuckets(t *testing.T) { t.Parallel() mpNotFound := mockpost.New() - tsNotFound, _, _, _ := newTestServer(t, testServerOptions{Post: mpNotFound, DebugAPI: true}) + tsNotFound, _, _, _ := newTestServer(t, testServerOptions{Post: mpNotFound}) jsonhttptest.Request(t, tsNotFound, http.MethodGet, "/stamps/"+batchOkStr+"/buckets", http.StatusNotFound) }) @@ -383,7 +376,6 @@ func TestReserveState(t *testing.T) { t.Parallel() ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, BatchStore: mock.New(mock.WithRadius(5)), Storer: mockstorer.New(), }) @@ -397,7 +389,6 @@ func TestReserveState(t *testing.T) { t.Parallel() ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, BatchStore: mock.New(), Storer: mockstorer.New(), }) @@ -418,7 +409,6 @@ func TestChainState(t *testing.T) { CurrentPrice: big.NewInt(5), } ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, BatchStore: mock.New(mock.WithChainState(cs)), BackendOpts: []backendmock.Option{backendmock.WithBlockNumberFunc(func(ctx context.Context) (uint64, error) { return 1, nil @@ -460,7 +450,6 @@ func TestPostageTopUpStamp(t *testing.T) { }), ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, PostageContract: contract, }) @@ -490,7 +479,6 @@ func TestPostageTopUpStamp(t *testing.T) { }), ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, PostageContract: contract, }) @@ -512,7 +500,6 @@ func TestPostageTopUpStamp(t *testing.T) { }), ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, PostageContract: contract, }) @@ -533,7 +520,6 @@ func TestPostageTopUpStamp(t *testing.T) { }), ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, PostageContract: contract, }) @@ -557,7 +543,6 @@ func TestPostageTopUpStamp(t *testing.T) { }), ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, PostageContract: contract, }) @@ -595,7 +580,6 @@ func TestPostageDiluteStamp(t *testing.T) { }), ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, PostageContract: contract, }) @@ -625,7 +609,6 @@ func TestPostageDiluteStamp(t *testing.T) { }), ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, PostageContract: contract, }) @@ -647,7 +630,6 @@ func TestPostageDiluteStamp(t *testing.T) { }), ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, PostageContract: contract, }) @@ -668,7 +650,6 @@ func TestPostageDiluteStamp(t *testing.T) { }), ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, PostageContract: contract, }) @@ -692,7 +673,6 @@ func TestPostageDiluteStamp(t *testing.T) { }), ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, PostageContract: contract, }) @@ -812,7 +792,6 @@ func TestPostageAccessHandler(t *testing.T) { ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, PostageContract: contract, }) @@ -837,7 +816,7 @@ func TestPostageAccessHandler(t *testing.T) { func Test_postageCreateHandler_invalidInputs(t *testing.T) { t.Parallel() - client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + client, _, _, _ := newTestServer(t, testServerOptions{}) tests := []struct { name string @@ -887,7 +866,7 @@ func Test_postageCreateHandler_invalidInputs(t *testing.T) { func Test_postageGetStampBucketsHandler_invalidInputs(t *testing.T) { t.Parallel() - client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + client, _, _, _ := newTestServer(t, testServerOptions{}) tests := []struct { name string @@ -949,7 +928,7 @@ func Test_postageGetStampBucketsHandler_invalidInputs(t *testing.T) { func Test_postageGetStampHandler_invalidInputs(t *testing.T) { t.Parallel() - client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + client, _, _, _ := newTestServer(t, testServerOptions{}) tests := []struct { name string @@ -1012,7 +991,7 @@ func Test_postageGetStampHandler_invalidInputs(t *testing.T) { func Test_postageTopUpHandler_invalidInputs(t *testing.T) { t.Parallel() - client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + client, _, _, _ := newTestServer(t, testServerOptions{}) tests := []struct { name string @@ -1091,7 +1070,7 @@ func Test_postageTopUpHandler_invalidInputs(t *testing.T) { func Test_postageDiluteHandler_invalidInputs(t *testing.T) { t.Parallel() - client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + client, _, _, _ := newTestServer(t, testServerOptions{}) tests := []struct { name string diff --git a/pkg/api/redistribution_test.go b/pkg/api/redistribution_test.go index 88bf00abe51..07b4bc28cfe 100644 --- a/pkg/api/redistribution_test.go +++ b/pkg/api/redistribution_test.go @@ -36,7 +36,6 @@ func TestRedistributionStatus(t *testing.T) { t.Errorf("redistribution put state: %v", err) } srv, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, StateStorer: store, TransactionOpts: []mock.Option{ mock.WithTransactionFeeFunc(func(ctx context.Context, txHash common.Hash) (*big.Int, error) { @@ -61,7 +60,6 @@ func TestRedistributionStatus(t *testing.T) { t.Parallel() srv, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, BeeMode: api.LightMode, StateStorer: statestore.NewStateStore(), TransactionOpts: []mock.Option{ diff --git a/pkg/api/router.go b/pkg/api/router.go index 0396dd2b2de..aaa75725b09 100644 --- a/pkg/api/router.go +++ b/pkg/api/router.go @@ -34,7 +34,7 @@ func (s *Service) MountTechnicalDebug() { s.mountTechnicalDebug() s.Handler = web.ChainHandlers( - httpaccess.NewHTTPAccessLogHandler(s.logger, s.tracer, "debug api access"), + httpaccess.NewHTTPAccessLogHandler(s.logger, s.tracer, "api access"), handlers.CompressHandler, s.corsHandler, web.NoCacheHeadersHandler, @@ -46,7 +46,7 @@ func (s *Service) MountDebug() { s.mountBusinessDebug() s.Handler = web.ChainHandlers( - httpaccess.NewHTTPAccessLogHandler(s.logger, s.tracer, "debug api access"), + httpaccess.NewHTTPAccessLogHandler(s.logger, s.tracer, "api access"), handlers.CompressHandler, s.corsHandler, web.NoCacheHeadersHandler, @@ -370,7 +370,6 @@ func (s *Service) mountAPI() { func (s *Service) mountBusinessDebug() { handle := func(path string, handler http.Handler) { - s.logger.Warning(fmt.Sprintf("DEPRECATION NOTICE: %s endpoint is now part of the main Bee API. The Debug API will be removed in the next release, version [2.2.0]. Update your integrations to use the main Bee API to avoid service disruptions.", path)) if s.Restricted { handler = web.ChainHandlers(auth.PermissionCheckHandler(s.auth), web.FinalHandler(handler)) } @@ -413,9 +412,9 @@ func (s *Service) mountBusinessDebug() { "DELETE": http.HandlerFunc(s.peerDisconnectHandler), }) - handle("/chunks/{address}", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.hasChunkHandler), - }) + //handle("/chunks/{address}", jsonhttp.MethodHandler{ + // "GET": http.HandlerFunc(s.hasChunkHandler), + //}) handle("/topology", jsonhttp.MethodHandler{ "GET": http.HandlerFunc(s.topologyHandler), diff --git a/pkg/api/settlements_test.go b/pkg/api/settlements_test.go index 86f2e2e42e1..f76997e1b8c 100644 --- a/pkg/api/settlements_test.go +++ b/pkg/api/settlements_test.go @@ -39,7 +39,6 @@ func TestSettlements(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, SwapOpts: []mock.Option{mock.WithSettlementsSentFunc(settlementsSentFunc), mock.WithSettlementsRecvFunc(settlementsRecvFunc)}, }) @@ -90,7 +89,6 @@ func TestSettlementsError(t *testing.T) { return nil, wantErr } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, SwapOpts: []mock.Option{mock.WithSettlementsSentFunc(settlementsSentFunc)}, }) @@ -110,7 +108,6 @@ func TestSettlementsPeers(t *testing.T) { return big.NewInt(1000000000000000000), nil } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, SwapOpts: []mock.Option{mock.WithSettlementSentFunc(settlementSentFunc)}, }) @@ -138,7 +135,6 @@ func TestSettlementsPeersNoSettlements(t *testing.T) { t.Parallel() testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, SwapOpts: []mock.Option{ mock.WithSettlementSentFunc(errFunc), mock.WithSettlementRecvFunc(noErrFunc), @@ -158,7 +154,6 @@ func TestSettlementsPeersNoSettlements(t *testing.T) { t.Parallel() testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, SwapOpts: []mock.Option{ mock.WithSettlementSentFunc(noErrFunc), mock.WithSettlementRecvFunc(errFunc), @@ -178,7 +173,7 @@ func TestSettlementsPeersNoSettlements(t *testing.T) { func Test_peerSettlementsHandler_invalidInputs(t *testing.T) { t.Parallel() - client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + client, _, _, _ := newTestServer(t, testServerOptions{}) tests := []struct { name string @@ -233,7 +228,6 @@ func TestSettlementsPeersError(t *testing.T) { return nil, wantErr } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, SwapOpts: []mock.Option{mock.WithSettlementSentFunc(settlementSentFunc)}, }) diff --git a/pkg/api/staking_test.go b/pkg/api/staking_test.go index e6f4d0b2296..0d6dac27587 100644 --- a/pkg/api/staking_test.go +++ b/pkg/api/staking_test.go @@ -39,7 +39,7 @@ func TestDepositStake(t *testing.T) { return txHash, nil }), ) - ts, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true, StakingContract: contract}) + ts, _, _, _ := newTestServer(t, testServerOptions{StakingContract: contract}) jsonhttptest.Request(t, ts, http.MethodPost, depositStake(minStake), http.StatusOK) }) @@ -52,7 +52,7 @@ func TestDepositStake(t *testing.T) { return common.Hash{}, staking.ErrInsufficientStakeAmount }), ) - ts, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true, StakingContract: contract}) + ts, _, _, _ := newTestServer(t, testServerOptions{StakingContract: contract}) jsonhttptest.Request(t, ts, http.MethodPost, depositStake(invalidMinStake), http.StatusBadRequest, jsonhttptest.WithExpectedJSONResponse(&jsonhttp.StatusResponse{Code: http.StatusBadRequest, Message: "insufficient stake amount"})) }) @@ -65,7 +65,7 @@ func TestDepositStake(t *testing.T) { return common.Hash{}, staking.ErrInsufficientFunds }), ) - ts, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true, StakingContract: contract}) + ts, _, _, _ := newTestServer(t, testServerOptions{StakingContract: contract}) jsonhttptest.Request(t, ts, http.MethodPost, depositStake(minStake), http.StatusBadRequest) jsonhttptest.WithExpectedJSONResponse(&jsonhttp.StatusResponse{Code: http.StatusBadRequest, Message: "out of funds"}) }) @@ -78,7 +78,7 @@ func TestDepositStake(t *testing.T) { return common.Hash{}, fmt.Errorf("some error") }), ) - ts, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true, StakingContract: contract}) + ts, _, _, _ := newTestServer(t, testServerOptions{StakingContract: contract}) jsonhttptest.Request(t, ts, http.MethodPost, depositStake(minStake), http.StatusInternalServerError) jsonhttptest.WithExpectedJSONResponse(&jsonhttp.StatusResponse{Code: http.StatusInternalServerError, Message: "cannot stake"}) }) @@ -96,7 +96,6 @@ func TestDepositStake(t *testing.T) { }), ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, StakingContract: contract, }) @@ -117,7 +116,7 @@ func TestGetStake(t *testing.T) { return big.NewInt(1), nil }), ) - ts, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true, StakingContract: contract}) + ts, _, _, _ := newTestServer(t, testServerOptions{StakingContract: contract}) jsonhttptest.Request(t, ts, http.MethodGet, "/stake", http.StatusOK, jsonhttptest.WithExpectedJSONResponse(&api.GetStakeResponse{StakedAmount: bigint.Wrap(big.NewInt(1))})) }) @@ -130,7 +129,7 @@ func TestGetStake(t *testing.T) { return big.NewInt(0), fmt.Errorf("get stake failed") }), ) - ts, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true, StakingContract: contractWithError}) + ts, _, _, _ := newTestServer(t, testServerOptions{StakingContract: contractWithError}) jsonhttptest.Request(t, ts, http.MethodGet, "/stake", http.StatusInternalServerError, jsonhttptest.WithExpectedJSONResponse(&jsonhttp.StatusResponse{Code: http.StatusInternalServerError, Message: "get staked amount failed"})) }) @@ -139,7 +138,7 @@ func TestGetStake(t *testing.T) { func Test_stakingDepositHandler_invalidInputs(t *testing.T) { t.Parallel() - client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + client, _, _, _ := newTestServer(t, testServerOptions{}) tests := []struct { name string @@ -185,7 +184,7 @@ func TestWithdrawAllStake(t *testing.T) { return txHash, nil }), ) - ts, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true, StakingContract: contract}) + ts, _, _, _ := newTestServer(t, testServerOptions{StakingContract: contract}) jsonhttptest.Request(t, ts, http.MethodDelete, "/stake", http.StatusOK, jsonhttptest.WithExpectedJSONResponse( &api.WithdrawAllStakeResponse{TxHash: txHash.String()})) }) @@ -198,7 +197,7 @@ func TestWithdrawAllStake(t *testing.T) { return common.Hash{}, staking.ErrInsufficientStake }), ) - ts, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true, StakingContract: contract}) + ts, _, _, _ := newTestServer(t, testServerOptions{StakingContract: contract}) jsonhttptest.Request(t, ts, http.MethodDelete, "/stake", http.StatusBadRequest, jsonhttptest.WithExpectedJSONResponse(&jsonhttp.StatusResponse{Code: http.StatusBadRequest, Message: "insufficient stake to withdraw"})) }) @@ -211,7 +210,7 @@ func TestWithdrawAllStake(t *testing.T) { return common.Hash{}, fmt.Errorf("some error") }), ) - ts, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true, StakingContract: contract}) + ts, _, _, _ := newTestServer(t, testServerOptions{StakingContract: contract}) jsonhttptest.Request(t, ts, http.MethodDelete, "/stake", http.StatusInternalServerError) jsonhttptest.WithExpectedJSONResponse(&jsonhttp.StatusResponse{Code: http.StatusInternalServerError, Message: "cannot withdraw stake"}) }) @@ -229,7 +228,6 @@ func TestWithdrawAllStake(t *testing.T) { }), ) ts, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, StakingContract: contract, }) diff --git a/pkg/api/status_test.go b/pkg/api/status_test.go index cc2c568abf4..8dadff7484c 100644 --- a/pkg/api/status_test.go +++ b/pkg/api/status_test.go @@ -58,7 +58,6 @@ func TestGetStatus(t *testing.T) { client, _, _, _ := newTestServer(t, testServerOptions{ BeeMode: mode, - DebugAPI: true, NodeStatus: statusSvc, }) @@ -71,8 +70,7 @@ func TestGetStatus(t *testing.T) { t.Parallel() client, _, _, _ := newTestServer(t, testServerOptions{ - BeeMode: api.DevMode, - DebugAPI: true, + BeeMode: api.DevMode, NodeStatus: status.NewService( log.Noop, nil, diff --git a/pkg/api/topology_test.go b/pkg/api/topology_test.go index ebd22532f3b..11dd6703791 100644 --- a/pkg/api/topology_test.go +++ b/pkg/api/topology_test.go @@ -14,7 +14,7 @@ import ( func TestTopologyOK(t *testing.T) { t.Parallel() - testServer, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + testServer, _, _, _ := newTestServer(t, testServerOptions{}) var body []byte opts := jsonhttptest.WithPutResponseBody(&body) diff --git a/pkg/api/transaction_test.go b/pkg/api/transaction_test.go index 46e066c4091..c23cc9cf3f5 100644 --- a/pkg/api/transaction_test.go +++ b/pkg/api/transaction_test.go @@ -42,7 +42,6 @@ func TestTransactionStoredTransaction(t *testing.T) { t.Parallel() testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, TransactionOpts: []mock.Option{ mock.WithStoredTransactionFunc(func(txHash common.Hash) (*transaction.StoredTransaction, error) { return &transaction.StoredTransaction{ @@ -84,7 +83,6 @@ func TestTransactionStoredTransaction(t *testing.T) { t.Parallel() testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, TransactionOpts: []mock.Option{ mock.WithStoredTransactionFunc(func(txHash common.Hash) (*transaction.StoredTransaction, error) { return nil, transaction.ErrUnknownTransaction @@ -103,7 +101,6 @@ func TestTransactionStoredTransaction(t *testing.T) { t.Parallel() testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, TransactionOpts: []mock.Option{ mock.WithStoredTransactionFunc(func(txHash common.Hash) (*transaction.StoredTransaction, error) { return nil, errors.New("err") @@ -155,7 +152,6 @@ func TestTransactionList(t *testing.T) { } testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, TransactionOpts: []mock.Option{ mock.WithPendingTransactionsFunc(func() ([]common.Hash, error) { return []common.Hash{txHash1, txHash2}, nil @@ -210,7 +206,6 @@ func TestTransactionListError(t *testing.T) { t.Parallel() testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, TransactionOpts: []mock.Option{ mock.WithPendingTransactionsFunc(func() ([]common.Hash, error) { return nil, errors.New("err") @@ -233,7 +228,6 @@ func TestTransactionListError(t *testing.T) { t.Parallel() testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, TransactionOpts: []mock.Option{ mock.WithPendingTransactionsFunc(func() ([]common.Hash, error) { return []common.Hash{txHash1}, nil @@ -261,7 +255,6 @@ func TestTransactionResend(t *testing.T) { t.Parallel() testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, TransactionOpts: []mock.Option{ mock.WithResendTransactionFunc(func(ctx context.Context, txHash common.Hash) error { return nil @@ -280,7 +273,6 @@ func TestTransactionResend(t *testing.T) { t.Parallel() testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, TransactionOpts: []mock.Option{ mock.WithResendTransactionFunc(func(ctx context.Context, txHash common.Hash) error { return transaction.ErrUnknownTransaction @@ -300,7 +292,6 @@ func TestTransactionResend(t *testing.T) { t.Parallel() testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, TransactionOpts: []mock.Option{ mock.WithResendTransactionFunc(func(ctx context.Context, txHash common.Hash) error { return transaction.ErrAlreadyImported @@ -320,7 +311,6 @@ func TestTransactionResend(t *testing.T) { t.Parallel() testServer, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, TransactionOpts: []mock.Option{ mock.WithResendTransactionFunc(func(ctx context.Context, txHash common.Hash) error { return errors.New("err") diff --git a/pkg/api/version.go b/pkg/api/version.go index b9bf36154c4..44086d8bc47 100644 --- a/pkg/api/version.go +++ b/pkg/api/version.go @@ -6,4 +6,3 @@ package api // Version is set in the build process. var Version = "0.0.0" -var DebugVersion = "0.0.0" diff --git a/pkg/api/wallet_test.go b/pkg/api/wallet_test.go index c6f7449d51a..d5c09efaeeb 100644 --- a/pkg/api/wallet_test.go +++ b/pkg/api/wallet_test.go @@ -28,7 +28,6 @@ func TestWallet(t *testing.T) { t.Parallel() srv, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, Erc20Opts: []erc20mock.Option{ erc20mock.WithBalanceOfFunc(func(ctx context.Context, address common.Address) (*big.Int, error) { return big.NewInt(10000000000000000), nil @@ -54,7 +53,6 @@ func TestWallet(t *testing.T) { t.Parallel() srv, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, BackendOpts: []backendmock.Option{ backendmock.WithBalanceAt(func(ctx context.Context, address common.Address, block *big.Int) (*big.Int, error) { return new(big.Int), nil @@ -73,7 +71,6 @@ func TestWallet(t *testing.T) { t.Parallel() srv, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, Erc20Opts: []erc20mock.Option{ erc20mock.WithBalanceOfFunc(func(ctx context.Context, address common.Address) (*big.Int, error) { return new(big.Int), nil @@ -95,7 +92,7 @@ func TestWalletWithdraw(t *testing.T) { t.Run("address not whitelisted", func(t *testing.T) { t.Parallel() - srv, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + srv, _, _, _ := newTestServer(t, testServerOptions{}) jsonhttptest.Request(t, srv, http.MethodPost, "/wallet/withdraw/BZZ?address=0xaf&amount=99999999", http.StatusBadRequest, jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ @@ -107,7 +104,7 @@ func TestWalletWithdraw(t *testing.T) { t.Run("invalid coin type", func(t *testing.T) { t.Parallel() - srv, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + srv, _, _, _ := newTestServer(t, testServerOptions{}) jsonhttptest.Request(t, srv, http.MethodPost, "/wallet/withdraw/BTC?address=0xaf&amount=99999999", http.StatusBadRequest, jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ @@ -120,7 +117,6 @@ func TestWalletWithdraw(t *testing.T) { t.Parallel() srv, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, WhitelistedAddr: "0xaf", }) @@ -135,7 +131,6 @@ func TestWalletWithdraw(t *testing.T) { t.Parallel() srv, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, WhitelistedAddr: "0xaf", Erc20Opts: []erc20mock.Option{ erc20mock.WithBalanceOfFunc(func(ctx context.Context, address common.Address) (*big.Int, error) { @@ -155,7 +150,6 @@ func TestWalletWithdraw(t *testing.T) { t.Parallel() srv, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, WhitelistedAddr: "0xaf", Erc20Opts: []erc20mock.Option{ erc20mock.WithBalanceOfFunc(func(ctx context.Context, address common.Address) (*big.Int, error) { @@ -177,7 +171,6 @@ func TestWalletWithdraw(t *testing.T) { txHash := common.HexToHash("0x00f") srv, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, WhitelistedAddr: "0xaf", Erc20Opts: []erc20mock.Option{ erc20mock.WithBalanceOfFunc(func(ctx context.Context, address common.Address) (*big.Int, error) { @@ -205,7 +198,6 @@ func TestWalletWithdraw(t *testing.T) { t.Parallel() srv, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, WhitelistedAddr: "0xaf", }) @@ -220,7 +212,6 @@ func TestWalletWithdraw(t *testing.T) { t.Parallel() srv, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, WhitelistedAddr: "0xaf", BackendOpts: []backendmock.Option{ backendmock.WithBalanceAt(func(ctx context.Context, address common.Address, block *big.Int) (*big.Int, error) { @@ -240,7 +231,6 @@ func TestWalletWithdraw(t *testing.T) { t.Parallel() srv, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, WhitelistedAddr: "0xaf", BackendOpts: []backendmock.Option{ backendmock.WithBalanceAt(func(ctx context.Context, address common.Address, block *big.Int) (*big.Int, error) { @@ -262,7 +252,6 @@ func TestWalletWithdraw(t *testing.T) { txHash := common.HexToHash("0x00f") srv, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, WhitelistedAddr: "0xaf", BackendOpts: []backendmock.Option{ backendmock.WithBalanceAt(func(ctx context.Context, address common.Address, block *big.Int) (*big.Int, error) { diff --git a/pkg/api/welcome_message_test.go b/pkg/api/welcome_message_test.go index 39d7c19a039..6eb6670e8ab 100644 --- a/pkg/api/welcome_message_test.go +++ b/pkg/api/welcome_message_test.go @@ -23,7 +23,6 @@ func TestGetWelcomeMessage(t *testing.T) { const DefaultTestWelcomeMessage = "Hello World!" srv, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, P2P: mock.New(mock.WithGetWelcomeMessageFunc(func() string { return DefaultTestWelcomeMessage }))}) @@ -75,8 +74,7 @@ func TestSetWelcomeMessage(t *testing.T) { mockP2P := mock.New() srv, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, - P2P: mockP2P, + P2P: mockP2P, }) if tC.wantMessage == "" { @@ -112,7 +110,6 @@ func TestSetWelcomeMessageInternalServerError(t *testing.T) { testURL := "/welcome-message" srv, _, _, _ := newTestServer(t, testServerOptions{ - DebugAPI: true, P2P: mock.New(mock.WithSetWelcomeMessageFunc(func(string) error { return testError })), diff --git a/pkg/node/devnode.go b/pkg/node/devnode.go index 1b090654295..2c64f990591 100644 --- a/pkg/node/devnode.go +++ b/pkg/node/devnode.go @@ -68,13 +68,11 @@ type DevBee struct { pssCloser io.Closer errorLogWriter io.Writer apiServer *http.Server - debugAPIServer *http.Server } type DevOptions struct { Logger log.Logger APIAddr string - DebugAPIAddr string CORSAllowedOrigins []string DBOpenFilesLimit uint64 ReserveCapacity uint64 @@ -194,37 +192,6 @@ func NewDevBee(logger log.Logger, o *DevOptions) (b *DevBee, err error) { } }(probe) - var debugApiService *api.Service - - if o.DebugAPIAddr != "" { - debugAPIListener, err := net.Listen("tcp", o.DebugAPIAddr) - if err != nil { - return nil, fmt.Errorf("debug api listener: %w", err) - } - - debugApiService = api.New(mockKey.PublicKey, mockKey.PublicKey, overlayEthAddress, nil, logger, mockTransaction, batchStore, api.DevMode, true, true, chainBackend, o.CORSAllowedOrigins, inmemstore.New()) - debugAPIServer := &http.Server{ - IdleTimeout: 30 * time.Second, - ReadHeaderTimeout: 3 * time.Second, - Handler: debugApiService, - ErrorLog: stdlog.New(b.errorLogWriter, "", 0), - } - - debugApiService.MountTechnicalDebug() - debugApiService.SetProbe(probe) - - go func() { - logger.Info("starting debug api server", "address", debugAPIListener.Addr()) - - if err := debugAPIServer.Serve(debugAPIListener); err != nil && !errors.Is(err, http.ErrServerClosed) { - logger.Debug("debug api server failed to start", "error", err) - logger.Error(nil, "debug api server failed to start") - } - }() - - b.debugAPIServer = debugAPIServer - } - localStore, err := storer.New(context.Background(), "", &storer.Options{ Logger: logger, CacheCapacity: 1_000_000, @@ -406,24 +373,12 @@ func NewDevBee(logger log.Logger, o *DevOptions) (b *DevBee, err error) { Restricted: o.Restricted, }, debugOpts, 1, erc20) apiService.MountTechnicalDebug() + apiService.MountDebug() apiService.MountAPI() - apiService.SetProbe(probe) + apiService.SetProbe(probe) apiService.SetP2P(p2ps) apiService.SetSwarmAddress(&swarmAddress) - apiService.MountDebug() - - if o.DebugAPIAddr != "" { - debugApiService.SetP2P(p2ps) - debugApiService.SetSwarmAddress(&swarmAddress) - debugApiService.MountDebug() - - debugApiService.Configure(signer, authenticator, tracer, api.Options{ - CORSAllowedOrigins: o.CORSAllowedOrigins, - WsPingPeriod: 60 * time.Second, - Restricted: o.Restricted, - }, debugOpts, 1, erc20) - } apiListener, err := net.Listen("tcp", o.APIAddr) if err != nil { @@ -477,15 +432,6 @@ func (b *DevBee) Shutdown() error { return nil }) } - if b.debugAPIServer != nil { - eg.Go(func() error { - if err := b.debugAPIServer.Shutdown(ctx); err != nil { - return fmt.Errorf("debug api server: %w", err) - } - return nil - }) - } - if err := eg.Wait(); err != nil { mErr = multierror.Append(mErr, err) } diff --git a/pkg/node/node.go b/pkg/node/node.go index fa3f3f861b5..f50574cdecc 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -89,7 +89,6 @@ type Bee struct { ctxCancel context.CancelFunc apiCloser io.Closer apiServer *http.Server - debugAPIServer *http.Server resolverCloser io.Closer errorLogWriter io.Writer tracerCloser io.Closer @@ -127,7 +126,6 @@ type Options struct { DBBlockCacheCapacity uint64 DBDisableSeeksCompaction bool APIAddr string - DebugAPIAddr string Addr string NATAddr string EnableWS bool @@ -379,7 +377,6 @@ func NewBee( logger.Info("starting with restricted APIs") } - // set up basic debug api endpoints for debugging and /health endpoint beeNodeMode := api.LightMode if o.FullNodeMode { beeNodeMode = api.FullMode @@ -405,59 +402,6 @@ func NewBee( b.stamperStoreCloser = stamperStore var apiService *api.Service - var debugService *api.Service - - if o.DebugAPIAddr != "" { - if o.MutexProfile { - _ = runtime.SetMutexProfileFraction(1) - } - - if o.BlockProfile { - runtime.SetBlockProfileRate(1) - } - - debugAPIListener, err := net.Listen("tcp", o.DebugAPIAddr) - if err != nil { - return nil, fmt.Errorf("debug api listener: %w", err) - } - - debugService = api.New( - *publicKey, - pssPrivateKey.PublicKey, - overlayEthAddress, - o.WhitelistedWithdrawalAddress, - logger, - transactionService, - batchStore, - beeNodeMode, - o.ChequebookEnable, - o.SwapEnable, - chainBackend, - o.CORSAllowedOrigins, - stamperStore, - ) - debugService.Restricted = o.Restricted - debugService.MountTechnicalDebug() - debugService.SetProbe(probe) - - debugAPIServer := &http.Server{ - IdleTimeout: 30 * time.Second, - ReadHeaderTimeout: 3 * time.Second, - Handler: debugService, - ErrorLog: stdlog.New(b.errorLogWriter, "", 0), - } - - go func() { - logger.Info("starting debug server", "address", debugAPIListener.Addr()) - - if err := debugAPIServer.Serve(debugAPIListener); err != nil && !errors.Is(err, http.ErrServerClosed) { - logger.Debug("debug api server failed to start", "error", err) - logger.Error(nil, "debug api server failed to start") - } - }() - - b.debugAPIServer = debugAPIServer - } if o.APIAddr != "" { if o.MutexProfile { @@ -635,9 +579,7 @@ func NewBee( var registry *promc.Registry - if debugService != nil { - registry = debugService.MetricsRegistry() - } else if apiService != nil { + if apiService != nil { registry = apiService.MetricsRegistry() } @@ -1153,73 +1095,13 @@ func NewBee( Restricted: o.Restricted, }, extraOpts, chainID, erc20Service) - apiService.MountAPI() apiService.MountDebug() + apiService.MountAPI() apiService.SetSwarmAddress(&swarmAddress) apiService.SetRedistributionAgent(agent) } - if o.DebugAPIAddr != "" { - // register metrics from components - debugService.MustRegisterMetrics(p2ps.Metrics()...) - debugService.MustRegisterMetrics(pingPong.Metrics()...) - debugService.MustRegisterMetrics(acc.Metrics()...) - debugService.MustRegisterMetrics(localStore.Metrics()...) - debugService.MustRegisterMetrics(kad.Metrics()...) - debugService.MustRegisterMetrics(saludService.Metrics()...) - debugService.MustRegisterMetrics(stateStoreMetrics.Metrics()...) - - if pullerService != nil { - debugService.MustRegisterMetrics(pullerService.Metrics()...) - } - - if agent != nil { - debugService.MustRegisterMetrics(agent.Metrics()...) - } - - debugService.MustRegisterMetrics(pushSyncProtocol.Metrics()...) - debugService.MustRegisterMetrics(pusherService.Metrics()...) - debugService.MustRegisterMetrics(pullSyncProtocol.Metrics()...) - debugService.MustRegisterMetrics(retrieval.Metrics()...) - debugService.MustRegisterMetrics(lightNodes.Metrics()...) - debugService.MustRegisterMetrics(hive.Metrics()...) - - if bs, ok := batchStore.(metrics.Collector); ok { - debugService.MustRegisterMetrics(bs.Metrics()...) - } - if ls, ok := eventListener.(metrics.Collector); ok { - debugService.MustRegisterMetrics(ls.Metrics()...) - } - if pssServiceMetrics, ok := pssService.(metrics.Collector); ok { - debugService.MustRegisterMetrics(pssServiceMetrics.Metrics()...) - } - if swapBackendMetrics, ok := chainBackend.(metrics.Collector); ok { - debugService.MustRegisterMetrics(swapBackendMetrics.Metrics()...) - } - if apiService != nil { - debugService.MustRegisterMetrics(apiService.Metrics()...) - } - if l, ok := logger.(metrics.Collector); ok { - debugService.MustRegisterMetrics(l.Metrics()...) - } - debugService.MustRegisterMetrics(pseudosettleService.Metrics()...) - if swapService != nil { - debugService.MustRegisterMetrics(swapService.Metrics()...) - } - - debugService.Configure(signer, authenticator, tracer, api.Options{ - CORSAllowedOrigins: o.CORSAllowedOrigins, - WsPingPeriod: 60 * time.Second, - Restricted: o.Restricted, - }, extraOpts, chainID, erc20Service) - - debugService.SetP2P(p2ps) - debugService.SetSwarmAddress(&swarmAddress) - debugService.MountDebug() - debugService.SetRedistributionAgent(agent) - } - if err := kad.Start(ctx); err != nil { return nil, err } @@ -1283,15 +1165,6 @@ func (b *Bee) Shutdown() error { return nil }) } - if b.debugAPIServer != nil { - eg.Go(func() error { - if err := b.debugAPIServer.Shutdown(ctx); err != nil { - return fmt.Errorf("debug api server: %w", err) - } - return nil - }) - } - if err := eg.Wait(); err != nil { mErr = multierror.Append(mErr, err) } diff --git a/pkg/topology/kademlia/doc.go b/pkg/topology/kademlia/doc.go index 39fb7c25624..2407d289535 100644 --- a/pkg/topology/kademlia/doc.go +++ b/pkg/topology/kademlia/doc.go @@ -10,7 +10,7 @@ A thorough explanation of the logic in the `manage()` forever loop: The `manageC` channel gets triggered every time there's a change in the information regarding peers we know about. This can be a result of: (1) A peer has disconnected from us (2) A peer has been added to the list of -known peers (from discovery, debugapi, bootnode flag or just because it +known peers (from discovery, api, bootnode flag or just because it was persisted in the address book and the node has been restarted). So the information has been changed, and potentially upon disconnection, From 8c61408db7cd63227cfe161967e3b70f90b9fde3 Mon Sep 17 00:00:00 2001 From: Acha Bill <57879913+acha-bill@users.noreply.github.com> Date: Fri, 31 May 2024 15:38:10 +0100 Subject: [PATCH 08/41] feat: remove auth (#4679) --- .github/workflows/beekeeper.yml | 3 - cmd/bee/cmd/bcrypt.go | 73 ------- cmd/bee/cmd/cmd.go | 10 - cmd/bee/cmd/start.go | 3 - cmd/bee/cmd/start_dev.go | 6 - go.mod | 3 - go.sum | 7 - openapi/Swarm.yaml | 101 +--------- pkg/api/api.go | 120 +----------- pkg/api/api_test.go | 17 +- pkg/api/auth_test.go | 116 ----------- pkg/api/export_test.go | 2 - pkg/api/router.go | 25 +-- pkg/auth/auth.go | 335 -------------------------------- pkg/auth/auth_test.go | 154 --------------- pkg/auth/handler.go | 56 ------ pkg/auth/main_test.go | 15 -- pkg/auth/mock/auth.go | 35 ---- pkg/node/devnode.go | 16 +- pkg/node/node.go | 17 +- 20 files changed, 8 insertions(+), 1106 deletions(-) delete mode 100644 cmd/bee/cmd/bcrypt.go delete mode 100644 pkg/api/auth_test.go delete mode 100644 pkg/auth/auth.go delete mode 100644 pkg/auth/auth_test.go delete mode 100644 pkg/auth/handler.go delete mode 100644 pkg/auth/main_test.go delete mode 100644 pkg/auth/mock/auth.go diff --git a/.github/workflows/beekeeper.yml b/.github/workflows/beekeeper.yml index 17117b83cc4..71c069a2366 100644 --- a/.github/workflows/beekeeper.yml +++ b/.github/workflows/beekeeper.yml @@ -154,9 +154,6 @@ jobs: - name: Test manifest id: manifest run: timeout ${TIMEOUT} beekeeper check --cluster-name local-dns --checks=ci-manifest - - name: Test authenticate - id: authenticate - run: timeout ${TIMEOUT} bash -c 'until beekeeper check --cluster-name local-dns --checks ci-authenticate; do echo "waiting for auth..."; sleep .3; done' - name: Test postage stamps id: postage-stamps run: timeout ${TIMEOUT} beekeeper check --cluster-name local-dns --checks ci-postage diff --git a/cmd/bee/cmd/bcrypt.go b/cmd/bee/cmd/bcrypt.go deleted file mode 100644 index 94c48a86d37..00000000000 --- a/cmd/bee/cmd/bcrypt.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2022 The Swarm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package cmd - -import ( - "errors" - "fmt" - - "github.com/spf13/cobra" - "golang.org/x/crypto/bcrypt" -) - -func (c *command) initHasherCmd() (err error) { - cmd := &cobra.Command{ - Use: "bcrypt", - Short: "Generate or validate a bcrypt hash", - Long: `Generate or validate a bcrypt hash - -Takes a single plain text argument in order to generate a bcrypt hash. -If '--check' flag is provided it will validate the first (plain text) argument against -the second one, which is expected to be a quoted bcrypt hash.`, - Example: ` -$> bee bcrypt super$ecret -$2a$10$eZP5YuhJq2k8DFmj9UJGWOIjDtXu6NcAQMrz7Zj1bgIVBcHA3bU5u - -$> bee bcrypt --check super$ecret '$2a$10$eZP5YuhJq2k8DFmj9UJGWOIjDtXu6NcAQMrz7Zj1bgIVBcHA3bU5u' -OK: password hash matches provided plain text`, - RunE: func(cmd *cobra.Command, args []string) error { - if len(args) < 1 || len(args) > 2 { - return cmd.Help() - } - - isCheck := c.config.GetBool("check") - - if isCheck { - if len(args) != 2 { - fmt.Println("Usage:", "bee bcrypt", "--check", "your-plain-text-password", "'password-hash'") - return nil - } - - err := bcrypt.CompareHashAndPassword([]byte(args[1]), []byte(args[0])) - if err != nil { - return errors.New("password hash does not match provided plain text") - } - - fmt.Println("OK: password hash matches provided plain text") - return nil - } - - if len(args) != 1 { - return cmd.Help() - } - - hashed, err := bcrypt.GenerateFromPassword([]byte(args[0]), bcrypt.DefaultCost) - if err != nil { - return errors.New("failed to generate password hash") - } - - fmt.Print(string(hashed)) - return nil - }, - PreRunE: func(cmd *cobra.Command, args []string) error { - return c.config.BindPFlags(cmd.Flags()) - }, - } - - cmd.Flags().Bool("check", false, "validate existing hash") - - c.root.AddCommand(cmd) - return nil -} diff --git a/cmd/bee/cmd/cmd.go b/cmd/bee/cmd/cmd.go index 964bb0754c1..f6028e878ec 100644 --- a/cmd/bee/cmd/cmd.go +++ b/cmd/bee/cmd/cmd.go @@ -76,9 +76,6 @@ const ( optionNameStaticNodes = "static-nodes" optionNameAllowPrivateCIDRs = "allow-private-cidrs" optionNameSleepAfter = "sleep-after" - optionNameRestrictedAPI = "restricted" - optionNameTokenEncryptionKey = "token-encryption-key" - optionNameAdminPasswordHash = "admin-password" optionNameUsePostageSnapshot = "use-postage-snapshot" optionNameStorageIncentivesEnable = "storage-incentives-enable" optionNameStateStoreCacheCapacity = "statestore-cache-capacity" @@ -142,10 +139,6 @@ func newCommand(opts ...option) (c *command, err error) { return nil, err } - if err := c.initHasherCmd(); err != nil { - return nil, err - } - if err := c.initInitCmd(); err != nil { return nil, err } @@ -293,9 +286,6 @@ func (c *command) setAllFlags(cmd *cobra.Command) { cmd.Flags().Bool(optionNamePProfMutex, false, "enable pprof mutex profile") cmd.Flags().StringSlice(optionNameStaticNodes, []string{}, "protect nodes from getting kicked out on bootnode") cmd.Flags().Bool(optionNameAllowPrivateCIDRs, false, "allow to advertise private CIDRs to the public network") - cmd.Flags().Bool(optionNameRestrictedAPI, false, "enable permission check on the http APIs") - cmd.Flags().String(optionNameTokenEncryptionKey, "", "admin username to get the security token") - cmd.Flags().String(optionNameAdminPasswordHash, "", "bcrypt hash of the admin password to get the security token") cmd.Flags().Bool(optionNameUsePostageSnapshot, false, "bootstrap node using postage snapshot from the network") cmd.Flags().Bool(optionNameStorageIncentivesEnable, true, "enable storage incentives feature") cmd.Flags().Uint64(optionNameStateStoreCacheCapacity, 100_000, "lru memory caching capacity in number of statestore entries") diff --git a/cmd/bee/cmd/start.go b/cmd/bee/cmd/start.go index d69be019c05..56dda6a2591 100644 --- a/cmd/bee/cmd/start.go +++ b/cmd/bee/cmd/start.go @@ -331,9 +331,6 @@ func buildBeeNode(ctx context.Context, c *command, cmd *cobra.Command, logger lo MutexProfile: c.config.GetBool(optionNamePProfMutex), StaticNodes: staticNodes, AllowPrivateCIDRs: c.config.GetBool(optionNameAllowPrivateCIDRs), - Restricted: c.config.GetBool(optionNameRestrictedAPI), - TokenEncryptionKey: c.config.GetString(optionNameTokenEncryptionKey), - AdminPasswordHash: c.config.GetString(optionNameAdminPasswordHash), UsePostageSnapshot: c.config.GetBool(optionNameUsePostageSnapshot), EnableStorageIncentives: c.config.GetBool(optionNameStorageIncentivesEnable), StatestoreCacheCapacity: c.config.GetUint64(optionNameStateStoreCacheCapacity), diff --git a/cmd/bee/cmd/start_dev.go b/cmd/bee/cmd/start_dev.go index 189363a455d..c53158daeaa 100644 --- a/cmd/bee/cmd/start_dev.go +++ b/cmd/bee/cmd/start_dev.go @@ -67,9 +67,6 @@ func (c *command) initStartDevCmd() (err error) { DBDisableSeeksCompaction: c.config.GetBool(optionNameDBDisableSeeksCompaction), CORSAllowedOrigins: c.config.GetStringSlice(optionCORSAllowedOrigins), ReserveCapacity: c.config.GetUint64(optionNameDevReserveCapacity), - Restricted: c.config.GetBool(optionNameRestrictedAPI), - TokenEncryptionKey: c.config.GetString(optionNameTokenEncryptionKey), - AdminPasswordHash: c.config.GetString(optionNameAdminPasswordHash), }) if err != nil { return err @@ -142,9 +139,6 @@ func (c *command) initStartDevCmd() (err error) { cmd.Flags().Uint64(optionNameDBBlockCacheCapacity, 32*1024*1024, "size of block cache of the database in bytes") cmd.Flags().Uint64(optionNameDBWriteBufferSize, 32*1024*1024, "size of the database write buffer in bytes") cmd.Flags().Bool(optionNameDBDisableSeeksCompaction, false, "disables db compactions triggered by seeks") - cmd.Flags().Bool(optionNameRestrictedAPI, false, "enable permission check on the http APIs") - cmd.Flags().String(optionNameTokenEncryptionKey, "", "security token encryption hash") - cmd.Flags().String(optionNameAdminPasswordHash, "$2a$10$Maw2HUQjcUINtqdnasOs1ee5MtQl7jxnkv2GqCGfbytAiCElzcbYC", "bcrypt hash of the admin password to get the security token") c.root.AddCommand(cmd) return nil diff --git a/go.mod b/go.mod index 705c161b9f3..da46b1d2016 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( contrib.go.opencensus.io/exporter/prometheus v0.4.2 github.com/armon/go-radix v1.0.0 github.com/btcsuite/btcd/btcec/v2 v2.3.2 - github.com/casbin/casbin/v2 v2.35.0 github.com/coreos/go-semver v0.3.0 github.com/ethereum/go-ethereum v1.14.3 github.com/ethersphere/go-price-oracle-abi v0.2.0 @@ -60,7 +59,6 @@ require ( require ( github.com/BurntSushi/toml v1.1.0 // indirect - github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/StackExchange/wmi v1.2.1 // indirect github.com/benbjohnson/clock v1.3.5 // indirect @@ -92,7 +90,6 @@ require ( github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/mock v1.6.0 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 // indirect diff --git a/go.sum b/go.sum index 2e18ee9e929..956c674a405 100644 --- a/go.sum +++ b/go.sum @@ -67,8 +67,6 @@ github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/HdrHistogram/hdrhistogram-go v0.0.0-20200919145931-8dac23c8dac1 h1:nEjGZtKHMK92888VT6XkzKwyiW14v5FFRGeWq2uV7N0= github.com/HdrHistogram/hdrhistogram-go v0.0.0-20200919145931-8dac23c8dac1/go.mod h1:nxrse8/Tzg2tg3DZcZjm6qEclQKK70g0KxO61gFFZD4= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= -github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -140,8 +138,6 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= -github.com/casbin/casbin/v2 v2.35.0 h1:f0prVg9LgTJTihjAxWEZhfJptXvah1GpZh12sb5KXNA= -github.com/casbin/casbin/v2 v2.35.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU= @@ -336,8 +332,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.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 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= @@ -1285,7 +1279,6 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= diff --git a/openapi/Swarm.yaml b/openapi/Swarm.yaml index 7bcd224a7d0..2ef77a999ef 100644 --- a/openapi/Swarm.yaml +++ b/openapi/Swarm.yaml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: - version: 6.0.0 + version: 7.0.0 title: Bee API description: "A list of the currently provided Interfaces to interact with the swarm, implementing file operations and sending messages" @@ -31,64 +31,6 @@ servers: description: Service port provided in bee node config paths: - "/auth": - post: - summary: "Authenticate - This endpoint is experimental" - tags: - - Auth - security: - - basicAuth: [ ] - requestBody: - required: true - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/SecurityTokenRequest" - responses: - "201": - description: Ok - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/SecurityTokenResponse" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - "401": - $ref: "SwarmCommon.yaml#/components/responses/401" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - - "/refresh": - post: - summary: "Refresh the auth token - This endpoint is experimental" - tags: - - Auth - security: - - bearerAuth: [ ] - requestBody: - required: true - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/SecurityTokenRequest" - responses: - "201": - description: Ok - content: - application/json: - schema: - $ref: "SwarmCommon.yaml#/components/schemas/SecurityTokenResponse" - "400": - $ref: "SwarmCommon.yaml#/components/responses/400" - "401": - $ref: "SwarmCommon.yaml#/components/responses/401" - "500": - $ref: "SwarmCommon.yaml#/components/responses/500" - default: - description: Default response - "/bytes": post: summary: "Upload data" @@ -930,7 +872,6 @@ paths: "/addresses": get: summary: Get overlay and underlay addresses of the node - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. tags: - Connectivity responses: @@ -982,7 +923,6 @@ paths: "/balances": get: summary: Get the balances with all known peers including prepaid services - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1002,7 +942,6 @@ paths: "/balances/{address}": get: summary: Get the balances with a specific peer including prepaid services - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1031,7 +970,6 @@ paths: "/blocklist": get: summary: Get a list of blocklisted peers - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1051,7 +989,6 @@ paths: "/consumed": get: summary: Get the past due consumption balances with all known peers - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1071,7 +1008,6 @@ paths: "/consumed/{address}": get: summary: Get the past due consumption balance with a specific peer - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1100,7 +1036,6 @@ paths: "/chequebook/address": get: summary: Get the address of the chequebook contract used - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1116,7 +1051,6 @@ paths: "/chequebook/balance": get: summary: Get the balance of the chequebook - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1190,7 +1124,6 @@ paths: "/connect/{multiAddress}": post: summary: Connect to address - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1220,7 +1153,6 @@ paths: "/reservestate": get: summary: Get reserve state - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1238,7 +1170,6 @@ paths: "/chainstate": get: summary: Get chain state - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1256,7 +1187,6 @@ paths: "/node": get: summary: Get information about the node - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. tags: - Status responses: @@ -1272,7 +1202,6 @@ paths: "/peers": get: summary: Get a list of peers - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1290,7 +1219,6 @@ paths: "/peers/{address}": delete: summary: Remove peer - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1319,7 +1247,6 @@ paths: "/pingpong/{address}": post: summary: Try connection to node - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1350,7 +1277,6 @@ paths: "/settlements/{address}": get: summary: Get amount of sent and received from settlements with a peer - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1379,7 +1305,6 @@ paths: "/settlements": get: summary: Get settlements with all known peers and total amount sent or received - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1399,7 +1324,6 @@ paths: "/timesettlements": get: summary: Get time based settlements with all known peers and total amount sent or received - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1419,7 +1343,6 @@ paths: "/topology": get: summary: Get topology of known network - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1435,7 +1358,6 @@ paths: "/welcome-message": get: summary: Get configured P2P welcome message - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1453,7 +1375,6 @@ paths: description: Default response post: summary: Set P2P welcome message - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1480,7 +1401,6 @@ paths: "/chequebook/cashout/{peer-id}": get: summary: Get last cashout action for the peer - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] parameters: @@ -1507,7 +1427,6 @@ paths: description: Default response post: summary: Cashout the last cheque for the peer - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] parameters: @@ -1540,7 +1459,6 @@ paths: "/chequebook/cheque/{peer-id}": get: summary: Get last cheques for the peer - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] parameters: @@ -1569,7 +1487,6 @@ paths: "/chequebook/cheque": get: summary: Get last cheques for all peers - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1591,7 +1508,6 @@ paths: "/chequebook/deposit": post: summary: Deposit tokens from overlay address into chequebook - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] parameters: @@ -1621,7 +1537,6 @@ paths: "/chequebook/withdraw": post: summary: Withdraw tokens from the chequebook to the overlay address - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] parameters: @@ -1651,7 +1566,6 @@ paths: "/transactions": get: summary: Get list of pending transactions - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. tags: - Transaction responses: @@ -1669,7 +1583,6 @@ paths: "/transactions/{txHash}": get: summary: Get information about a sent transaction - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. parameters: - in: path name: txHash @@ -1694,7 +1607,6 @@ paths: description: Default response post: summary: Rebroadcast existing transaction - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. parameters: - in: path name: txHash @@ -1719,7 +1631,6 @@ paths: description: Default response delete: summary: Cancel existing transaction - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. parameters: - in: path name: txHash @@ -1747,7 +1658,6 @@ paths: "/stamps": get: summary: Get stamps for this node - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1775,7 +1685,6 @@ paths: description: Swarm address of the stamp get: summary: Get an individual postage batch status - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1804,7 +1713,6 @@ paths: description: Swarm address of the stamp get: summary: Get extended bucket data of a batch - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: @@ -1830,8 +1738,6 @@ paths: - bearerAuth: [ ] description: | Be aware, this endpoint creates an on-chain transactions and transfers BZZ from the node's Ethereum account and hence directly manipulates the wallet balance! - - This endpoint can be restricted if the node is spawned with the `--restricted` flag. tags: - Postage Stamps parameters: @@ -1881,8 +1787,6 @@ paths: summary: Top up an existing postage batch. description: | Be aware, this endpoint creates on-chain transactions and transfers BZZ from the node's Ethereum account and hence directly manipulates the wallet balance! - - This endpoint can be restricted if the node is spawned with the `--restricted` flag. tags: - Postage Stamps parameters: @@ -1923,8 +1827,6 @@ paths: summary: Dilute an existing postage batch. description: | Be aware, this endpoint creates on-chain transactions and transfers BZZ from the node's Ethereum account and hence directly manipulates the wallet balance! - - This endpoint can be restricted if the node is spawned with the `--restricted` flag. tags: - Postage Stamps parameters: @@ -1961,7 +1863,6 @@ paths: "/batches": get: summary: Get all globally available batches that were purchased by all nodes. - description: This endpoint can be restricted if the node is spawned with the `--restricted` flag. security: - bearerAuth: [ ] tags: diff --git a/pkg/api/api.go b/pkg/api/api.go index fec8ab44afe..a7eea80cb30 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -11,7 +11,6 @@ import ( "crypto/ecdsa" "encoding/base64" "encoding/hex" - "encoding/json" "errors" "fmt" "io" @@ -28,7 +27,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethersphere/bee/v2/pkg/accounting" - "github.com/ethersphere/bee/v2/pkg/auth" "github.com/ethersphere/bee/v2/pkg/crypto" "github.com/ethersphere/bee/v2/pkg/feeds" "github.com/ethersphere/bee/v2/pkg/file/pipeline" @@ -136,7 +134,6 @@ type PinIntegrity interface { } type Service struct { - auth auth.Authenticator storer Storer resolver resolver.Interface pss pss.Interface @@ -227,7 +224,6 @@ func (s *Service) SetRedistributionAgent(redistributionAgent *storageincentives. type Options struct { CORSAllowedOrigins []string WsPingPeriod time.Duration - Restricted bool } type ExtraOptions struct { @@ -313,8 +309,7 @@ func New( } // Configure will create a and initialize a new API service. -func (s *Service) Configure(signer crypto.Signer, auth auth.Authenticator, tracer *tracing.Tracer, o Options, e ExtraOptions, chainID int64, erc20 erc20.Service) { - s.auth = auth +func (s *Service) Configure(signer crypto.Signer, tracer *tracing.Tracer, o Options, e ExtraOptions, chainID int64, erc20 erc20.Service) { s.signer = signer s.Options = o s.tracer = tracer @@ -429,119 +424,6 @@ func (s *Service) resolveNameOrAddress(str string) (swarm.Address, error) { return swarm.ZeroAddress, fmt.Errorf("%w: %w", errInvalidNameOrAddress, err) } -type securityTokenRsp struct { - Key string `json:"key"` -} - -type securityTokenReq struct { - Role string `json:"role"` - Expiry int `json:"expiry"` // duration in seconds -} - -func (s *Service) authHandler(w http.ResponseWriter, r *http.Request) { - _, pass, ok := r.BasicAuth() - - if !ok { - s.logger.Error(nil, "auth handler: missing basic auth") - w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) - jsonhttp.Unauthorized(w, "Unauthorized") - return - } - - if !s.auth.Authorize(pass) { - s.logger.Error(nil, "auth handler: unauthorized") - w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`) - jsonhttp.Unauthorized(w, "Unauthorized") - return - } - - body, err := io.ReadAll(r.Body) - if err != nil { - s.logger.Debug("auth handler: read request body failed", "error", err) - s.logger.Error(nil, "auth handler: read request body failed") - jsonhttp.BadRequest(w, "Read request body") - return - } - - var payload securityTokenReq - if err = json.Unmarshal(body, &payload); err != nil { - s.logger.Debug("auth handler: unmarshal request body failed", "error", err) - s.logger.Error(nil, "auth handler: unmarshal request body failed") - jsonhttp.BadRequest(w, "Unmarshal json body") - return - } - - key, err := s.auth.GenerateKey(payload.Role, time.Duration(payload.Expiry)*time.Second) - if errors.Is(err, auth.ErrExpiry) { - s.logger.Debug("auth handler: generate key failed", "error", err) - s.logger.Error(nil, "auth handler: generate key failed") - jsonhttp.BadRequest(w, "Expiry duration must be a positive number") - return - } - if err != nil { - s.logger.Debug("auth handler: add auth token failed", "error", err) - s.logger.Error(nil, "auth handler: add auth token failed") - jsonhttp.InternalServerError(w, "Error generating authorization token") - return - } - - jsonhttp.Created(w, securityTokenRsp{ - Key: key, - }) -} - -func (s *Service) refreshHandler(w http.ResponseWriter, r *http.Request) { - reqToken := r.Header.Get(AuthorizationHeader) - if !strings.HasPrefix(reqToken, "Bearer ") { - jsonhttp.Forbidden(w, "Missing bearer token") - return - } - - keys := strings.Split(reqToken, "Bearer ") - - if len(keys) != 2 || strings.Trim(keys[1], " ") == "" { - jsonhttp.Forbidden(w, "Missing security token") - return - } - - authToken := keys[1] - - body, err := io.ReadAll(r.Body) - if err != nil { - s.logger.Debug("auth handler: read request body failed", "error", err) - s.logger.Error(nil, "auth handler: read request body failed") - jsonhttp.BadRequest(w, "Read request body") - return - } - - var payload securityTokenReq - if err = json.Unmarshal(body, &payload); err != nil { - s.logger.Debug("auth handler: unmarshal request body failed", "error", err) - s.logger.Error(nil, "auth handler: unmarshal request body failed") - jsonhttp.BadRequest(w, "Unmarshal json body") - return - } - - key, err := s.auth.RefreshKey(authToken, time.Duration(payload.Expiry)*time.Second) - if errors.Is(err, auth.ErrTokenExpired) { - s.logger.Debug("auth handler: refresh key failed", "error", err) - s.logger.Error(nil, "auth handler: refresh key failed") - jsonhttp.BadRequest(w, "Token expired") - return - } - - if err != nil { - s.logger.Debug("auth handler: refresh token failed", "error", err) - s.logger.Error(nil, "auth handler: refresh token failed") - jsonhttp.InternalServerError(w, "Error refreshing authorization token") - return - } - - jsonhttp.Created(w, securityTokenRsp{ - Key: key, - }) -} - func (s *Service) newTracingHandler(spanName string) func(h http.Handler) http.Handler { return func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/api/api_test.go b/pkg/api/api_test.go index bf2c5288c5a..b8c8044998a 100644 --- a/pkg/api/api_test.go +++ b/pkg/api/api_test.go @@ -25,8 +25,6 @@ import ( "github.com/ethereum/go-ethereum/common" accountingmock "github.com/ethersphere/bee/v2/pkg/accounting/mock" "github.com/ethersphere/bee/v2/pkg/api" - "github.com/ethersphere/bee/v2/pkg/auth" - mockauth "github.com/ethersphere/bee/v2/pkg/auth/mock" "github.com/ethersphere/bee/v2/pkg/crypto" "github.com/ethersphere/bee/v2/pkg/feeds" "github.com/ethersphere/bee/v2/pkg/file/pipeline" @@ -104,8 +102,6 @@ type testServerOptions struct { Post postage.Service Steward steward.Interface WsHeaders http.Header - Authenticator auth.Authenticator - Restricted bool DirectUpload bool Probe *api.Probe @@ -157,13 +153,7 @@ func newTestServer(t *testing.T, o testServerOptions) (*http.Client, *websocket. if o.SyncStatus == nil { o.SyncStatus = func() (bool, error) { return true, nil } } - if o.Authenticator == nil { - o.Authenticator = &mockauth.Auth{ - EnforceFunc: func(_, _, _ string) (bool, error) { - return true, nil - }, - } - } + var chanStore *chanStorer topologyDriver := topologymock.NewTopologyDriver(o.TopologyOpts...) @@ -229,10 +219,9 @@ func newTestServer(t *testing.T, o testServerOptions) (*http.Client, *websocket. }) testutil.CleanupCloser(t, tracerCloser) - s.Configure(signer, o.Authenticator, noOpTracer, api.Options{ + s.Configure(signer, noOpTracer, api.Options{ CORSAllowedOrigins: o.CORSAllowedOrigins, WsPingPeriod: o.WsPingPeriod, - Restricted: o.Restricted, }, extraOpts, 1, erc20) s.MountTechnicalDebug() @@ -380,7 +369,7 @@ func TestParseName(t *testing.T) { signer := crypto.NewDefaultSigner(pk) s := api.New(pk.PublicKey, pk.PublicKey, common.Address{}, nil, log, nil, nil, 1, false, false, nil, []string{"*"}, inmemstore.New()) - s.Configure(signer, nil, nil, api.Options{}, api.ExtraOptions{Resolver: tC.res}, 1, nil) + s.Configure(signer, nil, api.Options{}, api.ExtraOptions{Resolver: tC.res}, 1, nil) s.MountAPI() tC := tC diff --git a/pkg/api/auth_test.go b/pkg/api/auth_test.go deleted file mode 100644 index f2cb0a99d59..00000000000 --- a/pkg/api/auth_test.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2021 The Swarm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package api_test - -import ( - "errors" - "net/http" - "testing" - - "github.com/ethersphere/bee/v2/pkg/api" - "github.com/ethersphere/bee/v2/pkg/auth/mock" - "github.com/ethersphere/bee/v2/pkg/jsonhttp" - "github.com/ethersphere/bee/v2/pkg/jsonhttp/jsonhttptest" - "github.com/ethersphere/bee/v2/pkg/log" -) - -// nolint:paralleltest -func TestAuth(t *testing.T) { - var ( - resource = "/auth" - logger = log.Noop - authenticator = &mock.Auth{ - AuthorizeFunc: func(string) bool { return true }, - GenerateKeyFunc: func(string) (string, error) { return "123", nil }, - } - client, _, _, _ = newTestServer(t, testServerOptions{ - Logger: logger, - Restricted: true, - Authenticator: authenticator, - }) - ) - - t.Run("missing authorization header", func(t *testing.T) { - jsonhttptest.Request(t, client, http.MethodPost, resource, http.StatusUnauthorized, - jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ - Message: "Unauthorized", - Code: http.StatusUnauthorized, - }), - ) - }) - t.Run("missing role", func(t *testing.T) { - jsonhttptest.Request(t, client, http.MethodPost, resource, http.StatusBadRequest, - jsonhttptest.WithRequestHeader(api.AuthorizationHeader, "Basic dGVzdDp0ZXN0"), - jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ - Message: "Unmarshal json body", - Code: http.StatusBadRequest, - }), - ) - }) - t.Run("bad authorization header", func(t *testing.T) { - jsonhttptest.Request(t, client, http.MethodPost, resource, http.StatusUnauthorized, - jsonhttptest.WithRequestHeader(api.AuthorizationHeader, "Basic dGV"), - jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ - Message: "Unauthorized", - Code: http.StatusUnauthorized, - }), - ) - }) - t.Run("bad request body", func(t *testing.T) { - jsonhttptest.Request(t, client, http.MethodPost, resource, http.StatusBadRequest, - jsonhttptest.WithRequestHeader(api.AuthorizationHeader, "Basic dGVzdDp0ZXN0"), - - jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ - Message: "Unmarshal json body", - Code: http.StatusBadRequest, - }), - ) - }) - t.Run("unauthorized", func(t *testing.T) { - original := authenticator.AuthorizeFunc - authenticator.AuthorizeFunc = func(string) bool { return false } - defer func() { - authenticator.AuthorizeFunc = original - }() - jsonhttptest.Request(t, client, http.MethodPost, resource, http.StatusUnauthorized, - jsonhttptest.WithRequestHeader(api.AuthorizationHeader, "Basic dGVzdDp0ZXN0"), - - jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ - Message: "Unauthorized", - Code: http.StatusUnauthorized, - }), - ) - }) - t.Run("failed to add key", func(t *testing.T) { - original := authenticator.GenerateKeyFunc - authenticator.GenerateKeyFunc = func(s string) (string, error) { - return "", errors.New("error adding key") - } - defer func() { - authenticator.GenerateKeyFunc = original - }() - jsonhttptest.Request(t, client, http.MethodPost, resource, http.StatusInternalServerError, - jsonhttptest.WithRequestHeader(api.AuthorizationHeader, "Basic dGVzdDp0ZXN0"), - jsonhttptest.WithJSONRequestBody(api.SecurityTokenRequest{ - Role: "consumer", - }), - jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ - Message: "Error generating authorization token", - Code: http.StatusInternalServerError, - }), - ) - }) - t.Run("success", func(t *testing.T) { - jsonhttptest.Request(t, client, http.MethodPost, resource, http.StatusCreated, - jsonhttptest.WithRequestHeader(api.AuthorizationHeader, "Basic dGVzdDp0ZXN0"), - jsonhttptest.WithJSONRequestBody(api.SecurityTokenRequest{ - Role: "consumer", - }), - jsonhttptest.WithExpectedJSONResponse(api.SecurityTokenResponse{ - Key: "123", - }), - ) - }) -} diff --git a/pkg/api/export_test.go b/pkg/api/export_test.go index 812ce522ba0..6eba8a56841 100644 --- a/pkg/api/export_test.go +++ b/pkg/api/export_test.go @@ -18,8 +18,6 @@ type ( TagRequest = tagRequest ListTagsResponse = listTagsResponse IsRetrievableResponse = isRetrievableResponse - SecurityTokenResponse = securityTokenRsp - SecurityTokenRequest = securityTokenReq ) var ( diff --git a/pkg/api/router.go b/pkg/api/router.go index aaa75725b09..e07f406bec7 100644 --- a/pkg/api/router.go +++ b/pkg/api/router.go @@ -11,7 +11,6 @@ import ( "net/http/pprof" "strings" - "github.com/ethersphere/bee/v2/pkg/auth" "github.com/ethersphere/bee/v2/pkg/jsonhttp" "github.com/ethersphere/bee/v2/pkg/log/httpaccess" "github.com/ethersphere/bee/v2/pkg/swarm" @@ -142,11 +141,7 @@ func (s *Service) mountTechnicalDebug() { s.router.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol)) s.router.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace)) - pprofRootHandlerF := pprof.Index - if s.Restricted { - pprofRootHandlerF = web.ChainHandlers(auth.PermissionCheckHandler(s.auth), web.FinalHandler(http.HandlerFunc(pprof.Index))).ServeHTTP - } - s.router.PathPrefix("/debug/pprof/").Handler(http.HandlerFunc(pprofRootHandlerF)) + s.router.PathPrefix("/debug/pprof/").Handler(http.HandlerFunc(pprof.Index)) s.router.Handle("/debug/vars", expvar.Handler()) s.router.Handle("/loggers", jsonhttp.MethodHandler{ @@ -351,28 +346,10 @@ func (s *Service) mountAPI() { httpaccess.NewHTTPAccessSuppressLogHandler(), web.FinalHandlerFunc(s.healthHandler), )) - - if s.Restricted { - handle("/auth", jsonhttp.MethodHandler{ - "POST": web.ChainHandlers( - jsonhttp.NewMaxBodyBytesHandler(512), - web.FinalHandlerFunc(s.authHandler), - ), - }) - handle("/refresh", jsonhttp.MethodHandler{ - "POST": web.ChainHandlers( - jsonhttp.NewMaxBodyBytesHandler(512), - web.FinalHandlerFunc(s.refreshHandler), - ), - }) - } } func (s *Service) mountBusinessDebug() { handle := func(path string, handler http.Handler) { - if s.Restricted { - handler = web.ChainHandlers(auth.PermissionCheckHandler(s.auth), web.FinalHandler(handler)) - } s.router.Handle(path, handler) s.router.Handle(rootPath+path, handler) } diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go deleted file mode 100644 index a5aaec89dd2..00000000000 --- a/pkg/auth/auth.go +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright 2021 The Swarm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package auth - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/md5" - "crypto/rand" - "encoding/base64" - "encoding/hex" - "encoding/json" - "errors" - "io" - "time" - - "github.com/casbin/casbin/v2" - "github.com/casbin/casbin/v2/model" - "github.com/ethersphere/bee/v2/pkg/log" - "golang.org/x/crypto/bcrypt" -) - -// loggerName is the tree path name of the logger for this package. -const loggerName = "auth" - -type Authenticator interface { - Authorize(string) bool - GenerateKey(string, time.Duration) (string, error) - RefreshKey(string, time.Duration) (string, error) - Enforce(string, string, string) (bool, error) -} - -type authRecord struct { - Role string `json:"r"` - Expiry time.Time `json:"e"` -} - -type authenticator struct { - passwordHash []byte - ciph *encrypter - enforcer *casbin.Enforcer - log log.Logger -} - -func New(encryptionKey, passwordHash string, logger log.Logger) (*authenticator, error) { - m, err := model.NewModelFromString(` - [request_definition] - r = sub, obj, act - - [role_definition] - g = _, _ - - [policy_definition] - p = sub, obj, act - - [policy_effect] - e = some(where (p.eft == allow)) - - [matchers] - m = (g(r.sub, p.sub) || r.sub == p.sub) && (keyMatch(r.obj, p.obj) || keyMatch(r.obj, '/v1'+p.obj)) && regexMatch(r.act, p.act)`) - - if err != nil { - return nil, err - } - - e, err := casbin.NewEnforcer(m) - if err != nil { - return nil, err - } - - if err := applyPolicies(e); err != nil { - return nil, err - } - - ciph, err := newEncrypter([]byte(encryptionKey)) - if err != nil { - return nil, err - } - - auth := authenticator{ - enforcer: e, - ciph: ciph, - passwordHash: []byte(passwordHash), - log: logger.WithName(loggerName).Register(), - } - - return &auth, nil -} - -func (a *authenticator) Authorize(password string) bool { - return nil == bcrypt.CompareHashAndPassword(a.passwordHash, []byte(password)) -} - -var ErrExpiry = errors.New("expiry duration must be a positive number") - -func (a *authenticator) GenerateKey(role string, expiryDuration time.Duration) (string, error) { - if expiryDuration == 0 { - return "", ErrExpiry - } - - ar := authRecord{ - Role: role, - Expiry: time.Now().Add(expiryDuration), - } - - data, err := json.Marshal(ar) - if err != nil { - return "", err - } - - encryptedBytes, err := a.ciph.encrypt(data) - if err != nil { - return "", err - } - - apiKey := base64.StdEncoding.EncodeToString(encryptedBytes) - - return apiKey, nil -} - -var ErrTokenExpired = errors.New("token expired") - -func (a *authenticator) RefreshKey(apiKey string, expiryDuration time.Duration) (string, error) { - if expiryDuration == 0 { - return "", ErrExpiry - } - - decoded, err := base64.StdEncoding.DecodeString(apiKey) - if err != nil { - return "", err - } - - decryptedBytes, err := a.ciph.decrypt(decoded) - if err != nil { - return "", err - } - - var ar authRecord - if err := json.Unmarshal(decryptedBytes, &ar); err != nil { - return "", err - } - - if time.Now().After(ar.Expiry) { - return "", ErrTokenExpired - } - - ar.Expiry = time.Now().Add(expiryDuration) - - data, err := json.Marshal(ar) - if err != nil { - return "", err - } - - encryptedBytes, err := a.ciph.encrypt(data) - if err != nil { - return "", err - } - - apiKey = base64.StdEncoding.EncodeToString(encryptedBytes) - - return apiKey, nil -} - -func (a *authenticator) Enforce(apiKey, obj, act string) (bool, error) { - decoded, err := base64.StdEncoding.DecodeString(apiKey) - if err != nil { - a.log.Error(err, "decode token failed") - return false, err - } - - decryptedBytes, err := a.ciph.decrypt(decoded) - if err != nil { - a.log.Error(err, "decrypt token failed") - return false, err - } - - var ar authRecord - if err := json.Unmarshal(decryptedBytes, &ar); err != nil { - a.log.Error(err, "unmarshal token failed") - return false, err - } - - if time.Now().After(ar.Expiry) { - a.log.Error(nil, "token expired") - return false, ErrTokenExpired - } - - allow, err := a.enforcer.Enforce(ar.Role, obj, act) - if err != nil { - a.log.Error(err, "enforce failed") - return false, err - } - - return allow, nil -} - -type encrypter struct { - gcm cipher.AEAD -} - -func newEncrypter(key []byte) (*encrypter, error) { - hasher := md5.New() - _, err := hasher.Write(key) - if err != nil { - return nil, err - } - hash := hex.EncodeToString(hasher.Sum(nil)) - block, err := aes.NewCipher([]byte(hash)) - if err != nil { - return nil, err - } - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - - return &encrypter{ - gcm: gcm, - }, nil -} - -func (e encrypter) encrypt(data []byte) ([]byte, error) { - nonce := make([]byte, e.gcm.NonceSize()) - if _, err := io.ReadFull(rand.Reader, nonce); err != nil { - return nil, err - } - ciphertext := e.gcm.Seal(nonce, nonce, data, nil) - return ciphertext, nil -} - -func (e encrypter) decrypt(data []byte) ([]byte, error) { - nonceSize := e.gcm.NonceSize() - nonce, ciphertext := data[:nonceSize], data[nonceSize:] - plaintext, err := e.gcm.Open(nil, nonce, ciphertext, nil) - if err != nil { - return nil, err - } - return plaintext, nil -} - -func applyPolicies(e *casbin.Enforcer) error { - _, err := e.AddPolicies([][]string{ - {"consumer", "/bytes/*", "(GET)|(HEAD)"}, - {"creator", "/bytes", "POST"}, - {"consumer", "/chunks/*", "GET"}, - {"creator", "/chunks", "POST"}, - {"consumer", "/bzz/*", "GET"}, - {"creator", "/bzz/*", "PATCH"}, - {"creator", "/bzz", "POST"}, - {"creator", "/bzz?*", "POST"}, - {"consumer", "/bzz/*/*", "(GET)|(HEAD)"}, - {"creator", "/tags", "GET"}, - {"creator", "/tags?*", "GET"}, - {"creator", "/tags", "POST"}, - {"creator", "/tags/*", "(GET)|(DELETE)|(PATCH)"}, - {"creator", "/pins/*", "(GET)|(DELETE)|(POST)"}, - {"maintainer", "/pins", "GET"}, - {"creator", "/pss/send/*", "POST"}, - {"consumer", "/pss/subscribe/*", "GET"}, - {"creator", "/soc/*/*", "POST"}, - {"creator", "/feeds/*/*", "POST"}, - {"consumer", "/feeds/*/*", "GET"}, - {"maintainer", "/stamps", "GET"}, - {"maintainer", "/stamps/*", "GET"}, - {"maintainer", "/stamps/*/*", "POST"}, - {"maintainer", "/stamps/topup/*/*", "PATCH"}, - {"maintainer", "/stamps/dilute/*/*", "PATCH"}, - {"maintainer", "/stake", "(GET)|(DELETE)"}, - {"maintainer", "/stake/*", "POST"}, - {"maintainer", "/addresses", "GET"}, - {"maintainer", "/blocklist", "GET"}, - {"maintainer", "/connect/*", "POST"}, - {"maintainer", "/peers", "GET"}, - {"maintainer", "/peers/*", "DELETE"}, - {"maintainer", "/pingpong/*", "POST"}, - {"maintainer", "/topology", "GET"}, - {"maintainer", "/welcome-message", "(GET)|(POST)"}, - {"maintainer", "/balances", "GET"}, - {"maintainer", "/balances/*", "GET"}, - {"maintainer", "/accounting", "GET"}, - {"maintainer", "/chequebook/cashout/*", "GET"}, - {"accountant", "/chequebook/cashout/*", "POST"}, - {"accountant", "/chequebook/withdraw", "POST"}, - {"accountant", "/chequebook/withdraw?*", "POST"}, - {"accountant", "/chequebook/deposit", "POST"}, - {"accountant", "/chequebook/deposit?*", "POST"}, - {"maintainer", "/chequebook/cheque/*", "GET"}, - {"maintainer", "/chequebook/cheque", "GET"}, - {"maintainer", "/chequebook/address", "GET"}, - {"maintainer", "/chequebook/balance", "GET"}, - {"maintainer", "/wallet", "GET"}, - {"maintainer", "/wallet/withdraw/*", "POST"}, - {"maintainer", "/chunks/*", "(GET)|(DELETE)"}, - {"maintainer", "/reservestate", "GET"}, - {"maintainer", "/chainstate", "GET"}, - {"maintainer", "/settlements/*", "GET"}, - {"maintainer", "/settlements", "GET"}, - {"maintainer", "/transactions", "GET"}, - {"consumer", "/transactions/*", "GET"}, - {"accountant", "/transactions/*", "(POST)|(DELETE)"}, - {"consumer", "/consumed", "GET"}, - {"consumer", "/consumed/*", "GET"}, - {"consumer", "/chunks/stream", "GET"}, - {"creator", "/stewardship/*", "GET"}, - {"consumer", "/stewardship/*", "PUT"}, - {"maintainer", "/redistributionstate", "GET"}, - {"maintainer", "/debugstore", "GET"}, - {"consumer", "/rchash", "GET"}, - {"maintainer", "/debug/*", "GET"}, - {"maintainer", "/metrics", "GET"}, - {"maintainer", "/node", "GET"}, - {"maintainer", "/loggers", "GET"}, - {"maintainer", "/loggers/*", "(GET)|(PUT)"}, - {"maintainer", "/status", "GET"}, - {"maintainer", "/status/peers", "GET"}, - {"maintainer", "/rcash/*", "GET"}, - {"maintainer", "/batches", "GET"}, - {"maintainer", "/timesettlements", "GET"}, - }) - - if err != nil { - return err - } - - // consumer > creator > accountant > maintainer - _, err = e.AddGroupingPolicies([][]string{ - {"creator", "consumer"}, - {"accountant", "creator"}, - {"maintainer", "accountant"}, - }) - - return err -} diff --git a/pkg/auth/auth_test.go b/pkg/auth/auth_test.go deleted file mode 100644 index 0d107a764a2..00000000000 --- a/pkg/auth/auth_test.go +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2021 The Swarm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package auth_test - -import ( - "errors" - "testing" - "time" - - "github.com/ethersphere/bee/v2/pkg/auth" - "github.com/ethersphere/bee/v2/pkg/log" -) - -const ( - encryptionKey = "mZIODMvjsiS2VdK1xgI1cOTizhGVNoVz" - passwordHash = "$2a$12$mZIODMvjsiS2VdK1xgI1cOTizhGVNoVz2Xn48H8ddFFLzX2B3lD3m" -) - -func TestAuthorize(t *testing.T) { - t.Parallel() - - a, err := auth.New(encryptionKey, passwordHash, log.Noop) - if err != nil { - t.Error(err) - } - - tt := []struct { - desc string - pass string - expected bool - }{ - { - desc: "correct credentials", - pass: "test", - expected: true, - }, { - desc: "wrong password", - pass: "notTest", - expected: false, - }, - } - for _, tC := range tt { - tC := tC - t.Run(tC.desc, func(t *testing.T) { - t.Parallel() - - res := a.Authorize(tC.pass) - if res != tC.expected { - t.Error("unexpected result", res) - } - }) - } -} - -func TestExpiry(t *testing.T) { - t.Parallel() - - const expiryDuration = time.Millisecond * 10 - - a, err := auth.New(encryptionKey, passwordHash, log.Noop) - if err != nil { - t.Error(err) - } - - key, err := a.GenerateKey("consumer", expiryDuration) - if err != nil { - t.Errorf("expected no error, got: %v", err) - } - - time.Sleep(expiryDuration * 2) - - result, err := a.Enforce(key, "/bytes/1", "GET") - if !errors.Is(err, auth.ErrTokenExpired) { - t.Errorf("expected token expired error, got: %v", err) - } - - if result { - t.Errorf("expected %v, got %v", false, result) - } -} - -func TestEnforce(t *testing.T) { - t.Parallel() - - const expiryDuration = time.Second - - a, err := auth.New(encryptionKey, passwordHash, log.Noop) - if err != nil { - t.Error(err) - } - - tt := []struct { - desc string - role, resource, action string - expected bool - }{ - { - desc: "success", - role: "maintainer", - resource: "/pingpong/someone", - action: "POST", - expected: true, - }, { - desc: "success with query param", - role: "creator", - resource: "/bzz?name=some-name", - action: "POST", - expected: true, - }, - { - desc: "bad role", - role: "consumer", - resource: "/pingpong/some-other-peer", - action: "POST", - }, - { - desc: "bad resource", - role: "maintainer", - resource: "/i-dont-exist", - action: "POST", - }, - { - desc: "bad action", - role: "maintainer", - resource: "/pingpong/someone", - action: "DELETE", - }, - } - - for _, tC := range tt { - tC := tC - t.Run(tC.desc, func(t *testing.T) { - t.Parallel() - - apiKey, err := a.GenerateKey(tC.role, expiryDuration) - - if err != nil { - t.Errorf("expected no error, got: %v", err) - } - - result, err := a.Enforce(apiKey, tC.resource, tC.action) - - if err != nil { - t.Errorf("expected no error, got: %v", err) - } - - if result != tC.expected { - t.Errorf("request from user with %s on object %s: expected %v, got %v", tC.role, tC.resource, tC.expected, result) - } - }) - } -} diff --git a/pkg/auth/handler.go b/pkg/auth/handler.go deleted file mode 100644 index 6a54b9269be..00000000000 --- a/pkg/auth/handler.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2021 The Swarm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package auth - -import ( - "errors" - "net/http" - "strings" - - "github.com/ethersphere/bee/v2/pkg/jsonhttp" -) - -type auth interface { - Enforce(string, string, string) (bool, error) -} - -func PermissionCheckHandler(auth auth) func(h http.Handler) http.Handler { - return func(h http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - reqToken := r.Header.Get("Authorization") - if !strings.HasPrefix(reqToken, "Bearer ") { - jsonhttp.Forbidden(w, "Missing bearer token") - return - } - - keys := strings.Split(reqToken, "Bearer ") - - if len(keys) != 2 || strings.Trim(keys[1], " ") == "" { - jsonhttp.Unauthorized(w, "Missing security token") - return - } - - apiKey := keys[1] - - allowed, err := auth.Enforce(apiKey, r.URL.Path, r.Method) - if errors.Is(err, ErrTokenExpired) { - jsonhttp.Unauthorized(w, "Token expired") - return - } - - if err != nil { - jsonhttp.InternalServerError(w, "Error occurred while validating the security token") - return - } - - if !allowed { - jsonhttp.Forbidden(w, "Provided security token does not grant access to the resource") - return - } - - h.ServeHTTP(w, r) - }) - } -} diff --git a/pkg/auth/main_test.go b/pkg/auth/main_test.go deleted file mode 100644 index be220f06760..00000000000 --- a/pkg/auth/main_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2022 The Swarm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package auth_test - -import ( - "testing" - - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - goleak.VerifyTestMain(m) -} diff --git a/pkg/auth/mock/auth.go b/pkg/auth/mock/auth.go deleted file mode 100644 index ef8ebd30b75..00000000000 --- a/pkg/auth/mock/auth.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2021 The Swarm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package mock - -import "time" - -type Auth struct { - AuthorizeFunc func(string) bool - GenerateKeyFunc func(string) (string, error) - EnforceFunc func(string, string, string) (bool, error) -} - -func (ma *Auth) Authorize(u string) bool { - if ma.AuthorizeFunc == nil { - return true - } - return ma.AuthorizeFunc(u) -} -func (ma *Auth) GenerateKey(k string, _ time.Duration) (string, error) { - if ma.GenerateKeyFunc == nil { - return "", nil - } - return ma.GenerateKeyFunc(k) -} -func (ma *Auth) RefreshKey(k string, _ time.Duration) (string, error) { - if ma.GenerateKeyFunc == nil { - return "", nil - } - return ma.GenerateKeyFunc(k) -} -func (ma *Auth) Enforce(a1 string, a2 string, a3 string) (bool, error) { - return ma.EnforceFunc(a1, a2, a3) -} diff --git a/pkg/node/devnode.go b/pkg/node/devnode.go index 2c64f990591..fe9476d9d00 100644 --- a/pkg/node/devnode.go +++ b/pkg/node/devnode.go @@ -19,7 +19,6 @@ import ( "github.com/ethereum/go-ethereum/common" mockAccounting "github.com/ethersphere/bee/v2/pkg/accounting/mock" "github.com/ethersphere/bee/v2/pkg/api" - "github.com/ethersphere/bee/v2/pkg/auth" "github.com/ethersphere/bee/v2/pkg/bzz" "github.com/ethersphere/bee/v2/pkg/crypto" "github.com/ethersphere/bee/v2/pkg/feeds/factory" @@ -79,9 +78,6 @@ type DevOptions struct { DBWriteBufferSize uint64 DBBlockCacheCapacity uint64 DBDisableSeeksCompaction bool - Restricted bool - TokenEncryptionKey string - AdminPasswordHash string } // NewDevBee starts the bee instance in 'development' mode @@ -139,15 +135,6 @@ func NewDevBee(logger log.Logger, o *DevOptions) (b *DevBee, err error) { return nil, fmt.Errorf("blockchain address: %w", err) } - var authenticator auth.Authenticator - - if o.Restricted { - if authenticator, err = auth.New(o.TokenEncryptionKey, o.AdminPasswordHash, logger); err != nil { - return nil, fmt.Errorf("authenticator: %w", err) - } - logger.Info("starting with restricted APIs") - } - var mockTransaction = transactionmock.New(transactionmock.WithPendingTransactionsFunc(func() ([]common.Hash, error) { return []common.Hash{common.HexToHash("abcd")}, nil }), transactionmock.WithResendTransactionFunc(func(ctx context.Context, txHash common.Hash) error { @@ -367,10 +354,9 @@ func NewDevBee(logger log.Logger, o *DevOptions) (b *DevBee, err error) { apiService := api.New(mockKey.PublicKey, mockKey.PublicKey, overlayEthAddress, nil, logger, mockTransaction, batchStore, api.DevMode, true, true, chainBackend, o.CORSAllowedOrigins, inmemstore.New()) - apiService.Configure(signer, authenticator, tracer, api.Options{ + apiService.Configure(signer, tracer, api.Options{ CORSAllowedOrigins: o.CORSAllowedOrigins, WsPingPeriod: 60 * time.Second, - Restricted: o.Restricted, }, debugOpts, 1, erc20) apiService.MountTechnicalDebug() apiService.MountDebug() diff --git a/pkg/node/node.go b/pkg/node/node.go index f50574cdecc..5f799298164 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -27,7 +27,6 @@ import ( "github.com/ethersphere/bee/v2/pkg/accounting" "github.com/ethersphere/bee/v2/pkg/addressbook" "github.com/ethersphere/bee/v2/pkg/api" - "github.com/ethersphere/bee/v2/pkg/auth" "github.com/ethersphere/bee/v2/pkg/config" "github.com/ethersphere/bee/v2/pkg/crypto" "github.com/ethersphere/bee/v2/pkg/feeds/factory" @@ -162,9 +161,6 @@ type Options struct { MutexProfile bool StaticNodes []swarm.Address AllowPrivateCIDRs bool - Restricted bool - TokenEncryptionKey string - AdminPasswordHash string UsePostageSnapshot bool EnableStorageIncentives bool StatestoreCacheCapacity uint64 @@ -368,15 +364,6 @@ func NewBee( b.transactionCloser = tracerCloser b.transactionMonitorCloser = transactionMonitor - var authenticator auth.Authenticator - - if o.Restricted { - if authenticator, err = auth.New(o.TokenEncryptionKey, o.AdminPasswordHash, logger); err != nil { - return nil, fmt.Errorf("authenticator: %w", err) - } - logger.Info("starting with restricted APIs") - } - beeNodeMode := api.LightMode if o.FullNodeMode { beeNodeMode = api.FullMode @@ -431,7 +418,6 @@ func NewBee( o.CORSAllowedOrigins, stamperStore, ) - apiService.Restricted = o.Restricted apiService.MountTechnicalDebug() apiService.SetProbe(probe) @@ -1089,10 +1075,9 @@ func NewBee( apiService.MustRegisterMetrics(swapService.Metrics()...) } - apiService.Configure(signer, authenticator, tracer, api.Options{ + apiService.Configure(signer, tracer, api.Options{ CORSAllowedOrigins: o.CORSAllowedOrigins, WsPingPeriod: 60 * time.Second, - Restricted: o.Restricted, }, extraOpts, chainID, erc20Service) apiService.MountDebug() From fa8315d545289375a809d742062e17c5d59b4f00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20S=C3=A1rai?= Date: Mon, 19 Feb 2024 01:13:55 +0100 Subject: [PATCH 09/41] feat: add act.go with TODOs feat: Add Act interface feat: Add Marshal, Unmarshal skeleton feat: Refactor AccessType to iota feat: Add upload feat: Rename GenerateAccessControlManifest -> create feat: Add saltLengthIs32 feat: Add Mrshal, Unmarshal impl feat: Add Marshal Unmarshal feat: Remove ManifestEntry json annotations feat: Modify to public finc/method feat: Add ErrSaltLength Add pkg/dynamicaccess Refactor interfaces and implement default structs Refactor typo Refactor History package to use NewHistory() function Add Act interface and default implementation Add ACT use cases to act_ucs.md Add new files and implement interfaces, refactor packeges Update act_ucs.md base usecases Refactor access logic and add mock implementations*** Add DiffieHellman implementation and remove Keystore*** Refactor NewAccessLogic function Replace encryption.go to pkg/encryption Refactor packages Update act_ucs.md Update act_ucs.md Update act_ucs.md Update act_ucs.md Update act_ucs.md --- act_ucs.md | 46 +++++++++++++++++++++++++ pkg/dynamicaccess/accesslogic.go | 41 ++++++++++++++++++++++ pkg/dynamicaccess/accesslogic_test.go | 11 ++++++ pkg/dynamicaccess/act.go | 18 ++++++++++ pkg/dynamicaccess/act_test.go | 19 ++++++++++ pkg/dynamicaccess/container.go | 7 ++++ pkg/dynamicaccess/controller.go | 18 ++++++++++ pkg/dynamicaccess/diffieHellman.go | 31 +++++++++++++++++ pkg/dynamicaccess/diffieHellman_test.go | 10 ++++++ pkg/dynamicaccess/feed.go | 36 +++++++++++++++++++ pkg/dynamicaccess/feed_test.go | 40 +++++++++++++++++++++ pkg/dynamicaccess/grantee.go | 26 ++++++++++++++ pkg/dynamicaccess/grantee_test.go | 24 +++++++++++++ pkg/dynamicaccess/history.go | 22 ++++++++++++ pkg/dynamicaccess/history_test.go | 19 ++++++++++ pkg/dynamicaccess/mock/accesslogic.go | 12 +++++++ pkg/dynamicaccess/mock/container.go | 20 +++++++++++ pkg/dynamicaccess/mock/diffieHellman.go | 12 +++++++ pkg/dynamicaccess/publish.go | 12 +++++++ pkg/dynamicaccess/publish_test.go | 11 ++++++ pkg/dynamicaccess/timestamp.go | 10 ++++++ pkg/dynamicaccess/timestamp_test.go | 1 + 22 files changed, 446 insertions(+) create mode 100644 act_ucs.md create mode 100644 pkg/dynamicaccess/accesslogic.go create mode 100644 pkg/dynamicaccess/accesslogic_test.go create mode 100644 pkg/dynamicaccess/act.go create mode 100644 pkg/dynamicaccess/act_test.go create mode 100644 pkg/dynamicaccess/container.go create mode 100644 pkg/dynamicaccess/controller.go create mode 100644 pkg/dynamicaccess/diffieHellman.go create mode 100644 pkg/dynamicaccess/diffieHellman_test.go create mode 100644 pkg/dynamicaccess/feed.go create mode 100644 pkg/dynamicaccess/feed_test.go create mode 100644 pkg/dynamicaccess/grantee.go create mode 100644 pkg/dynamicaccess/grantee_test.go create mode 100644 pkg/dynamicaccess/history.go create mode 100644 pkg/dynamicaccess/history_test.go create mode 100644 pkg/dynamicaccess/mock/accesslogic.go create mode 100644 pkg/dynamicaccess/mock/container.go create mode 100644 pkg/dynamicaccess/mock/diffieHellman.go create mode 100644 pkg/dynamicaccess/publish.go create mode 100644 pkg/dynamicaccess/publish_test.go create mode 100644 pkg/dynamicaccess/timestamp.go create mode 100644 pkg/dynamicaccess/timestamp_test.go diff --git a/act_ucs.md b/act_ucs.md new file mode 100644 index 00000000000..82067211bfd --- /dev/null +++ b/act_ucs.md @@ -0,0 +1,46 @@ +# ACT user stories + +This file contains the SWARM ACT user stories. + +ZenHub Link: [SpDevTeam](https://app.zenhub.com/workspaces/spdevteam-6544d91246b817002d853e69/board) + +- [ ] **1** +- I'm a publisher +- I upload a content +- I grant access to content +- Viewer try to access to the content +___ + +- [ ] **2** +- I'm a publisher +- I granteed access for viewers to my channel +___ + +- [ ] **2/a** +- I'm a publisher +- I granteed access for additional viewers to my channel +___ + +- [ ] **2/b** +- I'm a publisher +- I remove viewers from the access list to my channel +___ + +- [ ] **3** +- I'm a viewer +- I requested access to the content +- If I got, I can access it +- If I didn't get, I can't +___ + +- [ ] **4** +- I'm a viewer +- I lost access to the content +- I can't access new ones +___ + +- [ ] **5** +- I'm a viewer +- I lost access to the content +- I can not access new ones, but the old ones I can +___ diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go new file mode 100644 index 00000000000..269fe4c4638 --- /dev/null +++ b/pkg/dynamicaccess/accesslogic.go @@ -0,0 +1,41 @@ +package dynamicaccess + +import ( + "hash" + + "github.com/ethersphere/bee/pkg/dynamicaccess/mock" + encryption "github.com/ethersphere/bee/pkg/encryption" +) + +type AccessLogic interface { + Get(encryped_ref string, publisher string, tag string) (string, error) +} + +type DefaultAccessLogic struct { + diffieHellman DiffieHellman + encryption encryption.Interface + act Act +} + +func (al *DefaultAccessLogic) Get(encryped_ref string, publisher string, tag string) (string, error) { + return "", nil +} + +func NewAccessLogic(key encryption.Key, padding int, initCtr uint32, hashFunc func() hash.Hash) AccessLogic { + return &DefaultAccessLogic{ + diffieHellman: &mock.DiffieHellmanMock{ + SharedSecretFunc: func(publicKey string, tag string, moment []byte) (string, error) { + return publicKey, nil + }, + }, + encryption: encryption.New(key, padding, initCtr, hashFunc), + act: &mock.ContainerMock{ + AddFunc: func(ref string, publisher string, tag string) error { + return nil + }, + GetFunc: func(ref string, publisher string, tag string) (string, error) { + return "", nil + }, + }, + } +} diff --git a/pkg/dynamicaccess/accesslogic_test.go b/pkg/dynamicaccess/accesslogic_test.go new file mode 100644 index 00000000000..4f3ad6c44bc --- /dev/null +++ b/pkg/dynamicaccess/accesslogic_test.go @@ -0,0 +1,11 @@ +package dynamicaccess + +import "testing" + +func TestXxx(t *testing.T) { + //key encryption.Key, padding int, initCtr uint32, hashFunc func() hash.Hash + al := NewAccessLogic(nil, 0, 0, nil) + if al == nil { + t.Errorf("Error creating access logic") + } +} diff --git a/pkg/dynamicaccess/act.go b/pkg/dynamicaccess/act.go new file mode 100644 index 00000000000..b3448260312 --- /dev/null +++ b/pkg/dynamicaccess/act.go @@ -0,0 +1,18 @@ +package dynamicaccess + +type Act interface{} + +type defaultAct struct { +} + +func (a *defaultAct) Add(oldItemKey string, oldRootHash string) (newRootHash string, err error) { + return "", nil +} + +func (a *defaultAct) Get(rootKey string) (value string, err error) { + return "", nil +} + +func NewAct() Container { + return &defaultAct{} +} diff --git a/pkg/dynamicaccess/act_test.go b/pkg/dynamicaccess/act_test.go new file mode 100644 index 00000000000..e1d316d2841 --- /dev/null +++ b/pkg/dynamicaccess/act_test.go @@ -0,0 +1,19 @@ +package dynamicaccess + +import "testing" + +func TestAdd(t *testing.T) { + a := NewAct() + _, err := a.Add("", "") + if err != nil { + t.Error("Add() should not return an error") + } +} + +func TestGet(t *testing.T) { + a := NewAct() + _, err := a.Get("") + if err != nil { + t.Error("Get() should not return an error") + } +} diff --git a/pkg/dynamicaccess/container.go b/pkg/dynamicaccess/container.go new file mode 100644 index 00000000000..ae683aa0fcd --- /dev/null +++ b/pkg/dynamicaccess/container.go @@ -0,0 +1,7 @@ +package dynamicaccess + +// iterator +type Container interface { + Add(oldItemKey string, oldRootHash string) (newRootHash string, err error) + Get(rootKey string) (value string, err error) +} diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go new file mode 100644 index 00000000000..3dfee5bff73 --- /dev/null +++ b/pkg/dynamicaccess/controller.go @@ -0,0 +1,18 @@ +package dynamicaccess + +type Controller interface { +} + +type defaultController struct { + histrory History + uploader Publish + grantee Grantee +} + +func NewController(histrory History, uploader Publish, grantee Grantee) Controller { + return &defaultController{ + histrory: histrory, + uploader: uploader, + grantee: grantee, + } +} diff --git a/pkg/dynamicaccess/diffieHellman.go b/pkg/dynamicaccess/diffieHellman.go new file mode 100644 index 00000000000..8ee9bb1bcce --- /dev/null +++ b/pkg/dynamicaccess/diffieHellman.go @@ -0,0 +1,31 @@ +package dynamicaccess + +import ( + "crypto/ecdsa" + + Crypto "github.com/ethersphere/bee/pkg/crypto" + "github.com/ethersphere/bee/pkg/keystore" + KeyStoreMem "github.com/ethersphere/bee/pkg/keystore/mem" +) + +type DiffieHellman interface { + SharedSecret(pubKey, tag string, moment []byte) (string, error) +} + +type defaultDiffieHellman struct { + key *ecdsa.PrivateKey + keyStoreService keystore.Service + keyStoreEdg keystore.EDG +} + +func (d *defaultDiffieHellman) SharedSecret(pubKey string, tag string, moment []byte) (string, error) { + return "", nil +} + +func NewDiffieHellman(key *ecdsa.PrivateKey) DiffieHellman { + return &defaultDiffieHellman{ + key: key, + keyStoreService: KeyStoreMem.New(), + keyStoreEdg: Crypto.EDGSecp256_K1, + } +} diff --git a/pkg/dynamicaccess/diffieHellman_test.go b/pkg/dynamicaccess/diffieHellman_test.go new file mode 100644 index 00000000000..2e1c870a178 --- /dev/null +++ b/pkg/dynamicaccess/diffieHellman_test.go @@ -0,0 +1,10 @@ +package dynamicaccess + +import "testing" + +func TestSharedSecret(t *testing.T) { + _, err := NewDiffieHellman(nil).SharedSecret("", "", nil) + if err != nil { + t.Errorf("Error generating shared secret: %v", err) + } +} diff --git a/pkg/dynamicaccess/feed.go b/pkg/dynamicaccess/feed.go new file mode 100644 index 00000000000..2576c2733a5 --- /dev/null +++ b/pkg/dynamicaccess/feed.go @@ -0,0 +1,36 @@ +package dynamicaccess + +// referencia: history.go +type Feed interface { + Update(itemKey string, content string) error + Get(itemKey string) (content string, err error) + AddNewGrantee(itemKey string, grantee string) error + RemoveGrantee(itemKey string, grantee string) error + GetAccess(encryptedRef string, publisher string, tag string) (access string, err error) +} + +type defaultFeed struct{} + +func (f *defaultFeed) Update(itemKey string, content string) error { + return nil +} + +func (f *defaultFeed) Get(itemKey string) (content string, err error) { + return "", nil +} + +func (f *defaultFeed) AddNewGrantee(itemKey string, grantee string) error { + return nil +} + +func (f *defaultFeed) RemoveGrantee(itemKey string, grantee string) error { + return nil +} + +func (f *defaultFeed) GetAccess(encryptedRef string, publisher string, tag string) (access string, err error) { + return "", nil +} + +func NewFeed() Feed { + return &defaultFeed{} +} diff --git a/pkg/dynamicaccess/feed_test.go b/pkg/dynamicaccess/feed_test.go new file mode 100644 index 00000000000..1839e6230b9 --- /dev/null +++ b/pkg/dynamicaccess/feed_test.go @@ -0,0 +1,40 @@ +package dynamicaccess + +import "testing" + +func TestFeedUpdate(t *testing.T) { + err := NewFeed().Update("", "") + if err != nil { + t.Errorf("Error updating feed: %v", err) + } +} + +func TestFeedGet(t *testing.T) { + content, err := NewFeed().Get("") + if err != nil { + t.Errorf("Error getting feed: %v", err) + } + _ = content // Ignore the content if not needed +} + +func TestFeedAddNewGrantee(t *testing.T) { + err := NewFeed().AddNewGrantee("", "") + if err != nil { + t.Errorf("Error adding new grantee to feed: %v", err) + } +} + +func TestFeedRemoveGrantee(t *testing.T) { + err := NewFeed().RemoveGrantee("", "") + if err != nil { + t.Errorf("Error removing grantee from feed: %v", err) + } +} + +func TestFeedGetAccess(t *testing.T) { + access, err := NewFeed().GetAccess("", "", "") + if err != nil { + t.Errorf("Error getting access to feed: %v", err) + } + _ = access // Ignore the access if not needed +} diff --git a/pkg/dynamicaccess/grantee.go b/pkg/dynamicaccess/grantee.go new file mode 100644 index 00000000000..27eb1636a09 --- /dev/null +++ b/pkg/dynamicaccess/grantee.go @@ -0,0 +1,26 @@ +package dynamicaccess + +type Grantee interface { + Revoke(topic string) error + RevokeList(topic string, removeList []string, addList []string) (string, error) + Publish(topic string) error +} + +type defaultGrantee struct { +} + +func (g *defaultGrantee) Revoke(topic string) error { + return nil +} + +func (g *defaultGrantee) RevokeList(topic string, removeList []string, addList []string) (string, error) { + return "", nil +} + +func (g *defaultGrantee) Publish(topic string) error { + return nil +} + +func NewGrantee() Grantee { + return &defaultGrantee{} +} diff --git a/pkg/dynamicaccess/grantee_test.go b/pkg/dynamicaccess/grantee_test.go new file mode 100644 index 00000000000..38b2d10b347 --- /dev/null +++ b/pkg/dynamicaccess/grantee_test.go @@ -0,0 +1,24 @@ +package dynamicaccess + +import "testing" + +func TestGranteeRevoke(t *testing.T) { + err := NewGrantee().Revoke("") + if err != nil { + t.Errorf("Error revoking grantee: %v", err) + } +} + +func TestGranteeRevokeList(t *testing.T) { + _, err := NewGrantee().RevokeList("", nil, nil) + if err != nil { + t.Errorf("Error revoking list of grantees: %v", err) + } +} + +func TestGranteePublish(t *testing.T) { + err := NewGrantee().Publish("") + if err != nil { + t.Errorf("Error publishing grantee: %v", err) + } +} diff --git a/pkg/dynamicaccess/history.go b/pkg/dynamicaccess/history.go new file mode 100644 index 00000000000..b9a9438b331 --- /dev/null +++ b/pkg/dynamicaccess/history.go @@ -0,0 +1,22 @@ +package dynamicaccess + +// TODO FROM BEE!!!! +// timestamp alapú history +type History interface { + Add(oldItemKey string, oldRootHash string) (newRootHash string, err error) + Get(rootKey string) (value string, err error) +} + +type defaultHistory struct{} + +func (h *defaultHistory) Add(oldItemKey string, oldRootHash string) (newRootHash string, err error) { + return "", nil +} + +func (h *defaultHistory) Get(rootKey string) (value string, err error) { + return "", nil +} + +func NewHistory() History { + return &defaultHistory{} +} diff --git a/pkg/dynamicaccess/history_test.go b/pkg/dynamicaccess/history_test.go new file mode 100644 index 00000000000..9116e41f548 --- /dev/null +++ b/pkg/dynamicaccess/history_test.go @@ -0,0 +1,19 @@ +package dynamicaccess + +import "testing" + +func TestHistoryAdd(t *testing.T) { + newRootHash, err := NewHistory().Add("", "") + if err != nil { + t.Errorf("Error adding history: %v", err) + } + _ = newRootHash // Ignore the newRootHash if not needed +} + +func TestHistoryGet(t *testing.T) { + value, err := NewHistory().Get("") + if err != nil { + t.Errorf("Error getting history: %v", err) + } + _ = value // Ignore the value if not needed +} diff --git a/pkg/dynamicaccess/mock/accesslogic.go b/pkg/dynamicaccess/mock/accesslogic.go new file mode 100644 index 00000000000..8be8e3a07fd --- /dev/null +++ b/pkg/dynamicaccess/mock/accesslogic.go @@ -0,0 +1,12 @@ +package mock + +type AccessLogicMock struct { + GetFunc func(string, string, string) (string, error) +} + +func (ma *AccessLogicMock) Get(encryped_ref string, publisher string, tag string) (string, error) { + if ma.GetFunc == nil { + return "", nil + } + return ma.GetFunc(encryped_ref, publisher, tag) +} diff --git a/pkg/dynamicaccess/mock/container.go b/pkg/dynamicaccess/mock/container.go new file mode 100644 index 00000000000..3cad9badd39 --- /dev/null +++ b/pkg/dynamicaccess/mock/container.go @@ -0,0 +1,20 @@ +package mock + +type ContainerMock struct { + AddFunc func(string, string, string) error + GetFunc func(string, string, string) (string, error) +} + +func (ma *ContainerMock) Add(ref string, publisher string, tag string) error { + if ma.AddFunc == nil { + return nil + } + return ma.AddFunc(ref, publisher, tag) +} + +func (ma *ContainerMock) Get(ref string, publisher string, tag string) (string, error) { + if ma.GetFunc == nil { + return "", nil + } + return ma.GetFunc(ref, publisher, tag) +} diff --git a/pkg/dynamicaccess/mock/diffieHellman.go b/pkg/dynamicaccess/mock/diffieHellman.go new file mode 100644 index 00000000000..f71131b11ff --- /dev/null +++ b/pkg/dynamicaccess/mock/diffieHellman.go @@ -0,0 +1,12 @@ +package mock + +type DiffieHellmanMock struct { + SharedSecretFunc func(string, string, []byte) (string, error) +} + +func (ma *DiffieHellmanMock) SharedSecret(publicKey string, tag string, moment []byte) (string, error) { + if ma.SharedSecretFunc == nil { + return "", nil + } + return ma.SharedSecretFunc(publicKey, tag, moment) +} diff --git a/pkg/dynamicaccess/publish.go b/pkg/dynamicaccess/publish.go new file mode 100644 index 00000000000..f913288e4d5 --- /dev/null +++ b/pkg/dynamicaccess/publish.go @@ -0,0 +1,12 @@ +package dynamicaccess + +type Publish interface { + upload(ref string) (string, error) +} + +type DefaultPublish struct { +} + +func (d *DefaultPublish) upload(ref string) (string, error) { + return "default", nil +} diff --git a/pkg/dynamicaccess/publish_test.go b/pkg/dynamicaccess/publish_test.go new file mode 100644 index 00000000000..d31069da4a3 --- /dev/null +++ b/pkg/dynamicaccess/publish_test.go @@ -0,0 +1,11 @@ +package dynamicaccess + +import "testing" + +func TestUpload(t *testing.T) { + p := &DefaultPublish{} + _, err := p.upload("test") + if err != nil { + t.Errorf("Error uploading file: %v", err) + } +} diff --git a/pkg/dynamicaccess/timestamp.go b/pkg/dynamicaccess/timestamp.go new file mode 100644 index 00000000000..48347d33a7c --- /dev/null +++ b/pkg/dynamicaccess/timestamp.go @@ -0,0 +1,10 @@ +package dynamicaccess + +// container interface bee-ből a manifest +type Timestamp interface{} + +type defaultTimeStamp struct{} + +func NewTimestamp() Timestamp { + return &defaultTimeStamp{} +} diff --git a/pkg/dynamicaccess/timestamp_test.go b/pkg/dynamicaccess/timestamp_test.go new file mode 100644 index 00000000000..e39ccbcf0c5 --- /dev/null +++ b/pkg/dynamicaccess/timestamp_test.go @@ -0,0 +1 @@ +package dynamicaccess From c4883d037e71e00e38d2ff5ab668eb3c4c5ff043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20S=C3=A1rai?= Date: Wed, 13 Mar 2024 15:43:35 +0100 Subject: [PATCH 10/41] Diffie-Hellman (#3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use DiffieHellmanMock * Adds a comment about Get * Add support for ECDSA public key in DiffieHellman.SharedSecret function * Update defaultAct implementation * Adds pseudo code for Access Logic * Update default Act creation; Fix basic Act tests * Refactor access logic to use new ActMock implementation * feat(history): test mockups wip * Refactor DiffieHellman implementation * changes pseudocode for Diffie-Hellmann read * Co-authored-by: Bálint Ujvári * DiffieHellman mock generates a real sherd secret * Refactor Act * Adds manifest lookup * Extend act_test * Adds unit tests, some values are mocked * Refactor act mock impl with map[string]map[string]string * Add check mock implementation for DiffieHellman interface * Add Load, Store to Act interface. Refactor Act interface * refactor act, diffieHellman mocks, tests * Add TestLoadStore function to act_test.go * Remove unnecessary code in Load function * Add history mock and History lookup test * Act refactor Co-authored-by: Bálint Ujvári * Refactor Add method to return Act interface * Change Get method return type to []byte --------- Co-authored-by: Ferenc Sárai Co-authored-by: Peter Ott Co-authored-by: Bálint Ujvári Co-authored-by: Levente Kiss Co-authored-by: Roland Seres Co-authored-by: Kexort Co-authored-by: Bálint Ujvári --- pkg/dynamicaccess/accesslogic.go | 146 ++++++++++++++++--- pkg/dynamicaccess/accesslogic_test.go | 177 +++++++++++++++++++++++- pkg/dynamicaccess/act.go | 51 ++++++- pkg/dynamicaccess/act_test.go | 122 ++++++++++++++-- pkg/dynamicaccess/diffieHellman.go | 28 ++-- pkg/dynamicaccess/diffieHellman_test.go | 50 ++++++- pkg/dynamicaccess/feed.go | 36 ----- pkg/dynamicaccess/feed_test.go | 40 ------ pkg/dynamicaccess/history.go | 35 +++-- pkg/dynamicaccess/history_test.go | 65 +++++++-- pkg/dynamicaccess/mock/act.go | 47 +++++++ pkg/dynamicaccess/mock/diffieHellman.go | 20 ++- pkg/dynamicaccess/mock/history.go | 90 ++++++++++++ 13 files changed, 739 insertions(+), 168 deletions(-) delete mode 100644 pkg/dynamicaccess/feed.go delete mode 100644 pkg/dynamicaccess/feed_test.go create mode 100644 pkg/dynamicaccess/mock/act.go create mode 100644 pkg/dynamicaccess/mock/history.go diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index 269fe4c4638..ee141d7b69f 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -1,41 +1,143 @@ package dynamicaccess import ( - "hash" + "context" + "crypto/ecdsa" + "errors" - "github.com/ethersphere/bee/pkg/dynamicaccess/mock" encryption "github.com/ethersphere/bee/pkg/encryption" + file "github.com/ethersphere/bee/pkg/file" + manifest "github.com/ethersphere/bee/pkg/manifest" + "golang.org/x/crypto/sha3" ) +var hashFunc = sha3.NewLegacyKeccak256 + type AccessLogic interface { - Get(encryped_ref string, publisher string, tag string) (string, error) + Get(act_root_hash string, encryped_ref string, publisher string, tag string) (string, error) + GetLookUpKey(publisher string, tag string) (string, error) + GetAccessKeyDecriptionKey(publisher string, tag string) (string, error) + GetEncryptedAccessKey(act_root_hash string, lookup_key string) (manifest.Entry, error) } type DefaultAccessLogic struct { diffieHellman DiffieHellman - encryption encryption.Interface - act Act + //encryption encryption.Interface + act defaultAct +} + +// Will give back Swarm reference with symmertic encryption key (128 byte) +// @publisher: public key +func (al *DefaultAccessLogic) GetLookUpKey(publisher string, tag string) (string, error) { + zeroByteArray := []byte{0} + // Generate lookup key using Diffie Hellman + lookup_key, err := al.diffieHellman.SharedSecret(publisher, tag, zeroByteArray) + if err != nil { + return "", err + } + return lookup_key, nil + +} + +func (al *DefaultAccessLogic) GetAccessKeyDecriptionKey(publisher string, tag string) (string, error) { + oneByteArray := []byte{1} + // Generate access key decryption key using Diffie Hellman + access_key_decryption_key, err := al.diffieHellman.SharedSecret(publisher, tag, oneByteArray) + if err != nil { + return "", err + } + return access_key_decryption_key, nil } -func (al *DefaultAccessLogic) Get(encryped_ref string, publisher string, tag string) (string, error) { - return "", nil +func (al *DefaultAccessLogic) GetEncryptedAccessKey(act_root_hash string, lookup_key string) (manifest.Entry, error) { + if act_root_hash == "" { + return nil, errors.New("no ACT root hash was provided") + } + if lookup_key == "" { + return nil, errors.New("no lookup key") + } + + manifest_raw, err := al.act.Get(act_root_hash) + if err != nil { + return nil, err + } + al.act.Get(act_root_hash) + + // Lookup encrypted access key from the ACT manifest + var loadSaver file.LoadSaver + var ctx context.Context + loadSaver.Load(ctx, []byte(manifest_raw)) // Load the manifest file into loadSaver + //y, err := x.Load(ctx, []byte(manifest_obj)) + manifestObj, err := manifest.NewDefaultManifest(loadSaver, false) + if err != nil { + return nil, err + } + encrypted_access_key, err := manifestObj.Lookup(ctx, lookup_key) + if err != nil { + return nil, err + } + + return encrypted_access_key, nil } -func NewAccessLogic(key encryption.Key, padding int, initCtr uint32, hashFunc func() hash.Hash) AccessLogic { +func (al *DefaultAccessLogic) Get(act_root_hash string, encryped_ref string, publisher string, tag string) (string, error) { + + lookup_key, err := al.GetLookUpKey(publisher, tag) + if err != nil { + return "", err + } + access_key_decryption_key, err := al.GetAccessKeyDecriptionKey(publisher, tag) + if err != nil { + return "", err + } + + // Lookup encrypted access key from the ACT manifest + + encrypted_access_key, err := al.GetEncryptedAccessKey(act_root_hash, lookup_key) + if err != nil { + return "", err + } + + // Decrypt access key + access_key_cipher := encryption.New(encryption.Key(access_key_decryption_key), 4096, uint32(0), hashFunc) + access_key, err := access_key_cipher.Decrypt(encrypted_access_key.Reference().Bytes()) + if err != nil { + return "", err + } + + // Decrypt reference + ref_cipher := encryption.New(access_key, 4096, uint32(0), hashFunc) + ref, err := ref_cipher.Decrypt([]byte(encryped_ref)) + if err != nil { + return "", err + } + + return string(ref), nil +} + +func NewAccessLogic(diffieHellmanPrivateKey *ecdsa.PrivateKey) AccessLogic { return &DefaultAccessLogic{ - diffieHellman: &mock.DiffieHellmanMock{ - SharedSecretFunc: func(publicKey string, tag string, moment []byte) (string, error) { - return publicKey, nil - }, - }, - encryption: encryption.New(key, padding, initCtr, hashFunc), - act: &mock.ContainerMock{ - AddFunc: func(ref string, publisher string, tag string) error { - return nil - }, - GetFunc: func(ref string, publisher string, tag string) (string, error) { - return "", nil - }, - }, + diffieHellman: NewDiffieHellman(diffieHellmanPrivateKey), + //encryption: encryption.New(key, padding, initCtr, hashFunc), + act: defaultAct{}, + + // { + // AddFunc: func(ref string, publisher string, tag string) error { + // return nil + // }, + // GetFunc: func(ref string, publisher string, tag string) (string, error) { + // return "", nil + // }, + // }, } } + +// ------- +// act: &mock.ContainerMock{ +// AddFunc: func(ref string, publisher string, tag string) error { +// return nil +// }, +// GetFunc: func(ref string, publisher string, tag string) (string, error) { +// return "", nil +// }, +// }, diff --git a/pkg/dynamicaccess/accesslogic_test.go b/pkg/dynamicaccess/accesslogic_test.go index 4f3ad6c44bc..cc4ad28ebce 100644 --- a/pkg/dynamicaccess/accesslogic_test.go +++ b/pkg/dynamicaccess/accesslogic_test.go @@ -1,11 +1,176 @@ package dynamicaccess -import "testing" +import ( + "errors" + "testing" -func TestXxx(t *testing.T) { - //key encryption.Key, padding int, initCtr uint32, hashFunc func() hash.Hash - al := NewAccessLogic(nil, 0, 0, nil) - if al == nil { - t.Errorf("Error creating access logic") + "github.com/ethersphere/bee/pkg/crypto" +) + +func setupAccessLogic() AccessLogic { + privateKey, err := crypto.GenerateSecp256k1Key() + if err != nil { + errors.New("error creating private key") + } + al := NewAccessLogic(privateKey) + + return al +} + +func TestGetLookupKey_Success(t *testing.T) { + al := setupAccessLogic() + + publisher := "examplePublisher" + tag := "exampleTag" + + lookupKey, err := al.GetLookUpKey(publisher, tag) + if err != nil { + t.Errorf("Could not fetch lookup key from publisher and tag") + } + + expectedLookupKey := "expectedLookupKey" + if lookupKey != expectedLookupKey { + t.Errorf("The lookup key that was returned is not correct!") + } +} + +func TestGetLookupKey_Error(t *testing.T) { + al := setupAccessLogic() + + invalidPublisher := "" + tag := "exampleTag" + + lookupKey, err := al.GetLookUpKey(invalidPublisher, tag) + if err != nil { + t.Errorf("There was an error while fetching lookup key") + } + + if lookupKey != "" { + t.Errorf("Expected lookup key to be empty for invalid input") + } +} + +func TestGetAccessKeyDecriptionKey_Success(t *testing.T) { + al := setupAccessLogic() + + publisher := "examplePublisher" + tag := "exampleTag" + + access_key_decryption_key, err := al.GetAccessKeyDecriptionKey(publisher, tag) + if err != nil { + t.Errorf("GetAccessKeyDecriptionKey gave back error") + } + + expectedResult := "we-dont-know" + if access_key_decryption_key != expectedResult { + t.Errorf("The access key decryption key is not correct!") + } +} + +func TestGetAccessKeyDecriptionKey_Error(t *testing.T) { + al := setupAccessLogic() + + invalidPublisher := "" + tag := "exampleTag" + + access_key_decryption_key, err := al.GetAccessKeyDecriptionKey(invalidPublisher, tag) + if err != nil { + t.Errorf("GetAccessKeyDecriptionKey gave back error") + } + + if access_key_decryption_key != "" { + t.Errorf("GetAccessKeyDecriptionKey should give back empty string for invalid input!") + } +} + +func TestGetEncryptedAccessKey_Success(t *testing.T) { + al := setupAccessLogic() + + actRootHash := "0xabcexample" + lookupKey := "exampleLookupKey" + + encrypted_access_key, err := al.GetEncryptedAccessKey(actRootHash, lookupKey) + if err != nil { + t.Errorf("There was an error while executing GetEncryptedAccessKey") + } + + expectedEncryptedKey := "abc013encryptedkey" + if encrypted_access_key != expectedEncryptedKey { + t.Errorf("GetEncryptedAccessKey didn't give back the expected value!") + } +} + +func TestGetEncryptedAccessKey_Error(t *testing.T) { + al := setupAccessLogic() + + actRootHash := "0xabcexample" + lookupKey := "exampleLookupKey" + + empty_act_result, _ := al.GetEncryptedAccessKey("", lookupKey) + if empty_act_result != nil { + t.Errorf("GetEncryptedAccessKey should give back nil for empty act root hash!") + } + + empty_lookup_result, _ := al.GetEncryptedAccessKey(actRootHash, "") + + if empty_lookup_result != nil { + t.Errorf("GetEncryptedAccessKey should give back nil for empty lookup key!") + } +} + +func TestGet_Success(t *testing.T) { + al := setupAccessLogic() + + actRootHash := "0xabcexample" + encryptedRef := "bzzabcasab" + publisher := "examplePublisher" + tag := "exampleTag" + + ref, err := al.Get(actRootHash, encryptedRef, publisher, tag) + if err != nil { + t.Errorf("There was an error while calling Get") + } + + expectedRef := "bzzNotEncrypted128long" + if ref != expectedRef { + t.Errorf("Get gave back wrong Swarm reference!") + } +} + +func TestGet_Error(t *testing.T) { + al := setupAccessLogic() + + actRootHash := "0xabcexample" + encryptedRef := "bzzabcasab" + publisher := "examplePublisher" + tag := "exampleTag" + + refOne, _ := al.Get("", encryptedRef, publisher, tag) + if refOne != "" { + t.Errorf("Get should give back empty string if ACT root hash not provided!") + } + + refTwo, _ := al.Get(actRootHash, "", publisher, tag) + if refTwo != "" { + t.Errorf("Get should give back empty string if encrypted ref not provided!") + } + + refThree, _ := al.Get(actRootHash, encryptedRef, "", tag) + if refThree != "" { + t.Errorf("Get should give back empty string if publisher not provided!") + } + + refFour, _ := al.Get(actRootHash, encryptedRef, publisher, "") + if refFour != "" { + t.Errorf("Get should give back empty string if tag was not provided!") + } +} + +func TestNewAccessLogic(t *testing.T) { + logic := setupAccessLogic() + + _, ok := logic.(*DefaultAccessLogic) + if !ok { + t.Errorf("NewAccessLogic: expected type *DefaultAccessLogic, got %T", logic) } } diff --git a/pkg/dynamicaccess/act.go b/pkg/dynamicaccess/act.go index b3448260312..b63b1dafc6b 100644 --- a/pkg/dynamicaccess/act.go +++ b/pkg/dynamicaccess/act.go @@ -1,18 +1,55 @@ package dynamicaccess -type Act interface{} +import ( + "encoding/hex" + + "github.com/ethersphere/bee/pkg/manifest" + "github.com/ethersphere/bee/pkg/swarm" +) + +type Act interface { + Add(lookupKey []byte, encryptedAccessKey []byte) Act + Get(lookupKey []byte) []byte + Load(lookupKey []byte) manifest.Entry + Store(me manifest.Entry) +} + +var _ Act = (*defaultAct)(nil) type defaultAct struct { + container map[string]string +} + +func (act *defaultAct) Add(lookupKey []byte, encryptedAccessKey []byte) Act { + act.container[hex.EncodeToString(lookupKey)] = hex.EncodeToString(encryptedAccessKey) + return act +} + +func (act *defaultAct) Get(lookupKey []byte) []byte { + if key, ok := act.container[hex.EncodeToString(lookupKey)]; ok { + bytes, err := hex.DecodeString(key) + if err == nil { + return bytes + } + } + return make([]byte, 0) } -func (a *defaultAct) Add(oldItemKey string, oldRootHash string) (newRootHash string, err error) { - return "", nil +// to manifestEntry +func (act *defaultAct) Load(lookupKey []byte) manifest.Entry { + return manifest.NewEntry(swarm.NewAddress(lookupKey), act.container) } -func (a *defaultAct) Get(rootKey string) (value string, err error) { - return "", nil +// from manifestEntry +func (act *defaultAct) Store(me manifest.Entry) { + if act.container == nil { + act.container = make(map[string]string) + } + act.container = me.Metadata() } -func NewAct() Container { - return &defaultAct{} +func NewDefaultAct() Act { + return &defaultAct{ + container: make(map[string]string), + } } diff --git a/pkg/dynamicaccess/act_test.go b/pkg/dynamicaccess/act_test.go index e1d316d2841..0e5ca853f0b 100644 --- a/pkg/dynamicaccess/act_test.go +++ b/pkg/dynamicaccess/act_test.go @@ -1,19 +1,119 @@ -package dynamicaccess +package dynamicaccess_test -import "testing" +import ( + "bytes" + "context" + "encoding/hex" + "testing" -func TestAdd(t *testing.T) { - a := NewAct() - _, err := a.Add("", "") - if err != nil { - t.Error("Add() should not return an error") + "github.com/ethersphere/bee/pkg/dynamicaccess" + "github.com/ethersphere/bee/pkg/file/loadsave" + "github.com/ethersphere/bee/pkg/file/pipeline" + "github.com/ethersphere/bee/pkg/file/pipeline/builder" + "github.com/ethersphere/bee/pkg/file/redundancy" + "github.com/ethersphere/bee/pkg/manifest" + "github.com/ethersphere/bee/pkg/storage" + mockstorer "github.com/ethersphere/bee/pkg/storer/mock" + "github.com/ethersphere/bee/pkg/swarm" +) + +func TestActAddGet(t *testing.T) { + act := dynamicaccess.NewDefaultAct() + lookupKey := swarm.RandAddress(t).Bytes() + encryptedAccesskey := swarm.RandAddress(t).Bytes() + act2 := act.Add(lookupKey, encryptedAccesskey) + if act2 == nil { + t.Error("Add() should return an act") + } + + key := act.Get(lookupKey) + if !bytes.Equal(key, encryptedAccesskey) { + t.Errorf("Get() value is not the expected %s != %s", key, encryptedAccesskey) } } -func TestGet(t *testing.T) { - a := NewAct() - _, err := a.Get("") +func TestActWithManifest(t *testing.T) { + + storer := mockstorer.New() + encrypt := false + ctx := context.Background() + ls := loadsave.New(storer.ChunkStore(), storer.Cache(), pipelineFactory(storer.Cache(), false, 0)) + rootManifest, err := manifest.NewDefaultManifest(ls, encrypt) + if err != nil { + t.Error("DefaultManifest should not return an error") + } + + act := dynamicaccess.NewDefaultAct() + lookupKey := swarm.RandAddress(t).Bytes() + encryptedAccesskey := swarm.RandAddress(t).Bytes() + act2 := act.Add(lookupKey, encryptedAccesskey) + if act2 == nil { + t.Error("Add() should return an act") + } + + actManifEntry := act.Load(lookupKey) + if actManifEntry == nil { + t.Error("Load() should return a manifest.Entry") + } + + err = rootManifest.Add(ctx, hex.EncodeToString(lookupKey), actManifEntry) + if err != nil { + t.Error("rootManifest.Add() should not return an error") + } + + _, err = rootManifest.Store(ctx) + if err != nil { + t.Error("rootManifest.Store() should not return an error") + } + + actualMe, err := rootManifest.Lookup(ctx, hex.EncodeToString(lookupKey)) if err != nil { - t.Error("Get() should not return an error") + t.Error("rootManifest.Lookup() should not return an error") + } + + actualAct := dynamicaccess.NewDefaultAct() + actualAct.Store(actualMe) + actualEak := actualAct.Get(lookupKey) + if !bytes.Equal(actualEak, encryptedAccesskey) { + t.Errorf("actualAct.Store() value is not the expected %s != %s", actualEak, encryptedAccesskey) + } +} + +func TestActStore(t *testing.T) { + mp := make(map[string]string) + + lookupKey := swarm.RandAddress(t).Bytes() + encryptedAccesskey := swarm.RandAddress(t).Bytes() + mp[hex.EncodeToString(lookupKey)] = hex.EncodeToString(encryptedAccesskey) + + me := manifest.NewEntry(swarm.NewAddress(lookupKey), mp) + act := dynamicaccess.NewDefaultAct() + act.Store(me) + eak := act.Get(lookupKey) + + if !bytes.Equal(eak, encryptedAccesskey) { + t.Errorf("Store() value is not the expected %s != %s", eak, encryptedAccesskey) + } + +} + +func TestActLoad(t *testing.T) { + act := dynamicaccess.NewDefaultAct() + lookupKey := swarm.RandAddress(t).Bytes() + encryptedAccesskey := swarm.RandAddress(t).Bytes() + act.Add(lookupKey, encryptedAccesskey) + me := act.Load(lookupKey) + + eak := me.Metadata()[hex.EncodeToString(lookupKey)] + + if eak != hex.EncodeToString(encryptedAccesskey) { + t.Errorf("Load() value is not the expected %s != %s", eak, encryptedAccesskey) + } + +} + +func pipelineFactory(s storage.Putter, encrypt bool, rLevel redundancy.Level) func() pipeline.Interface { + return func() pipeline.Interface { + return builder.NewPipelineBuilder(context.Background(), s, encrypt, rLevel) } } diff --git a/pkg/dynamicaccess/diffieHellman.go b/pkg/dynamicaccess/diffieHellman.go index 8ee9bb1bcce..a9ff7f7f8cd 100644 --- a/pkg/dynamicaccess/diffieHellman.go +++ b/pkg/dynamicaccess/diffieHellman.go @@ -2,30 +2,30 @@ package dynamicaccess import ( "crypto/ecdsa" + "errors" - Crypto "github.com/ethersphere/bee/pkg/crypto" - "github.com/ethersphere/bee/pkg/keystore" - KeyStoreMem "github.com/ethersphere/bee/pkg/keystore/mem" + "github.com/ethersphere/bee/pkg/crypto" ) type DiffieHellman interface { - SharedSecret(pubKey, tag string, moment []byte) (string, error) + SharedSecret(publicKey *ecdsa.PublicKey, tag string, moment []byte) ([]byte, error) // tag- topic? } +var _ DiffieHellman = (*defaultDiffieHellman)(nil) + type defaultDiffieHellman struct { - key *ecdsa.PrivateKey - keyStoreService keystore.Service - keyStoreEdg keystore.EDG + key *ecdsa.PrivateKey } -func (d *defaultDiffieHellman) SharedSecret(pubKey string, tag string, moment []byte) (string, error) { - return "", nil +func (dh *defaultDiffieHellman) SharedSecret(publicKey *ecdsa.PublicKey, tag string, salt []byte) ([]byte, error) { + x, _ := publicKey.Curve.ScalarMult(publicKey.X, publicKey.Y, dh.key.D.Bytes()) + if x == nil { + return nil, errors.New("shared secret is point at infinity") + } + return crypto.LegacyKeccak256(append(x.Bytes(), salt...)) } func NewDiffieHellman(key *ecdsa.PrivateKey) DiffieHellman { - return &defaultDiffieHellman{ - key: key, - keyStoreService: KeyStoreMem.New(), - keyStoreEdg: Crypto.EDGSecp256_K1, - } + return &defaultDiffieHellman{key: key} + } diff --git a/pkg/dynamicaccess/diffieHellman_test.go b/pkg/dynamicaccess/diffieHellman_test.go index 2e1c870a178..5c80592b3ec 100644 --- a/pkg/dynamicaccess/diffieHellman_test.go +++ b/pkg/dynamicaccess/diffieHellman_test.go @@ -1,10 +1,54 @@ -package dynamicaccess +package dynamicaccess_test -import "testing" +import ( + "bytes" + "crypto/rand" + "encoding/hex" + "io" + "testing" + + "github.com/ethersphere/bee/pkg/crypto" + "github.com/ethersphere/bee/pkg/dynamicaccess" +) func TestSharedSecret(t *testing.T) { - _, err := NewDiffieHellman(nil).SharedSecret("", "", nil) + pk, _ := crypto.GenerateSecp256k1Key() + _, err := dynamicaccess.NewDiffieHellman(pk).SharedSecret(&pk.PublicKey, "", nil) if err != nil { t.Errorf("Error generating shared secret: %v", err) } } + +func TestECDHCorrect(t *testing.T) { + t.Parallel() + + key1, err := crypto.GenerateSecp256k1Key() + if err != nil { + t.Fatal(err) + } + dh1 := dynamicaccess.NewDiffieHellman(key1) + + key2, err := crypto.GenerateSecp256k1Key() + if err != nil { + t.Fatal(err) + } + dh2 := dynamicaccess.NewDiffieHellman(key2) + + moment := make([]byte, 1) + if _, err := io.ReadFull(rand.Reader, moment); err != nil { + t.Fatal(err) + } + + shared1, err := dh1.SharedSecret(&key2.PublicKey, "", moment) + if err != nil { + t.Fatal(err) + } + shared2, err := dh2.SharedSecret(&key1.PublicKey, "", moment) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(shared1, shared2) { + t.Fatalf("shared secrets do not match %s, %s", hex.EncodeToString(shared1), hex.EncodeToString(shared2)) + } +} diff --git a/pkg/dynamicaccess/feed.go b/pkg/dynamicaccess/feed.go deleted file mode 100644 index 2576c2733a5..00000000000 --- a/pkg/dynamicaccess/feed.go +++ /dev/null @@ -1,36 +0,0 @@ -package dynamicaccess - -// referencia: history.go -type Feed interface { - Update(itemKey string, content string) error - Get(itemKey string) (content string, err error) - AddNewGrantee(itemKey string, grantee string) error - RemoveGrantee(itemKey string, grantee string) error - GetAccess(encryptedRef string, publisher string, tag string) (access string, err error) -} - -type defaultFeed struct{} - -func (f *defaultFeed) Update(itemKey string, content string) error { - return nil -} - -func (f *defaultFeed) Get(itemKey string) (content string, err error) { - return "", nil -} - -func (f *defaultFeed) AddNewGrantee(itemKey string, grantee string) error { - return nil -} - -func (f *defaultFeed) RemoveGrantee(itemKey string, grantee string) error { - return nil -} - -func (f *defaultFeed) GetAccess(encryptedRef string, publisher string, tag string) (access string, err error) { - return "", nil -} - -func NewFeed() Feed { - return &defaultFeed{} -} diff --git a/pkg/dynamicaccess/feed_test.go b/pkg/dynamicaccess/feed_test.go deleted file mode 100644 index 1839e6230b9..00000000000 --- a/pkg/dynamicaccess/feed_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package dynamicaccess - -import "testing" - -func TestFeedUpdate(t *testing.T) { - err := NewFeed().Update("", "") - if err != nil { - t.Errorf("Error updating feed: %v", err) - } -} - -func TestFeedGet(t *testing.T) { - content, err := NewFeed().Get("") - if err != nil { - t.Errorf("Error getting feed: %v", err) - } - _ = content // Ignore the content if not needed -} - -func TestFeedAddNewGrantee(t *testing.T) { - err := NewFeed().AddNewGrantee("", "") - if err != nil { - t.Errorf("Error adding new grantee to feed: %v", err) - } -} - -func TestFeedRemoveGrantee(t *testing.T) { - err := NewFeed().RemoveGrantee("", "") - if err != nil { - t.Errorf("Error removing grantee from feed: %v", err) - } -} - -func TestFeedGetAccess(t *testing.T) { - access, err := NewFeed().GetAccess("", "", "") - if err != nil { - t.Errorf("Error getting access to feed: %v", err) - } - _ = access // Ignore the access if not needed -} diff --git a/pkg/dynamicaccess/history.go b/pkg/dynamicaccess/history.go index b9a9438b331..e2705fffeac 100644 --- a/pkg/dynamicaccess/history.go +++ b/pkg/dynamicaccess/history.go @@ -1,22 +1,35 @@ package dynamicaccess -// TODO FROM BEE!!!! -// timestamp alapú history +import ( + "github.com/ethereum/go-ethereum/common" +) + type History interface { - Add(oldItemKey string, oldRootHash string) (newRootHash string, err error) - Get(rootKey string) (value string, err error) + Add(timestamp int64, act Act) error + Get(timestamp int64) (Act, error) + Lookup(at int64) (Act, error) +} + +var _ History = (*history)(nil) + +type history struct { + history map[int64]*Act +} + +func NewHistory(topic []byte, owner common.Address) *history { + return &history{history: make(map[int64]*Act)} } -type defaultHistory struct{} +func (h *history) Add(timestamp int64, act Act) error { -func (h *defaultHistory) Add(oldItemKey string, oldRootHash string) (newRootHash string, err error) { - return "", nil + return nil } -func (h *defaultHistory) Get(rootKey string) (value string, err error) { - return "", nil +func (h *history) Lookup(at int64) (Act, error) { + return nil, nil } -func NewHistory() History { - return &defaultHistory{} +func (h *history) Get(timestamp int64) (Act, error) { + // get the feed + return nil, nil } diff --git a/pkg/dynamicaccess/history_test.go b/pkg/dynamicaccess/history_test.go index 9116e41f548..2a24e65d862 100644 --- a/pkg/dynamicaccess/history_test.go +++ b/pkg/dynamicaccess/history_test.go @@ -1,19 +1,58 @@ -package dynamicaccess +package dynamicaccess_test -import "testing" +import ( + "encoding/hex" + "testing" + "time" -func TestHistoryAdd(t *testing.T) { - newRootHash, err := NewHistory().Add("", "") - if err != nil { - t.Errorf("Error adding history: %v", err) + "github.com/ethersphere/bee/pkg/dynamicaccess" + "github.com/ethersphere/bee/pkg/dynamicaccess/mock" + "github.com/stretchr/testify/assert" +) + +func TestHistoryLookup(t *testing.T) { + h := pretareTestHistory() + now := time.Now() + + tests := []struct { + input int64 + expected string + }{ + {input: 0, expected: "value3"}, + {input: now.Unix(), expected: "value3"}, + {input: now.AddDate(0, -5, 0).Unix(), expected: "value3"}, + {input: now.AddDate(0, -6, 0).Unix(), expected: "value3"}, + {input: now.AddDate(-1, 0, 0).Unix(), expected: "value3"}, + {input: now.AddDate(-1, -6, 0).Unix(), expected: "value2"}, + {input: now.AddDate(-2, -0, 0).Unix(), expected: "value2"}, + {input: now.AddDate(-2, -6, 0).Unix(), expected: "value1"}, + {input: now.AddDate(-3, -0, 0).Unix(), expected: "value1"}, } - _ = newRootHash // Ignore the newRootHash if not needed -} -func TestHistoryGet(t *testing.T) { - value, err := NewHistory().Get("") - if err != nil { - t.Errorf("Error getting history: %v", err) + for _, tt := range tests { + t.Run("", func(t *testing.T) { + actAt, _ := h.Lookup(tt.input) + output := actAt.Get([]byte("key1")) + assert.Equal(t, output, hex.EncodeToString([]byte(tt.expected))) + }) } - _ = value // Ignore the value if not needed +} + +func pretareTestHistory() dynamicaccess.History { + var ( + h = mock.NewHistory() + now = time.Now() + act1 = dynamicaccess.NewDefaultAct() + act2 = dynamicaccess.NewDefaultAct() + act3 = dynamicaccess.NewDefaultAct() + ) + act1.Add([]byte("key1"), []byte("value1")) + act2.Add([]byte("key1"), []byte("value2")) + act3.Add([]byte("key1"), []byte("value3")) + + h.Insert(now.AddDate(-3, 0, 0).Unix(), act1) + h.Insert(now.AddDate(-2, 0, 0).Unix(), act2) + h.Insert(now.AddDate(-1, 0, 0).Unix(), act3) + + return h } diff --git a/pkg/dynamicaccess/mock/act.go b/pkg/dynamicaccess/mock/act.go new file mode 100644 index 00000000000..1fd68caa399 --- /dev/null +++ b/pkg/dynamicaccess/mock/act.go @@ -0,0 +1,47 @@ +package mock + +import ( + "github.com/ethersphere/bee/pkg/dynamicaccess" + "github.com/ethersphere/bee/pkg/manifest" +) + +type ActMock struct { + AddFunc func(lookupKey []byte, encryptedAccessKey []byte) dynamicaccess.Act + GetFunc func(lookupKey []byte) []byte + LoadFunc func(lookupKey []byte) manifest.Entry + StoreFunc func(me manifest.Entry) +} + +var _ dynamicaccess.Act = (*ActMock)(nil) + +func (act *ActMock) Add(lookupKey []byte, encryptedAccessKey []byte) dynamicaccess.Act { + if act.AddFunc == nil { + return act + } + return act.AddFunc(lookupKey, encryptedAccessKey) +} + +func (act *ActMock) Get(lookupKey []byte) []byte { + if act.GetFunc == nil { + return make([]byte, 0) + } + return act.GetFunc(lookupKey) +} + +func (act *ActMock) Load(lookupKey []byte) manifest.Entry { + if act.LoadFunc == nil { + return nil + } + return act.LoadFunc(lookupKey) +} + +func (act *ActMock) Store(me manifest.Entry) { + if act.StoreFunc == nil { + return + } + act.StoreFunc(me) +} + +func NewActMock() dynamicaccess.Act { + return &ActMock{} +} diff --git a/pkg/dynamicaccess/mock/diffieHellman.go b/pkg/dynamicaccess/mock/diffieHellman.go index f71131b11ff..91601026893 100644 --- a/pkg/dynamicaccess/mock/diffieHellman.go +++ b/pkg/dynamicaccess/mock/diffieHellman.go @@ -1,12 +1,22 @@ package mock +import ( + "crypto/ecdsa" +) + type DiffieHellmanMock struct { - SharedSecretFunc func(string, string, []byte) (string, error) + SharedSecretFunc func(publicKey *ecdsa.PublicKey, tag string, salt []byte) ([]byte, error) + key *ecdsa.PrivateKey } -func (ma *DiffieHellmanMock) SharedSecret(publicKey string, tag string, moment []byte) (string, error) { - if ma.SharedSecretFunc == nil { - return "", nil +func (dhm *DiffieHellmanMock) SharedSecret(publicKey *ecdsa.PublicKey, tag string, salt []byte) ([]byte, error) { + if dhm.SharedSecretFunc == nil { + return nil, nil } - return ma.SharedSecretFunc(publicKey, tag, moment) + return dhm.SharedSecretFunc(publicKey, tag, salt) + +} + +func NewDiffieHellmanMock(key *ecdsa.PrivateKey) *DiffieHellmanMock { + return &DiffieHellmanMock{key: key} } diff --git a/pkg/dynamicaccess/mock/history.go b/pkg/dynamicaccess/mock/history.go new file mode 100644 index 00000000000..68559c2277a --- /dev/null +++ b/pkg/dynamicaccess/mock/history.go @@ -0,0 +1,90 @@ +package mock + +import ( + "context" + "sort" + "time" + + "github.com/ethersphere/bee/pkg/crypto" + "github.com/ethersphere/bee/pkg/dynamicaccess" + "github.com/ethersphere/bee/pkg/feeds" + "github.com/ethersphere/bee/pkg/storage" + "github.com/ethersphere/bee/pkg/swarm" +) + +type historyMock struct { + history map[int64]dynamicaccess.Act +} + +func NewHistory() *historyMock { + return &historyMock{history: make(map[int64]dynamicaccess.Act)} +} + +func (h *historyMock) Add(timestamp int64, act dynamicaccess.Act) error { + h.history[timestamp] = act + return nil +} + +func (h *historyMock) Insert(timestamp int64, act dynamicaccess.Act) *historyMock { + h.Add(timestamp, act) + return h +} + +func (h *historyMock) Lookup(at int64) (dynamicaccess.Act, error) { + keys := []int64{} + for k := range h.history { + keys = append(keys, k) + } + + sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) + + timestamp := time.Now() + if at != 0 { + timestamp = time.Unix(at, 0) + } + + for i := len(keys) - 1; i >= 0; i-- { + update := time.Unix(keys[i], 0) + if update.Before(timestamp) || update.Equal(timestamp) { + return h.history[keys[i]], nil + } + } + return nil, nil +} + +func (h *historyMock) Get(timestamp int64) (dynamicaccess.Act, error) { + return h.history[timestamp], nil +} + +type finder struct { + getter *feeds.Getter +} + +type updater struct { + *feeds.Putter + next uint64 +} + +func (f *finder) At(ctx context.Context, at int64, after uint64) (chunk swarm.Chunk, currentIndex, nextIndex feeds.Index, err error) { + return nil, nil, nil, nil +} + +func HistoryFinder(getter storage.Getter, feed *feeds.Feed) feeds.Lookup { + return &finder{feeds.NewGetter(getter, feed)} +} + +func (u *updater) Update(ctx context.Context, at int64, payload []byte) error { + return nil +} + +func (u *updater) Feed() *feeds.Feed { + return nil +} + +func HistoryUpdater(putter storage.Putter, signer crypto.Signer, topic []byte) (feeds.Updater, error) { + p, err := feeds.NewPutter(putter, signer, topic) + if err != nil { + return nil, err + } + return &updater{Putter: p}, nil +} From 3b02370305efd1c5be48ec68939b1757537a8f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20Ar=C3=A1nyi?= Date: Wed, 13 Mar 2024 19:07:06 +0400 Subject: [PATCH 11/41] Acces Logic (#8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Use DiffieHellmanMock * Adds a comment about Get * Add support for ECDSA public key in DiffieHellman.SharedSecret function * Update defaultAct implementation * Adds pseudo code for Access Logic * Update default Act creation; Fix basic Act tests * Refactor access logic to use new ActMock implementation * feat(history): test mockups wip * Refactor DiffieHellman implementation * changes pseudocode for Diffie-Hellmann read * Co-authored-by: Bálint Ujvári * DiffieHellman mock generates a real sherd secret * Refactor Act * Adds manifest lookup * Extend act_test * Adds unit tests, some values are mocked * Refactor act mock impl with map[string]map[string]string * Add check mock implementation for DiffieHellman interface * started Add * changed some sig * save * new grantee addition handling * mod * changed helper function visibilities * some mod with grantee * test mod * save * no error in actInit * Add_New_Grantee_To_Content * comment * copied act_test.go * no compiler errors on our side * Adds Add_New_Grantee_To_Content and ActInit * almost complete grantee container * maybe complete grantee container * Solves merge conflict * access-logic-merge * fix merge issues --- pkg/dynamicaccess/accesslogic.go | 123 ++++++++++++++++++-------- pkg/dynamicaccess/accesslogic_test.go | 115 ++++++++++++++++++------ pkg/dynamicaccess/grantee.go | 48 ++++++++-- pkg/dynamicaccess/grantee_test.go | 102 ++++++++++++++++++--- 4 files changed, 304 insertions(+), 84 deletions(-) diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index ee141d7b69f..803c1110517 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -8,16 +8,22 @@ import ( encryption "github.com/ethersphere/bee/pkg/encryption" file "github.com/ethersphere/bee/pkg/file" manifest "github.com/ethersphere/bee/pkg/manifest" + "github.com/ethersphere/bee/pkg/swarm" "golang.org/x/crypto/sha3" ) var hashFunc = sha3.NewLegacyKeccak256 type AccessLogic interface { - Get(act_root_hash string, encryped_ref string, publisher string, tag string) (string, error) - GetLookUpKey(publisher string, tag string) (string, error) - GetAccessKeyDecriptionKey(publisher string, tag string) (string, error) - GetEncryptedAccessKey(act_root_hash string, lookup_key string) (manifest.Entry, error) + Get(act *Act, encryped_ref swarm.Address, publisher ecdsa.PublicKey, tag string) (string, error) + //Add(act *Act, ref string, publisher ecdsa.PublicKey, tag string) (string, error) + getLookUpKey(publisher ecdsa.PublicKey, tag string) (string, error) + getAccessKeyDecriptionKey(publisher ecdsa.PublicKey, tag string) (string, error) + getEncryptedAccessKey(act Act, lookup_key string) (manifest.Entry, error) + //createEncryptedAccessKey(ref string) + Add_New_Grantee_To_Content(act *Act, encryptedRef swarm.Address, publisherPubKey ecdsa.PublicKey, granteePubKey ecdsa.PublicKey) (*Act, error) + ActInit(ref swarm.Address, publisher ecdsa.PublicKey, tag string) (*Act, swarm.Address, error) + // CreateAccessKey() } type DefaultAccessLogic struct { @@ -26,42 +32,95 @@ type DefaultAccessLogic struct { act defaultAct } -// Will give back Swarm reference with symmertic encryption key (128 byte) -// @publisher: public key -func (al *DefaultAccessLogic) GetLookUpKey(publisher string, tag string) (string, error) { +// Will create a new Act list with only one element (the creator), and will also create encrypted_ref +func (al *DefaultAccessLogic) ActInit(ref swarm.Address, publisher ecdsa.PublicKey, tag string) (*Act, swarm.Address, error) { + act := NewDefaultAct() + + lookup_key, _ := al.getLookUpKey(publisher, "") + access_key_encryption_key, _ := al.getAccessKeyDecriptionKey(publisher, "") + + access_key_cipher := encryption.New(encryption.Key(access_key_encryption_key), 0, uint32(0), hashFunc) + access_key := encryption.GenerateRandomKey(encryption.KeyLength) + encrypted_access_key, _ := access_key_cipher.Encrypt([]byte(access_key)) + + ref_cipher := encryption.New(access_key, 0, uint32(0), hashFunc) + encrypted_ref, _ := ref_cipher.Encrypt(ref.Bytes()) + + act.Add([]byte(lookup_key), encrypted_access_key) + + return &act, swarm.NewAddress(encrypted_ref), nil +} + +// publisher is public key +func (al *DefaultAccessLogic) Add_New_Grantee_To_Content(act *Act, encryptedRef swarm.Address, publisherPubKey ecdsa.PublicKey, granteePubKey ecdsa.PublicKey) (*Act, error) { + + // error handling no encrypted_ref + + // 2 Diffie-Hellman for the publisher (the Creator) + publisher_lookup_key, _ := al.getLookUpKey(publisherPubKey, "") + publisher_ak_decryption_key, _ := al.getAccessKeyDecriptionKey(publisherPubKey, "") + + // Get previously generated access key + access_key_decryption_cipher := encryption.New(encryption.Key(publisher_ak_decryption_key), 0, uint32(0), hashFunc) + encrypted_ak, _ := al.getEncryptedAccessKey(*act, publisher_lookup_key) + access_key, _ := access_key_decryption_cipher.Decrypt(encrypted_ak.Reference().Bytes()) + + // --Encrypt access key for new Grantee-- + + // 2 Diffie-Hellman for the Grantee + lookup_key, _ := al.getLookUpKey(granteePubKey, "") + access_key_encryption_key, _ := al.getAccessKeyDecriptionKey(granteePubKey, "") + + // Encrypt the access key for the new Grantee + cipher := encryption.New(encryption.Key(access_key_encryption_key), 0, uint32(0), hashFunc) + granteeEncryptedAccessKey, _ := cipher.Encrypt(access_key) + // Add the new encrypted access key for the Act + actObj := *act + actObj.Add([]byte(lookup_key), granteeEncryptedAccessKey) + + return &actObj, nil + +} + +// +// act[lookupKey] := valamilyen_cipher.Encrypt(access_key) + +// end of pseudo code like code + +// func (al *DefaultAccessLogic) CreateAccessKey(reference string) { +// } + +func (al *DefaultAccessLogic) getLookUpKey(publisher ecdsa.PublicKey, tag string) (string, error) { zeroByteArray := []byte{0} // Generate lookup key using Diffie Hellman - lookup_key, err := al.diffieHellman.SharedSecret(publisher, tag, zeroByteArray) + lookup_key, err := al.diffieHellman.SharedSecret(&publisher, tag, zeroByteArray) if err != nil { return "", err } - return lookup_key, nil + return string(lookup_key), nil } -func (al *DefaultAccessLogic) GetAccessKeyDecriptionKey(publisher string, tag string) (string, error) { +func (al *DefaultAccessLogic) getAccessKeyDecriptionKey(publisher ecdsa.PublicKey, tag string) (string, error) { oneByteArray := []byte{1} // Generate access key decryption key using Diffie Hellman - access_key_decryption_key, err := al.diffieHellman.SharedSecret(publisher, tag, oneByteArray) + access_key_decryption_key, err := al.diffieHellman.SharedSecret(&publisher, tag, oneByteArray) if err != nil { return "", err } - return access_key_decryption_key, nil + return string(access_key_decryption_key), nil } -func (al *DefaultAccessLogic) GetEncryptedAccessKey(act_root_hash string, lookup_key string) (manifest.Entry, error) { - if act_root_hash == "" { +func (al *DefaultAccessLogic) getEncryptedAccessKey(act Act, lookup_key string) (manifest.Entry, error) { + if act == nil { return nil, errors.New("no ACT root hash was provided") } if lookup_key == "" { return nil, errors.New("no lookup key") } - manifest_raw, err := al.act.Get(act_root_hash) - if err != nil { - return nil, err - } - al.act.Get(act_root_hash) + manifest_raw := act.Get([]byte(lookup_key)) + //al.act.Get(act_root_hash) // Lookup encrypted access key from the ACT manifest var loadSaver file.LoadSaver @@ -80,20 +139,20 @@ func (al *DefaultAccessLogic) GetEncryptedAccessKey(act_root_hash string, lookup return encrypted_access_key, nil } -func (al *DefaultAccessLogic) Get(act_root_hash string, encryped_ref string, publisher string, tag string) (string, error) { +func (al *DefaultAccessLogic) Get(act *Act, encryped_ref swarm.Address, publisher ecdsa.PublicKey, tag string) (string, error) { - lookup_key, err := al.GetLookUpKey(publisher, tag) + lookup_key, err := al.getLookUpKey(publisher, tag) if err != nil { return "", err } - access_key_decryption_key, err := al.GetAccessKeyDecriptionKey(publisher, tag) + access_key_decryption_key, err := al.getAccessKeyDecriptionKey(publisher, tag) if err != nil { return "", err } // Lookup encrypted access key from the ACT manifest - encrypted_access_key, err := al.GetEncryptedAccessKey(act_root_hash, lookup_key) + encrypted_access_key, err := al.getEncryptedAccessKey(*act, lookup_key) if err != nil { return "", err } @@ -107,7 +166,7 @@ func (al *DefaultAccessLogic) Get(act_root_hash string, encryped_ref string, pub // Decrypt reference ref_cipher := encryption.New(access_key, 4096, uint32(0), hashFunc) - ref, err := ref_cipher.Decrypt([]byte(encryped_ref)) + ref, err := ref_cipher.Decrypt(encryped_ref.Bytes()) if err != nil { return "", err } @@ -115,20 +174,10 @@ func (al *DefaultAccessLogic) Get(act_root_hash string, encryped_ref string, pub return string(ref), nil } -func NewAccessLogic(diffieHellmanPrivateKey *ecdsa.PrivateKey) AccessLogic { +func NewAccessLogic(diffieHellman DiffieHellman) AccessLogic { return &DefaultAccessLogic{ - diffieHellman: NewDiffieHellman(diffieHellmanPrivateKey), - //encryption: encryption.New(key, padding, initCtr, hashFunc), - act: defaultAct{}, - - // { - // AddFunc: func(ref string, publisher string, tag string) error { - // return nil - // }, - // GetFunc: func(ref string, publisher string, tag string) (string, error) { - // return "", nil - // }, - // }, + diffieHellman: diffieHellman, + act: defaultAct{}, } } diff --git a/pkg/dynamicaccess/accesslogic_test.go b/pkg/dynamicaccess/accesslogic_test.go index cc4ad28ebce..ee7c16c43e0 100644 --- a/pkg/dynamicaccess/accesslogic_test.go +++ b/pkg/dynamicaccess/accesslogic_test.go @@ -1,10 +1,15 @@ package dynamicaccess import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" "errors" + "fmt" "testing" "github.com/ethersphere/bee/pkg/crypto" + "github.com/ethersphere/bee/pkg/swarm" ) func setupAccessLogic() AccessLogic { @@ -12,7 +17,8 @@ func setupAccessLogic() AccessLogic { if err != nil { errors.New("error creating private key") } - al := NewAccessLogic(privateKey) + diffieHellman := NewDiffieHellman(privateKey) + al := NewAccessLogic(diffieHellman) return al } @@ -20,27 +26,33 @@ func setupAccessLogic() AccessLogic { func TestGetLookupKey_Success(t *testing.T) { al := setupAccessLogic() - publisher := "examplePublisher" + id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + // ! this will be random, we can not know the lookup key for a randomly generated key + act, encryptedRef, _ := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") + fmt.Println(act, encryptedRef) + tag := "exampleTag" - lookupKey, err := al.GetLookUpKey(publisher, tag) + lookupKey, err := al.getLookUpKey(id0.PublicKey, tag) if err != nil { t.Errorf("Could not fetch lookup key from publisher and tag") } expectedLookupKey := "expectedLookupKey" if lookupKey != expectedLookupKey { + fmt.Println(string(lookupKey)) t.Errorf("The lookup key that was returned is not correct!") } } -func TestGetLookupKey_Error(t *testing.T) { +func TestGetLookUpKey_Error(t *testing.T) { al := setupAccessLogic() - invalidPublisher := "" + invalidPublisher := ecdsa.PublicKey{} tag := "exampleTag" - lookupKey, err := al.GetLookUpKey(invalidPublisher, tag) + lookupKey, err := al.getLookUpKey(invalidPublisher, tag) + if err != nil { t.Errorf("There was an error while fetching lookup key") } @@ -53,10 +65,10 @@ func TestGetLookupKey_Error(t *testing.T) { func TestGetAccessKeyDecriptionKey_Success(t *testing.T) { al := setupAccessLogic() - publisher := "examplePublisher" + id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) tag := "exampleTag" - access_key_decryption_key, err := al.GetAccessKeyDecriptionKey(publisher, tag) + access_key_decryption_key, err := al.getAccessKeyDecriptionKey(id0.PublicKey, tag) if err != nil { t.Errorf("GetAccessKeyDecriptionKey gave back error") } @@ -70,10 +82,10 @@ func TestGetAccessKeyDecriptionKey_Success(t *testing.T) { func TestGetAccessKeyDecriptionKey_Error(t *testing.T) { al := setupAccessLogic() - invalidPublisher := "" + invalidPublisher := ecdsa.PublicKey{} tag := "exampleTag" - access_key_decryption_key, err := al.GetAccessKeyDecriptionKey(invalidPublisher, tag) + access_key_decryption_key, err := al.getAccessKeyDecriptionKey(invalidPublisher, tag) if err != nil { t.Errorf("GetAccessKeyDecriptionKey gave back error") } @@ -86,16 +98,18 @@ func TestGetAccessKeyDecriptionKey_Error(t *testing.T) { func TestGetEncryptedAccessKey_Success(t *testing.T) { al := setupAccessLogic() - actRootHash := "0xabcexample" lookupKey := "exampleLookupKey" + id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - encrypted_access_key, err := al.GetEncryptedAccessKey(actRootHash, lookupKey) + act, _, _ := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") + + encrypted_access_key, err := al.getEncryptedAccessKey(*act, lookupKey) if err != nil { t.Errorf("There was an error while executing GetEncryptedAccessKey") } expectedEncryptedKey := "abc013encryptedkey" - if encrypted_access_key != expectedEncryptedKey { + if encrypted_access_key.Reference().String() != expectedEncryptedKey { t.Errorf("GetEncryptedAccessKey didn't give back the expected value!") } } @@ -103,15 +117,16 @@ func TestGetEncryptedAccessKey_Success(t *testing.T) { func TestGetEncryptedAccessKey_Error(t *testing.T) { al := setupAccessLogic() - actRootHash := "0xabcexample" lookupKey := "exampleLookupKey" + id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - empty_act_result, _ := al.GetEncryptedAccessKey("", lookupKey) + act, _, _ := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") + empty_act_result, _ := al.getEncryptedAccessKey(*act, lookupKey) if empty_act_result != nil { t.Errorf("GetEncryptedAccessKey should give back nil for empty act root hash!") } - empty_lookup_result, _ := al.GetEncryptedAccessKey(actRootHash, "") + empty_lookup_result, _ := al.getEncryptedAccessKey(*act, "") if empty_lookup_result != nil { t.Errorf("GetEncryptedAccessKey should give back nil for empty lookup key!") @@ -121,12 +136,11 @@ func TestGetEncryptedAccessKey_Error(t *testing.T) { func TestGet_Success(t *testing.T) { al := setupAccessLogic() - actRootHash := "0xabcexample" - encryptedRef := "bzzabcasab" - publisher := "examplePublisher" + id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + act, encryptedRef, _ := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") tag := "exampleTag" - ref, err := al.Get(actRootHash, encryptedRef, publisher, tag) + ref, err := al.Get(act, encryptedRef, id0.PublicKey, tag) if err != nil { t.Errorf("There was an error while calling Get") } @@ -140,27 +154,35 @@ func TestGet_Success(t *testing.T) { func TestGet_Error(t *testing.T) { al := setupAccessLogic() - actRootHash := "0xabcexample" - encryptedRef := "bzzabcasab" - publisher := "examplePublisher" + //actRootHash := "0xabcexample" + id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + act, encrypredRef, err := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") + if err != nil { + t.Errorf("Error initializing Act") + t.Errorf(err.Error()) + } + //encryptedRef := "bzzabcasab" tag := "exampleTag" - refOne, _ := al.Get("", encryptedRef, publisher, tag) + refOne, err := al.Get(act, encrypredRef, id0.PublicKey, tag) + if err != nil { + t.Errorf(err.Error()) + } if refOne != "" { t.Errorf("Get should give back empty string if ACT root hash not provided!") } - refTwo, _ := al.Get(actRootHash, "", publisher, tag) + refTwo, _ := al.Get(act, swarm.EmptyAddress, id0.PublicKey, tag) if refTwo != "" { t.Errorf("Get should give back empty string if encrypted ref not provided!") } - refThree, _ := al.Get(actRootHash, encryptedRef, "", tag) + refThree, _ := al.Get(act, encrypredRef, ecdsa.PublicKey{}, tag) if refThree != "" { t.Errorf("Get should give back empty string if publisher not provided!") } - refFour, _ := al.Get(actRootHash, encryptedRef, publisher, "") + refFour, _ := al.Get(act, encrypredRef, id0.PublicKey, "") if refFour != "" { t.Errorf("Get should give back empty string if tag was not provided!") } @@ -174,3 +196,42 @@ func TestNewAccessLogic(t *testing.T) { t.Errorf("NewAccessLogic: expected type *DefaultAccessLogic, got %T", logic) } } + +// func TestAddGrantee(t *testing.T) { +// al := setupAccessLogic() +// // ref := "example_ref" +// id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) +// testGranteeList := NewGrantee() + +// // Add grantee keys to the testGranteeList +// id1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) +// id2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) +// id3, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) +// testGranteeList.AddGrantees([]ecdsa.PublicKey{id1.PublicKey, id2.PublicKey, id3.PublicKey}) + +// // Initialize empty ACT +// actMock := MockAct.NewActMock() +// actMockRootHash := "exampleRootHash" + +// // Add each grantee to content using ActMock and validate the resulting ACT +// for i := 0; i < len(testGranteeList.GetGrantees()); i++ { +// lookupKey, _ := al.getLookUpKey(testGranteeList.GetGrantees()[i], "") +// encryptedAccessKey := "exampleEncryptedAccessKey" +// _, err := actMock.Add(actMockRootHash, []byte(lookupKey), []byte(encryptedAccessKey)) +// if err != nil { +// t.Fatalf("Failed to add grantee to content using ActMock: %v", err) +// } + +// // Validate the resulting ACT +// encryptedAccessKeyFromMock, err := actMock.Get(actMockRootHash, []byte(lookupKey)) +// if err != nil { +// t.Fatalf("Failed to retrieve encrypted access key from ActMock: %v", err) +// } +// encryptedAccessKeyFromMockBytes, _ := hex.DecodeString(encryptedAccessKeyFromMock) +// if string(encryptedAccessKeyFromMockBytes) != encryptedAccessKey { +// t.Errorf("Encrypted access key retrieved from ActMock doesn't match expected value") +// } +// } + +// al.Add_New_Grantee_To_Content(actMock, encryptedRef, id0.PublicKey, testGranteeList.GetGrantees()[i]) +// } diff --git a/pkg/dynamicaccess/grantee.go b/pkg/dynamicaccess/grantee.go index 27eb1636a09..bb39899e012 100644 --- a/pkg/dynamicaccess/grantee.go +++ b/pkg/dynamicaccess/grantee.go @@ -1,24 +1,54 @@ package dynamicaccess +import "crypto/ecdsa" + type Grantee interface { - Revoke(topic string) error - RevokeList(topic string, removeList []string, addList []string) (string, error) - Publish(topic string) error + //? ÁTBESZÉLNI + // Revoke(topic string) error + // Publish(topic string) error + + // RevokeList(topic string, removeList []string, addList []string) (string, error) + // RevokeGrantees(topic string, removeList []string) (string, error) + AddGrantees(addList []ecdsa.PublicKey) ([]ecdsa.PublicKey, error) + RemoveGrantees(removeList []ecdsa.PublicKey) ([]ecdsa.PublicKey, error) + GetGrantees() []ecdsa.PublicKey } type defaultGrantee struct { + topic string //lint:ignore U1000 Ignore unused struct field + grantees []ecdsa.PublicKey // Modified field name to start with an uppercase letter } -func (g *defaultGrantee) Revoke(topic string) error { - return nil +func (g *defaultGrantee) GetGrantees() []ecdsa.PublicKey { + return g.grantees } -func (g *defaultGrantee) RevokeList(topic string, removeList []string, addList []string) (string, error) { - return "", nil +// func (g *defaultGrantee) Revoke(topic string) error { +// return nil +// } + +// func (g *defaultGrantee) RevokeList(topic string, removeList []string, addList []string) (string, error) { +// return "", nil +// } + +// func (g *defaultGrantee) Publish(topic string) error { +// return nil +// } + +func (g *defaultGrantee) AddGrantees(addList []ecdsa.PublicKey) ([]ecdsa.PublicKey, error) { + g.grantees = append(g.grantees, addList...) + return g.grantees, nil } -func (g *defaultGrantee) Publish(topic string) error { - return nil +func (g *defaultGrantee) RemoveGrantees(removeList []ecdsa.PublicKey) ([]ecdsa.PublicKey, error) { + for _, remove := range removeList { + for i, grantee := range g.grantees { + if grantee == remove { + g.grantees = append(g.grantees[:i], g.grantees[i+1:]...) + } + } + } + return g.grantees, nil } func NewGrantee() Grantee { diff --git a/pkg/dynamicaccess/grantee_test.go b/pkg/dynamicaccess/grantee_test.go index 38b2d10b347..5140216d2a2 100644 --- a/pkg/dynamicaccess/grantee_test.go +++ b/pkg/dynamicaccess/grantee_test.go @@ -1,24 +1,104 @@ package dynamicaccess -import "testing" +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "reflect" + "testing" +) -func TestGranteeRevoke(t *testing.T) { - err := NewGrantee().Revoke("") - if err != nil { - t.Errorf("Error revoking grantee: %v", err) - } -} +// func TestGranteeRevoke(t *testing.T) { +// err := NewGrantee().Revoke("") +// if err != nil { +// t.Errorf("Error revoking grantee: %v", err) +// } +// } -func TestGranteeRevokeList(t *testing.T) { +/*func TestGranteeRevokeList(t *testing.T) { _, err := NewGrantee().RevokeList("", nil, nil) if err != nil { t.Errorf("Error revoking list of grantees: %v", err) } +}*/ + +// func TestGranteePublish(t *testing.T) { +// err := NewGrantee().Publish("") +// if err != nil { +// t.Errorf("Error publishing grantee: %v", err) +// } +// } + +func TestGranteeAddGrantees(t *testing.T) { + // Create a new Grantee + grantee := NewGrantee() + + // Generate some dummy ecdsa.PublicKey values + key1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + key2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + + // Add the keys to the grantee + addList := []ecdsa.PublicKey{key1.PublicKey, key2.PublicKey} + grantees, err := grantee.AddGrantees(addList) + + // Check for errors + if err != nil { + t.Fatalf("Expected no error, got %v", err) + } + + // Check if the keys were added correctly + if !reflect.DeepEqual(grantees, addList) { + t.Errorf("Expected grantees %v, got %v", addList, grantees) + } } -func TestGranteePublish(t *testing.T) { - err := NewGrantee().Publish("") +func TestRemoveGrantees(t *testing.T) { + // Create a new Grantee + grantee := NewGrantee() + + // Generate some dummy ecdsa.PublicKey values + key1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + key2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + + // Add the keys to the grantee + addList := []ecdsa.PublicKey{key1.PublicKey, key2.PublicKey} + grantee.AddGrantees(addList) + + // Remove one of the keys + removeList := []ecdsa.PublicKey{key1.PublicKey} + grantees, err := grantee.RemoveGrantees(removeList) + + // Check for errors if err != nil { - t.Errorf("Error publishing grantee: %v", err) + t.Fatalf("Expected no error, got %v", err) + } + + // Check if the key was removed correctly + expectedGrantees := []ecdsa.PublicKey{key2.PublicKey} + if !reflect.DeepEqual(grantees, expectedGrantees) { + t.Errorf("Expected grantees %v, got %v", expectedGrantees, grantees) } } + +func TestGetGrantees(t *testing.T) { + // Create a new Grantee + grantee := NewGrantee() + + // Generate some dummy ecdsa.PublicKey values + key1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + key2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + + // Add the keys to the grantee + addList := []ecdsa.PublicKey{key1.PublicKey, key2.PublicKey} + grantee.AddGrantees(addList) + + // Get the grantees + grantees := grantee.GetGrantees() + + // Check if the grantees were returned correctly + if !reflect.DeepEqual(grantees, addList) { + t.Errorf("Expected grantees %v, got %v", addList, grantees) + } +} + + From 9b1d4d7756f5e578c8b7fa8e5836f67a9febe7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20Ar=C3=A1nyi?= Date: Wed, 13 Mar 2024 19:27:36 +0400 Subject: [PATCH 12/41] Added context & details to use cases (#6) ZH #106 Added context & details to use cases --- act_ucs.md | 66 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/act_ucs.md b/act_ucs.md index 82067211bfd..14f93f346e4 100644 --- a/act_ucs.md +++ b/act_ucs.md @@ -4,43 +4,83 @@ This file contains the SWARM ACT user stories. ZenHub Link: [SpDevTeam](https://app.zenhub.com/workspaces/spdevteam-6544d91246b817002d853e69/board) +### Context & definitions +- Content is created as a 'channel', i.e. as a feed with a specific topic. +- The content is uploaded to the feed, and new version of the content could be uploaded any time. +- The content can be accessed by the publisher and any viewers if it does not have ACT defined. +- The content can be accessed by the publisher and any viewers if it has ACT defined and the viewer is in the grantees list. +- A publisher is a user who is uploading new content. +- A viewer is a user who is accessing content. +- When a viewer is losing access it means that in the newly generated ACT the viewer will no longer be present in the grantee list. +- The publisher should be able to modify the ACT, using the same private key, but on multiple nodes in parallel. +- The publisher should be able to modify the grantee list, using the same private key, but on multiple nodes in parallel (this would potentially require the grantee list to be encrypted as well). + +### Use cases + - [ ] **1** - I'm a publisher -- I upload a content -- I grant access to content -- Viewer try to access to the content +- I upload some new content +- I create an ACT for the content, but without additional grantees +- I can access the content +___ + +- [ ] **1/a** +- I'm a publisher +- I have an existing ACT for some content +- I can read and edit the ACT ___ - [ ] **2** - I'm a publisher -- I granteed access for viewers to my channel +- I have an existing ACT for some content, but without additional grantees +- I grant access to the content for some new viewers +- Those viewers can access the content ___ - [ ] **2/a** - I'm a publisher -- I granteed access for additional viewers to my channel +- I have an existing ACT for some content with some existing grantees +- I grant access to the content to additional viewers +- The existing viewers can access the content still +- The additional viewers can access the content ___ - [ ] **2/b** - I'm a publisher -- I remove viewers from the access list to my channel +- I remove viewers from the grantee list of the ACT content +- The content is unchanged +- The removed viewers can access the content still +- The existing viewers can access the content still +___ + +- [ ] **2/c** +- I'm a publisher +- I remove viewers from the grantee list of the ACT content +- The content is updated +- The removed viewers can't access the latest version of content anymore +- The removed viewers can access an older version of the content, the version up until the moment they were removed +- The existing viewers can access the content still, including the latest version ___ - [ ] **3** - I'm a viewer - I requested access to the content -- If I got, I can access it -- If I didn't get, I can't +- If I was granted access, I can access the content +- If I was not granted access, I can't access the content still ___ - [ ] **4** - I'm a viewer -- I lost access to the content -- I can't access new ones +- I had access to the content until now +- My access to the content got revoked +- The content is unchanged +- I can access the content still ___ -- [ ] **5** +- [ ] **4/a** - I'm a viewer -- I lost access to the content -- I can not access new ones, but the old ones I can +- I had access to the content until now +- My access to the content got revoked +- The content is updated +- I can't access versions of the content that were uploaded after I lost access ___ From ade71b01339092c6fb767782bdf8eafd45a38c37 Mon Sep 17 00:00:00 2001 From: Kexort Date: Thu, 14 Mar 2024 16:31:37 +0100 Subject: [PATCH 13/41] Add grantee management (#10) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add grantee management * Added controller test * Fix test fixture, refactor accesslogic * Add UploadHandler --------- Co-authored-by: Bálint Ujvári --- pkg/dynamicaccess/accesslogic.go | 124 +++--- pkg/dynamicaccess/accesslogic_test.go | 460 +++++++++++----------- pkg/dynamicaccess/controller.go | 42 +- pkg/dynamicaccess/controller_test.go | 99 +++++ pkg/dynamicaccess/grantee.go | 33 +- pkg/dynamicaccess/grantee_manager.go | 41 ++ pkg/dynamicaccess/grantee_manager_test.go | 41 ++ pkg/dynamicaccess/grantee_test.go | 35 +- pkg/dynamicaccess/history_test.go | 4 +- pkg/dynamicaccess/mock/act.go | 7 +- 10 files changed, 539 insertions(+), 347 deletions(-) create mode 100644 pkg/dynamicaccess/controller_test.go create mode 100644 pkg/dynamicaccess/grantee_manager.go create mode 100644 pkg/dynamicaccess/grantee_manager_test.go diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index 803c1110517..b3c01974f24 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -1,13 +1,9 @@ package dynamicaccess import ( - "context" "crypto/ecdsa" - "errors" encryption "github.com/ethersphere/bee/pkg/encryption" - file "github.com/ethersphere/bee/pkg/file" - manifest "github.com/ethersphere/bee/pkg/manifest" "github.com/ethersphere/bee/pkg/swarm" "golang.org/x/crypto/sha3" ) @@ -15,55 +11,53 @@ import ( var hashFunc = sha3.NewLegacyKeccak256 type AccessLogic interface { - Get(act *Act, encryped_ref swarm.Address, publisher ecdsa.PublicKey, tag string) (string, error) + Get(act Act, encryped_ref swarm.Address, publisher ecdsa.PublicKey, tag string) (swarm.Address, error) + EncryptRef(act Act, publisherPubKey ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) //Add(act *Act, ref string, publisher ecdsa.PublicKey, tag string) (string, error) - getLookUpKey(publisher ecdsa.PublicKey, tag string) (string, error) - getAccessKeyDecriptionKey(publisher ecdsa.PublicKey, tag string) (string, error) - getEncryptedAccessKey(act Act, lookup_key string) (manifest.Entry, error) + getLookUpKey(publisher ecdsa.PublicKey, tag string) ([]byte, error) + getAccessKeyDecriptionKey(publisher ecdsa.PublicKey, tag string) ([]byte, error) + getEncryptedAccessKey(act Act, lookup_key []byte) ([]byte, error) //createEncryptedAccessKey(ref string) - Add_New_Grantee_To_Content(act *Act, encryptedRef swarm.Address, publisherPubKey ecdsa.PublicKey, granteePubKey ecdsa.PublicKey) (*Act, error) - ActInit(ref swarm.Address, publisher ecdsa.PublicKey, tag string) (*Act, swarm.Address, error) + Add_New_Grantee_To_Content(act Act, publisherPubKey, granteePubKey ecdsa.PublicKey) (Act, error) + AddPublisher(act Act, publisher ecdsa.PublicKey, tag string) (Act, error) // CreateAccessKey() } type DefaultAccessLogic struct { diffieHellman DiffieHellman //encryption encryption.Interface - act defaultAct } // Will create a new Act list with only one element (the creator), and will also create encrypted_ref -func (al *DefaultAccessLogic) ActInit(ref swarm.Address, publisher ecdsa.PublicKey, tag string) (*Act, swarm.Address, error) { - act := NewDefaultAct() +func (al *DefaultAccessLogic) AddPublisher(act Act, publisher ecdsa.PublicKey, tag string) (Act, error) { + access_key := encryption.GenerateRandomKey(encryption.KeyLength) lookup_key, _ := al.getLookUpKey(publisher, "") access_key_encryption_key, _ := al.getAccessKeyDecriptionKey(publisher, "") access_key_cipher := encryption.New(encryption.Key(access_key_encryption_key), 0, uint32(0), hashFunc) - access_key := encryption.GenerateRandomKey(encryption.KeyLength) - encrypted_access_key, _ := access_key_cipher.Encrypt([]byte(access_key)) + encrypted_access_key, _ := access_key_cipher.Encrypt(access_key) - ref_cipher := encryption.New(access_key, 0, uint32(0), hashFunc) - encrypted_ref, _ := ref_cipher.Encrypt(ref.Bytes()) + act.Add(lookup_key, encrypted_access_key) - act.Add([]byte(lookup_key), encrypted_access_key) + return act, nil +} - return &act, swarm.NewAddress(encrypted_ref), nil +func (al *DefaultAccessLogic) EncryptRef(act Act, publisherPubKey ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) { + access_key := al.getAccessKey(act, publisherPubKey) + ref_cipher := encryption.New(access_key, 0, uint32(0), hashFunc) + encrypted_ref, _ := ref_cipher.Encrypt(ref.Bytes()) + return swarm.NewAddress(encrypted_ref), nil } // publisher is public key -func (al *DefaultAccessLogic) Add_New_Grantee_To_Content(act *Act, encryptedRef swarm.Address, publisherPubKey ecdsa.PublicKey, granteePubKey ecdsa.PublicKey) (*Act, error) { +func (al *DefaultAccessLogic) Add_New_Grantee_To_Content(act Act, publisherPubKey, granteePubKey ecdsa.PublicKey) (Act, error) { // error handling no encrypted_ref // 2 Diffie-Hellman for the publisher (the Creator) - publisher_lookup_key, _ := al.getLookUpKey(publisherPubKey, "") - publisher_ak_decryption_key, _ := al.getAccessKeyDecriptionKey(publisherPubKey, "") - // Get previously generated access key - access_key_decryption_cipher := encryption.New(encryption.Key(publisher_ak_decryption_key), 0, uint32(0), hashFunc) - encrypted_ak, _ := al.getEncryptedAccessKey(*act, publisher_lookup_key) - access_key, _ := access_key_decryption_cipher.Decrypt(encrypted_ak.Reference().Bytes()) + access_key := al.getAccessKey(act, publisherPubKey) // --Encrypt access key for new Grantee-- @@ -75,11 +69,20 @@ func (al *DefaultAccessLogic) Add_New_Grantee_To_Content(act *Act, encryptedRef cipher := encryption.New(encryption.Key(access_key_encryption_key), 0, uint32(0), hashFunc) granteeEncryptedAccessKey, _ := cipher.Encrypt(access_key) // Add the new encrypted access key for the Act - actObj := *act - actObj.Add([]byte(lookup_key), granteeEncryptedAccessKey) + act.Add(lookup_key, granteeEncryptedAccessKey) + + return act, nil + +} - return &actObj, nil +func (al *DefaultAccessLogic) getAccessKey(act Act, publisherPubKey ecdsa.PublicKey) []byte { + publisher_lookup_key, _ := al.getLookUpKey(publisherPubKey, "") + publisher_ak_decryption_key, _ := al.getAccessKeyDecriptionKey(publisherPubKey, "") + access_key_decryption_cipher := encryption.New(encryption.Key(publisher_ak_decryption_key), 0, uint32(0), hashFunc) + encrypted_ak, _ := al.getEncryptedAccessKey(act, publisher_lookup_key) + access_key, _ := access_key_decryption_cipher.Decrypt(encrypted_ak) + return access_key } // @@ -90,94 +93,69 @@ func (al *DefaultAccessLogic) Add_New_Grantee_To_Content(act *Act, encryptedRef // func (al *DefaultAccessLogic) CreateAccessKey(reference string) { // } -func (al *DefaultAccessLogic) getLookUpKey(publisher ecdsa.PublicKey, tag string) (string, error) { +func (al *DefaultAccessLogic) getLookUpKey(publisher ecdsa.PublicKey, tag string) ([]byte, error) { zeroByteArray := []byte{0} // Generate lookup key using Diffie Hellman lookup_key, err := al.diffieHellman.SharedSecret(&publisher, tag, zeroByteArray) if err != nil { - return "", err + return []byte{}, err } - return string(lookup_key), nil + return lookup_key, nil } -func (al *DefaultAccessLogic) getAccessKeyDecriptionKey(publisher ecdsa.PublicKey, tag string) (string, error) { +func (al *DefaultAccessLogic) getAccessKeyDecriptionKey(publisher ecdsa.PublicKey, tag string) ([]byte, error) { oneByteArray := []byte{1} // Generate access key decryption key using Diffie Hellman access_key_decryption_key, err := al.diffieHellman.SharedSecret(&publisher, tag, oneByteArray) if err != nil { - return "", err + return []byte{}, err } - return string(access_key_decryption_key), nil + return access_key_decryption_key, nil } -func (al *DefaultAccessLogic) getEncryptedAccessKey(act Act, lookup_key string) (manifest.Entry, error) { - if act == nil { - return nil, errors.New("no ACT root hash was provided") - } - if lookup_key == "" { - return nil, errors.New("no lookup key") - } - - manifest_raw := act.Get([]byte(lookup_key)) - //al.act.Get(act_root_hash) - - // Lookup encrypted access key from the ACT manifest - var loadSaver file.LoadSaver - var ctx context.Context - loadSaver.Load(ctx, []byte(manifest_raw)) // Load the manifest file into loadSaver - //y, err := x.Load(ctx, []byte(manifest_obj)) - manifestObj, err := manifest.NewDefaultManifest(loadSaver, false) - if err != nil { - return nil, err - } - encrypted_access_key, err := manifestObj.Lookup(ctx, lookup_key) - if err != nil { - return nil, err - } - - return encrypted_access_key, nil +func (al *DefaultAccessLogic) getEncryptedAccessKey(act Act, lookup_key []byte) ([]byte, error) { + return act.Get(lookup_key), nil } -func (al *DefaultAccessLogic) Get(act *Act, encryped_ref swarm.Address, publisher ecdsa.PublicKey, tag string) (string, error) { +func (al *DefaultAccessLogic) Get(act Act, encryped_ref swarm.Address, publisher ecdsa.PublicKey, tag string) (swarm.Address, error) { lookup_key, err := al.getLookUpKey(publisher, tag) if err != nil { - return "", err + return swarm.EmptyAddress, err } access_key_decryption_key, err := al.getAccessKeyDecriptionKey(publisher, tag) if err != nil { - return "", err + return swarm.EmptyAddress, err } // Lookup encrypted access key from the ACT manifest - encrypted_access_key, err := al.getEncryptedAccessKey(*act, lookup_key) + encrypted_access_key, err := al.getEncryptedAccessKey(act, lookup_key) if err != nil { - return "", err + return swarm.EmptyAddress, err } // Decrypt access key - access_key_cipher := encryption.New(encryption.Key(access_key_decryption_key), 4096, uint32(0), hashFunc) - access_key, err := access_key_cipher.Decrypt(encrypted_access_key.Reference().Bytes()) + access_key_cipher := encryption.New(encryption.Key(access_key_decryption_key), 0, uint32(0), hashFunc) + access_key, err := access_key_cipher.Decrypt(encrypted_access_key) if err != nil { - return "", err + return swarm.EmptyAddress, err } // Decrypt reference - ref_cipher := encryption.New(access_key, 4096, uint32(0), hashFunc) + ref_cipher := encryption.New(access_key, 0, uint32(0), hashFunc) ref, err := ref_cipher.Decrypt(encryped_ref.Bytes()) if err != nil { - return "", err + return swarm.EmptyAddress, err } - return string(ref), nil + return swarm.NewAddress(ref), nil } func NewAccessLogic(diffieHellman DiffieHellman) AccessLogic { return &DefaultAccessLogic{ diffieHellman: diffieHellman, - act: defaultAct{}, } } diff --git a/pkg/dynamicaccess/accesslogic_test.go b/pkg/dynamicaccess/accesslogic_test.go index ee7c16c43e0..c3125b65cd6 100644 --- a/pkg/dynamicaccess/accesslogic_test.go +++ b/pkg/dynamicaccess/accesslogic_test.go @@ -1,237 +1,237 @@ -package dynamicaccess - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "errors" - "fmt" - "testing" +package dynamicaccess_test + +// import ( +// "crypto/ecdsa" +// "crypto/elliptic" +// "crypto/rand" +// "errors" +// "fmt" +// "testing" + +// "github.com/ethersphere/bee/pkg/crypto" +// "github.com/ethersphere/bee/pkg/swarm" +// ) + +// func setupAccessLogic() AccessLogic { +// privateKey, err := crypto.GenerateSecp256k1Key() +// if err != nil { +// errors.New("error creating private key") +// } +// diffieHellman := NewDiffieHellman(privateKey) +// al := NewAccessLogic(diffieHellman) + +// return al +// } - "github.com/ethersphere/bee/pkg/crypto" - "github.com/ethersphere/bee/pkg/swarm" -) - -func setupAccessLogic() AccessLogic { - privateKey, err := crypto.GenerateSecp256k1Key() - if err != nil { - errors.New("error creating private key") - } - diffieHellman := NewDiffieHellman(privateKey) - al := NewAccessLogic(diffieHellman) +// func TestGetLookupKey_Success(t *testing.T) { +// al := setupAccessLogic() - return al -} - -func TestGetLookupKey_Success(t *testing.T) { - al := setupAccessLogic() - - id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - // ! this will be random, we can not know the lookup key for a randomly generated key - act, encryptedRef, _ := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") - fmt.Println(act, encryptedRef) - - tag := "exampleTag" +// id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) +// // ! this will be random, we can not know the lookup key for a randomly generated key +// act, encryptedRef, _ := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") +// fmt.Println(act, encryptedRef) - lookupKey, err := al.getLookUpKey(id0.PublicKey, tag) - if err != nil { - t.Errorf("Could not fetch lookup key from publisher and tag") - } - - expectedLookupKey := "expectedLookupKey" - if lookupKey != expectedLookupKey { - fmt.Println(string(lookupKey)) - t.Errorf("The lookup key that was returned is not correct!") - } -} +// tag := "exampleTag" -func TestGetLookUpKey_Error(t *testing.T) { - al := setupAccessLogic() - - invalidPublisher := ecdsa.PublicKey{} - tag := "exampleTag" - - lookupKey, err := al.getLookUpKey(invalidPublisher, tag) - - if err != nil { - t.Errorf("There was an error while fetching lookup key") - } - - if lookupKey != "" { - t.Errorf("Expected lookup key to be empty for invalid input") - } -} - -func TestGetAccessKeyDecriptionKey_Success(t *testing.T) { - al := setupAccessLogic() - - id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - tag := "exampleTag" - - access_key_decryption_key, err := al.getAccessKeyDecriptionKey(id0.PublicKey, tag) - if err != nil { - t.Errorf("GetAccessKeyDecriptionKey gave back error") - } - - expectedResult := "we-dont-know" - if access_key_decryption_key != expectedResult { - t.Errorf("The access key decryption key is not correct!") - } -} - -func TestGetAccessKeyDecriptionKey_Error(t *testing.T) { - al := setupAccessLogic() - - invalidPublisher := ecdsa.PublicKey{} - tag := "exampleTag" - - access_key_decryption_key, err := al.getAccessKeyDecriptionKey(invalidPublisher, tag) - if err != nil { - t.Errorf("GetAccessKeyDecriptionKey gave back error") - } - - if access_key_decryption_key != "" { - t.Errorf("GetAccessKeyDecriptionKey should give back empty string for invalid input!") - } -} - -func TestGetEncryptedAccessKey_Success(t *testing.T) { - al := setupAccessLogic() - - lookupKey := "exampleLookupKey" - id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - - act, _, _ := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") - - encrypted_access_key, err := al.getEncryptedAccessKey(*act, lookupKey) - if err != nil { - t.Errorf("There was an error while executing GetEncryptedAccessKey") - } - - expectedEncryptedKey := "abc013encryptedkey" - if encrypted_access_key.Reference().String() != expectedEncryptedKey { - t.Errorf("GetEncryptedAccessKey didn't give back the expected value!") - } -} - -func TestGetEncryptedAccessKey_Error(t *testing.T) { - al := setupAccessLogic() - - lookupKey := "exampleLookupKey" - id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - - act, _, _ := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") - empty_act_result, _ := al.getEncryptedAccessKey(*act, lookupKey) - if empty_act_result != nil { - t.Errorf("GetEncryptedAccessKey should give back nil for empty act root hash!") - } - - empty_lookup_result, _ := al.getEncryptedAccessKey(*act, "") - - if empty_lookup_result != nil { - t.Errorf("GetEncryptedAccessKey should give back nil for empty lookup key!") - } -} - -func TestGet_Success(t *testing.T) { - al := setupAccessLogic() - - id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - act, encryptedRef, _ := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") - tag := "exampleTag" - - ref, err := al.Get(act, encryptedRef, id0.PublicKey, tag) - if err != nil { - t.Errorf("There was an error while calling Get") - } - - expectedRef := "bzzNotEncrypted128long" - if ref != expectedRef { - t.Errorf("Get gave back wrong Swarm reference!") - } -} - -func TestGet_Error(t *testing.T) { - al := setupAccessLogic() - - //actRootHash := "0xabcexample" - id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - act, encrypredRef, err := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") - if err != nil { - t.Errorf("Error initializing Act") - t.Errorf(err.Error()) - } - //encryptedRef := "bzzabcasab" - tag := "exampleTag" - - refOne, err := al.Get(act, encrypredRef, id0.PublicKey, tag) - if err != nil { - t.Errorf(err.Error()) - } - if refOne != "" { - t.Errorf("Get should give back empty string if ACT root hash not provided!") - } - - refTwo, _ := al.Get(act, swarm.EmptyAddress, id0.PublicKey, tag) - if refTwo != "" { - t.Errorf("Get should give back empty string if encrypted ref not provided!") - } - - refThree, _ := al.Get(act, encrypredRef, ecdsa.PublicKey{}, tag) - if refThree != "" { - t.Errorf("Get should give back empty string if publisher not provided!") - } - - refFour, _ := al.Get(act, encrypredRef, id0.PublicKey, "") - if refFour != "" { - t.Errorf("Get should give back empty string if tag was not provided!") - } -} - -func TestNewAccessLogic(t *testing.T) { - logic := setupAccessLogic() - - _, ok := logic.(*DefaultAccessLogic) - if !ok { - t.Errorf("NewAccessLogic: expected type *DefaultAccessLogic, got %T", logic) - } -} - -// func TestAddGrantee(t *testing.T) { +// lookupKey, err := al.getLookUpKey(id0.PublicKey, tag) +// if err != nil { +// t.Errorf("Could not fetch lookup key from publisher and tag") +// } + +// expectedLookupKey := "expectedLookupKey" +// if lookupKey != expectedLookupKey { +// fmt.Println(string(lookupKey)) +// t.Errorf("The lookup key that was returned is not correct!") +// } +// } + +// func TestGetLookUpKey_Error(t *testing.T) { // al := setupAccessLogic() -// // ref := "example_ref" + +// invalidPublisher := ecdsa.PublicKey{} +// tag := "exampleTag" + +// lookupKey, err := al.getLookUpKey(invalidPublisher, tag) + +// if err != nil { +// t.Errorf("There was an error while fetching lookup key") +// } + +// if lookupKey != "" { +// t.Errorf("Expected lookup key to be empty for invalid input") +// } +// } + +// func TestGetAccessKeyDecriptionKey_Success(t *testing.T) { +// al := setupAccessLogic() + // id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) -// testGranteeList := NewGrantee() - -// // Add grantee keys to the testGranteeList -// id1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) -// id2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) -// id3, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) -// testGranteeList.AddGrantees([]ecdsa.PublicKey{id1.PublicKey, id2.PublicKey, id3.PublicKey}) - -// // Initialize empty ACT -// actMock := MockAct.NewActMock() -// actMockRootHash := "exampleRootHash" - -// // Add each grantee to content using ActMock and validate the resulting ACT -// for i := 0; i < len(testGranteeList.GetGrantees()); i++ { -// lookupKey, _ := al.getLookUpKey(testGranteeList.GetGrantees()[i], "") -// encryptedAccessKey := "exampleEncryptedAccessKey" -// _, err := actMock.Add(actMockRootHash, []byte(lookupKey), []byte(encryptedAccessKey)) -// if err != nil { -// t.Fatalf("Failed to add grantee to content using ActMock: %v", err) -// } - -// // Validate the resulting ACT -// encryptedAccessKeyFromMock, err := actMock.Get(actMockRootHash, []byte(lookupKey)) -// if err != nil { -// t.Fatalf("Failed to retrieve encrypted access key from ActMock: %v", err) -// } -// encryptedAccessKeyFromMockBytes, _ := hex.DecodeString(encryptedAccessKeyFromMock) -// if string(encryptedAccessKeyFromMockBytes) != encryptedAccessKey { -// t.Errorf("Encrypted access key retrieved from ActMock doesn't match expected value") -// } -// } - -// al.Add_New_Grantee_To_Content(actMock, encryptedRef, id0.PublicKey, testGranteeList.GetGrantees()[i]) +// tag := "exampleTag" + +// access_key_decryption_key, err := al.getAccessKeyDecriptionKey(id0.PublicKey, tag) +// if err != nil { +// t.Errorf("GetAccessKeyDecriptionKey gave back error") +// } + +// expectedResult := "we-dont-know" +// if access_key_decryption_key != expectedResult { +// t.Errorf("The access key decryption key is not correct!") +// } +// } + +// func TestGetAccessKeyDecriptionKey_Error(t *testing.T) { +// al := setupAccessLogic() + +// invalidPublisher := ecdsa.PublicKey{} +// tag := "exampleTag" + +// access_key_decryption_key, err := al.getAccessKeyDecriptionKey(invalidPublisher, tag) +// if err != nil { +// t.Errorf("GetAccessKeyDecriptionKey gave back error") +// } + +// if access_key_decryption_key != "" { +// t.Errorf("GetAccessKeyDecriptionKey should give back empty string for invalid input!") +// } // } + +// func TestGetEncryptedAccessKey_Success(t *testing.T) { +// al := setupAccessLogic() + +// lookupKey := "exampleLookupKey" +// id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + +// act, _, _ := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") + +// encrypted_access_key, err := al.getEncryptedAccessKey(*act, lookupKey) +// if err != nil { +// t.Errorf("There was an error while executing GetEncryptedAccessKey") +// } + +// expectedEncryptedKey := "abc013encryptedkey" +// if encrypted_access_key.Reference().String() != expectedEncryptedKey { +// t.Errorf("GetEncryptedAccessKey didn't give back the expected value!") +// } +// } + +// func TestGetEncryptedAccessKey_Error(t *testing.T) { +// al := setupAccessLogic() + +// lookupKey := "exampleLookupKey" +// id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + +// act, _, _ := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") +// empty_act_result, _ := al.getEncryptedAccessKey(*act, lookupKey) +// if empty_act_result != nil { +// t.Errorf("GetEncryptedAccessKey should give back nil for empty act root hash!") +// } + +// empty_lookup_result, _ := al.getEncryptedAccessKey(*act, "") + +// if empty_lookup_result != nil { +// t.Errorf("GetEncryptedAccessKey should give back nil for empty lookup key!") +// } +// } + +// func TestGet_Success(t *testing.T) { +// al := setupAccessLogic() + +// id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) +// act, encryptedRef, _ := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") +// tag := "exampleTag" + +// ref, err := al.Get(act, encryptedRef, id0.PublicKey, tag) +// if err != nil { +// t.Errorf("There was an error while calling Get") +// } + +// expectedRef := "bzzNotEncrypted128long" +// if ref != expectedRef { +// t.Errorf("Get gave back wrong Swarm reference!") +// } +// } + +// func TestGet_Error(t *testing.T) { +// al := setupAccessLogic() + +// //actRootHash := "0xabcexample" +// id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) +// act, encrypredRef, err := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") +// if err != nil { +// t.Errorf("Error initializing Act") +// t.Errorf(err.Error()) +// } +// //encryptedRef := "bzzabcasab" +// tag := "exampleTag" + +// refOne, err := al.Get(act, encrypredRef, id0.PublicKey, tag) +// if err != nil { +// t.Errorf(err.Error()) +// } +// if refOne != "" { +// t.Errorf("Get should give back empty string if ACT root hash not provided!") +// } + +// refTwo, _ := al.Get(act, swarm.EmptyAddress, id0.PublicKey, tag) +// if refTwo != "" { +// t.Errorf("Get should give back empty string if encrypted ref not provided!") +// } + +// refThree, _ := al.Get(act, encrypredRef, ecdsa.PublicKey{}, tag) +// if refThree != "" { +// t.Errorf("Get should give back empty string if publisher not provided!") +// } + +// refFour, _ := al.Get(act, encrypredRef, id0.PublicKey, "") +// if refFour != "" { +// t.Errorf("Get should give back empty string if tag was not provided!") +// } +// } + +// func TestNewAccessLogic(t *testing.T) { +// logic := setupAccessLogic() + +// _, ok := logic.(*DefaultAccessLogic) +// if !ok { +// t.Errorf("NewAccessLogic: expected type *DefaultAccessLogic, got %T", logic) +// } +// } + +// // func TestAddGrantee(t *testing.T) { +// // al := setupAccessLogic() +// // // ref := "example_ref" +// // id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) +// // testGranteeList := NewGrantee() + +// // // Add grantee keys to the testGranteeList +// // id1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) +// // id2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) +// // id3, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) +// // testGranteeList.AddGrantees([]ecdsa.PublicKey{id1.PublicKey, id2.PublicKey, id3.PublicKey}) + +// // // Initialize empty ACT +// // actMock := MockAct.NewActMock() +// // actMockRootHash := "exampleRootHash" + +// // // Add each grantee to content using ActMock and validate the resulting ACT +// // for i := 0; i < len(testGranteeList.GetGrantees()); i++ { +// // lookupKey, _ := al.getLookUpKey(testGranteeList.GetGrantees()[i], "") +// // encryptedAccessKey := "exampleEncryptedAccessKey" +// // _, err := actMock.Add(actMockRootHash, []byte(lookupKey), []byte(encryptedAccessKey)) +// // if err != nil { +// // t.Fatalf("Failed to add grantee to content using ActMock: %v", err) +// // } + +// // // Validate the resulting ACT +// // encryptedAccessKeyFromMock, err := actMock.Get(actMockRootHash, []byte(lookupKey)) +// // if err != nil { +// // t.Fatalf("Failed to retrieve encrypted access key from ActMock: %v", err) +// // } +// // encryptedAccessKeyFromMockBytes, _ := hex.DecodeString(encryptedAccessKeyFromMock) +// // if string(encryptedAccessKeyFromMockBytes) != encryptedAccessKey { +// // t.Errorf("Encrypted access key retrieved from ActMock doesn't match expected value") +// // } +// // } + +// // al.Add_New_Grantee_To_Content(actMock, encryptedRef, id0.PublicKey, testGranteeList.GetGrantees()[i]) +// // } diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index 3dfee5bff73..1637234d9c2 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -1,18 +1,46 @@ package dynamicaccess +import ( + "crypto/ecdsa" + + "github.com/ethersphere/bee/pkg/swarm" +) + type Controller interface { + DownloadHandler(timestamp int64, enryptedRef swarm.Address, publisher *ecdsa.PublicKey, tag string) (swarm.Address, error) + UploadHandler(ref swarm.Address, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error) } type defaultController struct { - histrory History - uploader Publish - grantee Grantee + history History + granteeManager GranteeManager + accessLogic AccessLogic +} + +func (c *defaultController) DownloadHandler(timestamp int64, enryptedRef swarm.Address, publisher *ecdsa.PublicKey, tag string) (swarm.Address, error) { + act, err := c.history.Lookup(timestamp) + if err != nil { + return swarm.EmptyAddress, err + } + addr, err := c.accessLogic.Get(act, enryptedRef, *publisher, tag) + return addr, err +} + +func (c *defaultController) UploadHandler(ref swarm.Address, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error) { + act, _ := c.history.Lookup(0) + if act == nil { + // new feed + act = NewDefaultAct() + act = c.granteeManager.Publish(act, *publisher, topic) + } + //FIXME: check if ACT is consistent with the grantee list + return c.accessLogic.EncryptRef(act, *publisher, ref) } -func NewController(histrory History, uploader Publish, grantee Grantee) Controller { +func NewController(history History, granteeManager GranteeManager, accessLogic AccessLogic) Controller { return &defaultController{ - histrory: histrory, - uploader: uploader, - grantee: grantee, + history: history, + granteeManager: granteeManager, + accessLogic: accessLogic, } } diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go new file mode 100644 index 00000000000..d043a18532c --- /dev/null +++ b/pkg/dynamicaccess/controller_test.go @@ -0,0 +1,99 @@ +package dynamicaccess_test + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "encoding/hex" + "testing" + "time" + + "github.com/ethersphere/bee/pkg/crypto" + "github.com/ethersphere/bee/pkg/dynamicaccess" + "github.com/ethersphere/bee/pkg/dynamicaccess/mock" + "github.com/ethersphere/bee/pkg/encryption" + "github.com/ethersphere/bee/pkg/swarm" + "golang.org/x/crypto/sha3" +) + +var hashFunc = sha3.NewLegacyKeccak256 + +func mockTestHistory(key, val []byte) dynamicaccess.History { + var ( + h = mock.NewHistory() + now = time.Now() + act = mock.NewActMock(nil, func(lookupKey []byte) []byte { + return val + }) + ) + // act.Add(key, val) + h.Insert(now.AddDate(-3, 0, 0).Unix(), act) + return h +} + +func TestDecrypt(t *testing.T) { + pk := getPrivateKey() + ak := encryption.Key([]byte("cica")) + + dh := dynamicaccess.NewDiffieHellman(pk) + aek, _ := dh.SharedSecret(&pk.PublicKey, "", []byte{1}) + e2 := encryption.New(aek, 0, uint32(0), hashFunc) + peak, _ := e2.Encrypt(ak) + + h := mockTestHistory(nil, peak) + al := setupAccessLogic(pk) + gm := dynamicaccess.NewGranteeManager(al) + c := dynamicaccess.NewController(h, gm, al) + eref, ref := prepareEncryptedChunkReference(ak) + // ech := al.EncryptRef(ch, "tag") + + ts := int64(0) + addr, err := c.DownloadHandler(ts, eref, &pk.PublicKey, "tag") + if err != nil { + t.Fatalf("DownloadHandler() returned an error: %v", err) + } + if !addr.Equal(ref) { + t.Fatalf("Decrypted chunk address: %s is not the expected: %s", addr, ref) + } +} + +func TestEncrypt(t *testing.T) { + pk := getPrivateKey() + ak := encryption.Key([]byte("cica")) + + dh := dynamicaccess.NewDiffieHellman(pk) + aek, _ := dh.SharedSecret(&pk.PublicKey, "", []byte{1}) + e2 := encryption.New(aek, 0, uint32(0), hashFunc) + peak, _ := e2.Encrypt(ak) + + h := mockTestHistory(nil, peak) + al := setupAccessLogic(pk) + gm := dynamicaccess.NewGranteeManager(al) + c := dynamicaccess.NewController(h, gm, al) + eref, ref := prepareEncryptedChunkReference(ak) + + key1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + gm.Add("topic", []*ecdsa.PublicKey{&key1.PublicKey}) + + addr, _ := c.UploadHandler(ref, &pk.PublicKey, "topic") + if !addr.Equal(eref) { + t.Fatalf("Decrypted chunk address: %s is not the expected: %s", addr, eref) + } +} + +func prepareEncryptedChunkReference(ak []byte) (swarm.Address, swarm.Address) { + addr, _ := hex.DecodeString("f7b1a45b70ee91d3dbfd98a2a692387f24db7279a9c96c447409e9205cf265baef29bf6aa294264762e33f6a18318562c86383dd8bfea2cec14fae08a8039bf3") + e1 := encryption.New(ak, 0, uint32(0), hashFunc) + ech, err := e1.Encrypt(addr) + if err != nil { + return swarm.EmptyAddress, swarm.NewAddress(addr) + } + return swarm.NewAddress(ech), swarm.NewAddress(addr) +} + +func getPrivateKey() *ecdsa.PrivateKey { + data, _ := hex.DecodeString("c786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfa") + + privKey, _ := crypto.DecodeSecp256k1PrivateKey(data) + return privKey +} diff --git a/pkg/dynamicaccess/grantee.go b/pkg/dynamicaccess/grantee.go index bb39899e012..2a07d84b3ed 100644 --- a/pkg/dynamicaccess/grantee.go +++ b/pkg/dynamicaccess/grantee.go @@ -1,6 +1,8 @@ package dynamicaccess -import "crypto/ecdsa" +import ( + "crypto/ecdsa" +) type Grantee interface { //? ÁTBESZÉLNI @@ -9,18 +11,17 @@ type Grantee interface { // RevokeList(topic string, removeList []string, addList []string) (string, error) // RevokeGrantees(topic string, removeList []string) (string, error) - AddGrantees(addList []ecdsa.PublicKey) ([]ecdsa.PublicKey, error) - RemoveGrantees(removeList []ecdsa.PublicKey) ([]ecdsa.PublicKey, error) - GetGrantees() []ecdsa.PublicKey + AddGrantees(topic string, addList []*ecdsa.PublicKey) error + RemoveGrantees(topic string, removeList []*ecdsa.PublicKey) error + GetGrantees(topic string) []*ecdsa.PublicKey } type defaultGrantee struct { - topic string //lint:ignore U1000 Ignore unused struct field - grantees []ecdsa.PublicKey // Modified field name to start with an uppercase letter + grantees map[string][]*ecdsa.PublicKey // Modified field name to start with an uppercase letter } -func (g *defaultGrantee) GetGrantees() []ecdsa.PublicKey { - return g.grantees +func (g *defaultGrantee) GetGrantees(topic string) []*ecdsa.PublicKey { + return g.grantees[topic] } // func (g *defaultGrantee) Revoke(topic string) error { @@ -35,22 +36,22 @@ func (g *defaultGrantee) GetGrantees() []ecdsa.PublicKey { // return nil // } -func (g *defaultGrantee) AddGrantees(addList []ecdsa.PublicKey) ([]ecdsa.PublicKey, error) { - g.grantees = append(g.grantees, addList...) - return g.grantees, nil +func (g *defaultGrantee) AddGrantees(topic string, addList []*ecdsa.PublicKey) error { + g.grantees[topic] = append(g.grantees[topic], addList...) + return nil } -func (g *defaultGrantee) RemoveGrantees(removeList []ecdsa.PublicKey) ([]ecdsa.PublicKey, error) { +func (g *defaultGrantee) RemoveGrantees(topic string, removeList []*ecdsa.PublicKey) error { for _, remove := range removeList { - for i, grantee := range g.grantees { + for i, grantee := range g.grantees[topic] { if grantee == remove { - g.grantees = append(g.grantees[:i], g.grantees[i+1:]...) + g.grantees[topic] = append(g.grantees[topic][:i], g.grantees[topic][i+1:]...) } } } - return g.grantees, nil + return nil } func NewGrantee() Grantee { - return &defaultGrantee{} + return &defaultGrantee{grantees: make(map[string][]*ecdsa.PublicKey)} } diff --git a/pkg/dynamicaccess/grantee_manager.go b/pkg/dynamicaccess/grantee_manager.go new file mode 100644 index 00000000000..9172a05db03 --- /dev/null +++ b/pkg/dynamicaccess/grantee_manager.go @@ -0,0 +1,41 @@ +package dynamicaccess + +import "crypto/ecdsa" + +type GranteeManager interface { + Get(topic string) []*ecdsa.PublicKey + Add(topic string, addList []*ecdsa.PublicKey) error + Publish(act Act, publisher ecdsa.PublicKey, topic string) Act + + // HandleGrantees(topic string, addList, removeList []*ecdsa.PublicKey) *Act + + // Load(grantee Grantee) + // Save() +} + +var _ GranteeManager = (*granteeManager)(nil) + +type granteeManager struct { + accessLogic AccessLogic + granteeList Grantee +} + +func NewGranteeManager(al AccessLogic) *granteeManager { + return &granteeManager{accessLogic: al, granteeList: NewGrantee()} +} + +func (gm *granteeManager) Get(topic string) []*ecdsa.PublicKey { + return gm.granteeList.GetGrantees(topic) +} + +func (gm *granteeManager) Add(topic string, addList []*ecdsa.PublicKey) error { + return gm.granteeList.AddGrantees(topic, addList) +} + +func (gm *granteeManager) Publish(act Act, publisher ecdsa.PublicKey, topic string) Act { + gm.accessLogic.AddPublisher(act, publisher, "") + for _, grantee := range gm.granteeList.GetGrantees(topic) { + gm.accessLogic.Add_New_Grantee_To_Content(act, publisher, *grantee) + } + return act +} diff --git a/pkg/dynamicaccess/grantee_manager_test.go b/pkg/dynamicaccess/grantee_manager_test.go new file mode 100644 index 00000000000..d8e806498e0 --- /dev/null +++ b/pkg/dynamicaccess/grantee_manager_test.go @@ -0,0 +1,41 @@ +package dynamicaccess_test + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "fmt" + "testing" + + "github.com/ethersphere/bee/pkg/dynamicaccess" +) + +func setupAccessLogic(privateKey *ecdsa.PrivateKey) dynamicaccess.AccessLogic { + // privateKey, err := crypto.GenerateSecp256k1Key() + // if err != nil { + // errors.New("error creating private key") + // } + diffieHellman := dynamicaccess.NewDiffieHellman(privateKey) + al := dynamicaccess.NewAccessLogic(diffieHellman) + + return al +} + +func TestAdd(t *testing.T) { + act := dynamicaccess.NewDefaultAct() + m := dynamicaccess.NewGranteeManager(setupAccessLogic(getPrivateKey())) + pub, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + + id1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + id2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + err := m.Add("topic", []*ecdsa.PublicKey{&id1.PublicKey}) + if err != nil { + t.Errorf("Add() returned an error") + } + err = m.Add("topic", []*ecdsa.PublicKey{&id2.PublicKey}) + if err != nil { + t.Errorf("Add() returned an error") + } + m.Publish(act, pub.PublicKey, "topic") + fmt.Println("") +} diff --git a/pkg/dynamicaccess/grantee_test.go b/pkg/dynamicaccess/grantee_test.go index 5140216d2a2..2d795ab2eef 100644 --- a/pkg/dynamicaccess/grantee_test.go +++ b/pkg/dynamicaccess/grantee_test.go @@ -1,4 +1,4 @@ -package dynamicaccess +package dynamicaccess_test import ( "crypto/ecdsa" @@ -6,6 +6,8 @@ import ( "crypto/rand" "reflect" "testing" + + "github.com/ethersphere/bee/pkg/dynamicaccess" ) // func TestGranteeRevoke(t *testing.T) { @@ -31,16 +33,16 @@ import ( func TestGranteeAddGrantees(t *testing.T) { // Create a new Grantee - grantee := NewGrantee() + grantee := dynamicaccess.NewGrantee() // Generate some dummy ecdsa.PublicKey values key1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) key2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) // Add the keys to the grantee - addList := []ecdsa.PublicKey{key1.PublicKey, key2.PublicKey} - grantees, err := grantee.AddGrantees(addList) - + addList := []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey} + err := grantee.AddGrantees("topicName", addList) + grantees := grantee.GetGrantees("topicName") // Check for errors if err != nil { t.Fatalf("Expected no error, got %v", err) @@ -54,19 +56,20 @@ func TestGranteeAddGrantees(t *testing.T) { func TestRemoveGrantees(t *testing.T) { // Create a new Grantee - grantee := NewGrantee() + grantee := dynamicaccess.NewGrantee() // Generate some dummy ecdsa.PublicKey values key1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) key2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) // Add the keys to the grantee - addList := []ecdsa.PublicKey{key1.PublicKey, key2.PublicKey} - grantee.AddGrantees(addList) + addList := []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey} + grantee.AddGrantees("topicName", addList) // Remove one of the keys - removeList := []ecdsa.PublicKey{key1.PublicKey} - grantees, err := grantee.RemoveGrantees(removeList) + removeList := []*ecdsa.PublicKey{&key1.PublicKey} + err := grantee.RemoveGrantees("topicName", removeList) + grantees := grantee.GetGrantees("topicName") // Check for errors if err != nil { @@ -74,7 +77,7 @@ func TestRemoveGrantees(t *testing.T) { } // Check if the key was removed correctly - expectedGrantees := []ecdsa.PublicKey{key2.PublicKey} + expectedGrantees := []*ecdsa.PublicKey{&key2.PublicKey} if !reflect.DeepEqual(grantees, expectedGrantees) { t.Errorf("Expected grantees %v, got %v", expectedGrantees, grantees) } @@ -82,23 +85,21 @@ func TestRemoveGrantees(t *testing.T) { func TestGetGrantees(t *testing.T) { // Create a new Grantee - grantee := NewGrantee() + grantee := dynamicaccess.NewGrantee() // Generate some dummy ecdsa.PublicKey values key1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) key2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) // Add the keys to the grantee - addList := []ecdsa.PublicKey{key1.PublicKey, key2.PublicKey} - grantee.AddGrantees(addList) + addList := []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey} + grantee.AddGrantees("topicName", addList) // Get the grantees - grantees := grantee.GetGrantees() + grantees := grantee.GetGrantees("topicName") // Check if the grantees were returned correctly if !reflect.DeepEqual(grantees, addList) { t.Errorf("Expected grantees %v, got %v", addList, grantees) } } - - diff --git a/pkg/dynamicaccess/history_test.go b/pkg/dynamicaccess/history_test.go index 2a24e65d862..ac333edc919 100644 --- a/pkg/dynamicaccess/history_test.go +++ b/pkg/dynamicaccess/history_test.go @@ -11,7 +11,7 @@ import ( ) func TestHistoryLookup(t *testing.T) { - h := pretareTestHistory() + h := prepareTestHistory() now := time.Now() tests := []struct { @@ -38,7 +38,7 @@ func TestHistoryLookup(t *testing.T) { } } -func pretareTestHistory() dynamicaccess.History { +func prepareTestHistory() dynamicaccess.History { var ( h = mock.NewHistory() now = time.Now() diff --git a/pkg/dynamicaccess/mock/act.go b/pkg/dynamicaccess/mock/act.go index 1fd68caa399..a83fad36f8c 100644 --- a/pkg/dynamicaccess/mock/act.go +++ b/pkg/dynamicaccess/mock/act.go @@ -42,6 +42,9 @@ func (act *ActMock) Store(me manifest.Entry) { act.StoreFunc(me) } -func NewActMock() dynamicaccess.Act { - return &ActMock{} +func NewActMock(addFunc func(lookupKey []byte, encryptedAccessKey []byte) dynamicaccess.Act, getFunc func(lookupKey []byte) []byte) dynamicaccess.Act { + return &ActMock{ + AddFunc: addFunc, + GetFunc: getFunc, + } } From e53fc7e85d19d10c339d061a628ef9d2e99e4566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20S=C3=A1rai?= Date: Tue, 19 Mar 2024 11:06:08 +0100 Subject: [PATCH 14/41] (refactor): from `Get` to `Lookup` to improve clarity and consistency. The changes have been made in the `accesslogic.go`, `act.go`, `act_test.go`, `history_test.go`, and `mock/act.go` files. (#13) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ferenc Sárai --- pkg/dynamicaccess/accesslogic.go | 2 +- pkg/dynamicaccess/act.go | 4 ++-- pkg/dynamicaccess/act_test.go | 8 ++++---- pkg/dynamicaccess/history_test.go | 2 +- pkg/dynamicaccess/mock/act.go | 18 +++++++++--------- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index b3c01974f24..c4c6e7438a8 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -115,7 +115,7 @@ func (al *DefaultAccessLogic) getAccessKeyDecriptionKey(publisher ecdsa.PublicKe } func (al *DefaultAccessLogic) getEncryptedAccessKey(act Act, lookup_key []byte) ([]byte, error) { - return act.Get(lookup_key), nil + return act.Lookup(lookup_key), nil } func (al *DefaultAccessLogic) Get(act Act, encryped_ref swarm.Address, publisher ecdsa.PublicKey, tag string) (swarm.Address, error) { diff --git a/pkg/dynamicaccess/act.go b/pkg/dynamicaccess/act.go index b63b1dafc6b..2c1765d1d54 100644 --- a/pkg/dynamicaccess/act.go +++ b/pkg/dynamicaccess/act.go @@ -9,7 +9,7 @@ import ( type Act interface { Add(lookupKey []byte, encryptedAccessKey []byte) Act - Get(lookupKey []byte) []byte + Lookup(lookupKey []byte) []byte Load(lookupKey []byte) manifest.Entry Store(me manifest.Entry) } @@ -25,7 +25,7 @@ func (act *defaultAct) Add(lookupKey []byte, encryptedAccessKey []byte) Act { return act } -func (act *defaultAct) Get(lookupKey []byte) []byte { +func (act *defaultAct) Lookup(lookupKey []byte) []byte { if key, ok := act.container[hex.EncodeToString(lookupKey)]; ok { bytes, err := hex.DecodeString(key) if err == nil { diff --git a/pkg/dynamicaccess/act_test.go b/pkg/dynamicaccess/act_test.go index 0e5ca853f0b..a46cc3b228c 100644 --- a/pkg/dynamicaccess/act_test.go +++ b/pkg/dynamicaccess/act_test.go @@ -17,7 +17,7 @@ import ( "github.com/ethersphere/bee/pkg/swarm" ) -func TestActAddGet(t *testing.T) { +func TestActAddLookup(t *testing.T) { act := dynamicaccess.NewDefaultAct() lookupKey := swarm.RandAddress(t).Bytes() encryptedAccesskey := swarm.RandAddress(t).Bytes() @@ -26,7 +26,7 @@ func TestActAddGet(t *testing.T) { t.Error("Add() should return an act") } - key := act.Get(lookupKey) + key := act.Lookup(lookupKey) if !bytes.Equal(key, encryptedAccesskey) { t.Errorf("Get() value is not the expected %s != %s", key, encryptedAccesskey) } @@ -73,7 +73,7 @@ func TestActWithManifest(t *testing.T) { actualAct := dynamicaccess.NewDefaultAct() actualAct.Store(actualMe) - actualEak := actualAct.Get(lookupKey) + actualEak := actualAct.Lookup(lookupKey) if !bytes.Equal(actualEak, encryptedAccesskey) { t.Errorf("actualAct.Store() value is not the expected %s != %s", actualEak, encryptedAccesskey) } @@ -89,7 +89,7 @@ func TestActStore(t *testing.T) { me := manifest.NewEntry(swarm.NewAddress(lookupKey), mp) act := dynamicaccess.NewDefaultAct() act.Store(me) - eak := act.Get(lookupKey) + eak := act.Lookup(lookupKey) if !bytes.Equal(eak, encryptedAccesskey) { t.Errorf("Store() value is not the expected %s != %s", eak, encryptedAccesskey) diff --git a/pkg/dynamicaccess/history_test.go b/pkg/dynamicaccess/history_test.go index ac333edc919..340b2adcb35 100644 --- a/pkg/dynamicaccess/history_test.go +++ b/pkg/dynamicaccess/history_test.go @@ -32,7 +32,7 @@ func TestHistoryLookup(t *testing.T) { for _, tt := range tests { t.Run("", func(t *testing.T) { actAt, _ := h.Lookup(tt.input) - output := actAt.Get([]byte("key1")) + output := actAt.Lookup([]byte("key1")) assert.Equal(t, output, hex.EncodeToString([]byte(tt.expected))) }) } diff --git a/pkg/dynamicaccess/mock/act.go b/pkg/dynamicaccess/mock/act.go index a83fad36f8c..8bcfb8ff0b9 100644 --- a/pkg/dynamicaccess/mock/act.go +++ b/pkg/dynamicaccess/mock/act.go @@ -6,10 +6,10 @@ import ( ) type ActMock struct { - AddFunc func(lookupKey []byte, encryptedAccessKey []byte) dynamicaccess.Act - GetFunc func(lookupKey []byte) []byte - LoadFunc func(lookupKey []byte) manifest.Entry - StoreFunc func(me manifest.Entry) + AddFunc func(lookupKey []byte, encryptedAccessKey []byte) dynamicaccess.Act + LookupFunc func(lookupKey []byte) []byte + LoadFunc func(lookupKey []byte) manifest.Entry + StoreFunc func(me manifest.Entry) } var _ dynamicaccess.Act = (*ActMock)(nil) @@ -21,11 +21,11 @@ func (act *ActMock) Add(lookupKey []byte, encryptedAccessKey []byte) dynamicacce return act.AddFunc(lookupKey, encryptedAccessKey) } -func (act *ActMock) Get(lookupKey []byte) []byte { - if act.GetFunc == nil { +func (act *ActMock) Lookup(lookupKey []byte) []byte { + if act.LookupFunc == nil { return make([]byte, 0) } - return act.GetFunc(lookupKey) + return act.LookupFunc(lookupKey) } func (act *ActMock) Load(lookupKey []byte) manifest.Entry { @@ -44,7 +44,7 @@ func (act *ActMock) Store(me manifest.Entry) { func NewActMock(addFunc func(lookupKey []byte, encryptedAccessKey []byte) dynamicaccess.Act, getFunc func(lookupKey []byte) []byte) dynamicaccess.Act { return &ActMock{ - AddFunc: addFunc, - GetFunc: getFunc, + AddFunc: addFunc, + LookupFunc: getFunc, } } From 273eea39e7b734c9e1ff18b2bd8540c9367ad833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20S=C3=A1rai?= Date: Tue, 19 Mar 2024 14:09:42 +0100 Subject: [PATCH 15/41] Act params rename doc (#14) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (refactor): ACT interface params + add doc comments * Revert "(refactor): ACT interface params + add doc comments" This reverts commit ee8da04fe7468a4fa65bd390fa17f72f2e93d301. * (refactor): ACT interface params + add doc comments * (refactor): Add error to ACT interface methods --------- Co-authored-by: Ferenc Sárai --- pkg/dynamicaccess/accesslogic.go | 6 +++- pkg/dynamicaccess/act.go | 44 ++++++++++++++++++---------- pkg/dynamicaccess/act_test.go | 26 +++++++++------- pkg/dynamicaccess/controller_test.go | 4 +-- pkg/dynamicaccess/history_test.go | 2 +- pkg/dynamicaccess/mock/act.go | 37 +++++++++++++---------- 6 files changed, 73 insertions(+), 46 deletions(-) diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index c4c6e7438a8..256dbd9f5ef 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -115,7 +115,11 @@ func (al *DefaultAccessLogic) getAccessKeyDecriptionKey(publisher ecdsa.PublicKe } func (al *DefaultAccessLogic) getEncryptedAccessKey(act Act, lookup_key []byte) ([]byte, error) { - return act.Lookup(lookup_key), nil + val, err := act.Lookup(lookup_key) + if err != nil { + return []byte{}, err + } + return val, nil } func (al *DefaultAccessLogic) Get(act Act, encryped_ref swarm.Address, publisher ecdsa.PublicKey, tag string) (swarm.Address, error) { diff --git a/pkg/dynamicaccess/act.go b/pkg/dynamicaccess/act.go index 2c1765d1d54..07728ebca67 100644 --- a/pkg/dynamicaccess/act.go +++ b/pkg/dynamicaccess/act.go @@ -1,3 +1,7 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package dynamicaccess import ( @@ -7,11 +11,19 @@ import ( "github.com/ethersphere/bee/pkg/swarm" ) +// Act represents an interface for accessing and manipulating data. type Act interface { - Add(lookupKey []byte, encryptedAccessKey []byte) Act - Lookup(lookupKey []byte) []byte - Load(lookupKey []byte) manifest.Entry - Store(me manifest.Entry) + // Add adds a key-value pair to the data store. + Add(key []byte, val []byte) error + + // Lookup retrieves the value associated with the given key from the data store. + Lookup(key []byte) ([]byte, error) + + // Load retrieves the manifest entry associated with the given key from the data store. + Load(key []byte) (manifest.Entry, error) + + // Store stores the given manifest entry in the data store. + Store(me manifest.Entry) error } var _ Act = (*defaultAct)(nil) @@ -20,32 +32,34 @@ type defaultAct struct { container map[string]string } -func (act *defaultAct) Add(lookupKey []byte, encryptedAccessKey []byte) Act { - act.container[hex.EncodeToString(lookupKey)] = hex.EncodeToString(encryptedAccessKey) - return act +func (act *defaultAct) Add(key []byte, val []byte) error { + act.container[hex.EncodeToString(key)] = hex.EncodeToString(val) + return nil } -func (act *defaultAct) Lookup(lookupKey []byte) []byte { - if key, ok := act.container[hex.EncodeToString(lookupKey)]; ok { +func (act *defaultAct) Lookup(key []byte) ([]byte, error) { + if key, ok := act.container[hex.EncodeToString(key)]; ok { bytes, err := hex.DecodeString(key) - if err == nil { - return bytes + if err != nil { + return nil, err } + return bytes, nil } - return make([]byte, 0) + return make([]byte, 0), nil } // to manifestEntry -func (act *defaultAct) Load(lookupKey []byte) manifest.Entry { - return manifest.NewEntry(swarm.NewAddress(lookupKey), act.container) +func (act *defaultAct) Load(key []byte) (manifest.Entry, error) { + return manifest.NewEntry(swarm.NewAddress(key), act.container), nil } // from manifestEntry -func (act *defaultAct) Store(me manifest.Entry) { +func (act *defaultAct) Store(me manifest.Entry) error { if act.container == nil { act.container = make(map[string]string) } act.container = me.Metadata() + return nil } func NewDefaultAct() Act { diff --git a/pkg/dynamicaccess/act_test.go b/pkg/dynamicaccess/act_test.go index a46cc3b228c..0bd807cbcc2 100644 --- a/pkg/dynamicaccess/act_test.go +++ b/pkg/dynamicaccess/act_test.go @@ -1,3 +1,7 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package dynamicaccess_test import ( @@ -21,12 +25,12 @@ func TestActAddLookup(t *testing.T) { act := dynamicaccess.NewDefaultAct() lookupKey := swarm.RandAddress(t).Bytes() encryptedAccesskey := swarm.RandAddress(t).Bytes() - act2 := act.Add(lookupKey, encryptedAccesskey) - if act2 == nil { - t.Error("Add() should return an act") + err := act.Add(lookupKey, encryptedAccesskey) + if err != nil { + t.Error("Add() should not return an error") } - key := act.Lookup(lookupKey) + key, _ := act.Lookup(lookupKey) if !bytes.Equal(key, encryptedAccesskey) { t.Errorf("Get() value is not the expected %s != %s", key, encryptedAccesskey) } @@ -46,12 +50,12 @@ func TestActWithManifest(t *testing.T) { act := dynamicaccess.NewDefaultAct() lookupKey := swarm.RandAddress(t).Bytes() encryptedAccesskey := swarm.RandAddress(t).Bytes() - act2 := act.Add(lookupKey, encryptedAccesskey) - if act2 == nil { - t.Error("Add() should return an act") + err = act.Add(lookupKey, encryptedAccesskey) + if err != nil { + t.Error("Add() should not return an error") } - actManifEntry := act.Load(lookupKey) + actManifEntry, _ := act.Load(lookupKey) if actManifEntry == nil { t.Error("Load() should return a manifest.Entry") } @@ -73,7 +77,7 @@ func TestActWithManifest(t *testing.T) { actualAct := dynamicaccess.NewDefaultAct() actualAct.Store(actualMe) - actualEak := actualAct.Lookup(lookupKey) + actualEak, _ := actualAct.Lookup(lookupKey) if !bytes.Equal(actualEak, encryptedAccesskey) { t.Errorf("actualAct.Store() value is not the expected %s != %s", actualEak, encryptedAccesskey) } @@ -89,7 +93,7 @@ func TestActStore(t *testing.T) { me := manifest.NewEntry(swarm.NewAddress(lookupKey), mp) act := dynamicaccess.NewDefaultAct() act.Store(me) - eak := act.Lookup(lookupKey) + eak, _ := act.Lookup(lookupKey) if !bytes.Equal(eak, encryptedAccesskey) { t.Errorf("Store() value is not the expected %s != %s", eak, encryptedAccesskey) @@ -102,7 +106,7 @@ func TestActLoad(t *testing.T) { lookupKey := swarm.RandAddress(t).Bytes() encryptedAccesskey := swarm.RandAddress(t).Bytes() act.Add(lookupKey, encryptedAccesskey) - me := act.Load(lookupKey) + me, _ := act.Load(lookupKey) eak := me.Metadata()[hex.EncodeToString(lookupKey)] diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index d043a18532c..d42df211fc9 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -22,8 +22,8 @@ func mockTestHistory(key, val []byte) dynamicaccess.History { var ( h = mock.NewHistory() now = time.Now() - act = mock.NewActMock(nil, func(lookupKey []byte) []byte { - return val + act = mock.NewActMock(nil, func(lookupKey []byte) ([]byte, error) { + return val, nil }) ) // act.Add(key, val) diff --git a/pkg/dynamicaccess/history_test.go b/pkg/dynamicaccess/history_test.go index 340b2adcb35..f7997d55fcd 100644 --- a/pkg/dynamicaccess/history_test.go +++ b/pkg/dynamicaccess/history_test.go @@ -32,7 +32,7 @@ func TestHistoryLookup(t *testing.T) { for _, tt := range tests { t.Run("", func(t *testing.T) { actAt, _ := h.Lookup(tt.input) - output := actAt.Lookup([]byte("key1")) + output, _ := actAt.Lookup([]byte("key1")) assert.Equal(t, output, hex.EncodeToString([]byte(tt.expected))) }) } diff --git a/pkg/dynamicaccess/mock/act.go b/pkg/dynamicaccess/mock/act.go index 8bcfb8ff0b9..4f320c13dce 100644 --- a/pkg/dynamicaccess/mock/act.go +++ b/pkg/dynamicaccess/mock/act.go @@ -1,3 +1,7 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package mock import ( @@ -6,43 +10,44 @@ import ( ) type ActMock struct { - AddFunc func(lookupKey []byte, encryptedAccessKey []byte) dynamicaccess.Act - LookupFunc func(lookupKey []byte) []byte - LoadFunc func(lookupKey []byte) manifest.Entry - StoreFunc func(me manifest.Entry) + AddFunc func(key []byte, val []byte) error + LookupFunc func(key []byte) ([]byte, error) + LoadFunc func(key []byte) (manifest.Entry, error) + StoreFunc func(me manifest.Entry) error } var _ dynamicaccess.Act = (*ActMock)(nil) -func (act *ActMock) Add(lookupKey []byte, encryptedAccessKey []byte) dynamicaccess.Act { +func (act *ActMock) Add(key []byte, val []byte) error { if act.AddFunc == nil { - return act + return nil } - return act.AddFunc(lookupKey, encryptedAccessKey) + return act.AddFunc(key, val) } -func (act *ActMock) Lookup(lookupKey []byte) []byte { +func (act *ActMock) Lookup(key []byte) ([]byte, error) { if act.LookupFunc == nil { - return make([]byte, 0) + return make([]byte, 0), nil } - return act.LookupFunc(lookupKey) + return act.LookupFunc(key) } -func (act *ActMock) Load(lookupKey []byte) manifest.Entry { +func (act *ActMock) Load(key []byte) (manifest.Entry, error) { if act.LoadFunc == nil { - return nil + return nil, nil } - return act.LoadFunc(lookupKey) + return act.LoadFunc(key) } -func (act *ActMock) Store(me manifest.Entry) { +func (act *ActMock) Store(me manifest.Entry) error { if act.StoreFunc == nil { - return + return nil } act.StoreFunc(me) + return nil } -func NewActMock(addFunc func(lookupKey []byte, encryptedAccessKey []byte) dynamicaccess.Act, getFunc func(lookupKey []byte) []byte) dynamicaccess.Act { +func NewActMock(addFunc func(key []byte, val []byte) error, getFunc func(key []byte) ([]byte, error)) dynamicaccess.Act { return &ActMock{ AddFunc: addFunc, LookupFunc: getFunc, From 67c4bb4a27b9af4db3529250c00736c063cc17b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= <58116288+bosi95@users.noreply.github.com> Date: Tue, 19 Mar 2024 15:48:54 +0100 Subject: [PATCH 16/41] Move and refactor ACT diffieHellman to Session. Add Key and NewFromKeystore functions. (#16) --- pkg/dynamicaccess/accesslogic.go | 72 +++++------- pkg/dynamicaccess/controller_test.go | 12 +- pkg/dynamicaccess/diffieHellman.go | 31 ----- pkg/dynamicaccess/diffieHellman_test.go | 54 --------- pkg/dynamicaccess/grantee_manager_test.go | 4 +- pkg/dynamicaccess/mock/diffieHellman.go | 22 ---- pkg/dynamicaccess/mock/session.go | 41 +++++++ pkg/dynamicaccess/session.go | 49 ++++++++ pkg/dynamicaccess/session_test.go | 132 ++++++++++++++++++++++ 9 files changed, 261 insertions(+), 156 deletions(-) delete mode 100644 pkg/dynamicaccess/diffieHellman.go delete mode 100644 pkg/dynamicaccess/diffieHellman_test.go delete mode 100644 pkg/dynamicaccess/mock/diffieHellman.go create mode 100644 pkg/dynamicaccess/mock/session.go create mode 100644 pkg/dynamicaccess/session.go create mode 100644 pkg/dynamicaccess/session_test.go diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index 256dbd9f5ef..8f46256cd85 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -14,8 +14,7 @@ type AccessLogic interface { Get(act Act, encryped_ref swarm.Address, publisher ecdsa.PublicKey, tag string) (swarm.Address, error) EncryptRef(act Act, publisherPubKey ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) //Add(act *Act, ref string, publisher ecdsa.PublicKey, tag string) (string, error) - getLookUpKey(publisher ecdsa.PublicKey, tag string) ([]byte, error) - getAccessKeyDecriptionKey(publisher ecdsa.PublicKey, tag string) ([]byte, error) + getKeys(publicKey ecdsa.PublicKey) ([][]byte, error) getEncryptedAccessKey(act Act, lookup_key []byte) ([]byte, error) //createEncryptedAccessKey(ref string) Add_New_Grantee_To_Content(act Act, publisherPubKey, granteePubKey ecdsa.PublicKey) (Act, error) @@ -24,7 +23,7 @@ type AccessLogic interface { } type DefaultAccessLogic struct { - diffieHellman DiffieHellman + session Session //encryption encryption.Interface } @@ -32,8 +31,12 @@ type DefaultAccessLogic struct { func (al *DefaultAccessLogic) AddPublisher(act Act, publisher ecdsa.PublicKey, tag string) (Act, error) { access_key := encryption.GenerateRandomKey(encryption.KeyLength) - lookup_key, _ := al.getLookUpKey(publisher, "") - access_key_encryption_key, _ := al.getAccessKeyDecriptionKey(publisher, "") + keys, err := al.getKeys(publisher) + if err != nil { + return nil, err + } + lookup_key := keys[0] + access_key_encryption_key := keys[1] access_key_cipher := encryption.New(encryption.Key(access_key_encryption_key), 0, uint32(0), hashFunc) encrypted_access_key, _ := access_key_cipher.Encrypt(access_key) @@ -62,8 +65,12 @@ func (al *DefaultAccessLogic) Add_New_Grantee_To_Content(act Act, publisherPubKe // --Encrypt access key for new Grantee-- // 2 Diffie-Hellman for the Grantee - lookup_key, _ := al.getLookUpKey(granteePubKey, "") - access_key_encryption_key, _ := al.getAccessKeyDecriptionKey(granteePubKey, "") + keys, err := al.getKeys(granteePubKey) + if err != nil { + return nil, err + } + lookup_key := keys[0] + access_key_encryption_key := keys[1] // Encrypt the access key for the new Grantee cipher := encryption.New(encryption.Key(access_key_encryption_key), 0, uint32(0), hashFunc) @@ -76,8 +83,12 @@ func (al *DefaultAccessLogic) Add_New_Grantee_To_Content(act Act, publisherPubKe } func (al *DefaultAccessLogic) getAccessKey(act Act, publisherPubKey ecdsa.PublicKey) []byte { - publisher_lookup_key, _ := al.getLookUpKey(publisherPubKey, "") - publisher_ak_decryption_key, _ := al.getAccessKeyDecriptionKey(publisherPubKey, "") + keys, err := al.getKeys(publisherPubKey) + if err != nil { + return nil + } + publisher_lookup_key := keys[0] + publisher_ak_decryption_key := keys[1] access_key_decryption_cipher := encryption.New(encryption.Key(publisher_ak_decryption_key), 0, uint32(0), hashFunc) encrypted_ak, _ := al.getEncryptedAccessKey(act, publisher_lookup_key) @@ -93,25 +104,16 @@ func (al *DefaultAccessLogic) getAccessKey(act Act, publisherPubKey ecdsa.Public // func (al *DefaultAccessLogic) CreateAccessKey(reference string) { // } -func (al *DefaultAccessLogic) getLookUpKey(publisher ecdsa.PublicKey, tag string) ([]byte, error) { +func (al *DefaultAccessLogic) getKeys(publicKey ecdsa.PublicKey) ([][]byte, error) { + // Generate lookup key and access key decryption + oneByteArray := []byte{1} zeroByteArray := []byte{0} - // Generate lookup key using Diffie Hellman - lookup_key, err := al.diffieHellman.SharedSecret(&publisher, tag, zeroByteArray) - if err != nil { - return []byte{}, err - } - return lookup_key, nil - -} -func (al *DefaultAccessLogic) getAccessKeyDecriptionKey(publisher ecdsa.PublicKey, tag string) ([]byte, error) { - oneByteArray := []byte{1} - // Generate access key decryption key using Diffie Hellman - access_key_decryption_key, err := al.diffieHellman.SharedSecret(&publisher, tag, oneByteArray) + keys, err := al.session.Key(&publicKey, [][]byte{zeroByteArray, oneByteArray}) if err != nil { - return []byte{}, err + return [][]byte{}, err } - return access_key_decryption_key, nil + return keys, nil } func (al *DefaultAccessLogic) getEncryptedAccessKey(act Act, lookup_key []byte) ([]byte, error) { @@ -124,14 +126,12 @@ func (al *DefaultAccessLogic) getEncryptedAccessKey(act Act, lookup_key []byte) func (al *DefaultAccessLogic) Get(act Act, encryped_ref swarm.Address, publisher ecdsa.PublicKey, tag string) (swarm.Address, error) { - lookup_key, err := al.getLookUpKey(publisher, tag) - if err != nil { - return swarm.EmptyAddress, err - } - access_key_decryption_key, err := al.getAccessKeyDecriptionKey(publisher, tag) + keys, err := al.getKeys(publisher) if err != nil { return swarm.EmptyAddress, err } + lookup_key := keys[0] + access_key_decryption_key := keys[1] // Lookup encrypted access key from the ACT manifest @@ -157,18 +157,8 @@ func (al *DefaultAccessLogic) Get(act Act, encryped_ref swarm.Address, publisher return swarm.NewAddress(ref), nil } -func NewAccessLogic(diffieHellman DiffieHellman) AccessLogic { +func NewAccessLogic(s Session) AccessLogic { return &DefaultAccessLogic{ - diffieHellman: diffieHellman, + session: s, } } - -// ------- -// act: &mock.ContainerMock{ -// AddFunc: func(ref string, publisher string, tag string) error { -// return nil -// }, -// GetFunc: func(ref string, publisher string, tag string) (string, error) { -// return "", nil -// }, -// }, diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index d42df211fc9..30e17cde542 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -35,9 +35,9 @@ func TestDecrypt(t *testing.T) { pk := getPrivateKey() ak := encryption.Key([]byte("cica")) - dh := dynamicaccess.NewDiffieHellman(pk) - aek, _ := dh.SharedSecret(&pk.PublicKey, "", []byte{1}) - e2 := encryption.New(aek, 0, uint32(0), hashFunc) + si := dynamicaccess.NewDefaultSession(pk) + aek, _ := si.Key(&pk.PublicKey, [][]byte{{1}}) + e2 := encryption.New(aek[0], 0, uint32(0), hashFunc) peak, _ := e2.Encrypt(ak) h := mockTestHistory(nil, peak) @@ -61,9 +61,9 @@ func TestEncrypt(t *testing.T) { pk := getPrivateKey() ak := encryption.Key([]byte("cica")) - dh := dynamicaccess.NewDiffieHellman(pk) - aek, _ := dh.SharedSecret(&pk.PublicKey, "", []byte{1}) - e2 := encryption.New(aek, 0, uint32(0), hashFunc) + si := dynamicaccess.NewDefaultSession(pk) + aek, _ := si.Key(&pk.PublicKey, [][]byte{{1}}) + e2 := encryption.New(aek[0], 0, uint32(0), hashFunc) peak, _ := e2.Encrypt(ak) h := mockTestHistory(nil, peak) diff --git a/pkg/dynamicaccess/diffieHellman.go b/pkg/dynamicaccess/diffieHellman.go deleted file mode 100644 index a9ff7f7f8cd..00000000000 --- a/pkg/dynamicaccess/diffieHellman.go +++ /dev/null @@ -1,31 +0,0 @@ -package dynamicaccess - -import ( - "crypto/ecdsa" - "errors" - - "github.com/ethersphere/bee/pkg/crypto" -) - -type DiffieHellman interface { - SharedSecret(publicKey *ecdsa.PublicKey, tag string, moment []byte) ([]byte, error) // tag- topic? -} - -var _ DiffieHellman = (*defaultDiffieHellman)(nil) - -type defaultDiffieHellman struct { - key *ecdsa.PrivateKey -} - -func (dh *defaultDiffieHellman) SharedSecret(publicKey *ecdsa.PublicKey, tag string, salt []byte) ([]byte, error) { - x, _ := publicKey.Curve.ScalarMult(publicKey.X, publicKey.Y, dh.key.D.Bytes()) - if x == nil { - return nil, errors.New("shared secret is point at infinity") - } - return crypto.LegacyKeccak256(append(x.Bytes(), salt...)) -} - -func NewDiffieHellman(key *ecdsa.PrivateKey) DiffieHellman { - return &defaultDiffieHellman{key: key} - -} diff --git a/pkg/dynamicaccess/diffieHellman_test.go b/pkg/dynamicaccess/diffieHellman_test.go deleted file mode 100644 index 5c80592b3ec..00000000000 --- a/pkg/dynamicaccess/diffieHellman_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package dynamicaccess_test - -import ( - "bytes" - "crypto/rand" - "encoding/hex" - "io" - "testing" - - "github.com/ethersphere/bee/pkg/crypto" - "github.com/ethersphere/bee/pkg/dynamicaccess" -) - -func TestSharedSecret(t *testing.T) { - pk, _ := crypto.GenerateSecp256k1Key() - _, err := dynamicaccess.NewDiffieHellman(pk).SharedSecret(&pk.PublicKey, "", nil) - if err != nil { - t.Errorf("Error generating shared secret: %v", err) - } -} - -func TestECDHCorrect(t *testing.T) { - t.Parallel() - - key1, err := crypto.GenerateSecp256k1Key() - if err != nil { - t.Fatal(err) - } - dh1 := dynamicaccess.NewDiffieHellman(key1) - - key2, err := crypto.GenerateSecp256k1Key() - if err != nil { - t.Fatal(err) - } - dh2 := dynamicaccess.NewDiffieHellman(key2) - - moment := make([]byte, 1) - if _, err := io.ReadFull(rand.Reader, moment); err != nil { - t.Fatal(err) - } - - shared1, err := dh1.SharedSecret(&key2.PublicKey, "", moment) - if err != nil { - t.Fatal(err) - } - shared2, err := dh2.SharedSecret(&key1.PublicKey, "", moment) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(shared1, shared2) { - t.Fatalf("shared secrets do not match %s, %s", hex.EncodeToString(shared1), hex.EncodeToString(shared2)) - } -} diff --git a/pkg/dynamicaccess/grantee_manager_test.go b/pkg/dynamicaccess/grantee_manager_test.go index d8e806498e0..4b55400f465 100644 --- a/pkg/dynamicaccess/grantee_manager_test.go +++ b/pkg/dynamicaccess/grantee_manager_test.go @@ -15,8 +15,8 @@ func setupAccessLogic(privateKey *ecdsa.PrivateKey) dynamicaccess.AccessLogic { // if err != nil { // errors.New("error creating private key") // } - diffieHellman := dynamicaccess.NewDiffieHellman(privateKey) - al := dynamicaccess.NewAccessLogic(diffieHellman) + si := dynamicaccess.NewDefaultSession(privateKey) + al := dynamicaccess.NewAccessLogic(si) return al } diff --git a/pkg/dynamicaccess/mock/diffieHellman.go b/pkg/dynamicaccess/mock/diffieHellman.go deleted file mode 100644 index 91601026893..00000000000 --- a/pkg/dynamicaccess/mock/diffieHellman.go +++ /dev/null @@ -1,22 +0,0 @@ -package mock - -import ( - "crypto/ecdsa" -) - -type DiffieHellmanMock struct { - SharedSecretFunc func(publicKey *ecdsa.PublicKey, tag string, salt []byte) ([]byte, error) - key *ecdsa.PrivateKey -} - -func (dhm *DiffieHellmanMock) SharedSecret(publicKey *ecdsa.PublicKey, tag string, salt []byte) ([]byte, error) { - if dhm.SharedSecretFunc == nil { - return nil, nil - } - return dhm.SharedSecretFunc(publicKey, tag, salt) - -} - -func NewDiffieHellmanMock(key *ecdsa.PrivateKey) *DiffieHellmanMock { - return &DiffieHellmanMock{key: key} -} diff --git a/pkg/dynamicaccess/mock/session.go b/pkg/dynamicaccess/mock/session.go new file mode 100644 index 00000000000..ba3e3f8c8f2 --- /dev/null +++ b/pkg/dynamicaccess/mock/session.go @@ -0,0 +1,41 @@ +package mock + +import ( + "crypto/ecdsa" + + "github.com/ethersphere/bee/pkg/crypto" + "github.com/ethersphere/bee/pkg/keystore" +) + +type SessionMock struct { + KeyFunc func(publicKey *ecdsa.PublicKey, nonces [][]byte) ([][]byte, error) + key *ecdsa.PrivateKey +} + +func (s *SessionMock) Key(publicKey *ecdsa.PublicKey, nonces [][]byte) ([][]byte, error) { + if s.KeyFunc == nil { + return nil, nil + } + return s.KeyFunc(publicKey, nonces) + +} + +func NewSessionMock(key *ecdsa.PrivateKey) *SessionMock { + return &SessionMock{key: key} +} + +func NewFromKeystore( + ks keystore.Service, + tag, + password string, + keyFunc func(publicKey *ecdsa.PublicKey, nonces [][]byte) ([][]byte, error), +) *SessionMock { + key, created, err := ks.Key(tag, password, crypto.EDGSecp256_K1) + if !created || err != nil { + return nil + } + return &SessionMock{ + key: key, + KeyFunc: keyFunc, + } +} diff --git a/pkg/dynamicaccess/session.go b/pkg/dynamicaccess/session.go new file mode 100644 index 00000000000..95a3c07e87b --- /dev/null +++ b/pkg/dynamicaccess/session.go @@ -0,0 +1,49 @@ +package dynamicaccess + +import ( + "crypto/ecdsa" + "errors" + + "github.com/ethersphere/bee/pkg/crypto" + "github.com/ethersphere/bee/pkg/keystore" +) + +// Session represents an interface for a Diffie-Helmann key derivation +type Session interface { + // Key returns a derived key for each nonce + Key(publicKey *ecdsa.PublicKey, nonces [][]byte) ([][]byte, error) +} + +var _ Session = (*session)(nil) + +type session struct { + key *ecdsa.PrivateKey +} + +func (s *session) Key(publicKey *ecdsa.PublicKey, nonces [][]byte) ([][]byte, error) { + x, _ := publicKey.Curve.ScalarMult(publicKey.X, publicKey.Y, s.key.D.Bytes()) + if x == nil { + return nil, errors.New("shared secret is point at infinity") + } + + keys := make([][]byte, len(nonces)) + for _, nonce := range nonces { + key, err := crypto.LegacyKeccak256(append(x.Bytes(), nonce...)) + if err != nil { + return nil, err + } + keys = append(keys, key) + } + + return keys, nil +} + +func NewDefaultSession(key *ecdsa.PrivateKey) Session { + return &session{ + key: key, + } +} + +func NewFromKeystore(ks keystore.Service, tag, password string) Session { + return nil +} diff --git a/pkg/dynamicaccess/session_test.go b/pkg/dynamicaccess/session_test.go new file mode 100644 index 00000000000..0cfee7691da --- /dev/null +++ b/pkg/dynamicaccess/session_test.go @@ -0,0 +1,132 @@ +package dynamicaccess_test + +import ( + "bytes" + "crypto/ecdsa" + "crypto/rand" + "encoding/hex" + "io" + "testing" + + "github.com/ethersphere/bee/pkg/crypto" + "github.com/ethersphere/bee/pkg/dynamicaccess" + "github.com/ethersphere/bee/pkg/dynamicaccess/mock" + memkeystore "github.com/ethersphere/bee/pkg/keystore/mem" +) + +func mockKeyFunc(publicKey *ecdsa.PublicKey, nonces [][]byte) ([][]byte, error) { + return [][]byte{{1}}, nil +} + +func TestSessionNewDefaultSession(t *testing.T) { + pk, err := crypto.GenerateSecp256k1Key() + if err != nil { + t.Fatalf("Error generating private key: %v", err) + } + si := dynamicaccess.NewDefaultSession(pk) + if si == nil { + t.Fatal("Session instance is nil") + } +} + +func TestSessionNewFromKeystore(t *testing.T) { + ks := memkeystore.New() + si := mock.NewFromKeystore(ks, "tag", "password", mockKeyFunc) + if si == nil { + t.Fatal("Session instance is nil") + } +} + +func TestSessionKey(t *testing.T) { + t.Parallel() + + key1, err := crypto.GenerateSecp256k1Key() + if err != nil { + t.Fatal(err) + } + si1 := dynamicaccess.NewDefaultSession(key1) + + key2, err := crypto.GenerateSecp256k1Key() + if err != nil { + t.Fatal(err) + } + si2 := dynamicaccess.NewDefaultSession(key2) + + nonces := make([][]byte, 1) + if _, err := io.ReadFull(rand.Reader, nonces[0]); err != nil { + t.Fatal(err) + } + + keys1, err := si1.Key(&key2.PublicKey, nonces) + if err != nil { + t.Fatal(err) + } + keys2, err := si2.Key(&key1.PublicKey, nonces) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(keys1[0], keys2[0]) { + t.Fatalf("shared secrets do not match %s, %s", hex.EncodeToString(keys1[0]), hex.EncodeToString(keys2[0])) + } +} + +func TestSessionKeyFromKeystore(t *testing.T) { + t.Parallel() + + ks := memkeystore.New() + tag1 := "tag1" + tag2 := "tag2" + password1 := "password1" + password2 := "password2" + + si1 := mock.NewFromKeystore(ks, tag1, password1, mockKeyFunc) + exists, err := ks.Exists(tag1) + if err != nil { + t.Fatal(err) + } + if !exists { + t.Fatal("Key1 should exist") + } + key1, created, err := ks.Key(tag1, password1, crypto.EDGSecp256_K1) + if err != nil { + t.Fatal(err) + } + if created { + t.Fatal("Key1 should not be created") + } + + si2 := mock.NewFromKeystore(ks, tag2, password2, mockKeyFunc) + exists, err = ks.Exists(tag2) + if err != nil { + t.Fatal(err) + } + if !exists { + t.Fatal("Key2 should exist") + } + key2, created, err := ks.Key(tag2, password2, crypto.EDGSecp256_K1) + if err != nil { + t.Fatal(err) + } + if created { + t.Fatal("Key2 should not be created") + } + + nonces := make([][]byte, 1) + if _, err := io.ReadFull(rand.Reader, nonces[0]); err != nil { + t.Fatal(err) + } + + keys1, err := si1.Key(&key2.PublicKey, nonces) + if err != nil { + t.Fatal(err) + } + keys2, err := si2.Key(&key1.PublicKey, nonces) + if err != nil { + t.Fatal(err) + } + + if !bytes.Equal(keys1[0], keys2[0]) { + t.Fatalf("shared secrets do not match %s, %s", hex.EncodeToString(keys1[0]), hex.EncodeToString(keys2[0])) + } +} From 8f350341efd58dd42093d5db06110fc75e8e33aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20S=C3=A1rai?= Date: Tue, 19 Mar 2024 15:50:12 +0100 Subject: [PATCH 17/41] Act swarm address (#15) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (refactor): ACT interface params + add doc comments * Revert "(refactor): ACT interface params + add doc comments" This reverts commit ee8da04fe7468a4fa65bd390fa17f72f2e93d301. * (refactor): ACT interface params + add doc comments * (refactor): Add error to ACT interface methods * Add in-memory storage and implement Store and Load methods * Move and refactor ACT diffieHellman to Session. Add Key and NewFromKeystore functions. --------- Co-authored-by: Ferenc Sárai Co-authored-by: Bálint Ujvári --- pkg/dynamicaccess/act.go | 68 ++++++++++++++++++++++------ pkg/dynamicaccess/act_test.go | 83 +++-------------------------------- pkg/dynamicaccess/mock/act.go | 19 ++++---- 3 files changed, 70 insertions(+), 100 deletions(-) diff --git a/pkg/dynamicaccess/act.go b/pkg/dynamicaccess/act.go index 07728ebca67..c8cbd9fa663 100644 --- a/pkg/dynamicaccess/act.go +++ b/pkg/dynamicaccess/act.go @@ -5,12 +5,44 @@ package dynamicaccess import ( + "crypto/rand" "encoding/hex" + "fmt" + "sync" "github.com/ethersphere/bee/pkg/manifest" "github.com/ethersphere/bee/pkg/swarm" ) +var lock = &sync.Mutex{} + +type single struct { + memoryMock map[string]manifest.Entry +} + +var singleInMemorySwarm *single + +func getInMemorySwarm() *single { + if singleInMemorySwarm == nil { + lock.Lock() + defer lock.Unlock() + if singleInMemorySwarm == nil { + singleInMemorySwarm = &single{ + memoryMock: make(map[string]manifest.Entry)} + } + } + return singleInMemorySwarm +} + +func getMemory() map[string]manifest.Entry { + ch := make(chan *single) + go func() { + ch <- getInMemorySwarm() + }() + mem := <-ch + return mem.memoryMock +} + // Act represents an interface for accessing and manipulating data. type Act interface { // Add adds a key-value pair to the data store. @@ -19,15 +51,16 @@ type Act interface { // Lookup retrieves the value associated with the given key from the data store. Lookup(key []byte) ([]byte, error) - // Load retrieves the manifest entry associated with the given key from the data store. - Load(key []byte) (manifest.Entry, error) + // Load loads the data store from the given address. + Load(addr swarm.Address) error - // Store stores the given manifest entry in the data store. - Store(me manifest.Entry) error + // Store stores the current state of the data store and returns the address of the ACT. + Store() (swarm.Address, error) } var _ Act = (*defaultAct)(nil) +// defaultAct is a simple implementation of the Act interface, with in memory storage. type defaultAct struct { container map[string]string } @@ -48,20 +81,29 @@ func (act *defaultAct) Lookup(key []byte) ([]byte, error) { return make([]byte, 0), nil } -// to manifestEntry -func (act *defaultAct) Load(key []byte) (manifest.Entry, error) { - return manifest.NewEntry(swarm.NewAddress(key), act.container), nil -} - -// from manifestEntry -func (act *defaultAct) Store(me manifest.Entry) error { - if act.container == nil { - act.container = make(map[string]string) +func (act *defaultAct) Load(addr swarm.Address) error { + memory := getMemory() + me := memory[addr.String()] + if me == nil { + return fmt.Errorf("ACT not found at address: %s", addr.String()) } act.container = me.Metadata() return nil } +func (act *defaultAct) Store() (swarm.Address, error) { + // Generate a random swarm.Address + b := make([]byte, 32) + if _, err := rand.Read(b); err != nil { + return swarm.EmptyAddress, fmt.Errorf("failed to generate random address: %w", err) + } + swarm_ref := swarm.NewAddress(b) + mem := getMemory() + mem[swarm_ref.String()] = manifest.NewEntry(swarm_ref, act.container) + + return swarm_ref, nil +} + func NewDefaultAct() Act { return &defaultAct{ container: make(map[string]string), diff --git a/pkg/dynamicaccess/act_test.go b/pkg/dynamicaccess/act_test.go index 0bd807cbcc2..5a980977f9b 100644 --- a/pkg/dynamicaccess/act_test.go +++ b/pkg/dynamicaccess/act_test.go @@ -6,18 +6,10 @@ package dynamicaccess_test import ( "bytes" - "context" "encoding/hex" "testing" "github.com/ethersphere/bee/pkg/dynamicaccess" - "github.com/ethersphere/bee/pkg/file/loadsave" - "github.com/ethersphere/bee/pkg/file/pipeline" - "github.com/ethersphere/bee/pkg/file/pipeline/builder" - "github.com/ethersphere/bee/pkg/file/redundancy" - "github.com/ethersphere/bee/pkg/manifest" - "github.com/ethersphere/bee/pkg/storage" - mockstorer "github.com/ethersphere/bee/pkg/storer/mock" "github.com/ethersphere/bee/pkg/swarm" ) @@ -36,88 +28,25 @@ func TestActAddLookup(t *testing.T) { } } -func TestActWithManifest(t *testing.T) { - - storer := mockstorer.New() - encrypt := false - ctx := context.Background() - ls := loadsave.New(storer.ChunkStore(), storer.Cache(), pipelineFactory(storer.Cache(), false, 0)) - rootManifest, err := manifest.NewDefaultManifest(ls, encrypt) - if err != nil { - t.Error("DefaultManifest should not return an error") - } +func TestActStoreLoad(t *testing.T) { act := dynamicaccess.NewDefaultAct() lookupKey := swarm.RandAddress(t).Bytes() encryptedAccesskey := swarm.RandAddress(t).Bytes() - err = act.Add(lookupKey, encryptedAccesskey) + err := act.Add(lookupKey, encryptedAccesskey) if err != nil { t.Error("Add() should not return an error") } - actManifEntry, _ := act.Load(lookupKey) - if actManifEntry == nil { - t.Error("Load() should return a manifest.Entry") - } - - err = rootManifest.Add(ctx, hex.EncodeToString(lookupKey), actManifEntry) - if err != nil { - t.Error("rootManifest.Add() should not return an error") - } - - _, err = rootManifest.Store(ctx) + swarm_ref, err := act.Store() if err != nil { - t.Error("rootManifest.Store() should not return an error") - } - - actualMe, err := rootManifest.Lookup(ctx, hex.EncodeToString(lookupKey)) - if err != nil { - t.Error("rootManifest.Lookup() should not return an error") + t.Error("Store() should not return an error") } actualAct := dynamicaccess.NewDefaultAct() - actualAct.Store(actualMe) + actualAct.Load(swarm_ref) actualEak, _ := actualAct.Lookup(lookupKey) if !bytes.Equal(actualEak, encryptedAccesskey) { - t.Errorf("actualAct.Store() value is not the expected %s != %s", actualEak, encryptedAccesskey) - } -} - -func TestActStore(t *testing.T) { - mp := make(map[string]string) - - lookupKey := swarm.RandAddress(t).Bytes() - encryptedAccesskey := swarm.RandAddress(t).Bytes() - mp[hex.EncodeToString(lookupKey)] = hex.EncodeToString(encryptedAccesskey) - - me := manifest.NewEntry(swarm.NewAddress(lookupKey), mp) - act := dynamicaccess.NewDefaultAct() - act.Store(me) - eak, _ := act.Lookup(lookupKey) - - if !bytes.Equal(eak, encryptedAccesskey) { - t.Errorf("Store() value is not the expected %s != %s", eak, encryptedAccesskey) - } - -} - -func TestActLoad(t *testing.T) { - act := dynamicaccess.NewDefaultAct() - lookupKey := swarm.RandAddress(t).Bytes() - encryptedAccesskey := swarm.RandAddress(t).Bytes() - act.Add(lookupKey, encryptedAccesskey) - me, _ := act.Load(lookupKey) - - eak := me.Metadata()[hex.EncodeToString(lookupKey)] - - if eak != hex.EncodeToString(encryptedAccesskey) { - t.Errorf("Load() value is not the expected %s != %s", eak, encryptedAccesskey) - } - -} - -func pipelineFactory(s storage.Putter, encrypt bool, rLevel redundancy.Level) func() pipeline.Interface { - return func() pipeline.Interface { - return builder.NewPipelineBuilder(context.Background(), s, encrypt, rLevel) + t.Errorf("actualAct.Load() value is not the expected %s != %s", hex.EncodeToString(actualEak), hex.EncodeToString(encryptedAccesskey)) } } diff --git a/pkg/dynamicaccess/mock/act.go b/pkg/dynamicaccess/mock/act.go index 4f320c13dce..a915f38f123 100644 --- a/pkg/dynamicaccess/mock/act.go +++ b/pkg/dynamicaccess/mock/act.go @@ -6,14 +6,14 @@ package mock import ( "github.com/ethersphere/bee/pkg/dynamicaccess" - "github.com/ethersphere/bee/pkg/manifest" + "github.com/ethersphere/bee/pkg/swarm" ) type ActMock struct { AddFunc func(key []byte, val []byte) error LookupFunc func(key []byte) ([]byte, error) - LoadFunc func(key []byte) (manifest.Entry, error) - StoreFunc func(me manifest.Entry) error + LoadFunc func(addr swarm.Address) error + StoreFunc func() (swarm.Address, error) } var _ dynamicaccess.Act = (*ActMock)(nil) @@ -32,19 +32,18 @@ func (act *ActMock) Lookup(key []byte) ([]byte, error) { return act.LookupFunc(key) } -func (act *ActMock) Load(key []byte) (manifest.Entry, error) { +func (act *ActMock) Load(addr swarm.Address) error { if act.LoadFunc == nil { - return nil, nil + return nil } - return act.LoadFunc(key) + return act.LoadFunc(addr) } -func (act *ActMock) Store(me manifest.Entry) error { +func (act *ActMock) Store() (swarm.Address, error) { if act.StoreFunc == nil { - return nil + return swarm.EmptyAddress, nil } - act.StoreFunc(me) - return nil + return act.StoreFunc() } func NewActMock(addFunc func(key []byte, val []byte) error, getFunc func(key []byte) ([]byte, error)) dynamicaccess.Act { From 2ddeb488ce3208cf7aa388849cb6b48194d89ed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20S=C3=A1rai?= Date: Tue, 19 Mar 2024 17:00:52 +0100 Subject: [PATCH 18/41] (rename): defaultAct to inMemoryAct (#17) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (refactor): ACT interface params + add doc comments * Revert "(refactor): ACT interface params + add doc comments" This reverts commit ee8da04fe7468a4fa65bd390fa17f72f2e93d301. * (refactor): ACT interface params + add doc comments * (refactor): Add error to ACT interface methods * Add in-memory storage and implement Store and Load methods * *refactor) Rename defaultAct to inMemroryAct --------- Co-authored-by: Ferenc Sárai --- pkg/dynamicaccess/act.go | 18 +++++++++--------- pkg/dynamicaccess/act_test.go | 6 +++--- pkg/dynamicaccess/controller.go | 2 +- pkg/dynamicaccess/grantee_manager_test.go | 2 +- pkg/dynamicaccess/history_test.go | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pkg/dynamicaccess/act.go b/pkg/dynamicaccess/act.go index c8cbd9fa663..1153f2fc868 100644 --- a/pkg/dynamicaccess/act.go +++ b/pkg/dynamicaccess/act.go @@ -58,19 +58,19 @@ type Act interface { Store() (swarm.Address, error) } -var _ Act = (*defaultAct)(nil) +var _ Act = (*inMemoryAct)(nil) -// defaultAct is a simple implementation of the Act interface, with in memory storage. -type defaultAct struct { +// inMemoryAct is a simple implementation of the Act interface, with in memory storage. +type inMemoryAct struct { container map[string]string } -func (act *defaultAct) Add(key []byte, val []byte) error { +func (act *inMemoryAct) Add(key []byte, val []byte) error { act.container[hex.EncodeToString(key)] = hex.EncodeToString(val) return nil } -func (act *defaultAct) Lookup(key []byte) ([]byte, error) { +func (act *inMemoryAct) Lookup(key []byte) ([]byte, error) { if key, ok := act.container[hex.EncodeToString(key)]; ok { bytes, err := hex.DecodeString(key) if err != nil { @@ -81,7 +81,7 @@ func (act *defaultAct) Lookup(key []byte) ([]byte, error) { return make([]byte, 0), nil } -func (act *defaultAct) Load(addr swarm.Address) error { +func (act *inMemoryAct) Load(addr swarm.Address) error { memory := getMemory() me := memory[addr.String()] if me == nil { @@ -91,7 +91,7 @@ func (act *defaultAct) Load(addr swarm.Address) error { return nil } -func (act *defaultAct) Store() (swarm.Address, error) { +func (act *inMemoryAct) Store() (swarm.Address, error) { // Generate a random swarm.Address b := make([]byte, 32) if _, err := rand.Read(b); err != nil { @@ -104,8 +104,8 @@ func (act *defaultAct) Store() (swarm.Address, error) { return swarm_ref, nil } -func NewDefaultAct() Act { - return &defaultAct{ +func NewInMemoryAct() Act { + return &inMemoryAct{ container: make(map[string]string), } } diff --git a/pkg/dynamicaccess/act_test.go b/pkg/dynamicaccess/act_test.go index 5a980977f9b..3480077102a 100644 --- a/pkg/dynamicaccess/act_test.go +++ b/pkg/dynamicaccess/act_test.go @@ -14,7 +14,7 @@ import ( ) func TestActAddLookup(t *testing.T) { - act := dynamicaccess.NewDefaultAct() + act := dynamicaccess.NewInMemoryAct() lookupKey := swarm.RandAddress(t).Bytes() encryptedAccesskey := swarm.RandAddress(t).Bytes() err := act.Add(lookupKey, encryptedAccesskey) @@ -30,7 +30,7 @@ func TestActAddLookup(t *testing.T) { func TestActStoreLoad(t *testing.T) { - act := dynamicaccess.NewDefaultAct() + act := dynamicaccess.NewInMemoryAct() lookupKey := swarm.RandAddress(t).Bytes() encryptedAccesskey := swarm.RandAddress(t).Bytes() err := act.Add(lookupKey, encryptedAccesskey) @@ -43,7 +43,7 @@ func TestActStoreLoad(t *testing.T) { t.Error("Store() should not return an error") } - actualAct := dynamicaccess.NewDefaultAct() + actualAct := dynamicaccess.NewInMemoryAct() actualAct.Load(swarm_ref) actualEak, _ := actualAct.Lookup(lookupKey) if !bytes.Equal(actualEak, encryptedAccesskey) { diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index 1637234d9c2..6267a878279 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -30,7 +30,7 @@ func (c *defaultController) UploadHandler(ref swarm.Address, publisher *ecdsa.Pu act, _ := c.history.Lookup(0) if act == nil { // new feed - act = NewDefaultAct() + act = NewInMemoryAct() act = c.granteeManager.Publish(act, *publisher, topic) } //FIXME: check if ACT is consistent with the grantee list diff --git a/pkg/dynamicaccess/grantee_manager_test.go b/pkg/dynamicaccess/grantee_manager_test.go index 4b55400f465..bc90ef504ae 100644 --- a/pkg/dynamicaccess/grantee_manager_test.go +++ b/pkg/dynamicaccess/grantee_manager_test.go @@ -22,7 +22,7 @@ func setupAccessLogic(privateKey *ecdsa.PrivateKey) dynamicaccess.AccessLogic { } func TestAdd(t *testing.T) { - act := dynamicaccess.NewDefaultAct() + act := dynamicaccess.NewInMemoryAct() m := dynamicaccess.NewGranteeManager(setupAccessLogic(getPrivateKey())) pub, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) diff --git a/pkg/dynamicaccess/history_test.go b/pkg/dynamicaccess/history_test.go index f7997d55fcd..a58aa44ecaa 100644 --- a/pkg/dynamicaccess/history_test.go +++ b/pkg/dynamicaccess/history_test.go @@ -42,9 +42,9 @@ func prepareTestHistory() dynamicaccess.History { var ( h = mock.NewHistory() now = time.Now() - act1 = dynamicaccess.NewDefaultAct() - act2 = dynamicaccess.NewDefaultAct() - act3 = dynamicaccess.NewDefaultAct() + act1 = dynamicaccess.NewInMemoryAct() + act2 = dynamicaccess.NewInMemoryAct() + act3 = dynamicaccess.NewInMemoryAct() ) act1.Add([]byte("key1"), []byte("value1")) act2.Add([]byte("key1"), []byte("value2")) From 64d4b904b67b9f44fb93551a2ed43a7b192b108a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20S=C3=A1rai?= Date: Wed, 20 Mar 2024 10:12:43 +0100 Subject: [PATCH 19/41] (refactor): Update controller_test.go to use NewInMemoryAct, modify Session.Key to return correct dimensional byte slice (#18) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (refactor): Update controller_test.go to use NewInMemoryAct, modify Session.Key to return two-dimensional byte slice * (refactor:) Refactor session Key function to use append instead of index-based assignment --------- Co-authored-by: Ferenc Sárai --- pkg/dynamicaccess/controller_test.go | 18 ++++++++---------- pkg/dynamicaccess/session.go | 2 +- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index 30e17cde542..531470eceee 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -22,11 +22,9 @@ func mockTestHistory(key, val []byte) dynamicaccess.History { var ( h = mock.NewHistory() now = time.Now() - act = mock.NewActMock(nil, func(lookupKey []byte) ([]byte, error) { - return val, nil - }) + act = dynamicaccess.NewInMemoryAct() ) - // act.Add(key, val) + act.Add(key, val) h.Insert(now.AddDate(-3, 0, 0).Unix(), act) return h } @@ -36,11 +34,11 @@ func TestDecrypt(t *testing.T) { ak := encryption.Key([]byte("cica")) si := dynamicaccess.NewDefaultSession(pk) - aek, _ := si.Key(&pk.PublicKey, [][]byte{{1}}) - e2 := encryption.New(aek[0], 0, uint32(0), hashFunc) + aek, _ := si.Key(&pk.PublicKey, [][]byte{{0}, {1}}) + e2 := encryption.New(aek[1], 0, uint32(0), hashFunc) peak, _ := e2.Encrypt(ak) - h := mockTestHistory(nil, peak) + h := mockTestHistory(aek[0], peak) al := setupAccessLogic(pk) gm := dynamicaccess.NewGranteeManager(al) c := dynamicaccess.NewController(h, gm, al) @@ -62,11 +60,11 @@ func TestEncrypt(t *testing.T) { ak := encryption.Key([]byte("cica")) si := dynamicaccess.NewDefaultSession(pk) - aek, _ := si.Key(&pk.PublicKey, [][]byte{{1}}) - e2 := encryption.New(aek[0], 0, uint32(0), hashFunc) + aek, _ := si.Key(&pk.PublicKey, [][]byte{{0}, {1}}) + e2 := encryption.New(aek[1], 0, uint32(0), hashFunc) peak, _ := e2.Encrypt(ak) - h := mockTestHistory(nil, peak) + h := mockTestHistory(aek[0], peak) al := setupAccessLogic(pk) gm := dynamicaccess.NewGranteeManager(al) c := dynamicaccess.NewController(h, gm, al) diff --git a/pkg/dynamicaccess/session.go b/pkg/dynamicaccess/session.go index 95a3c07e87b..9d7634ffd01 100644 --- a/pkg/dynamicaccess/session.go +++ b/pkg/dynamicaccess/session.go @@ -26,7 +26,7 @@ func (s *session) Key(publicKey *ecdsa.PublicKey, nonces [][]byte) ([][]byte, er return nil, errors.New("shared secret is point at infinity") } - keys := make([][]byte, len(nonces)) + keys := make([][]byte, 0, len(nonces)) for _, nonce := range nonces { key, err := crypto.LegacyKeccak256(append(x.Bytes(), nonce...)) if err != nil { From a244177fb9421a3951fdc78dd0d79ba175ad3267 Mon Sep 17 00:00:00 2001 From: rolandlor <33499567+rolandlor@users.noreply.github.com> Date: Thu, 21 Mar 2024 13:58:43 +0100 Subject: [PATCH 20/41] Act access logic merge (#19) * grantee container and access logc tests are passed * refactored access logic and grantee container * PR 19 comments resolving * Refactor * Refactor --- pkg/dynamicaccess/accesslogic.go | 153 ++++---- pkg/dynamicaccess/accesslogic_test.go | 447 ++++++++++------------ pkg/dynamicaccess/act.go | 2 +- pkg/dynamicaccess/controller.go | 10 +- pkg/dynamicaccess/grantee.go | 34 +- pkg/dynamicaccess/grantee_manager.go | 12 +- pkg/dynamicaccess/grantee_manager_test.go | 6 +- pkg/dynamicaccess/grantee_test.go | 115 +++--- 8 files changed, 375 insertions(+), 404 deletions(-) diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index 8f46256cd85..417a4a40878 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -2,6 +2,7 @@ package dynamicaccess import ( "crypto/ecdsa" + "fmt" encryption "github.com/ethersphere/bee/pkg/encryption" "github.com/ethersphere/bee/pkg/swarm" @@ -10,146 +11,154 @@ import ( var hashFunc = sha3.NewLegacyKeccak256 -type AccessLogic interface { - Get(act Act, encryped_ref swarm.Address, publisher ecdsa.PublicKey, tag string) (swarm.Address, error) - EncryptRef(act Act, publisherPubKey ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) - //Add(act *Act, ref string, publisher ecdsa.PublicKey, tag string) (string, error) - getKeys(publicKey ecdsa.PublicKey) ([][]byte, error) - getEncryptedAccessKey(act Act, lookup_key []byte) ([]byte, error) - //createEncryptedAccessKey(ref string) - Add_New_Grantee_To_Content(act Act, publisherPubKey, granteePubKey ecdsa.PublicKey) (Act, error) - AddPublisher(act Act, publisher ecdsa.PublicKey, tag string) (Act, error) - // CreateAccessKey() +// Logic has the responsibility to return a ref for a given grantee and create new encrypted reference for a grantee +type Logic interface { + // Adds a new grantee to the ACT + AddNewGranteeToContent(act Act, publisherPubKey, granteePubKey *ecdsa.PublicKey) (Act, error) + // Get will return a decrypted reference, for given encrypted reference and grantee !!!!!!!!!!!!!!!!!!!!! + Get(act Act, encryped_ref swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) } -type DefaultAccessLogic struct { +type ActLogic struct { session Session - //encryption encryption.Interface } -// Will create a new Act list with only one element (the creator), and will also create encrypted_ref -func (al *DefaultAccessLogic) AddPublisher(act Act, publisher ecdsa.PublicKey, tag string) (Act, error) { - access_key := encryption.GenerateRandomKey(encryption.KeyLength) +var _ Logic = (*ActLogic)(nil) + +// Adds a new publisher to an empty act +func (al ActLogic) AddPublisher(act Act, publisher *ecdsa.PublicKey) (Act, error) { + accessKey := encryption.GenerateRandomKey(encryption.KeyLength) keys, err := al.getKeys(publisher) if err != nil { return nil, err } - lookup_key := keys[0] - access_key_encryption_key := keys[1] + lookupKey := keys[0] + accessKeyEncryptionKey := keys[1] - access_key_cipher := encryption.New(encryption.Key(access_key_encryption_key), 0, uint32(0), hashFunc) - encrypted_access_key, _ := access_key_cipher.Encrypt(access_key) + accessKeyCipher := encryption.New(encryption.Key(accessKeyEncryptionKey), 0, uint32(0), hashFunc) + encryptedAccessKey, err := accessKeyCipher.Encrypt([]byte(accessKey)) + if err != nil { + return nil, err + } - act.Add(lookup_key, encrypted_access_key) + act.Add(lookupKey, encryptedAccessKey) return act, nil } -func (al *DefaultAccessLogic) EncryptRef(act Act, publisherPubKey ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) { - access_key := al.getAccessKey(act, publisherPubKey) - ref_cipher := encryption.New(access_key, 0, uint32(0), hashFunc) - encrypted_ref, _ := ref_cipher.Encrypt(ref.Bytes()) - return swarm.NewAddress(encrypted_ref), nil -} - -// publisher is public key -func (al *DefaultAccessLogic) Add_New_Grantee_To_Content(act Act, publisherPubKey, granteePubKey ecdsa.PublicKey) (Act, error) { +// Encrypts a SWARM reference for a publisher +func (al ActLogic) EncryptRef(act Act, publisherPubKey *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) { + accessKey := al.getAccessKey(act, publisherPubKey) + refCipher := encryption.New(accessKey, 0, uint32(0), hashFunc) + encryptedRef, _ := refCipher.Encrypt(ref.Bytes()) - // error handling no encrypted_ref + return swarm.NewAddress(encryptedRef), nil +} - // 2 Diffie-Hellman for the publisher (the Creator) +// Adds a new grantee to the ACT +func (al ActLogic) AddNewGranteeToContent(act Act, publisherPubKey, granteePubKey *ecdsa.PublicKey) (Act, error) { // Get previously generated access key - access_key := al.getAccessKey(act, publisherPubKey) - - // --Encrypt access key for new Grantee-- + accessKey := al.getAccessKey(act, publisherPubKey) - // 2 Diffie-Hellman for the Grantee + // Encrypt the access key for the new Grantee keys, err := al.getKeys(granteePubKey) if err != nil { return nil, err } - lookup_key := keys[0] - access_key_encryption_key := keys[1] + lookupKey := keys[0] + accessKeyEncryptionKey := keys[1] // Encrypt the access key for the new Grantee - cipher := encryption.New(encryption.Key(access_key_encryption_key), 0, uint32(0), hashFunc) - granteeEncryptedAccessKey, _ := cipher.Encrypt(access_key) + cipher := encryption.New(encryption.Key(accessKeyEncryptionKey), 0, uint32(0), hashFunc) + granteeEncryptedAccessKey, err := cipher.Encrypt(accessKey) + if err != nil { + return nil, err + } + // Add the new encrypted access key for the Act - act.Add(lookup_key, granteeEncryptedAccessKey) + act.Add(lookupKey, granteeEncryptedAccessKey) return act, nil } -func (al *DefaultAccessLogic) getAccessKey(act Act, publisherPubKey ecdsa.PublicKey) []byte { +// Will return the access key for a publisher (public key) +func (al *ActLogic) getAccessKey(act Act, publisherPubKey *ecdsa.PublicKey) []byte { keys, err := al.getKeys(publisherPubKey) if err != nil { return nil } - publisher_lookup_key := keys[0] - publisher_ak_decryption_key := keys[1] - - access_key_decryption_cipher := encryption.New(encryption.Key(publisher_ak_decryption_key), 0, uint32(0), hashFunc) - encrypted_ak, _ := al.getEncryptedAccessKey(act, publisher_lookup_key) - access_key, _ := access_key_decryption_cipher.Decrypt(encrypted_ak) - return access_key -} + publisherLookupKey := keys[0] + publisherAKDecryptionKey := keys[1] -// -// act[lookupKey] := valamilyen_cipher.Encrypt(access_key) + accessKeyDecryptionCipher := encryption.New(encryption.Key(publisherAKDecryptionKey), 0, uint32(0), hashFunc) + encryptedAK, err := al.getEncryptedAccessKey(act, publisherLookupKey) + if err != nil { + return nil + } -// end of pseudo code like code + accessKey, err := accessKeyDecryptionCipher.Decrypt(encryptedAK) + if err != nil { + return nil + } -// func (al *DefaultAccessLogic) CreateAccessKey(reference string) { -// } + return accessKey +} -func (al *DefaultAccessLogic) getKeys(publicKey ecdsa.PublicKey) ([][]byte, error) { +func (al *ActLogic) getKeys(publicKey *ecdsa.PublicKey) ([][]byte, error) { // Generate lookup key and access key decryption oneByteArray := []byte{1} zeroByteArray := []byte{0} - keys, err := al.session.Key(&publicKey, [][]byte{zeroByteArray, oneByteArray}) + keys, err := al.session.Key(publicKey, [][]byte{zeroByteArray, oneByteArray}) if err != nil { - return [][]byte{}, err + return nil, err } return keys, nil } -func (al *DefaultAccessLogic) getEncryptedAccessKey(act Act, lookup_key []byte) ([]byte, error) { +// Gets the encrypted access key for a given grantee +func (al *ActLogic) getEncryptedAccessKey(act Act, lookup_key []byte) ([]byte, error) { val, err := act.Lookup(lookup_key) if err != nil { - return []byte{}, err + return nil, err } return val, nil } -func (al *DefaultAccessLogic) Get(act Act, encryped_ref swarm.Address, publisher ecdsa.PublicKey, tag string) (swarm.Address, error) { +// Get will return a decrypted reference, for given encrypted reference and grantee +func (al ActLogic) Get(act Act, encryped_ref swarm.Address, grantee *ecdsa.PublicKey) (swarm.Address, error) { + if encryped_ref.Compare(swarm.EmptyAddress) == 0 { + return swarm.EmptyAddress, fmt.Errorf("encrypted ref not provided") + } + if grantee == nil { + return swarm.EmptyAddress, fmt.Errorf("grantee not provided") + } - keys, err := al.getKeys(publisher) + keys, err := al.getKeys(grantee) if err != nil { return swarm.EmptyAddress, err } - lookup_key := keys[0] - access_key_decryption_key := keys[1] + lookupKey := keys[0] + accessKeyDecryptionKey := keys[1] // Lookup encrypted access key from the ACT manifest - - encrypted_access_key, err := al.getEncryptedAccessKey(act, lookup_key) + encryptedAccessKey, err := al.getEncryptedAccessKey(act, lookupKey) if err != nil { return swarm.EmptyAddress, err } // Decrypt access key - access_key_cipher := encryption.New(encryption.Key(access_key_decryption_key), 0, uint32(0), hashFunc) - access_key, err := access_key_cipher.Decrypt(encrypted_access_key) + accessKeyCipher := encryption.New(encryption.Key(accessKeyDecryptionKey), 0, uint32(0), hashFunc) + accessKey, err := accessKeyCipher.Decrypt(encryptedAccessKey) if err != nil { return swarm.EmptyAddress, err } // Decrypt reference - ref_cipher := encryption.New(access_key, 0, uint32(0), hashFunc) - ref, err := ref_cipher.Decrypt(encryped_ref.Bytes()) + refCipher := encryption.New(accessKey, 0, uint32(0), hashFunc) + ref, err := refCipher.Decrypt(encryped_ref.Bytes()) if err != nil { return swarm.EmptyAddress, err } @@ -157,8 +166,8 @@ func (al *DefaultAccessLogic) Get(act Act, encryped_ref swarm.Address, publisher return swarm.NewAddress(ref), nil } -func NewAccessLogic(s Session) AccessLogic { - return &DefaultAccessLogic{ +func NewLogic(s Session) ActLogic { + return ActLogic{ session: s, } -} +} \ No newline at end of file diff --git a/pkg/dynamicaccess/accesslogic_test.go b/pkg/dynamicaccess/accesslogic_test.go index c3125b65cd6..46a4cc39ec6 100644 --- a/pkg/dynamicaccess/accesslogic_test.go +++ b/pkg/dynamicaccess/accesslogic_test.go @@ -1,237 +1,214 @@ package dynamicaccess_test -// import ( -// "crypto/ecdsa" -// "crypto/elliptic" -// "crypto/rand" -// "errors" -// "fmt" -// "testing" - -// "github.com/ethersphere/bee/pkg/crypto" -// "github.com/ethersphere/bee/pkg/swarm" -// ) - -// func setupAccessLogic() AccessLogic { -// privateKey, err := crypto.GenerateSecp256k1Key() -// if err != nil { -// errors.New("error creating private key") -// } -// diffieHellman := NewDiffieHellman(privateKey) -// al := NewAccessLogic(diffieHellman) - -// return al -// } - -// func TestGetLookupKey_Success(t *testing.T) { -// al := setupAccessLogic() - -// id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) -// // ! this will be random, we can not know the lookup key for a randomly generated key -// act, encryptedRef, _ := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") -// fmt.Println(act, encryptedRef) - -// tag := "exampleTag" - -// lookupKey, err := al.getLookUpKey(id0.PublicKey, tag) -// if err != nil { -// t.Errorf("Could not fetch lookup key from publisher and tag") -// } - -// expectedLookupKey := "expectedLookupKey" -// if lookupKey != expectedLookupKey { -// fmt.Println(string(lookupKey)) -// t.Errorf("The lookup key that was returned is not correct!") -// } -// } - -// func TestGetLookUpKey_Error(t *testing.T) { -// al := setupAccessLogic() - -// invalidPublisher := ecdsa.PublicKey{} -// tag := "exampleTag" - -// lookupKey, err := al.getLookUpKey(invalidPublisher, tag) - -// if err != nil { -// t.Errorf("There was an error while fetching lookup key") -// } - -// if lookupKey != "" { -// t.Errorf("Expected lookup key to be empty for invalid input") -// } -// } - -// func TestGetAccessKeyDecriptionKey_Success(t *testing.T) { -// al := setupAccessLogic() - -// id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) -// tag := "exampleTag" - -// access_key_decryption_key, err := al.getAccessKeyDecriptionKey(id0.PublicKey, tag) -// if err != nil { -// t.Errorf("GetAccessKeyDecriptionKey gave back error") -// } - -// expectedResult := "we-dont-know" -// if access_key_decryption_key != expectedResult { -// t.Errorf("The access key decryption key is not correct!") -// } -// } - -// func TestGetAccessKeyDecriptionKey_Error(t *testing.T) { -// al := setupAccessLogic() - -// invalidPublisher := ecdsa.PublicKey{} -// tag := "exampleTag" - -// access_key_decryption_key, err := al.getAccessKeyDecriptionKey(invalidPublisher, tag) -// if err != nil { -// t.Errorf("GetAccessKeyDecriptionKey gave back error") -// } - -// if access_key_decryption_key != "" { -// t.Errorf("GetAccessKeyDecriptionKey should give back empty string for invalid input!") -// } -// } - -// func TestGetEncryptedAccessKey_Success(t *testing.T) { -// al := setupAccessLogic() - -// lookupKey := "exampleLookupKey" -// id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - -// act, _, _ := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") - -// encrypted_access_key, err := al.getEncryptedAccessKey(*act, lookupKey) -// if err != nil { -// t.Errorf("There was an error while executing GetEncryptedAccessKey") -// } - -// expectedEncryptedKey := "abc013encryptedkey" -// if encrypted_access_key.Reference().String() != expectedEncryptedKey { -// t.Errorf("GetEncryptedAccessKey didn't give back the expected value!") -// } -// } - -// func TestGetEncryptedAccessKey_Error(t *testing.T) { -// al := setupAccessLogic() - -// lookupKey := "exampleLookupKey" -// id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - -// act, _, _ := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") -// empty_act_result, _ := al.getEncryptedAccessKey(*act, lookupKey) -// if empty_act_result != nil { -// t.Errorf("GetEncryptedAccessKey should give back nil for empty act root hash!") -// } - -// empty_lookup_result, _ := al.getEncryptedAccessKey(*act, "") - -// if empty_lookup_result != nil { -// t.Errorf("GetEncryptedAccessKey should give back nil for empty lookup key!") -// } -// } - -// func TestGet_Success(t *testing.T) { -// al := setupAccessLogic() - -// id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) -// act, encryptedRef, _ := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") -// tag := "exampleTag" - -// ref, err := al.Get(act, encryptedRef, id0.PublicKey, tag) -// if err != nil { -// t.Errorf("There was an error while calling Get") -// } - -// expectedRef := "bzzNotEncrypted128long" -// if ref != expectedRef { -// t.Errorf("Get gave back wrong Swarm reference!") -// } -// } - -// func TestGet_Error(t *testing.T) { -// al := setupAccessLogic() - -// //actRootHash := "0xabcexample" -// id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) -// act, encrypredRef, err := al.ActInit(swarm.NewAddress([]byte("42")), id0.PublicKey, "") -// if err != nil { -// t.Errorf("Error initializing Act") -// t.Errorf(err.Error()) -// } -// //encryptedRef := "bzzabcasab" -// tag := "exampleTag" - -// refOne, err := al.Get(act, encrypredRef, id0.PublicKey, tag) -// if err != nil { -// t.Errorf(err.Error()) -// } -// if refOne != "" { -// t.Errorf("Get should give back empty string if ACT root hash not provided!") -// } - -// refTwo, _ := al.Get(act, swarm.EmptyAddress, id0.PublicKey, tag) -// if refTwo != "" { -// t.Errorf("Get should give back empty string if encrypted ref not provided!") -// } - -// refThree, _ := al.Get(act, encrypredRef, ecdsa.PublicKey{}, tag) -// if refThree != "" { -// t.Errorf("Get should give back empty string if publisher not provided!") -// } - -// refFour, _ := al.Get(act, encrypredRef, id0.PublicKey, "") -// if refFour != "" { -// t.Errorf("Get should give back empty string if tag was not provided!") -// } -// } - -// func TestNewAccessLogic(t *testing.T) { -// logic := setupAccessLogic() - -// _, ok := logic.(*DefaultAccessLogic) -// if !ok { -// t.Errorf("NewAccessLogic: expected type *DefaultAccessLogic, got %T", logic) -// } -// } - -// // func TestAddGrantee(t *testing.T) { -// // al := setupAccessLogic() -// // // ref := "example_ref" -// // id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) -// // testGranteeList := NewGrantee() - -// // // Add grantee keys to the testGranteeList -// // id1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) -// // id2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) -// // id3, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) -// // testGranteeList.AddGrantees([]ecdsa.PublicKey{id1.PublicKey, id2.PublicKey, id3.PublicKey}) - -// // // Initialize empty ACT -// // actMock := MockAct.NewActMock() -// // actMockRootHash := "exampleRootHash" - -// // // Add each grantee to content using ActMock and validate the resulting ACT -// // for i := 0; i < len(testGranteeList.GetGrantees()); i++ { -// // lookupKey, _ := al.getLookUpKey(testGranteeList.GetGrantees()[i], "") -// // encryptedAccessKey := "exampleEncryptedAccessKey" -// // _, err := actMock.Add(actMockRootHash, []byte(lookupKey), []byte(encryptedAccessKey)) -// // if err != nil { -// // t.Fatalf("Failed to add grantee to content using ActMock: %v", err) -// // } - -// // // Validate the resulting ACT -// // encryptedAccessKeyFromMock, err := actMock.Get(actMockRootHash, []byte(lookupKey)) -// // if err != nil { -// // t.Fatalf("Failed to retrieve encrypted access key from ActMock: %v", err) -// // } -// // encryptedAccessKeyFromMockBytes, _ := hex.DecodeString(encryptedAccessKeyFromMock) -// // if string(encryptedAccessKeyFromMockBytes) != encryptedAccessKey { -// // t.Errorf("Encrypted access key retrieved from ActMock doesn't match expected value") -// // } -// // } - -// // al.Add_New_Grantee_To_Content(actMock, encryptedRef, id0.PublicKey, testGranteeList.GetGrantees()[i]) -// // } +import ( + "crypto/ecdsa" + "crypto/elliptic" + "encoding/hex" + "math/big" + "testing" + + "github.com/ethersphere/bee/pkg/dynamicaccess" + "github.com/ethersphere/bee/pkg/swarm" +) + +// Generates a new test environment with a fix private key +func setupAccessLogic2() dynamicaccess.ActLogic { + privateKey := generateFixPrivateKey(1000) + diffieHellman := dynamicaccess.NewDefaultSession(&privateKey) + al := dynamicaccess.NewLogic(diffieHellman) + + return al +} + +// Generates a fixed identity with private and public key. The private key is generated from the input +func generateFixPrivateKey(input int64) ecdsa.PrivateKey { + fixedD := big.NewInt(input) + curve := elliptic.P256() + x, y := curve.ScalarBaseMult(fixedD.Bytes()) + + privateKey := ecdsa.PrivateKey{ + PublicKey: ecdsa.PublicKey{ + Curve: curve, + X: x, + Y: y, + }, + D: fixedD, + } + + return privateKey +} + +func TestGet_Success(t *testing.T) { + al := setupAccessLogic2() + id0 := generateFixPrivateKey(0) + + act := dynamicaccess.NewInMemoryAct() + act, err := al.AddPublisher(act, &id0.PublicKey) + if err != nil { + t.Errorf("AddPublisher: expected no error, got %v", err) + } + + byteRef, _ := hex.DecodeString("39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559") + + expectedRef := swarm.NewAddress(byteRef) + t.Logf("encryptedRef: %s", expectedRef.String()) + + encryptedRef, err := al.EncryptRef(act, &id0.PublicKey, expectedRef) + t.Logf("encryptedRef: %s", encryptedRef.String()) + if err != nil { + t.Errorf("There was an error while calling EncryptRef: ") + t.Error(err) + } + + ref, err := al.Get(act, encryptedRef, &id0.PublicKey) + if err != nil { + t.Errorf("There was an error while calling Get: ") + t.Error(err) + } + + if expectedRef.Compare(ref) != 0 { + + t.Errorf("Get gave back wrong Swarm reference!") + } +} + +// This test function tests those cases where different parameters are missing +func TestGet_Error(t *testing.T) { + al := setupAccessLogic2() + id0 := generateFixPrivateKey(0) + + act := dynamicaccess.NewInMemoryAct() + act, err := al.AddPublisher(act, &id0.PublicKey) + if err != nil { + t.Errorf("AddPublisher: expected no error, got %v", err) + } + + expectedRef := "39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559" + + encryptedRef, _ := al.EncryptRef(act, &id0.PublicKey, swarm.NewAddress([]byte(expectedRef))) + + _, err = al.Get(dynamicaccess.NewInMemoryAct(), encryptedRef, &id0.PublicKey) + if err == nil { + t.Errorf("Get should give back encrypted access key not found error!") + } + + refTwo, _ := al.Get(act, swarm.EmptyAddress, &id0.PublicKey) + if swarm.EmptyAddress.Compare(refTwo) != 0 { + t.Errorf("Get should give back empty string if encrypted ref not provided!") + } + + _, err = al.Get(act, encryptedRef, nil) + if err == nil { + t.Errorf("Get should give back error if grantee not provided!") + } +} + +func TestAddPublisher(t *testing.T) { + al := setupAccessLogic2() + id0 := generateFixPrivateKey(0) + savedLookupKey := "bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a" + act := dynamicaccess.NewInMemoryAct() + act, err := al.AddPublisher(act, &id0.PublicKey) + if err != nil { + t.Errorf("AddPublisher: expected no error, got %v", err) + } + + decodedSavedLookupKey, err := hex.DecodeString(savedLookupKey) + if err != nil { + t.Errorf("AddPublisher: expected no error, got %v", err) + } + + encryptedAccessKey, _ := act.Lookup(decodedSavedLookupKey) + decodedEncryptedAccessKey := hex.EncodeToString(encryptedAccessKey) + + // A random value is returned so it is only possibly to check the length of the returned value + // We know the lookup key because the generated private key is fixed + if len(decodedEncryptedAccessKey) != 64 { + t.Errorf("AddPublisher: expected encrypted access key length 64, got %d", len(decodedEncryptedAccessKey)) + } + if act == nil { + t.Errorf("AddPublisher: expected act, got nil") + } +} + +func TestAdd_New_Grantee_To_Content(t *testing.T) { + al := setupAccessLogic2() + + id0 := generateFixPrivateKey(0) + id1 := generateFixPrivateKey(1) + id2 := generateFixPrivateKey(2) + + publisherLookupKey := "bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a" + firstAddedGranteeLookupKey := "e221a2abf64357260e8f2c937ee938aed98dce097e537c1a3fd4caf73510dbe4" + secondAddedGranteeLookupKey := "8fe8dff7cd15a6a0095c1b25071a5691e7c901fd0b95857a96c0e4659b48716a" + + act := dynamicaccess.NewInMemoryAct() + act, err := al.AddPublisher(act, &id0.PublicKey) + if err != nil { + t.Errorf("AddNewGrantee: expected no error, got %v", err) + } + + act, err = al.AddNewGranteeToContent(act, &id0.PublicKey, &id1.PublicKey) + if err != nil { + t.Errorf("AddNewGrantee: expected no error, got %v", err) + } + + act, err = al.AddNewGranteeToContent(act, &id0.PublicKey, &id2.PublicKey) + if err != nil { + t.Errorf("AddNewGrantee: expected no error, got %v", err) + } + + lookupKeyAsByte, err := hex.DecodeString(publisherLookupKey) + if err != nil { + t.Errorf("AddNewGrantee: expected no error, got %v", err) + } + result, _ := act.Lookup(lookupKeyAsByte) + hexEncodedEncryptedAK := hex.EncodeToString(result) + if len(hexEncodedEncryptedAK) != 64 { + t.Errorf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) + } + + lookupKeyAsByte, err = hex.DecodeString(firstAddedGranteeLookupKey) + if err != nil { + t.Errorf("AddNewGrantee: expected no error, got %v", err) + } + result, _ = act.Lookup(lookupKeyAsByte) + hexEncodedEncryptedAK = hex.EncodeToString(result) + if len(hexEncodedEncryptedAK) != 64 { + t.Errorf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) + } + + lookupKeyAsByte, err = hex.DecodeString(secondAddedGranteeLookupKey) + if err != nil { + t.Errorf("AddNewGrantee: expected no error, got %v", err) + } + result, _ = act.Lookup(lookupKeyAsByte) + hexEncodedEncryptedAK = hex.EncodeToString(result) + if len(hexEncodedEncryptedAK) != 64 { + t.Errorf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) + } +} + +func TestEncryptRef(t *testing.T) { + ref := "39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559" + savedEncryptedRef := "230cdcfb2e67adddb2822b38f70105213ab3e4f97d03560bfbfbb218f487c5303e9aa9a97e62aa1a8003f162679e7c65e1c8e3aacaec2043fd5d2a4a7d69285e" + + al := setupAccessLogic2() + id0 := generateFixPrivateKey(0) + act := dynamicaccess.NewInMemoryAct() + decodedLookupKey, err := hex.DecodeString("bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a") + if err != nil { + t.Errorf("EncryptRef: expected no error, got %v", err) + } + + act.Add(decodedLookupKey, []byte("42")) + + encryptedRefValue, err := al.EncryptRef(act, &id0.PublicKey, swarm.NewAddress([]byte(ref))) + if err != nil { + t.Errorf("EncryptRef: expected no error, got %v", err) + } + + if encryptedRefValue.String() != savedEncryptedRef { + t.Errorf("EncryptRef: expected encrypted ref, got empty address") + } +} \ No newline at end of file diff --git a/pkg/dynamicaccess/act.go b/pkg/dynamicaccess/act.go index 1153f2fc868..1f1dbb85897 100644 --- a/pkg/dynamicaccess/act.go +++ b/pkg/dynamicaccess/act.go @@ -78,7 +78,7 @@ func (act *inMemoryAct) Lookup(key []byte) ([]byte, error) { } return bytes, nil } - return make([]byte, 0), nil + return nil, fmt.Errorf("key not found") } func (act *inMemoryAct) Load(addr swarm.Address) error { diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index 6267a878279..92363014c9b 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -14,7 +14,7 @@ type Controller interface { type defaultController struct { history History granteeManager GranteeManager - accessLogic AccessLogic + accessLogic ActLogic } func (c *defaultController) DownloadHandler(timestamp int64, enryptedRef swarm.Address, publisher *ecdsa.PublicKey, tag string) (swarm.Address, error) { @@ -22,7 +22,7 @@ func (c *defaultController) DownloadHandler(timestamp int64, enryptedRef swarm.A if err != nil { return swarm.EmptyAddress, err } - addr, err := c.accessLogic.Get(act, enryptedRef, *publisher, tag) + addr, err := c.accessLogic.Get(act, enryptedRef, publisher) return addr, err } @@ -31,13 +31,13 @@ func (c *defaultController) UploadHandler(ref swarm.Address, publisher *ecdsa.Pu if act == nil { // new feed act = NewInMemoryAct() - act = c.granteeManager.Publish(act, *publisher, topic) + act = c.granteeManager.Publish(act, publisher, topic) } //FIXME: check if ACT is consistent with the grantee list - return c.accessLogic.EncryptRef(act, *publisher, ref) + return c.accessLogic.EncryptRef(act, publisher, ref) } -func NewController(history History, granteeManager GranteeManager, accessLogic AccessLogic) Controller { +func NewController(history History, granteeManager GranteeManager, accessLogic ActLogic) Controller { return &defaultController{ history: history, granteeManager: granteeManager, diff --git a/pkg/dynamicaccess/grantee.go b/pkg/dynamicaccess/grantee.go index 2a07d84b3ed..dfd7015e49f 100644 --- a/pkg/dynamicaccess/grantee.go +++ b/pkg/dynamicaccess/grantee.go @@ -5,37 +5,22 @@ import ( ) type Grantee interface { - //? ÁTBESZÉLNI - // Revoke(topic string) error - // Publish(topic string) error - - // RevokeList(topic string, removeList []string, addList []string) (string, error) - // RevokeGrantees(topic string, removeList []string) (string, error) AddGrantees(topic string, addList []*ecdsa.PublicKey) error RemoveGrantees(topic string, removeList []*ecdsa.PublicKey) error GetGrantees(topic string) []*ecdsa.PublicKey } type defaultGrantee struct { - grantees map[string][]*ecdsa.PublicKey // Modified field name to start with an uppercase letter + grantees map[string][]*ecdsa.PublicKey } func (g *defaultGrantee) GetGrantees(topic string) []*ecdsa.PublicKey { - return g.grantees[topic] + grantees := g.grantees[topic] + keys := make([]*ecdsa.PublicKey, len(grantees)) + copy(keys, grantees) + return keys } -// func (g *defaultGrantee) Revoke(topic string) error { -// return nil -// } - -// func (g *defaultGrantee) RevokeList(topic string, removeList []string, addList []string) (string, error) { -// return "", nil -// } - -// func (g *defaultGrantee) Publish(topic string) error { -// return nil -// } - func (g *defaultGrantee) AddGrantees(topic string, addList []*ecdsa.PublicKey) error { g.grantees[topic] = append(g.grantees[topic], addList...) return nil @@ -44,14 +29,17 @@ func (g *defaultGrantee) AddGrantees(topic string, addList []*ecdsa.PublicKey) e func (g *defaultGrantee) RemoveGrantees(topic string, removeList []*ecdsa.PublicKey) error { for _, remove := range removeList { for i, grantee := range g.grantees[topic] { - if grantee == remove { - g.grantees[topic] = append(g.grantees[topic][:i], g.grantees[topic][i+1:]...) + if *grantee == *remove { + g.grantees[topic][i] = g.grantees[topic][len(g.grantees[topic])-1] + g.grantees[topic] = g.grantees[topic][:len(g.grantees[topic])-1] } } } + + return nil } func NewGrantee() Grantee { return &defaultGrantee{grantees: make(map[string][]*ecdsa.PublicKey)} -} +} \ No newline at end of file diff --git a/pkg/dynamicaccess/grantee_manager.go b/pkg/dynamicaccess/grantee_manager.go index 9172a05db03..6b287af6941 100644 --- a/pkg/dynamicaccess/grantee_manager.go +++ b/pkg/dynamicaccess/grantee_manager.go @@ -5,7 +5,7 @@ import "crypto/ecdsa" type GranteeManager interface { Get(topic string) []*ecdsa.PublicKey Add(topic string, addList []*ecdsa.PublicKey) error - Publish(act Act, publisher ecdsa.PublicKey, topic string) Act + Publish(act Act, publisher *ecdsa.PublicKey, topic string) Act // HandleGrantees(topic string, addList, removeList []*ecdsa.PublicKey) *Act @@ -16,11 +16,11 @@ type GranteeManager interface { var _ GranteeManager = (*granteeManager)(nil) type granteeManager struct { - accessLogic AccessLogic + accessLogic ActLogic granteeList Grantee } -func NewGranteeManager(al AccessLogic) *granteeManager { +func NewGranteeManager(al ActLogic) *granteeManager { return &granteeManager{accessLogic: al, granteeList: NewGrantee()} } @@ -32,10 +32,10 @@ func (gm *granteeManager) Add(topic string, addList []*ecdsa.PublicKey) error { return gm.granteeList.AddGrantees(topic, addList) } -func (gm *granteeManager) Publish(act Act, publisher ecdsa.PublicKey, topic string) Act { - gm.accessLogic.AddPublisher(act, publisher, "") +func (gm *granteeManager) Publish(act Act, publisher *ecdsa.PublicKey, topic string) Act { + gm.accessLogic.AddPublisher(act, publisher) for _, grantee := range gm.granteeList.GetGrantees(topic) { - gm.accessLogic.Add_New_Grantee_To_Content(act, publisher, *grantee) + gm.accessLogic.AddNewGranteeToContent(act, publisher, grantee) } return act } diff --git a/pkg/dynamicaccess/grantee_manager_test.go b/pkg/dynamicaccess/grantee_manager_test.go index bc90ef504ae..fc5969f4492 100644 --- a/pkg/dynamicaccess/grantee_manager_test.go +++ b/pkg/dynamicaccess/grantee_manager_test.go @@ -10,13 +10,13 @@ import ( "github.com/ethersphere/bee/pkg/dynamicaccess" ) -func setupAccessLogic(privateKey *ecdsa.PrivateKey) dynamicaccess.AccessLogic { +func setupAccessLogic(privateKey *ecdsa.PrivateKey) dynamicaccess.ActLogic { // privateKey, err := crypto.GenerateSecp256k1Key() // if err != nil { // errors.New("error creating private key") // } si := dynamicaccess.NewDefaultSession(privateKey) - al := dynamicaccess.NewAccessLogic(si) + al := dynamicaccess.NewLogic(si) return al } @@ -36,6 +36,6 @@ func TestAdd(t *testing.T) { if err != nil { t.Errorf("Add() returned an error") } - m.Publish(act, pub.PublicKey, "topic") + m.Publish(act, &pub.PublicKey, "topic") fmt.Println("") } diff --git a/pkg/dynamicaccess/grantee_test.go b/pkg/dynamicaccess/grantee_test.go index 2d795ab2eef..7418a741d77 100644 --- a/pkg/dynamicaccess/grantee_test.go +++ b/pkg/dynamicaccess/grantee_test.go @@ -10,96 +10,93 @@ import ( "github.com/ethersphere/bee/pkg/dynamicaccess" ) -// func TestGranteeRevoke(t *testing.T) { -// err := NewGrantee().Revoke("") -// if err != nil { -// t.Errorf("Error revoking grantee: %v", err) -// } -// } - -/*func TestGranteeRevokeList(t *testing.T) { - _, err := NewGrantee().RevokeList("", nil, nil) - if err != nil { - t.Errorf("Error revoking list of grantees: %v", err) - } -}*/ - -// func TestGranteePublish(t *testing.T) { -// err := NewGrantee().Publish("") -// if err != nil { -// t.Errorf("Error publishing grantee: %v", err) -// } -// } - func TestGranteeAddGrantees(t *testing.T) { - // Create a new Grantee grantee := dynamicaccess.NewGrantee() - // Generate some dummy ecdsa.PublicKey values - key1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - key2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + key1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Errorf("Expected no error, got %v", err) + } + + key2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Errorf("Expected no error, got %v", err) + } - // Add the keys to the grantee addList := []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey} - err := grantee.AddGrantees("topicName", addList) - grantees := grantee.GetGrantees("topicName") - // Check for errors + exampleTopic := "topic" + err = grantee.AddGrantees(exampleTopic, addList) + if err != nil { - t.Fatalf("Expected no error, got %v", err) + t.Errorf("Expected no error, got %v", err) } - // Check if the keys were added correctly + grantees := grantee.GetGrantees(exampleTopic) if !reflect.DeepEqual(grantees, addList) { t.Errorf("Expected grantees %v, got %v", addList, grantees) } } func TestRemoveGrantees(t *testing.T) { - // Create a new Grantee grantee := dynamicaccess.NewGrantee() - // Generate some dummy ecdsa.PublicKey values - key1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - key2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + key1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Errorf("Expected no error, got %v", err) + } + + key2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Errorf("Expected no error, got %v", err) + } - // Add the keys to the grantee addList := []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey} - grantee.AddGrantees("topicName", addList) + exampleTopic := "topic" + err = grantee.AddGrantees(exampleTopic, addList) + if err != nil { + t.Errorf("Expected no error, got %v", err) + } - // Remove one of the keys removeList := []*ecdsa.PublicKey{&key1.PublicKey} - err := grantee.RemoveGrantees("topicName", removeList) - grantees := grantee.GetGrantees("topicName") - - // Check for errors + err = grantee.RemoveGrantees(exampleTopic, removeList) if err != nil { - t.Fatalf("Expected no error, got %v", err) + t.Errorf("Expected no error, got %v", err) } - // Check if the key was removed correctly + grantees := grantee.GetGrantees(exampleTopic) expectedGrantees := []*ecdsa.PublicKey{&key2.PublicKey} - if !reflect.DeepEqual(grantees, expectedGrantees) { - t.Errorf("Expected grantees %v, got %v", expectedGrantees, grantees) + + for i, grantee := range grantees { + if grantee != expectedGrantees[i] { + t.Errorf("Expected grantee %v, got %v", expectedGrantees[i], grantee) + } } } func TestGetGrantees(t *testing.T) { - // Create a new Grantee grantee := dynamicaccess.NewGrantee() - // Generate some dummy ecdsa.PublicKey values - key1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - key2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + key1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Errorf("Expected no error, got %v", err) + } - // Add the keys to the grantee - addList := []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey} - grantee.AddGrantees("topicName", addList) + key2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Errorf("Expected no error, got %v", err) + } - // Get the grantees - grantees := grantee.GetGrantees("topicName") + addList := []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey} + exampleTopic := "topic" + err = grantee.AddGrantees(exampleTopic, addList) + if err != nil { + t.Errorf("Expected no error, got %v", err) + } - // Check if the grantees were returned correctly - if !reflect.DeepEqual(grantees, addList) { - t.Errorf("Expected grantees %v, got %v", addList, grantees) + grantees := grantee.GetGrantees(exampleTopic) + for i, grantee := range grantees { + if grantee != addList[i] { + t.Errorf("Expected grantee %v, got %v", addList[i], grantee) + } } -} +} \ No newline at end of file From 56430a52d17328cd73e19d631b57acccc08acf14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20S=C3=A1rai?= Date: Fri, 22 Mar 2024 13:09:37 +0100 Subject: [PATCH 21/41] Act kvs merge (#22) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * grantee container and access logc tests are passed * refactored access logic and grantee container * PR 19 comments resolving * Refactor * Refactor * working manifest ACT with basic tests * (refactor:) Refactor act_test * (refactor:) Refactor kvs -> kvs.manifest, kvs.memory * (refactror:) kvs * refactor kvs contsructors --------- Co-authored-by: Roland Seres Co-authored-by: Bálint Ujvári Co-authored-by: Ferenc Sárai --- pkg/dynamicaccess/accesslogic.go | 50 +++++----- pkg/dynamicaccess/accesslogic_test.go | 72 +++++++------- pkg/dynamicaccess/act.go | 110 +++++++--------------- pkg/dynamicaccess/act_test.go | 72 ++++++++++++-- pkg/dynamicaccess/container.go | 7 -- pkg/dynamicaccess/controller.go | 18 ++-- pkg/dynamicaccess/controller_test.go | 2 +- pkg/dynamicaccess/grantee_manager.go | 16 ++-- pkg/dynamicaccess/grantee_manager_test.go | 11 +-- pkg/dynamicaccess/history_test.go | 9 +- pkg/dynamicaccess/mock/act.go | 18 ++-- pkg/kvs/kvs.go | 34 +++++++ pkg/kvs/manifest/kvs.go | 86 +++++++++++++++++ pkg/kvs/memory/kvs.go | 67 +++++++++++++ 14 files changed, 388 insertions(+), 184 deletions(-) delete mode 100644 pkg/dynamicaccess/container.go create mode 100644 pkg/kvs/kvs.go create mode 100644 pkg/kvs/manifest/kvs.go create mode 100644 pkg/kvs/memory/kvs.go diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index 417a4a40878..4b5abcd739e 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -14,24 +14,25 @@ var hashFunc = sha3.NewLegacyKeccak256 // Logic has the responsibility to return a ref for a given grantee and create new encrypted reference for a grantee type Logic interface { // Adds a new grantee to the ACT - AddNewGranteeToContent(act Act, publisherPubKey, granteePubKey *ecdsa.PublicKey) (Act, error) - // Get will return a decrypted reference, for given encrypted reference and grantee !!!!!!!!!!!!!!!!!!!!! - Get(act Act, encryped_ref swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) + AddNewGranteeToContent(rootHash swarm.Address, publisherPubKey, granteePubKey *ecdsa.PublicKey) (swarm.Address, error) + // Get will return a decrypted reference, for given encrypted reference and grantee + Get(rootHash swarm.Address, encryped_ref swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) } type ActLogic struct { session Session + act Act } var _ Logic = (*ActLogic)(nil) // Adds a new publisher to an empty act -func (al ActLogic) AddPublisher(act Act, publisher *ecdsa.PublicKey) (Act, error) { +func (al ActLogic) AddPublisher(rootHash swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) { accessKey := encryption.GenerateRandomKey(encryption.KeyLength) keys, err := al.getKeys(publisher) if err != nil { - return nil, err + return swarm.EmptyAddress, err } lookupKey := keys[0] accessKeyEncryptionKey := keys[1] @@ -39,17 +40,15 @@ func (al ActLogic) AddPublisher(act Act, publisher *ecdsa.PublicKey) (Act, error accessKeyCipher := encryption.New(encryption.Key(accessKeyEncryptionKey), 0, uint32(0), hashFunc) encryptedAccessKey, err := accessKeyCipher.Encrypt([]byte(accessKey)) if err != nil { - return nil, err + return swarm.EmptyAddress, err } - act.Add(lookupKey, encryptedAccessKey) - - return act, nil + return al.act.Add(rootHash, lookupKey, encryptedAccessKey) } // Encrypts a SWARM reference for a publisher -func (al ActLogic) EncryptRef(act Act, publisherPubKey *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) { - accessKey := al.getAccessKey(act, publisherPubKey) +func (al ActLogic) EncryptRef(rootHash swarm.Address, publisherPubKey *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) { + accessKey := al.getAccessKey(rootHash, publisherPubKey) refCipher := encryption.New(accessKey, 0, uint32(0), hashFunc) encryptedRef, _ := refCipher.Encrypt(ref.Bytes()) @@ -57,14 +56,14 @@ func (al ActLogic) EncryptRef(act Act, publisherPubKey *ecdsa.PublicKey, ref swa } // Adds a new grantee to the ACT -func (al ActLogic) AddNewGranteeToContent(act Act, publisherPubKey, granteePubKey *ecdsa.PublicKey) (Act, error) { +func (al ActLogic) AddNewGranteeToContent(rootHash swarm.Address, publisherPubKey, granteePubKey *ecdsa.PublicKey) (swarm.Address, error) { // Get previously generated access key - accessKey := al.getAccessKey(act, publisherPubKey) + accessKey := al.getAccessKey(rootHash, publisherPubKey) // Encrypt the access key for the new Grantee keys, err := al.getKeys(granteePubKey) if err != nil { - return nil, err + return swarm.EmptyAddress, err } lookupKey := keys[0] accessKeyEncryptionKey := keys[1] @@ -73,18 +72,16 @@ func (al ActLogic) AddNewGranteeToContent(act Act, publisherPubKey, granteePubKe cipher := encryption.New(encryption.Key(accessKeyEncryptionKey), 0, uint32(0), hashFunc) granteeEncryptedAccessKey, err := cipher.Encrypt(accessKey) if err != nil { - return nil, err + return swarm.EmptyAddress, err } // Add the new encrypted access key for the Act - act.Add(lookupKey, granteeEncryptedAccessKey) - - return act, nil + return al.act.Add(rootHash, lookupKey, granteeEncryptedAccessKey) } // Will return the access key for a publisher (public key) -func (al *ActLogic) getAccessKey(act Act, publisherPubKey *ecdsa.PublicKey) []byte { +func (al *ActLogic) getAccessKey(rootHash swarm.Address, publisherPubKey *ecdsa.PublicKey) []byte { keys, err := al.getKeys(publisherPubKey) if err != nil { return nil @@ -93,7 +90,7 @@ func (al *ActLogic) getAccessKey(act Act, publisherPubKey *ecdsa.PublicKey) []by publisherAKDecryptionKey := keys[1] accessKeyDecryptionCipher := encryption.New(encryption.Key(publisherAKDecryptionKey), 0, uint32(0), hashFunc) - encryptedAK, err := al.getEncryptedAccessKey(act, publisherLookupKey) + encryptedAK, err := al.getEncryptedAccessKey(rootHash, publisherLookupKey) if err != nil { return nil } @@ -119,8 +116,8 @@ func (al *ActLogic) getKeys(publicKey *ecdsa.PublicKey) ([][]byte, error) { } // Gets the encrypted access key for a given grantee -func (al *ActLogic) getEncryptedAccessKey(act Act, lookup_key []byte) ([]byte, error) { - val, err := act.Lookup(lookup_key) +func (al *ActLogic) getEncryptedAccessKey(rootHash swarm.Address, lookup_key []byte) ([]byte, error) { + val, err := al.act.Lookup(rootHash, lookup_key) if err != nil { return nil, err } @@ -128,7 +125,7 @@ func (al *ActLogic) getEncryptedAccessKey(act Act, lookup_key []byte) ([]byte, e } // Get will return a decrypted reference, for given encrypted reference and grantee -func (al ActLogic) Get(act Act, encryped_ref swarm.Address, grantee *ecdsa.PublicKey) (swarm.Address, error) { +func (al ActLogic) Get(rootHash swarm.Address, encryped_ref swarm.Address, grantee *ecdsa.PublicKey) (swarm.Address, error) { if encryped_ref.Compare(swarm.EmptyAddress) == 0 { return swarm.EmptyAddress, fmt.Errorf("encrypted ref not provided") } @@ -144,7 +141,7 @@ func (al ActLogic) Get(act Act, encryped_ref swarm.Address, grantee *ecdsa.Publi accessKeyDecryptionKey := keys[1] // Lookup encrypted access key from the ACT manifest - encryptedAccessKey, err := al.getEncryptedAccessKey(act, lookupKey) + encryptedAccessKey, err := al.getEncryptedAccessKey(rootHash, lookupKey) if err != nil { return swarm.EmptyAddress, err } @@ -166,8 +163,9 @@ func (al ActLogic) Get(act Act, encryped_ref swarm.Address, grantee *ecdsa.Publi return swarm.NewAddress(ref), nil } -func NewLogic(s Session) ActLogic { +func NewLogic(s Session, act Act) ActLogic { return ActLogic{ session: s, + act: act, } -} \ No newline at end of file +} diff --git a/pkg/dynamicaccess/accesslogic_test.go b/pkg/dynamicaccess/accesslogic_test.go index 46a4cc39ec6..b87bac84ffd 100644 --- a/pkg/dynamicaccess/accesslogic_test.go +++ b/pkg/dynamicaccess/accesslogic_test.go @@ -8,14 +8,15 @@ import ( "testing" "github.com/ethersphere/bee/pkg/dynamicaccess" + mockstorer "github.com/ethersphere/bee/pkg/storer/mock" "github.com/ethersphere/bee/pkg/swarm" ) // Generates a new test environment with a fix private key -func setupAccessLogic2() dynamicaccess.ActLogic { +func setupAccessLogic2(act dynamicaccess.Act) dynamicaccess.ActLogic { privateKey := generateFixPrivateKey(1000) diffieHellman := dynamicaccess.NewDefaultSession(&privateKey) - al := dynamicaccess.NewLogic(diffieHellman) + al := dynamicaccess.NewLogic(diffieHellman, act) return al } @@ -39,11 +40,11 @@ func generateFixPrivateKey(input int64) ecdsa.PrivateKey { } func TestGet_Success(t *testing.T) { - al := setupAccessLogic2() id0 := generateFixPrivateKey(0) - - act := dynamicaccess.NewInMemoryAct() - act, err := al.AddPublisher(act, &id0.PublicKey) + var mockStorer = mockstorer.New() + act := dynamicaccess.NewInManifestAct(mockStorer) + al := setupAccessLogic2(act) + ref, err := al.AddPublisher(swarm.EmptyAddress, &id0.PublicKey) if err != nil { t.Errorf("AddPublisher: expected no error, got %v", err) } @@ -53,20 +54,20 @@ func TestGet_Success(t *testing.T) { expectedRef := swarm.NewAddress(byteRef) t.Logf("encryptedRef: %s", expectedRef.String()) - encryptedRef, err := al.EncryptRef(act, &id0.PublicKey, expectedRef) + encryptedRef, err := al.EncryptRef(ref, &id0.PublicKey, expectedRef) t.Logf("encryptedRef: %s", encryptedRef.String()) if err != nil { t.Errorf("There was an error while calling EncryptRef: ") t.Error(err) } - ref, err := al.Get(act, encryptedRef, &id0.PublicKey) + acutalRef, err := al.Get(ref, encryptedRef, &id0.PublicKey) if err != nil { t.Errorf("There was an error while calling Get: ") t.Error(err) } - if expectedRef.Compare(ref) != 0 { + if expectedRef.Compare(acutalRef) != 0 { t.Errorf("Get gave back wrong Swarm reference!") } @@ -74,51 +75,55 @@ func TestGet_Success(t *testing.T) { // This test function tests those cases where different parameters are missing func TestGet_Error(t *testing.T) { - al := setupAccessLogic2() id0 := generateFixPrivateKey(0) act := dynamicaccess.NewInMemoryAct() - act, err := al.AddPublisher(act, &id0.PublicKey) + al := setupAccessLogic2(act) + ref, err := al.AddPublisher(swarm.EmptyAddress, &id0.PublicKey) if err != nil { t.Errorf("AddPublisher: expected no error, got %v", err) } expectedRef := "39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559" - encryptedRef, _ := al.EncryptRef(act, &id0.PublicKey, swarm.NewAddress([]byte(expectedRef))) + encryptedRef, _ := al.EncryptRef(ref, &id0.PublicKey, swarm.NewAddress([]byte(expectedRef))) - _, err = al.Get(dynamicaccess.NewInMemoryAct(), encryptedRef, &id0.PublicKey) + r, err := al.Get(swarm.RandAddress(t), encryptedRef, &id0.PublicKey) if err == nil { + t.Logf("r: %s", r.String()) t.Errorf("Get should give back encrypted access key not found error!") } - refTwo, _ := al.Get(act, swarm.EmptyAddress, &id0.PublicKey) + refTwo, _ := al.Get(swarm.RandAddress(t), swarm.EmptyAddress, &id0.PublicKey) if swarm.EmptyAddress.Compare(refTwo) != 0 { t.Errorf("Get should give back empty string if encrypted ref not provided!") } - _, err = al.Get(act, encryptedRef, nil) + _, err = al.Get(swarm.RandAddress(t), encryptedRef, nil) if err == nil { t.Errorf("Get should give back error if grantee not provided!") } } func TestAddPublisher(t *testing.T) { - al := setupAccessLogic2() id0 := generateFixPrivateKey(0) savedLookupKey := "bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a" act := dynamicaccess.NewInMemoryAct() - act, err := al.AddPublisher(act, &id0.PublicKey) + al := setupAccessLogic2(act) + ref, err := al.AddPublisher(swarm.EmptyAddress, &id0.PublicKey) if err != nil { t.Errorf("AddPublisher: expected no error, got %v", err) } decodedSavedLookupKey, err := hex.DecodeString(savedLookupKey) if err != nil { - t.Errorf("AddPublisher: expected no error, got %v", err) + t.Errorf("DecodeString: expected no error, got %v", err) } - encryptedAccessKey, _ := act.Lookup(decodedSavedLookupKey) + encryptedAccessKey, err := act.Lookup(ref, decodedSavedLookupKey) + if err != nil { + t.Errorf("Lookup: expected no error, got %v", err) + } decodedEncryptedAccessKey := hex.EncodeToString(encryptedAccessKey) // A random value is returned so it is only possibly to check the length of the returned value @@ -132,7 +137,6 @@ func TestAddPublisher(t *testing.T) { } func TestAdd_New_Grantee_To_Content(t *testing.T) { - al := setupAccessLogic2() id0 := generateFixPrivateKey(0) id1 := generateFixPrivateKey(1) @@ -143,17 +147,18 @@ func TestAdd_New_Grantee_To_Content(t *testing.T) { secondAddedGranteeLookupKey := "8fe8dff7cd15a6a0095c1b25071a5691e7c901fd0b95857a96c0e4659b48716a" act := dynamicaccess.NewInMemoryAct() - act, err := al.AddPublisher(act, &id0.PublicKey) - if err != nil { + al := setupAccessLogic2(act) + ref, err := al.AddPublisher(swarm.EmptyAddress, &id0.PublicKey) + if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } - act, err = al.AddNewGranteeToContent(act, &id0.PublicKey, &id1.PublicKey) + ref, err = al.AddNewGranteeToContent(ref, &id0.PublicKey, &id1.PublicKey) if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } - act, err = al.AddNewGranteeToContent(act, &id0.PublicKey, &id2.PublicKey) + ref, err = al.AddNewGranteeToContent(ref, &id0.PublicKey, &id2.PublicKey) if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } @@ -162,7 +167,7 @@ func TestAdd_New_Grantee_To_Content(t *testing.T) { if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } - result, _ := act.Lookup(lookupKeyAsByte) + result, _ := act.Lookup(ref, lookupKeyAsByte) hexEncodedEncryptedAK := hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { t.Errorf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) @@ -172,7 +177,7 @@ func TestAdd_New_Grantee_To_Content(t *testing.T) { if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } - result, _ = act.Lookup(lookupKeyAsByte) + result, _ = act.Lookup(ref, lookupKeyAsByte) hexEncodedEncryptedAK = hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { t.Errorf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) @@ -182,7 +187,7 @@ func TestAdd_New_Grantee_To_Content(t *testing.T) { if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } - result, _ = act.Lookup(lookupKeyAsByte) + result, _ = act.Lookup(ref, lookupKeyAsByte) hexEncodedEncryptedAK = hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { t.Errorf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) @@ -193,17 +198,20 @@ func TestEncryptRef(t *testing.T) { ref := "39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559" savedEncryptedRef := "230cdcfb2e67adddb2822b38f70105213ab3e4f97d03560bfbfbb218f487c5303e9aa9a97e62aa1a8003f162679e7c65e1c8e3aacaec2043fd5d2a4a7d69285e" - al := setupAccessLogic2() id0 := generateFixPrivateKey(0) act := dynamicaccess.NewInMemoryAct() + al := setupAccessLogic2(act) decodedLookupKey, err := hex.DecodeString("bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a") if err != nil { t.Errorf("EncryptRef: expected no error, got %v", err) } - act.Add(decodedLookupKey, []byte("42")) - - encryptedRefValue, err := al.EncryptRef(act, &id0.PublicKey, swarm.NewAddress([]byte(ref))) + addRef, err := act.Add(swarm.EmptyAddress, decodedLookupKey, []byte("42")) + if err != nil { + t.Errorf("Add: expected no error, got %v", err) + } + + encryptedRefValue, err := al.EncryptRef(addRef, &id0.PublicKey, swarm.NewAddress([]byte(ref))) if err != nil { t.Errorf("EncryptRef: expected no error, got %v", err) } @@ -211,4 +219,4 @@ func TestEncryptRef(t *testing.T) { if encryptedRefValue.String() != savedEncryptedRef { t.Errorf("EncryptRef: expected encrypted ref, got empty address") } -} \ No newline at end of file +} diff --git a/pkg/dynamicaccess/act.go b/pkg/dynamicaccess/act.go index 1f1dbb85897..f7595b5b1ec 100644 --- a/pkg/dynamicaccess/act.go +++ b/pkg/dynamicaccess/act.go @@ -5,107 +5,61 @@ package dynamicaccess import ( - "crypto/rand" - "encoding/hex" - "fmt" - "sync" - - "github.com/ethersphere/bee/pkg/manifest" + "github.com/ethersphere/bee/pkg/api" + "github.com/ethersphere/bee/pkg/kvs" + kvsmanifest "github.com/ethersphere/bee/pkg/kvs/manifest" + kvsmemory "github.com/ethersphere/bee/pkg/kvs/memory" "github.com/ethersphere/bee/pkg/swarm" ) -var lock = &sync.Mutex{} - -type single struct { - memoryMock map[string]manifest.Entry -} - -var singleInMemorySwarm *single - -func getInMemorySwarm() *single { - if singleInMemorySwarm == nil { - lock.Lock() - defer lock.Unlock() - if singleInMemorySwarm == nil { - singleInMemorySwarm = &single{ - memoryMock: make(map[string]manifest.Entry)} - } - } - return singleInMemorySwarm -} - -func getMemory() map[string]manifest.Entry { - ch := make(chan *single) - go func() { - ch <- getInMemorySwarm() - }() - mem := <-ch - return mem.memoryMock -} - // Act represents an interface for accessing and manipulating data. type Act interface { // Add adds a key-value pair to the data store. - Add(key []byte, val []byte) error + Add(rootHash swarm.Address, key []byte, val []byte) (swarm.Address, error) // Lookup retrieves the value associated with the given key from the data store. - Lookup(key []byte) ([]byte, error) + Lookup(rootHash swarm.Address, key []byte) ([]byte, error) // Load loads the data store from the given address. - Load(addr swarm.Address) error + //Load(addr swarm.Address) error // Store stores the current state of the data store and returns the address of the ACT. - Store() (swarm.Address, error) + //Store() (swarm.Address, error) } -var _ Act = (*inMemoryAct)(nil) - -// inMemoryAct is a simple implementation of the Act interface, with in memory storage. -type inMemoryAct struct { - container map[string]string +// inKvsAct is an implementation of the Act interface that uses kvs storage. +type inKvsAct struct { + storage kvs.KeyValueStore } -func (act *inMemoryAct) Add(key []byte, val []byte) error { - act.container[hex.EncodeToString(key)] = hex.EncodeToString(val) - return nil +// Add adds a key-value pair to the in-memory data store. +func (act *inKvsAct) Add(rootHash swarm.Address, key []byte, val []byte) (swarm.Address, error) { + return act.storage.Put(rootHash, key, val) } -func (act *inMemoryAct) Lookup(key []byte) ([]byte, error) { - if key, ok := act.container[hex.EncodeToString(key)]; ok { - bytes, err := hex.DecodeString(key) - if err != nil { - return nil, err - } - return bytes, nil - } - return nil, fmt.Errorf("key not found") +// Lookup retrieves the value associated with the given key from the in-memory data store. +func (act *inKvsAct) Lookup(rootHash swarm.Address, key []byte) ([]byte, error) { + return act.storage.Get(rootHash, key) } -func (act *inMemoryAct) Load(addr swarm.Address) error { - memory := getMemory() - me := memory[addr.String()] - if me == nil { - return fmt.Errorf("ACT not found at address: %s", addr.String()) +// NewInMemoryAct creates a new instance of the Act interface with in-memory storage. +func NewInMemoryAct() Act { + s, err := kvs.NewKeyValueStore(nil, kvsmemory.KvsTypeMemory) + if err != nil { + return nil } - act.container = me.Metadata() - return nil -} - -func (act *inMemoryAct) Store() (swarm.Address, error) { - // Generate a random swarm.Address - b := make([]byte, 32) - if _, err := rand.Read(b); err != nil { - return swarm.EmptyAddress, fmt.Errorf("failed to generate random address: %w", err) + return &inKvsAct{ + storage: s, } - swarm_ref := swarm.NewAddress(b) - mem := getMemory() - mem[swarm_ref.String()] = manifest.NewEntry(swarm_ref, act.container) - - return swarm_ref, nil } -func NewInMemoryAct() Act { - return &inMemoryAct{ - container: make(map[string]string), +// NewInManifestAct creates a new instance of the Act interface with manifest storage. +func NewInManifestAct(storer api.Storer) Act { + s, err := kvs.NewKeyValueStore(storer, kvsmanifest.KvsTypeManifest) + if err != nil { + return nil + } + return &inKvsAct{ + storage: s, } } diff --git a/pkg/dynamicaccess/act_test.go b/pkg/dynamicaccess/act_test.go index 3480077102a..6522a76b9ed 100644 --- a/pkg/dynamicaccess/act_test.go +++ b/pkg/dynamicaccess/act_test.go @@ -10,24 +10,77 @@ import ( "testing" "github.com/ethersphere/bee/pkg/dynamicaccess" + mockstorer "github.com/ethersphere/bee/pkg/storer/mock" "github.com/ethersphere/bee/pkg/swarm" ) +type ActType int + +const ( + Memory ActType = iota + Manifest +) + +func (a ActType) String() string { + return [...]string{"Memory", "Manifest"}[a] +} + +var mockStorer = mockstorer.New() + +var acts = map[ActType]func() dynamicaccess.Act{ + Memory: dynamicaccess.NewInMemoryAct, + Manifest: func() dynamicaccess.Act { return dynamicaccess.NewInManifestAct(mockStorer) }, +} + func TestActAddLookup(t *testing.T) { - act := dynamicaccess.NewInMemoryAct() - lookupKey := swarm.RandAddress(t).Bytes() - encryptedAccesskey := swarm.RandAddress(t).Bytes() - err := act.Add(lookupKey, encryptedAccesskey) - if err != nil { - t.Error("Add() should not return an error") + for actType, actCreate := range acts { + t.Logf("Running test for ActType: %s", actType) + act := actCreate() + + lookupKey := swarm.RandAddress(t).Bytes() + encryptedAccesskey := swarm.RandAddress(t).Bytes() + + ref, err := act.Add(swarm.EmptyAddress, lookupKey, encryptedAccesskey) + if err != nil { + t.Errorf("Add() should not return an error: %v", err) + } + + key, err := act.Lookup(ref, lookupKey) + if err != nil { + t.Errorf("Lookup() should not return an error: %v", err) + } + + if !bytes.Equal(key, encryptedAccesskey) { + t.Errorf("Get() value is not the expected %s != %s", hex.EncodeToString(key), hex.EncodeToString(encryptedAccesskey)) + } } +} + +func TestActAddLookupWithNew(t *testing.T) { + for actType, actCreate := range acts { + t.Logf("Running test for ActType: %s", actType) + act1 := actCreate() + lookupKey := swarm.RandAddress(t).Bytes() + encryptedAccesskey := swarm.RandAddress(t).Bytes() + + ref, err := act1.Add(swarm.EmptyAddress, lookupKey, encryptedAccesskey) + if err != nil { + t.Fatalf("Add() should not return an error: %v", err) + } + + act2 := actCreate() + key, err := act2.Lookup(ref, lookupKey) + if err != nil { + t.Fatalf("Lookup() should not return an error: %v", err) + } - key, _ := act.Lookup(lookupKey) - if !bytes.Equal(key, encryptedAccesskey) { - t.Errorf("Get() value is not the expected %s != %s", key, encryptedAccesskey) + if !bytes.Equal(key, encryptedAccesskey) { + t.Errorf("Get() value is not the expected %s != %s", hex.EncodeToString(key), hex.EncodeToString(encryptedAccesskey)) + } } } +/* func TestActStoreLoad(t *testing.T) { act := dynamicaccess.NewInMemoryAct() @@ -50,3 +103,4 @@ func TestActStoreLoad(t *testing.T) { t.Errorf("actualAct.Load() value is not the expected %s != %s", hex.EncodeToString(actualEak), hex.EncodeToString(encryptedAccesskey)) } } +*/ diff --git a/pkg/dynamicaccess/container.go b/pkg/dynamicaccess/container.go deleted file mode 100644 index ae683aa0fcd..00000000000 --- a/pkg/dynamicaccess/container.go +++ /dev/null @@ -1,7 +0,0 @@ -package dynamicaccess - -// iterator -type Container interface { - Add(oldItemKey string, oldRootHash string) (newRootHash string, err error) - Get(rootKey string) (value string, err error) -} diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index 92363014c9b..9ced8a4d873 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -18,23 +18,29 @@ type defaultController struct { } func (c *defaultController) DownloadHandler(timestamp int64, enryptedRef swarm.Address, publisher *ecdsa.PublicKey, tag string) (swarm.Address, error) { - act, err := c.history.Lookup(timestamp) + _, err := c.history.Lookup(timestamp) if err != nil { return swarm.EmptyAddress, err } - addr, err := c.accessLogic.Get(act, enryptedRef, publisher) + addr, err := c.accessLogic.Get(swarm.EmptyAddress, enryptedRef, publisher) return addr, err } func (c *defaultController) UploadHandler(ref swarm.Address, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error) { - act, _ := c.history.Lookup(0) + act, err := c.history.Lookup(0) + if err != nil { + return swarm.EmptyAddress, err + } + var actRef swarm.Address if act == nil { // new feed - act = NewInMemoryAct() - act = c.granteeManager.Publish(act, publisher, topic) + actRef, err = c.granteeManager.Publish(swarm.EmptyAddress, publisher, topic) + if err != nil { + return swarm.EmptyAddress, err + } } //FIXME: check if ACT is consistent with the grantee list - return c.accessLogic.EncryptRef(act, publisher, ref) + return c.accessLogic.EncryptRef(actRef, publisher, ref) } func NewController(history History, granteeManager GranteeManager, accessLogic ActLogic) Controller { diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index 531470eceee..8f876232b05 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -24,7 +24,7 @@ func mockTestHistory(key, val []byte) dynamicaccess.History { now = time.Now() act = dynamicaccess.NewInMemoryAct() ) - act.Add(key, val) + act.Add(swarm.EmptyAddress, key, val) h.Insert(now.AddDate(-3, 0, 0).Unix(), act) return h } diff --git a/pkg/dynamicaccess/grantee_manager.go b/pkg/dynamicaccess/grantee_manager.go index 6b287af6941..e6884f77bea 100644 --- a/pkg/dynamicaccess/grantee_manager.go +++ b/pkg/dynamicaccess/grantee_manager.go @@ -1,11 +1,15 @@ package dynamicaccess -import "crypto/ecdsa" +import ( + "crypto/ecdsa" + + "github.com/ethersphere/bee/pkg/swarm" +) type GranteeManager interface { Get(topic string) []*ecdsa.PublicKey Add(topic string, addList []*ecdsa.PublicKey) error - Publish(act Act, publisher *ecdsa.PublicKey, topic string) Act + Publish(rootHash swarm.Address, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error) // HandleGrantees(topic string, addList, removeList []*ecdsa.PublicKey) *Act @@ -32,10 +36,10 @@ func (gm *granteeManager) Add(topic string, addList []*ecdsa.PublicKey) error { return gm.granteeList.AddGrantees(topic, addList) } -func (gm *granteeManager) Publish(act Act, publisher *ecdsa.PublicKey, topic string) Act { - gm.accessLogic.AddPublisher(act, publisher) +func (gm *granteeManager) Publish(rootHash swarm.Address, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error) { + ref, err := gm.accessLogic.AddPublisher(rootHash, publisher) for _, grantee := range gm.granteeList.GetGrantees(topic) { - gm.accessLogic.AddNewGranteeToContent(act, publisher, grantee) + ref, err = gm.accessLogic.AddNewGranteeToContent(ref, publisher, grantee) } - return act + return ref, err } diff --git a/pkg/dynamicaccess/grantee_manager_test.go b/pkg/dynamicaccess/grantee_manager_test.go index fc5969f4492..2144fd1410a 100644 --- a/pkg/dynamicaccess/grantee_manager_test.go +++ b/pkg/dynamicaccess/grantee_manager_test.go @@ -8,21 +8,18 @@ import ( "testing" "github.com/ethersphere/bee/pkg/dynamicaccess" + "github.com/ethersphere/bee/pkg/swarm" ) func setupAccessLogic(privateKey *ecdsa.PrivateKey) dynamicaccess.ActLogic { - // privateKey, err := crypto.GenerateSecp256k1Key() - // if err != nil { - // errors.New("error creating private key") - // } + act := dynamicaccess.NewInMemoryAct() si := dynamicaccess.NewDefaultSession(privateKey) - al := dynamicaccess.NewLogic(si) + al := dynamicaccess.NewLogic(si, act) return al } func TestAdd(t *testing.T) { - act := dynamicaccess.NewInMemoryAct() m := dynamicaccess.NewGranteeManager(setupAccessLogic(getPrivateKey())) pub, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) @@ -36,6 +33,6 @@ func TestAdd(t *testing.T) { if err != nil { t.Errorf("Add() returned an error") } - m.Publish(act, &pub.PublicKey, "topic") + m.Publish(swarm.EmptyAddress, &pub.PublicKey, "topic") fmt.Println("") } diff --git a/pkg/dynamicaccess/history_test.go b/pkg/dynamicaccess/history_test.go index a58aa44ecaa..e501e075697 100644 --- a/pkg/dynamicaccess/history_test.go +++ b/pkg/dynamicaccess/history_test.go @@ -7,6 +7,7 @@ import ( "github.com/ethersphere/bee/pkg/dynamicaccess" "github.com/ethersphere/bee/pkg/dynamicaccess/mock" + "github.com/ethersphere/bee/pkg/swarm" "github.com/stretchr/testify/assert" ) @@ -32,7 +33,7 @@ func TestHistoryLookup(t *testing.T) { for _, tt := range tests { t.Run("", func(t *testing.T) { actAt, _ := h.Lookup(tt.input) - output, _ := actAt.Lookup([]byte("key1")) + output, _ := actAt.Lookup(swarm.EmptyAddress, []byte("key1")) assert.Equal(t, output, hex.EncodeToString([]byte(tt.expected))) }) } @@ -46,9 +47,9 @@ func prepareTestHistory() dynamicaccess.History { act2 = dynamicaccess.NewInMemoryAct() act3 = dynamicaccess.NewInMemoryAct() ) - act1.Add([]byte("key1"), []byte("value1")) - act2.Add([]byte("key1"), []byte("value2")) - act3.Add([]byte("key1"), []byte("value3")) + act1.Add(swarm.EmptyAddress, []byte("key1"), []byte("value1")) + act2.Add(swarm.EmptyAddress, []byte("key1"), []byte("value2")) + act3.Add(swarm.EmptyAddress, []byte("key1"), []byte("value3")) h.Insert(now.AddDate(-3, 0, 0).Unix(), act1) h.Insert(now.AddDate(-2, 0, 0).Unix(), act2) diff --git a/pkg/dynamicaccess/mock/act.go b/pkg/dynamicaccess/mock/act.go index a915f38f123..721a4778829 100644 --- a/pkg/dynamicaccess/mock/act.go +++ b/pkg/dynamicaccess/mock/act.go @@ -10,26 +10,26 @@ import ( ) type ActMock struct { - AddFunc func(key []byte, val []byte) error - LookupFunc func(key []byte) ([]byte, error) + AddFunc func(root swarm.Address, key []byte, val []byte) (swarm.Address, error) + LookupFunc func(root swarm.Address, key []byte) ([]byte, error) LoadFunc func(addr swarm.Address) error StoreFunc func() (swarm.Address, error) } var _ dynamicaccess.Act = (*ActMock)(nil) -func (act *ActMock) Add(key []byte, val []byte) error { +func (act *ActMock) Add(root swarm.Address, key []byte, val []byte) (swarm.Address, error) { if act.AddFunc == nil { - return nil + return swarm.EmptyAddress, nil } - return act.AddFunc(key, val) + return act.AddFunc(root, key, val) } -func (act *ActMock) Lookup(key []byte) ([]byte, error) { +func (act *ActMock) Lookup(root swarm.Address, key []byte) ([]byte, error) { if act.LookupFunc == nil { return make([]byte, 0), nil } - return act.LookupFunc(key) + return act.LookupFunc(root, key) } func (act *ActMock) Load(addr swarm.Address) error { @@ -46,7 +46,9 @@ func (act *ActMock) Store() (swarm.Address, error) { return act.StoreFunc() } -func NewActMock(addFunc func(key []byte, val []byte) error, getFunc func(key []byte) ([]byte, error)) dynamicaccess.Act { +func NewActMock( + addFunc func(swarm.Address, []byte, []byte) (swarm.Address, error), + getFunc func(swarm.Address, []byte) ([]byte, error)) dynamicaccess.Act { return &ActMock{ AddFunc: addFunc, LookupFunc: getFunc, diff --git a/pkg/kvs/kvs.go b/pkg/kvs/kvs.go new file mode 100644 index 00000000000..65c2a0be8b9 --- /dev/null +++ b/pkg/kvs/kvs.go @@ -0,0 +1,34 @@ +package kvs + +import ( + "errors" + + "github.com/ethersphere/bee/pkg/api" + "github.com/ethersphere/bee/pkg/kvs/manifest" + "github.com/ethersphere/bee/pkg/kvs/memory" + "github.com/ethersphere/bee/pkg/swarm" +) + +var ErrInvalidKvsType = errors.New("kvs: invalid type") + +type KeyValueStore interface { + Get(rootHash swarm.Address, key []byte) ([]byte, error) + Put(rootHash swarm.Address, key, value []byte) (swarm.Address, error) +} + +// func NewDefaultKeyValueStore(storer api.Storer) (KeyValueStore, error) { +// return NewKeyValueStore(storer, memory.KvsTypeMemory) +// } + +func NewKeyValueStore(storer api.Storer, kvsType string) (KeyValueStore, error) { + switch kvsType { + case "": + return memory.NewMemoryKeyValueStore() + case memory.KvsTypeMemory: + return memory.NewMemoryKeyValueStore() + case manifest.KvsTypeManifest: + return manifest.NewManifestKeyValueStore(storer) + default: + return nil, ErrInvalidKvsType + } +} diff --git a/pkg/kvs/manifest/kvs.go b/pkg/kvs/manifest/kvs.go new file mode 100644 index 00000000000..19ca983d54a --- /dev/null +++ b/pkg/kvs/manifest/kvs.go @@ -0,0 +1,86 @@ +package manifest + +import ( + "context" + "encoding/hex" + + "github.com/ethersphere/bee/pkg/api" + "github.com/ethersphere/bee/pkg/file/loadsave" + "github.com/ethersphere/bee/pkg/file/pipeline" + "github.com/ethersphere/bee/pkg/file/pipeline/builder" + "github.com/ethersphere/bee/pkg/file/redundancy" + "github.com/ethersphere/bee/pkg/manifest" + "github.com/ethersphere/bee/pkg/storage" + "github.com/ethersphere/bee/pkg/swarm" +) + +const ( + // KvsTypeManifest represents + KvsTypeManifest = "Manifest" +) + +type ManifestKeyValueStore interface { + Get(rootHash swarm.Address, key []byte) ([]byte, error) + Put(rootHash swarm.Address, key, value []byte) (swarm.Address, error) +} + +type manifestKeyValueStore struct { + storer api.Storer +} + +// TODO: pass context as dep. +func (m *manifestKeyValueStore) Get(rootHash swarm.Address, key []byte) ([]byte, error) { + ls := loadsave.NewReadonly(m.storer.ChunkStore()) + // existing manif + manif, err := manifest.NewSimpleManifestReference(rootHash, ls) + if err != nil { + return nil, err + } + entry, err := manif.Lookup(context.Background(), hex.EncodeToString(key)) + if err != nil { + return nil, err + } + ref := entry.Reference() + return ref.Bytes(), nil +} + +func (m *manifestKeyValueStore) Put(rootHash swarm.Address, key []byte, value []byte) (swarm.Address, error) { + factory := requestPipelineFactory(context.Background(), m.storer.Cache(), false, redundancy.NONE) + ls := loadsave.New(m.storer.ChunkStore(), m.storer.Cache(), factory) + // existing manif + manif, err := manifest.NewSimpleManifestReference(rootHash, ls) + if err != nil { + // new manif + manif, err = manifest.NewSimpleManifest(ls) + if err != nil { + return swarm.EmptyAddress, err + } + } + err = manif.Add(context.Background(), hex.EncodeToString(key), manifest.NewEntry(swarm.NewAddress(value), map[string]string{})) + if err != nil { + return swarm.EmptyAddress, err + } + manifRef, err := manif.Store(context.Background()) + if err != nil { + return swarm.EmptyAddress, err + } + + putter := m.storer.DirectUpload() + err = putter.Done(manifRef) + if err != nil { + return swarm.EmptyAddress, err + } + return manifRef, nil +} + +func NewManifestKeyValueStore(storer api.Storer) (ManifestKeyValueStore, error) { + return &manifestKeyValueStore{ + storer: storer, + }, nil +} + +func requestPipelineFactory(ctx context.Context, s storage.Putter, encrypt bool, rLevel redundancy.Level) func() pipeline.Interface { + return func() pipeline.Interface { + return builder.NewPipelineBuilder(ctx, s, encrypt, rLevel) + } +} diff --git a/pkg/kvs/memory/kvs.go b/pkg/kvs/memory/kvs.go new file mode 100644 index 00000000000..3a1abc69c0e --- /dev/null +++ b/pkg/kvs/memory/kvs.go @@ -0,0 +1,67 @@ +package memory + +import ( + "encoding/hex" + "sync" + + "github.com/ethersphere/bee/pkg/swarm" +) + +const ( + // KvsTypeMemory represents + KvsTypeMemory = "Memory" +) + +type MemoryKeyValueStore interface { + Get(rootHash swarm.Address, key []byte) ([]byte, error) + Put(rootHash swarm.Address, key, value []byte) (swarm.Address, error) +} + +var lock = &sync.Mutex{} + +type single struct { + // TODO string -> []byte ? + memoryMock map[string][]byte +} + +var singleInMemorySwarm *single + +func getInMemorySwarm() *single { + if singleInMemorySwarm == nil { + lock.Lock() + defer lock.Unlock() + if singleInMemorySwarm == nil { + singleInMemorySwarm = &single{ + memoryMock: make(map[string][]byte)} + } + } + return singleInMemorySwarm +} + +func getMemory() map[string][]byte { + ch := make(chan *single) + go func() { + ch <- getInMemorySwarm() + }() + mem := <-ch + return mem.memoryMock +} + +type memoryKeyValueStore struct { +} + +func (m *memoryKeyValueStore) Get(rootHash swarm.Address, key []byte) ([]byte, error) { + mem := getMemory() + val := mem[hex.EncodeToString(key)] + return val, nil +} + +func (m *memoryKeyValueStore) Put(rootHash swarm.Address, key []byte, value []byte) (swarm.Address, error) { + mem := getMemory() + mem[hex.EncodeToString(key)] = value + return swarm.EmptyAddress, nil +} + +func NewMemoryKeyValueStore() (MemoryKeyValueStore, error) { + return &memoryKeyValueStore{}, nil +} From 294f2d48b6c26f471f6dcb6a06b37044959c118a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= <58116288+bosi95@users.noreply.github.com> Date: Tue, 26 Mar 2024 10:53:35 +0100 Subject: [PATCH 22/41] Session refactor (#24) * pr comment fix * add comment to session.NewFromKeystore --- pkg/dynamicaccess/session.go | 9 ++++-- pkg/dynamicaccess/session_test.go | 51 ++++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/pkg/dynamicaccess/session.go b/pkg/dynamicaccess/session.go index 9d7634ffd01..68cea8ccbfb 100644 --- a/pkg/dynamicaccess/session.go +++ b/pkg/dynamicaccess/session.go @@ -21,11 +21,15 @@ type session struct { } func (s *session) Key(publicKey *ecdsa.PublicKey, nonces [][]byte) ([][]byte, error) { - x, _ := publicKey.Curve.ScalarMult(publicKey.X, publicKey.Y, s.key.D.Bytes()) - if x == nil { + x, y := publicKey.Curve.ScalarMult(publicKey.X, publicKey.Y, s.key.D.Bytes()) + if x == nil || y == nil { return nil, errors.New("shared secret is point at infinity") } + if len(nonces) == 0 { + return [][]byte{(*x).Bytes()}, nil + } + keys := make([][]byte, 0, len(nonces)) for _, nonce := range nonces { key, err := crypto.LegacyKeccak256(append(x.Bytes(), nonce...)) @@ -44,6 +48,7 @@ func NewDefaultSession(key *ecdsa.PrivateKey) Session { } } +// Currently implemented only in mock/session.go func NewFromKeystore(ks keystore.Service, tag, password string) Session { return nil } diff --git a/pkg/dynamicaccess/session_test.go b/pkg/dynamicaccess/session_test.go index 0cfee7691da..501d1abd2b6 100644 --- a/pkg/dynamicaccess/session_test.go +++ b/pkg/dynamicaccess/session_test.go @@ -52,9 +52,11 @@ func TestSessionKey(t *testing.T) { } si2 := dynamicaccess.NewDefaultSession(key2) - nonces := make([][]byte, 1) - if _, err := io.ReadFull(rand.Reader, nonces[0]); err != nil { - t.Fatal(err) + nonces := make([][]byte, 2) + for i := range nonces { + if _, err := io.ReadFull(rand.Reader, nonces[i]); err != nil { + t.Fatal(err) + } } keys1, err := si1.Key(&key2.PublicKey, nonces) @@ -66,6 +68,38 @@ func TestSessionKey(t *testing.T) { t.Fatal(err) } + if !bytes.Equal(keys1[0], keys2[0]) { + t.Fatalf("shared secrets do not match %s, %s", hex.EncodeToString(keys1[0]), hex.EncodeToString(keys2[0])) + } + if !bytes.Equal(keys1[1], keys2[1]) { + t.Fatalf("shared secrets do not match %s, %s", hex.EncodeToString(keys1[0]), hex.EncodeToString(keys2[0])) + } +} + +func TestSessionKeyWithoutNonces(t *testing.T) { + t.Parallel() + + key1, err := crypto.GenerateSecp256k1Key() + if err != nil { + t.Fatal(err) + } + si1 := dynamicaccess.NewDefaultSession(key1) + + key2, err := crypto.GenerateSecp256k1Key() + if err != nil { + t.Fatal(err) + } + si2 := dynamicaccess.NewDefaultSession(key2) + + keys1, err := si1.Key(&key2.PublicKey, nil) + if err != nil { + t.Fatal(err) + } + keys2, err := si2.Key(&key1.PublicKey, nil) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(keys1[0], keys2[0]) { t.Fatalf("shared secrets do not match %s, %s", hex.EncodeToString(keys1[0]), hex.EncodeToString(keys2[0])) } @@ -81,6 +115,7 @@ func TestSessionKeyFromKeystore(t *testing.T) { password2 := "password2" si1 := mock.NewFromKeystore(ks, tag1, password1, mockKeyFunc) + // si1 := dynamicaccess.NewFromKeystore(ks, tag1, password1) exists, err := ks.Exists(tag1) if err != nil { t.Fatal(err) @@ -97,6 +132,7 @@ func TestSessionKeyFromKeystore(t *testing.T) { } si2 := mock.NewFromKeystore(ks, tag2, password2, mockKeyFunc) + // si2 := dynamicaccess.NewFromKeystore(ks, tag2, password2) exists, err = ks.Exists(tag2) if err != nil { t.Fatal(err) @@ -113,8 +149,10 @@ func TestSessionKeyFromKeystore(t *testing.T) { } nonces := make([][]byte, 1) - if _, err := io.ReadFull(rand.Reader, nonces[0]); err != nil { - t.Fatal(err) + for i := range nonces { + if _, err := io.ReadFull(rand.Reader, nonces[i]); err != nil { + t.Fatal(err) + } } keys1, err := si1.Key(&key2.PublicKey, nonces) @@ -129,4 +167,7 @@ func TestSessionKeyFromKeystore(t *testing.T) { if !bytes.Equal(keys1[0], keys2[0]) { t.Fatalf("shared secrets do not match %s, %s", hex.EncodeToString(keys1[0]), hex.EncodeToString(keys2[0])) } + // if !bytes.Equal(keys1[1], keys2[1]) { + // t.Fatalf("shared secrets do not match %s, %s", hex.EncodeToString(keys1[0]), hex.EncodeToString(keys2[0])) + // } } From 64471a01a535f9a17f8a91fb4663a3ccfb068be9 Mon Sep 17 00:00:00 2001 From: rolandlor <33499567+rolandlor@users.noreply.github.com> Date: Tue, 26 Mar 2024 16:48:22 +0100 Subject: [PATCH 23/41] Access logic refactor (#25) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactors access logic --------- Co-authored-by: Peter Ott Co-authored-by: Ferenc Sárai Co-authored-by: Bálint Ujvári Co-authored-by: Peter Ott --- pkg/dynamicaccess/accesslogic.go | 121 +++++++++++--------------- pkg/dynamicaccess/accesslogic_test.go | 25 ++---- pkg/dynamicaccess/controller.go | 2 +- pkg/dynamicaccess/grantee.go | 23 +++-- pkg/dynamicaccess/grantee_manager.go | 10 +-- pkg/dynamicaccess/grantee_test.go | 18 ++-- pkg/dynamicaccess/session.go | 3 + 7 files changed, 90 insertions(+), 112 deletions(-) diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index 4b5abcd739e..591c9557a36 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -2,7 +2,6 @@ package dynamicaccess import ( "crypto/ecdsa" - "fmt" encryption "github.com/ethersphere/bee/pkg/encryption" "github.com/ethersphere/bee/pkg/swarm" @@ -11,44 +10,44 @@ import ( var hashFunc = sha3.NewLegacyKeccak256 -// Logic has the responsibility to return a ref for a given grantee and create new encrypted reference for a grantee -type Logic interface { +// Read-only interface for the ACT +type Decryptor interface { + // DecryptRef will return a decrypted reference, for given encrypted reference and grantee + DecryptRef(rootHash swarm.Address, encryped_ref swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) + // Embedding the Session interface + Session +} + +// Control interface for the ACT (does write operations) +type Control interface { + // Embedding the Decryptor interface + Decryptor // Adds a new grantee to the ACT - AddNewGranteeToContent(rootHash swarm.Address, publisherPubKey, granteePubKey *ecdsa.PublicKey) (swarm.Address, error) - // Get will return a decrypted reference, for given encrypted reference and grantee - Get(rootHash swarm.Address, encryped_ref swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) + AddGrantee(rootHash swarm.Address, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKey *encryption.Key) (swarm.Address, error) + // Encrypts a Swarm reference for a given grantee + EncryptRef(rootHash swarm.Address, grantee *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) } type ActLogic struct { - session Session - act Act + Session + act Act } -var _ Logic = (*ActLogic)(nil) +var _ Decryptor = (*ActLogic)(nil) // Adds a new publisher to an empty act func (al ActLogic) AddPublisher(rootHash swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) { accessKey := encryption.GenerateRandomKey(encryption.KeyLength) - keys, err := al.getKeys(publisher) - if err != nil { - return swarm.EmptyAddress, err - } - lookupKey := keys[0] - accessKeyEncryptionKey := keys[1] - - accessKeyCipher := encryption.New(encryption.Key(accessKeyEncryptionKey), 0, uint32(0), hashFunc) - encryptedAccessKey, err := accessKeyCipher.Encrypt([]byte(accessKey)) - if err != nil { - return swarm.EmptyAddress, err - } - - return al.act.Add(rootHash, lookupKey, encryptedAccessKey) + return al.AddGrantee(rootHash, publisher, publisher, &accessKey) } // Encrypts a SWARM reference for a publisher func (al ActLogic) EncryptRef(rootHash swarm.Address, publisherPubKey *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) { - accessKey := al.getAccessKey(rootHash, publisherPubKey) + accessKey, err := al.getAccessKey(rootHash, publisherPubKey) + if err != nil { + return swarm.EmptyAddress, err + } refCipher := encryption.New(accessKey, 0, uint32(0), hashFunc) encryptedRef, _ := refCipher.Encrypt(ref.Bytes()) @@ -56,9 +55,20 @@ func (al ActLogic) EncryptRef(rootHash swarm.Address, publisherPubKey *ecdsa.Pub } // Adds a new grantee to the ACT -func (al ActLogic) AddNewGranteeToContent(rootHash swarm.Address, publisherPubKey, granteePubKey *ecdsa.PublicKey) (swarm.Address, error) { - // Get previously generated access key - accessKey := al.getAccessKey(rootHash, publisherPubKey) +func (al ActLogic) AddGrantee(rootHash swarm.Address, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKeyPointer *encryption.Key) (swarm.Address, error) { + var accessKey encryption.Key + var err error // Declare the "err" variable + + if accessKeyPointer == nil { + // Get previously generated access key + accessKey, err = al.getAccessKey(rootHash, publisherPubKey) + if err != nil { + return swarm.EmptyAddress, err + } + } else { + // This is a newly created access key, because grantee is publisher (they are the same) + accessKey = *accessKeyPointer + } // Encrypt the access key for the new Grantee keys, err := al.getKeys(granteePubKey) @@ -77,62 +87,37 @@ func (al ActLogic) AddNewGranteeToContent(rootHash swarm.Address, publisherPubKe // Add the new encrypted access key for the Act return al.act.Add(rootHash, lookupKey, granteeEncryptedAccessKey) - } // Will return the access key for a publisher (public key) -func (al *ActLogic) getAccessKey(rootHash swarm.Address, publisherPubKey *ecdsa.PublicKey) []byte { +func (al *ActLogic) getAccessKey(rootHash swarm.Address, publisherPubKey *ecdsa.PublicKey) ([]byte, error) { keys, err := al.getKeys(publisherPubKey) if err != nil { - return nil + return nil, err } publisherLookupKey := keys[0] publisherAKDecryptionKey := keys[1] - + // no need to constructor call if value not found in act accessKeyDecryptionCipher := encryption.New(encryption.Key(publisherAKDecryptionKey), 0, uint32(0), hashFunc) - encryptedAK, err := al.getEncryptedAccessKey(rootHash, publisherLookupKey) + encryptedAK, err := al.act.Lookup(rootHash, publisherLookupKey) if err != nil { - return nil + return nil, err } - accessKey, err := accessKeyDecryptionCipher.Decrypt(encryptedAK) - if err != nil { - return nil - } + return accessKeyDecryptionCipher.Decrypt(encryptedAK) - return accessKey } -func (al *ActLogic) getKeys(publicKey *ecdsa.PublicKey) ([][]byte, error) { - // Generate lookup key and access key decryption - oneByteArray := []byte{1} - zeroByteArray := []byte{0} - - keys, err := al.session.Key(publicKey, [][]byte{zeroByteArray, oneByteArray}) - if err != nil { - return nil, err - } - return keys, nil -} +var oneByteArray = []byte{1} +var zeroByteArray = []byte{0} -// Gets the encrypted access key for a given grantee -func (al *ActLogic) getEncryptedAccessKey(rootHash swarm.Address, lookup_key []byte) ([]byte, error) { - val, err := al.act.Lookup(rootHash, lookup_key) - if err != nil { - return nil, err - } - return val, nil +// Generate lookup key and access key decryption key for a given public key +func (al *ActLogic) getKeys(publicKey *ecdsa.PublicKey) ([][]byte, error) { + return al.Session.Key(publicKey, [][]byte{zeroByteArray, oneByteArray}) } -// Get will return a decrypted reference, for given encrypted reference and grantee -func (al ActLogic) Get(rootHash swarm.Address, encryped_ref swarm.Address, grantee *ecdsa.PublicKey) (swarm.Address, error) { - if encryped_ref.Compare(swarm.EmptyAddress) == 0 { - return swarm.EmptyAddress, fmt.Errorf("encrypted ref not provided") - } - if grantee == nil { - return swarm.EmptyAddress, fmt.Errorf("grantee not provided") - } - +// DecryptRef will return a decrypted reference, for given encrypted reference and grantee +func (al ActLogic) DecryptRef(rootHash swarm.Address, encryped_ref swarm.Address, grantee *ecdsa.PublicKey) (swarm.Address, error) { keys, err := al.getKeys(grantee) if err != nil { return swarm.EmptyAddress, err @@ -141,7 +126,7 @@ func (al ActLogic) Get(rootHash swarm.Address, encryped_ref swarm.Address, grant accessKeyDecryptionKey := keys[1] // Lookup encrypted access key from the ACT manifest - encryptedAccessKey, err := al.getEncryptedAccessKey(rootHash, lookupKey) + encryptedAccessKey, err := al.act.Lookup(rootHash, lookupKey) if err != nil { return swarm.EmptyAddress, err } @@ -163,9 +148,9 @@ func (al ActLogic) Get(rootHash swarm.Address, encryped_ref swarm.Address, grant return swarm.NewAddress(ref), nil } -func NewLogic(s Session, act Act) ActLogic { +func NewLogic(S Session, act Act) ActLogic { return ActLogic{ - session: s, + Session: S, act: act, } } diff --git a/pkg/dynamicaccess/accesslogic_test.go b/pkg/dynamicaccess/accesslogic_test.go index b87bac84ffd..1a6da8af80b 100644 --- a/pkg/dynamicaccess/accesslogic_test.go +++ b/pkg/dynamicaccess/accesslogic_test.go @@ -39,7 +39,7 @@ func generateFixPrivateKey(input int64) ecdsa.PrivateKey { return privateKey } -func TestGet_Success(t *testing.T) { +func TestDecryptRef_Success(t *testing.T) { id0 := generateFixPrivateKey(0) var mockStorer = mockstorer.New() act := dynamicaccess.NewInManifestAct(mockStorer) @@ -61,7 +61,7 @@ func TestGet_Success(t *testing.T) { t.Error(err) } - acutalRef, err := al.Get(ref, encryptedRef, &id0.PublicKey) + acutalRef, err := al.DecryptRef(ref, encryptedRef, &id0.PublicKey) if err != nil { t.Errorf("There was an error while calling Get: ") t.Error(err) @@ -73,8 +73,7 @@ func TestGet_Success(t *testing.T) { } } -// This test function tests those cases where different parameters are missing -func TestGet_Error(t *testing.T) { +func TestDecryptRef_Error(t *testing.T) { id0 := generateFixPrivateKey(0) act := dynamicaccess.NewInMemoryAct() @@ -88,21 +87,11 @@ func TestGet_Error(t *testing.T) { encryptedRef, _ := al.EncryptRef(ref, &id0.PublicKey, swarm.NewAddress([]byte(expectedRef))) - r, err := al.Get(swarm.RandAddress(t), encryptedRef, &id0.PublicKey) + r, err := al.DecryptRef(swarm.RandAddress(t), encryptedRef, nil) if err == nil { t.Logf("r: %s", r.String()) t.Errorf("Get should give back encrypted access key not found error!") } - - refTwo, _ := al.Get(swarm.RandAddress(t), swarm.EmptyAddress, &id0.PublicKey) - if swarm.EmptyAddress.Compare(refTwo) != 0 { - t.Errorf("Get should give back empty string if encrypted ref not provided!") - } - - _, err = al.Get(swarm.RandAddress(t), encryptedRef, nil) - if err == nil { - t.Errorf("Get should give back error if grantee not provided!") - } } func TestAddPublisher(t *testing.T) { @@ -136,7 +125,7 @@ func TestAddPublisher(t *testing.T) { } } -func TestAdd_New_Grantee_To_Content(t *testing.T) { +func TestAddNewGranteeToContent(t *testing.T) { id0 := generateFixPrivateKey(0) id1 := generateFixPrivateKey(1) @@ -153,12 +142,12 @@ func TestAdd_New_Grantee_To_Content(t *testing.T) { t.Errorf("AddNewGrantee: expected no error, got %v", err) } - ref, err = al.AddNewGranteeToContent(ref, &id0.PublicKey, &id1.PublicKey) + ref, err = al.AddGrantee(ref, &id0.PublicKey, &id1.PublicKey, nil) if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } - ref, err = al.AddNewGranteeToContent(ref, &id0.PublicKey, &id2.PublicKey) + ref, err = al.AddGrantee(ref, &id0.PublicKey, &id2.PublicKey, nil) if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index 9ced8a4d873..b1c6e306934 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -22,7 +22,7 @@ func (c *defaultController) DownloadHandler(timestamp int64, enryptedRef swarm.A if err != nil { return swarm.EmptyAddress, err } - addr, err := c.accessLogic.Get(swarm.EmptyAddress, enryptedRef, publisher) + addr, err := c.accessLogic.DecryptRef(swarm.EmptyAddress, enryptedRef, publisher) return addr, err } diff --git a/pkg/dynamicaccess/grantee.go b/pkg/dynamicaccess/grantee.go index dfd7015e49f..d85ebf217ea 100644 --- a/pkg/dynamicaccess/grantee.go +++ b/pkg/dynamicaccess/grantee.go @@ -4,29 +4,29 @@ import ( "crypto/ecdsa" ) -type Grantee interface { - AddGrantees(topic string, addList []*ecdsa.PublicKey) error - RemoveGrantees(topic string, removeList []*ecdsa.PublicKey) error - GetGrantees(topic string) []*ecdsa.PublicKey +type GranteeList interface { + Add(topic string, addList []*ecdsa.PublicKey) error + Remove(topic string, removeList []*ecdsa.PublicKey) error + Get(topic string) []*ecdsa.PublicKey } -type defaultGrantee struct { +type GranteeListStruct struct { grantees map[string][]*ecdsa.PublicKey } -func (g *defaultGrantee) GetGrantees(topic string) []*ecdsa.PublicKey { +func (g *GranteeListStruct) Get(topic string) []*ecdsa.PublicKey { grantees := g.grantees[topic] keys := make([]*ecdsa.PublicKey, len(grantees)) copy(keys, grantees) return keys } -func (g *defaultGrantee) AddGrantees(topic string, addList []*ecdsa.PublicKey) error { +func (g *GranteeListStruct) Add(topic string, addList []*ecdsa.PublicKey) error { g.grantees[topic] = append(g.grantees[topic], addList...) return nil } -func (g *defaultGrantee) RemoveGrantees(topic string, removeList []*ecdsa.PublicKey) error { +func (g *GranteeListStruct) Remove(topic string, removeList []*ecdsa.PublicKey) error { for _, remove := range removeList { for i, grantee := range g.grantees[topic] { if *grantee == *remove { @@ -36,10 +36,9 @@ func (g *defaultGrantee) RemoveGrantees(topic string, removeList []*ecdsa.Public } } - return nil } -func NewGrantee() Grantee { - return &defaultGrantee{grantees: make(map[string][]*ecdsa.PublicKey)} -} \ No newline at end of file +func NewGrantee() *GranteeListStruct { + return &GranteeListStruct{grantees: make(map[string][]*ecdsa.PublicKey)} +} diff --git a/pkg/dynamicaccess/grantee_manager.go b/pkg/dynamicaccess/grantee_manager.go index e6884f77bea..0627db9794a 100644 --- a/pkg/dynamicaccess/grantee_manager.go +++ b/pkg/dynamicaccess/grantee_manager.go @@ -21,7 +21,7 @@ var _ GranteeManager = (*granteeManager)(nil) type granteeManager struct { accessLogic ActLogic - granteeList Grantee + granteeList GranteeList } func NewGranteeManager(al ActLogic) *granteeManager { @@ -29,17 +29,17 @@ func NewGranteeManager(al ActLogic) *granteeManager { } func (gm *granteeManager) Get(topic string) []*ecdsa.PublicKey { - return gm.granteeList.GetGrantees(topic) + return gm.granteeList.Get(topic) } func (gm *granteeManager) Add(topic string, addList []*ecdsa.PublicKey) error { - return gm.granteeList.AddGrantees(topic, addList) + return gm.granteeList.Add(topic, addList) } func (gm *granteeManager) Publish(rootHash swarm.Address, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error) { ref, err := gm.accessLogic.AddPublisher(rootHash, publisher) - for _, grantee := range gm.granteeList.GetGrantees(topic) { - ref, err = gm.accessLogic.AddNewGranteeToContent(ref, publisher, grantee) + for _, grantee := range gm.granteeList.Get(topic) { + ref, err = gm.accessLogic.AddGrantee(ref, publisher, grantee, nil) } return ref, err } diff --git a/pkg/dynamicaccess/grantee_test.go b/pkg/dynamicaccess/grantee_test.go index 7418a741d77..7962690ccb4 100644 --- a/pkg/dynamicaccess/grantee_test.go +++ b/pkg/dynamicaccess/grantee_test.go @@ -10,6 +10,8 @@ import ( "github.com/ethersphere/bee/pkg/dynamicaccess" ) +var _ dynamicaccess.GranteeList = (*dynamicaccess.GranteeListStruct)(nil) + func TestGranteeAddGrantees(t *testing.T) { grantee := dynamicaccess.NewGrantee() @@ -25,13 +27,13 @@ func TestGranteeAddGrantees(t *testing.T) { addList := []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey} exampleTopic := "topic" - err = grantee.AddGrantees(exampleTopic, addList) + err = grantee.Add(exampleTopic, addList) if err != nil { t.Errorf("Expected no error, got %v", err) } - grantees := grantee.GetGrantees(exampleTopic) + grantees := grantee.Get(exampleTopic) if !reflect.DeepEqual(grantees, addList) { t.Errorf("Expected grantees %v, got %v", addList, grantees) } @@ -52,18 +54,18 @@ func TestRemoveGrantees(t *testing.T) { addList := []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey} exampleTopic := "topic" - err = grantee.AddGrantees(exampleTopic, addList) + err = grantee.Add(exampleTopic, addList) if err != nil { t.Errorf("Expected no error, got %v", err) } removeList := []*ecdsa.PublicKey{&key1.PublicKey} - err = grantee.RemoveGrantees(exampleTopic, removeList) + err = grantee.Remove(exampleTopic, removeList) if err != nil { t.Errorf("Expected no error, got %v", err) } - grantees := grantee.GetGrantees(exampleTopic) + grantees := grantee.Get(exampleTopic) expectedGrantees := []*ecdsa.PublicKey{&key2.PublicKey} for i, grantee := range grantees { @@ -88,15 +90,15 @@ func TestGetGrantees(t *testing.T) { addList := []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey} exampleTopic := "topic" - err = grantee.AddGrantees(exampleTopic, addList) + err = grantee.Add(exampleTopic, addList) if err != nil { t.Errorf("Expected no error, got %v", err) } - grantees := grantee.GetGrantees(exampleTopic) + grantees := grantee.Get(exampleTopic) for i, grantee := range grantees { if grantee != addList[i] { t.Errorf("Expected grantee %v, got %v", addList[i], grantee) } } -} \ No newline at end of file +} diff --git a/pkg/dynamicaccess/session.go b/pkg/dynamicaccess/session.go index 68cea8ccbfb..b2718aedd67 100644 --- a/pkg/dynamicaccess/session.go +++ b/pkg/dynamicaccess/session.go @@ -21,6 +21,9 @@ type session struct { } func (s *session) Key(publicKey *ecdsa.PublicKey, nonces [][]byte) ([][]byte, error) { + if publicKey == nil { + return nil, errors.New("invalid public key") + } x, y := publicKey.Curve.ScalarMult(publicKey.X, publicKey.Y, s.key.D.Bytes()) if x == nil || y == nil { return nil, errors.New("shared secret is point at infinity") From ce2353ccf6f2b74d76453228f5af6d3f6e1b240e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20S=C3=A1rai?= Date: Wed, 27 Mar 2024 09:00:12 +0100 Subject: [PATCH 24/41] (refactor:) PR comments (#23) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * grantee-refactor * Dried up code, related to AddPublisher - AddNewGranteeToContent * Refactor * removed getEncryptedAccessKey * Renamed AddGrentees, RemoveGrantees, etc to Add, Remove, etc * (refactor:) PR comments * (refactor:) compile check * removed encrypted_ref, grantee check (validation) * changed interface * comments * some more comments * refactor kvs and add load and store * (refactor:) Use ref * renamed defaultGrantee to granteeList * removed null encrypted test in in TestGet_Error * refactor kvs: pass kvs IF argument instead of storing it * Refactor according to the result of the workshop * refactor kvs IF and mock * fix merge errors and Logic/get_error test * (test:) Add test for put/get after kvs.Save --------- Co-authored-by: Roland Seres Co-authored-by: Peter Ott Co-authored-by: Ferenc Sárai Co-authored-by: Bálint Ujvári Co-authored-by: Peter Ott --- pkg/dynamicaccess/accesslogic.go | 39 ++++--- pkg/dynamicaccess/accesslogic_test.go | 81 +++++-------- pkg/dynamicaccess/act.go | 65 ----------- pkg/dynamicaccess/act_test.go | 106 ----------------- pkg/dynamicaccess/controller.go | 18 +-- pkg/dynamicaccess/controller_test.go | 7 +- pkg/dynamicaccess/grantee_manager.go | 11 +- pkg/dynamicaccess/grantee_manager_test.go | 8 +- pkg/dynamicaccess/history.go | 17 +-- pkg/dynamicaccess/history_test.go | 28 ++--- pkg/dynamicaccess/mock/act.go | 56 --------- pkg/dynamicaccess/mock/history.go | 14 +-- pkg/kvs/kvs.go | 80 +++++++++---- pkg/kvs/kvs_test.go | 132 ++++++++++++++++++++++ pkg/kvs/manifest/kvs.go | 86 -------------- pkg/kvs/{memory => mock}/kvs.go | 31 +++-- 16 files changed, 304 insertions(+), 475 deletions(-) delete mode 100644 pkg/dynamicaccess/act.go delete mode 100644 pkg/dynamicaccess/act_test.go delete mode 100644 pkg/dynamicaccess/mock/act.go create mode 100644 pkg/kvs/kvs_test.go delete mode 100644 pkg/kvs/manifest/kvs.go rename pkg/kvs/{memory => mock}/kvs.go (55%) diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index 591c9557a36..ad33ba5eb16 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -4,6 +4,7 @@ import ( "crypto/ecdsa" encryption "github.com/ethersphere/bee/pkg/encryption" + "github.com/ethersphere/bee/pkg/kvs" "github.com/ethersphere/bee/pkg/swarm" "golang.org/x/crypto/sha3" ) @@ -13,7 +14,7 @@ var hashFunc = sha3.NewLegacyKeccak256 // Read-only interface for the ACT type Decryptor interface { // DecryptRef will return a decrypted reference, for given encrypted reference and grantee - DecryptRef(rootHash swarm.Address, encryped_ref swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) + DecryptRef(storage kvs.KeyValueStore, encryped_ref swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) // Embedding the Session interface Session } @@ -23,28 +24,27 @@ type Control interface { // Embedding the Decryptor interface Decryptor // Adds a new grantee to the ACT - AddGrantee(rootHash swarm.Address, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKey *encryption.Key) (swarm.Address, error) + AddGrantee(storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKey *encryption.Key) error // Encrypts a Swarm reference for a given grantee - EncryptRef(rootHash swarm.Address, grantee *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) + EncryptRef(storage kvs.KeyValueStore, grantee *ecdsa.PublicKey, ref swarm.Address) error } type ActLogic struct { Session - act Act } var _ Decryptor = (*ActLogic)(nil) // Adds a new publisher to an empty act -func (al ActLogic) AddPublisher(rootHash swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) { +func (al ActLogic) AddPublisher(storage kvs.KeyValueStore, publisher *ecdsa.PublicKey) error { accessKey := encryption.GenerateRandomKey(encryption.KeyLength) - return al.AddGrantee(rootHash, publisher, publisher, &accessKey) + return al.AddGrantee(storage, publisher, publisher, &accessKey) } // Encrypts a SWARM reference for a publisher -func (al ActLogic) EncryptRef(rootHash swarm.Address, publisherPubKey *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) { - accessKey, err := al.getAccessKey(rootHash, publisherPubKey) +func (al ActLogic) EncryptRef(storage kvs.KeyValueStore, publisherPubKey *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) { + accessKey, err := al.getAccessKey(storage, publisherPubKey) if err != nil { return swarm.EmptyAddress, err } @@ -55,15 +55,15 @@ func (al ActLogic) EncryptRef(rootHash swarm.Address, publisherPubKey *ecdsa.Pub } // Adds a new grantee to the ACT -func (al ActLogic) AddGrantee(rootHash swarm.Address, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKeyPointer *encryption.Key) (swarm.Address, error) { +func (al ActLogic) AddGrantee(storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKeyPointer *encryption.Key) error { var accessKey encryption.Key var err error // Declare the "err" variable if accessKeyPointer == nil { // Get previously generated access key - accessKey, err = al.getAccessKey(rootHash, publisherPubKey) + accessKey, err = al.getAccessKey(storage, publisherPubKey) if err != nil { - return swarm.EmptyAddress, err + return err } } else { // This is a newly created access key, because grantee is publisher (they are the same) @@ -73,7 +73,7 @@ func (al ActLogic) AddGrantee(rootHash swarm.Address, publisherPubKey, granteePu // Encrypt the access key for the new Grantee keys, err := al.getKeys(granteePubKey) if err != nil { - return swarm.EmptyAddress, err + return err } lookupKey := keys[0] accessKeyEncryptionKey := keys[1] @@ -82,15 +82,15 @@ func (al ActLogic) AddGrantee(rootHash swarm.Address, publisherPubKey, granteePu cipher := encryption.New(encryption.Key(accessKeyEncryptionKey), 0, uint32(0), hashFunc) granteeEncryptedAccessKey, err := cipher.Encrypt(accessKey) if err != nil { - return swarm.EmptyAddress, err + return err } // Add the new encrypted access key for the Act - return al.act.Add(rootHash, lookupKey, granteeEncryptedAccessKey) + return storage.Put(lookupKey, granteeEncryptedAccessKey) } // Will return the access key for a publisher (public key) -func (al *ActLogic) getAccessKey(rootHash swarm.Address, publisherPubKey *ecdsa.PublicKey) ([]byte, error) { +func (al *ActLogic) getAccessKey(storage kvs.KeyValueStore, publisherPubKey *ecdsa.PublicKey) ([]byte, error) { keys, err := al.getKeys(publisherPubKey) if err != nil { return nil, err @@ -99,7 +99,7 @@ func (al *ActLogic) getAccessKey(rootHash swarm.Address, publisherPubKey *ecdsa. publisherAKDecryptionKey := keys[1] // no need to constructor call if value not found in act accessKeyDecryptionCipher := encryption.New(encryption.Key(publisherAKDecryptionKey), 0, uint32(0), hashFunc) - encryptedAK, err := al.act.Lookup(rootHash, publisherLookupKey) + encryptedAK, err := storage.Get(publisherLookupKey) if err != nil { return nil, err } @@ -117,7 +117,7 @@ func (al *ActLogic) getKeys(publicKey *ecdsa.PublicKey) ([][]byte, error) { } // DecryptRef will return a decrypted reference, for given encrypted reference and grantee -func (al ActLogic) DecryptRef(rootHash swarm.Address, encryped_ref swarm.Address, grantee *ecdsa.PublicKey) (swarm.Address, error) { +func (al ActLogic) DecryptRef(storage kvs.KeyValueStore, encryped_ref swarm.Address, grantee *ecdsa.PublicKey) (swarm.Address, error) { keys, err := al.getKeys(grantee) if err != nil { return swarm.EmptyAddress, err @@ -126,7 +126,7 @@ func (al ActLogic) DecryptRef(rootHash swarm.Address, encryped_ref swarm.Address accessKeyDecryptionKey := keys[1] // Lookup encrypted access key from the ACT manifest - encryptedAccessKey, err := al.act.Lookup(rootHash, lookupKey) + encryptedAccessKey, err := storage.Get(lookupKey) if err != nil { return swarm.EmptyAddress, err } @@ -148,9 +148,8 @@ func (al ActLogic) DecryptRef(rootHash swarm.Address, encryped_ref swarm.Address return swarm.NewAddress(ref), nil } -func NewLogic(S Session, act Act) ActLogic { +func NewLogic(S Session) ActLogic { return ActLogic{ Session: S, - act: act, } } diff --git a/pkg/dynamicaccess/accesslogic_test.go b/pkg/dynamicaccess/accesslogic_test.go index 1a6da8af80b..cb5ff0a6c78 100644 --- a/pkg/dynamicaccess/accesslogic_test.go +++ b/pkg/dynamicaccess/accesslogic_test.go @@ -8,15 +8,15 @@ import ( "testing" "github.com/ethersphere/bee/pkg/dynamicaccess" - mockstorer "github.com/ethersphere/bee/pkg/storer/mock" + kvsmock "github.com/ethersphere/bee/pkg/kvs/mock" "github.com/ethersphere/bee/pkg/swarm" ) // Generates a new test environment with a fix private key -func setupAccessLogic2(act dynamicaccess.Act) dynamicaccess.ActLogic { +func setupAccessLogic2() dynamicaccess.ActLogic { privateKey := generateFixPrivateKey(1000) diffieHellman := dynamicaccess.NewDefaultSession(&privateKey) - al := dynamicaccess.NewLogic(diffieHellman, act) + al := dynamicaccess.NewLogic(diffieHellman) return al } @@ -41,10 +41,9 @@ func generateFixPrivateKey(input int64) ecdsa.PrivateKey { func TestDecryptRef_Success(t *testing.T) { id0 := generateFixPrivateKey(0) - var mockStorer = mockstorer.New() - act := dynamicaccess.NewInManifestAct(mockStorer) - al := setupAccessLogic2(act) - ref, err := al.AddPublisher(swarm.EmptyAddress, &id0.PublicKey) + s := kvsmock.New() + al := setupAccessLogic2() + err := al.AddPublisher(s, &id0.PublicKey) if err != nil { t.Errorf("AddPublisher: expected no error, got %v", err) } @@ -54,14 +53,14 @@ func TestDecryptRef_Success(t *testing.T) { expectedRef := swarm.NewAddress(byteRef) t.Logf("encryptedRef: %s", expectedRef.String()) - encryptedRef, err := al.EncryptRef(ref, &id0.PublicKey, expectedRef) + encryptedRef, err := al.EncryptRef(s, &id0.PublicKey, expectedRef) t.Logf("encryptedRef: %s", encryptedRef.String()) if err != nil { t.Errorf("There was an error while calling EncryptRef: ") t.Error(err) } - acutalRef, err := al.DecryptRef(ref, encryptedRef, &id0.PublicKey) + acutalRef, err := al.DecryptRef(s, encryptedRef, &id0.PublicKey) if err != nil { t.Errorf("There was an error while calling Get: ") t.Error(err) @@ -76,18 +75,18 @@ func TestDecryptRef_Success(t *testing.T) { func TestDecryptRef_Error(t *testing.T) { id0 := generateFixPrivateKey(0) - act := dynamicaccess.NewInMemoryAct() - al := setupAccessLogic2(act) - ref, err := al.AddPublisher(swarm.EmptyAddress, &id0.PublicKey) + s := kvsmock.New() + al := setupAccessLogic2() + err := al.AddPublisher(s, &id0.PublicKey) if err != nil { t.Errorf("AddPublisher: expected no error, got %v", err) } expectedRef := "39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559" - encryptedRef, _ := al.EncryptRef(ref, &id0.PublicKey, swarm.NewAddress([]byte(expectedRef))) + encryptedRef, _ := al.EncryptRef(s, &id0.PublicKey, swarm.NewAddress([]byte(expectedRef))) - r, err := al.DecryptRef(swarm.RandAddress(t), encryptedRef, nil) + r, err := al.DecryptRef(s, encryptedRef, nil) if err == nil { t.Logf("r: %s", r.String()) t.Errorf("Get should give back encrypted access key not found error!") @@ -97,9 +96,10 @@ func TestDecryptRef_Error(t *testing.T) { func TestAddPublisher(t *testing.T) { id0 := generateFixPrivateKey(0) savedLookupKey := "bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a" - act := dynamicaccess.NewInMemoryAct() - al := setupAccessLogic2(act) - ref, err := al.AddPublisher(swarm.EmptyAddress, &id0.PublicKey) + s := kvsmock.New() + + al := setupAccessLogic2() + err := al.AddPublisher(s, &id0.PublicKey) if err != nil { t.Errorf("AddPublisher: expected no error, got %v", err) } @@ -109,7 +109,7 @@ func TestAddPublisher(t *testing.T) { t.Errorf("DecodeString: expected no error, got %v", err) } - encryptedAccessKey, err := act.Lookup(ref, decodedSavedLookupKey) + encryptedAccessKey, err := s.Get(decodedSavedLookupKey) if err != nil { t.Errorf("Lookup: expected no error, got %v", err) } @@ -120,7 +120,7 @@ func TestAddPublisher(t *testing.T) { if len(decodedEncryptedAccessKey) != 64 { t.Errorf("AddPublisher: expected encrypted access key length 64, got %d", len(decodedEncryptedAccessKey)) } - if act == nil { + if s == nil { t.Errorf("AddPublisher: expected act, got nil") } } @@ -135,19 +135,19 @@ func TestAddNewGranteeToContent(t *testing.T) { firstAddedGranteeLookupKey := "e221a2abf64357260e8f2c937ee938aed98dce097e537c1a3fd4caf73510dbe4" secondAddedGranteeLookupKey := "8fe8dff7cd15a6a0095c1b25071a5691e7c901fd0b95857a96c0e4659b48716a" - act := dynamicaccess.NewInMemoryAct() - al := setupAccessLogic2(act) - ref, err := al.AddPublisher(swarm.EmptyAddress, &id0.PublicKey) + s := kvsmock.New() + al := setupAccessLogic2() + err := al.AddPublisher(s, &id0.PublicKey) if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } - ref, err = al.AddGrantee(ref, &id0.PublicKey, &id1.PublicKey, nil) + err = al.AddGrantee(s, &id0.PublicKey, &id1.PublicKey, nil) if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } - ref, err = al.AddGrantee(ref, &id0.PublicKey, &id2.PublicKey, nil) + err = al.AddGrantee(s, &id0.PublicKey, &id2.PublicKey, nil) if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } @@ -156,7 +156,7 @@ func TestAddNewGranteeToContent(t *testing.T) { if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } - result, _ := act.Lookup(ref, lookupKeyAsByte) + result, _ := s.Get(lookupKeyAsByte) hexEncodedEncryptedAK := hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { t.Errorf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) @@ -166,7 +166,7 @@ func TestAddNewGranteeToContent(t *testing.T) { if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } - result, _ = act.Lookup(ref, lookupKeyAsByte) + result, _ = s.Get(lookupKeyAsByte) hexEncodedEncryptedAK = hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { t.Errorf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) @@ -176,36 +176,9 @@ func TestAddNewGranteeToContent(t *testing.T) { if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } - result, _ = act.Lookup(ref, lookupKeyAsByte) + result, _ = s.Get(lookupKeyAsByte) hexEncodedEncryptedAK = hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { t.Errorf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) } } - -func TestEncryptRef(t *testing.T) { - ref := "39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559" - savedEncryptedRef := "230cdcfb2e67adddb2822b38f70105213ab3e4f97d03560bfbfbb218f487c5303e9aa9a97e62aa1a8003f162679e7c65e1c8e3aacaec2043fd5d2a4a7d69285e" - - id0 := generateFixPrivateKey(0) - act := dynamicaccess.NewInMemoryAct() - al := setupAccessLogic2(act) - decodedLookupKey, err := hex.DecodeString("bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a") - if err != nil { - t.Errorf("EncryptRef: expected no error, got %v", err) - } - - addRef, err := act.Add(swarm.EmptyAddress, decodedLookupKey, []byte("42")) - if err != nil { - t.Errorf("Add: expected no error, got %v", err) - } - - encryptedRefValue, err := al.EncryptRef(addRef, &id0.PublicKey, swarm.NewAddress([]byte(ref))) - if err != nil { - t.Errorf("EncryptRef: expected no error, got %v", err) - } - - if encryptedRefValue.String() != savedEncryptedRef { - t.Errorf("EncryptRef: expected encrypted ref, got empty address") - } -} diff --git a/pkg/dynamicaccess/act.go b/pkg/dynamicaccess/act.go deleted file mode 100644 index f7595b5b1ec..00000000000 --- a/pkg/dynamicaccess/act.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2024 The Swarm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package dynamicaccess - -import ( - "github.com/ethersphere/bee/pkg/api" - "github.com/ethersphere/bee/pkg/kvs" - kvsmanifest "github.com/ethersphere/bee/pkg/kvs/manifest" - kvsmemory "github.com/ethersphere/bee/pkg/kvs/memory" - "github.com/ethersphere/bee/pkg/swarm" -) - -// Act represents an interface for accessing and manipulating data. -type Act interface { - // Add adds a key-value pair to the data store. - Add(rootHash swarm.Address, key []byte, val []byte) (swarm.Address, error) - - // Lookup retrieves the value associated with the given key from the data store. - Lookup(rootHash swarm.Address, key []byte) ([]byte, error) - - // Load loads the data store from the given address. - //Load(addr swarm.Address) error - - // Store stores the current state of the data store and returns the address of the ACT. - //Store() (swarm.Address, error) -} - -// inKvsAct is an implementation of the Act interface that uses kvs storage. -type inKvsAct struct { - storage kvs.KeyValueStore -} - -// Add adds a key-value pair to the in-memory data store. -func (act *inKvsAct) Add(rootHash swarm.Address, key []byte, val []byte) (swarm.Address, error) { - return act.storage.Put(rootHash, key, val) -} - -// Lookup retrieves the value associated with the given key from the in-memory data store. -func (act *inKvsAct) Lookup(rootHash swarm.Address, key []byte) ([]byte, error) { - return act.storage.Get(rootHash, key) -} - -// NewInMemoryAct creates a new instance of the Act interface with in-memory storage. -func NewInMemoryAct() Act { - s, err := kvs.NewKeyValueStore(nil, kvsmemory.KvsTypeMemory) - if err != nil { - return nil - } - return &inKvsAct{ - storage: s, - } -} - -// NewInManifestAct creates a new instance of the Act interface with manifest storage. -func NewInManifestAct(storer api.Storer) Act { - s, err := kvs.NewKeyValueStore(storer, kvsmanifest.KvsTypeManifest) - if err != nil { - return nil - } - return &inKvsAct{ - storage: s, - } -} diff --git a/pkg/dynamicaccess/act_test.go b/pkg/dynamicaccess/act_test.go deleted file mode 100644 index 6522a76b9ed..00000000000 --- a/pkg/dynamicaccess/act_test.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2024 The Swarm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package dynamicaccess_test - -import ( - "bytes" - "encoding/hex" - "testing" - - "github.com/ethersphere/bee/pkg/dynamicaccess" - mockstorer "github.com/ethersphere/bee/pkg/storer/mock" - "github.com/ethersphere/bee/pkg/swarm" -) - -type ActType int - -const ( - Memory ActType = iota - Manifest -) - -func (a ActType) String() string { - return [...]string{"Memory", "Manifest"}[a] -} - -var mockStorer = mockstorer.New() - -var acts = map[ActType]func() dynamicaccess.Act{ - Memory: dynamicaccess.NewInMemoryAct, - Manifest: func() dynamicaccess.Act { return dynamicaccess.NewInManifestAct(mockStorer) }, -} - -func TestActAddLookup(t *testing.T) { - for actType, actCreate := range acts { - t.Logf("Running test for ActType: %s", actType) - act := actCreate() - - lookupKey := swarm.RandAddress(t).Bytes() - encryptedAccesskey := swarm.RandAddress(t).Bytes() - - ref, err := act.Add(swarm.EmptyAddress, lookupKey, encryptedAccesskey) - if err != nil { - t.Errorf("Add() should not return an error: %v", err) - } - - key, err := act.Lookup(ref, lookupKey) - if err != nil { - t.Errorf("Lookup() should not return an error: %v", err) - } - - if !bytes.Equal(key, encryptedAccesskey) { - t.Errorf("Get() value is not the expected %s != %s", hex.EncodeToString(key), hex.EncodeToString(encryptedAccesskey)) - } - } -} - -func TestActAddLookupWithNew(t *testing.T) { - for actType, actCreate := range acts { - t.Logf("Running test for ActType: %s", actType) - act1 := actCreate() - lookupKey := swarm.RandAddress(t).Bytes() - encryptedAccesskey := swarm.RandAddress(t).Bytes() - - ref, err := act1.Add(swarm.EmptyAddress, lookupKey, encryptedAccesskey) - if err != nil { - t.Fatalf("Add() should not return an error: %v", err) - } - - act2 := actCreate() - key, err := act2.Lookup(ref, lookupKey) - if err != nil { - t.Fatalf("Lookup() should not return an error: %v", err) - } - - if !bytes.Equal(key, encryptedAccesskey) { - t.Errorf("Get() value is not the expected %s != %s", hex.EncodeToString(key), hex.EncodeToString(encryptedAccesskey)) - } - } -} - -/* -func TestActStoreLoad(t *testing.T) { - - act := dynamicaccess.NewInMemoryAct() - lookupKey := swarm.RandAddress(t).Bytes() - encryptedAccesskey := swarm.RandAddress(t).Bytes() - err := act.Add(lookupKey, encryptedAccesskey) - if err != nil { - t.Error("Add() should not return an error") - } - - swarm_ref, err := act.Store() - if err != nil { - t.Error("Store() should not return an error") - } - - actualAct := dynamicaccess.NewInMemoryAct() - actualAct.Load(swarm_ref) - actualEak, _ := actualAct.Lookup(lookupKey) - if !bytes.Equal(actualEak, encryptedAccesskey) { - t.Errorf("actualAct.Load() value is not the expected %s != %s", hex.EncodeToString(actualEak), hex.EncodeToString(encryptedAccesskey)) - } -} -*/ diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index b1c6e306934..d118e22489f 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -3,6 +3,7 @@ package dynamicaccess import ( "crypto/ecdsa" + kvsmock "github.com/ethersphere/bee/pkg/kvs/mock" "github.com/ethersphere/bee/pkg/swarm" ) @@ -18,29 +19,30 @@ type defaultController struct { } func (c *defaultController) DownloadHandler(timestamp int64, enryptedRef swarm.Address, publisher *ecdsa.PublicKey, tag string) (swarm.Address, error) { - _, err := c.history.Lookup(timestamp) + kvs, err := c.history.Lookup(timestamp) if err != nil { return swarm.EmptyAddress, err } - addr, err := c.accessLogic.DecryptRef(swarm.EmptyAddress, enryptedRef, publisher) + addr, err := c.accessLogic.DecryptRef(kvs, enryptedRef, publisher) return addr, err } func (c *defaultController) UploadHandler(ref swarm.Address, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error) { - act, err := c.history.Lookup(0) + kvs, err := c.history.Lookup(0) if err != nil { return swarm.EmptyAddress, err } - var actRef swarm.Address - if act == nil { + if kvs == nil { // new feed - actRef, err = c.granteeManager.Publish(swarm.EmptyAddress, publisher, topic) + // TODO: putter session to create kvs + kvs = kvsmock.New() + _, err = c.granteeManager.Publish(kvs, publisher, topic) if err != nil { return swarm.EmptyAddress, err } } - //FIXME: check if ACT is consistent with the grantee list - return c.accessLogic.EncryptRef(actRef, publisher, ref) + //FIXME: check if kvs is consistent with the grantee list + return c.accessLogic.EncryptRef(kvs, publisher, ref) } func NewController(history History, granteeManager GranteeManager, accessLogic ActLogic) Controller { diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index 8f876232b05..63f80059e08 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -12,6 +12,7 @@ import ( "github.com/ethersphere/bee/pkg/dynamicaccess" "github.com/ethersphere/bee/pkg/dynamicaccess/mock" "github.com/ethersphere/bee/pkg/encryption" + kvsmock "github.com/ethersphere/bee/pkg/kvs/mock" "github.com/ethersphere/bee/pkg/swarm" "golang.org/x/crypto/sha3" ) @@ -22,10 +23,10 @@ func mockTestHistory(key, val []byte) dynamicaccess.History { var ( h = mock.NewHistory() now = time.Now() - act = dynamicaccess.NewInMemoryAct() + s = kvsmock.New() ) - act.Add(swarm.EmptyAddress, key, val) - h.Insert(now.AddDate(-3, 0, 0).Unix(), act) + _ = s.Put(key, val) + h.Insert(now.AddDate(-3, 0, 0).Unix(), s) return h } diff --git a/pkg/dynamicaccess/grantee_manager.go b/pkg/dynamicaccess/grantee_manager.go index 0627db9794a..004a933b006 100644 --- a/pkg/dynamicaccess/grantee_manager.go +++ b/pkg/dynamicaccess/grantee_manager.go @@ -3,13 +3,14 @@ package dynamicaccess import ( "crypto/ecdsa" + "github.com/ethersphere/bee/pkg/kvs" "github.com/ethersphere/bee/pkg/swarm" ) type GranteeManager interface { Get(topic string) []*ecdsa.PublicKey Add(topic string, addList []*ecdsa.PublicKey) error - Publish(rootHash swarm.Address, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error) + Publish(kvs kvs.KeyValueStore, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error) // HandleGrantees(topic string, addList, removeList []*ecdsa.PublicKey) *Act @@ -36,10 +37,10 @@ func (gm *granteeManager) Add(topic string, addList []*ecdsa.PublicKey) error { return gm.granteeList.Add(topic, addList) } -func (gm *granteeManager) Publish(rootHash swarm.Address, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error) { - ref, err := gm.accessLogic.AddPublisher(rootHash, publisher) +func (gm *granteeManager) Publish(kvs kvs.KeyValueStore, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error) { + err := gm.accessLogic.AddPublisher(kvs, publisher) for _, grantee := range gm.granteeList.Get(topic) { - ref, err = gm.accessLogic.AddGrantee(ref, publisher, grantee, nil) + err = gm.accessLogic.AddGrantee(kvs, publisher, grantee, nil) } - return ref, err + return swarm.EmptyAddress, err } diff --git a/pkg/dynamicaccess/grantee_manager_test.go b/pkg/dynamicaccess/grantee_manager_test.go index 2144fd1410a..7ac43425e10 100644 --- a/pkg/dynamicaccess/grantee_manager_test.go +++ b/pkg/dynamicaccess/grantee_manager_test.go @@ -8,13 +8,12 @@ import ( "testing" "github.com/ethersphere/bee/pkg/dynamicaccess" - "github.com/ethersphere/bee/pkg/swarm" + kvsmock "github.com/ethersphere/bee/pkg/kvs/mock" ) func setupAccessLogic(privateKey *ecdsa.PrivateKey) dynamicaccess.ActLogic { - act := dynamicaccess.NewInMemoryAct() si := dynamicaccess.NewDefaultSession(privateKey) - al := dynamicaccess.NewLogic(si, act) + al := dynamicaccess.NewLogic(si) return al } @@ -33,6 +32,7 @@ func TestAdd(t *testing.T) { if err != nil { t.Errorf("Add() returned an error") } - m.Publish(swarm.EmptyAddress, &pub.PublicKey, "topic") + s := kvsmock.New() + m.Publish(s, &pub.PublicKey, "topic") fmt.Println("") } diff --git a/pkg/dynamicaccess/history.go b/pkg/dynamicaccess/history.go index e2705fffeac..63ec00847a6 100644 --- a/pkg/dynamicaccess/history.go +++ b/pkg/dynamicaccess/history.go @@ -2,34 +2,35 @@ package dynamicaccess import ( "github.com/ethereum/go-ethereum/common" + "github.com/ethersphere/bee/pkg/kvs" ) type History interface { - Add(timestamp int64, act Act) error - Get(timestamp int64) (Act, error) - Lookup(at int64) (Act, error) + Add(timestamp int64, kvs kvs.KeyValueStore) error + Get(timestamp int64) (kvs.KeyValueStore, error) + Lookup(at int64) (kvs.KeyValueStore, error) } var _ History = (*history)(nil) type history struct { - history map[int64]*Act + history map[int64]*kvs.KeyValueStore } func NewHistory(topic []byte, owner common.Address) *history { - return &history{history: make(map[int64]*Act)} + return &history{history: make(map[int64]*kvs.KeyValueStore)} } -func (h *history) Add(timestamp int64, act Act) error { +func (h *history) Add(timestamp int64, kvs kvs.KeyValueStore) error { return nil } -func (h *history) Lookup(at int64) (Act, error) { +func (h *history) Lookup(at int64) (kvs.KeyValueStore, error) { return nil, nil } -func (h *history) Get(timestamp int64) (Act, error) { +func (h *history) Get(timestamp int64) (kvs.KeyValueStore, error) { // get the feed return nil, nil } diff --git a/pkg/dynamicaccess/history_test.go b/pkg/dynamicaccess/history_test.go index e501e075697..c2164df7d39 100644 --- a/pkg/dynamicaccess/history_test.go +++ b/pkg/dynamicaccess/history_test.go @@ -7,7 +7,7 @@ import ( "github.com/ethersphere/bee/pkg/dynamicaccess" "github.com/ethersphere/bee/pkg/dynamicaccess/mock" - "github.com/ethersphere/bee/pkg/swarm" + kvsmock "github.com/ethersphere/bee/pkg/kvs/mock" "github.com/stretchr/testify/assert" ) @@ -32,8 +32,8 @@ func TestHistoryLookup(t *testing.T) { for _, tt := range tests { t.Run("", func(t *testing.T) { - actAt, _ := h.Lookup(tt.input) - output, _ := actAt.Lookup(swarm.EmptyAddress, []byte("key1")) + sAt, _ := h.Lookup(tt.input) + output, _ := sAt.Get([]byte("key1")) assert.Equal(t, output, hex.EncodeToString([]byte(tt.expected))) }) } @@ -41,19 +41,19 @@ func TestHistoryLookup(t *testing.T) { func prepareTestHistory() dynamicaccess.History { var ( - h = mock.NewHistory() - now = time.Now() - act1 = dynamicaccess.NewInMemoryAct() - act2 = dynamicaccess.NewInMemoryAct() - act3 = dynamicaccess.NewInMemoryAct() + h = mock.NewHistory() + now = time.Now() + s1 = kvsmock.New() + s2 = kvsmock.New() + s3 = kvsmock.New() ) - act1.Add(swarm.EmptyAddress, []byte("key1"), []byte("value1")) - act2.Add(swarm.EmptyAddress, []byte("key1"), []byte("value2")) - act3.Add(swarm.EmptyAddress, []byte("key1"), []byte("value3")) + s1.Put([]byte("key1"), []byte("value1")) + s2.Put([]byte("key1"), []byte("value2")) + s3.Put([]byte("key1"), []byte("value3")) - h.Insert(now.AddDate(-3, 0, 0).Unix(), act1) - h.Insert(now.AddDate(-2, 0, 0).Unix(), act2) - h.Insert(now.AddDate(-1, 0, 0).Unix(), act3) + h.Insert(now.AddDate(-3, 0, 0).Unix(), s1) + h.Insert(now.AddDate(-2, 0, 0).Unix(), s2) + h.Insert(now.AddDate(-1, 0, 0).Unix(), s3) return h } diff --git a/pkg/dynamicaccess/mock/act.go b/pkg/dynamicaccess/mock/act.go deleted file mode 100644 index 721a4778829..00000000000 --- a/pkg/dynamicaccess/mock/act.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2024 The Swarm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package mock - -import ( - "github.com/ethersphere/bee/pkg/dynamicaccess" - "github.com/ethersphere/bee/pkg/swarm" -) - -type ActMock struct { - AddFunc func(root swarm.Address, key []byte, val []byte) (swarm.Address, error) - LookupFunc func(root swarm.Address, key []byte) ([]byte, error) - LoadFunc func(addr swarm.Address) error - StoreFunc func() (swarm.Address, error) -} - -var _ dynamicaccess.Act = (*ActMock)(nil) - -func (act *ActMock) Add(root swarm.Address, key []byte, val []byte) (swarm.Address, error) { - if act.AddFunc == nil { - return swarm.EmptyAddress, nil - } - return act.AddFunc(root, key, val) -} - -func (act *ActMock) Lookup(root swarm.Address, key []byte) ([]byte, error) { - if act.LookupFunc == nil { - return make([]byte, 0), nil - } - return act.LookupFunc(root, key) -} - -func (act *ActMock) Load(addr swarm.Address) error { - if act.LoadFunc == nil { - return nil - } - return act.LoadFunc(addr) -} - -func (act *ActMock) Store() (swarm.Address, error) { - if act.StoreFunc == nil { - return swarm.EmptyAddress, nil - } - return act.StoreFunc() -} - -func NewActMock( - addFunc func(swarm.Address, []byte, []byte) (swarm.Address, error), - getFunc func(swarm.Address, []byte) ([]byte, error)) dynamicaccess.Act { - return &ActMock{ - AddFunc: addFunc, - LookupFunc: getFunc, - } -} diff --git a/pkg/dynamicaccess/mock/history.go b/pkg/dynamicaccess/mock/history.go index 68559c2277a..91b544a0760 100644 --- a/pkg/dynamicaccess/mock/history.go +++ b/pkg/dynamicaccess/mock/history.go @@ -6,31 +6,31 @@ import ( "time" "github.com/ethersphere/bee/pkg/crypto" - "github.com/ethersphere/bee/pkg/dynamicaccess" "github.com/ethersphere/bee/pkg/feeds" + "github.com/ethersphere/bee/pkg/kvs" "github.com/ethersphere/bee/pkg/storage" "github.com/ethersphere/bee/pkg/swarm" ) type historyMock struct { - history map[int64]dynamicaccess.Act + history map[int64]kvs.KeyValueStore } func NewHistory() *historyMock { - return &historyMock{history: make(map[int64]dynamicaccess.Act)} + return &historyMock{history: make(map[int64]kvs.KeyValueStore)} } -func (h *historyMock) Add(timestamp int64, act dynamicaccess.Act) error { +func (h *historyMock) Add(timestamp int64, act kvs.KeyValueStore) error { h.history[timestamp] = act return nil } -func (h *historyMock) Insert(timestamp int64, act dynamicaccess.Act) *historyMock { +func (h *historyMock) Insert(timestamp int64, act kvs.KeyValueStore) *historyMock { h.Add(timestamp, act) return h } -func (h *historyMock) Lookup(at int64) (dynamicaccess.Act, error) { +func (h *historyMock) Lookup(at int64) (kvs.KeyValueStore, error) { keys := []int64{} for k := range h.history { keys = append(keys, k) @@ -52,7 +52,7 @@ func (h *historyMock) Lookup(at int64) (dynamicaccess.Act, error) { return nil, nil } -func (h *historyMock) Get(timestamp int64) (dynamicaccess.Act, error) { +func (h *historyMock) Get(timestamp int64) (kvs.KeyValueStore, error) { return h.history[timestamp], nil } diff --git a/pkg/kvs/kvs.go b/pkg/kvs/kvs.go index 65c2a0be8b9..91731f24af3 100644 --- a/pkg/kvs/kvs.go +++ b/pkg/kvs/kvs.go @@ -1,34 +1,70 @@ package kvs import ( - "errors" + "context" + "encoding/hex" - "github.com/ethersphere/bee/pkg/api" - "github.com/ethersphere/bee/pkg/kvs/manifest" - "github.com/ethersphere/bee/pkg/kvs/memory" + "github.com/ethersphere/bee/pkg/file" + "github.com/ethersphere/bee/pkg/manifest" + "github.com/ethersphere/bee/pkg/storer" "github.com/ethersphere/bee/pkg/swarm" ) -var ErrInvalidKvsType = errors.New("kvs: invalid type") - type KeyValueStore interface { - Get(rootHash swarm.Address, key []byte) ([]byte, error) - Put(rootHash swarm.Address, key, value []byte) (swarm.Address, error) + Get(key []byte) ([]byte, error) + Put(key, value []byte) error + Save() (swarm.Address, error) +} + +type keyValueStore struct { + manifest manifest.Interface + putter storer.PutterSession +} + +var _ KeyValueStore = (*keyValueStore)(nil) + +// TODO: pass context as dep. +func (s *keyValueStore) Get(key []byte) ([]byte, error) { + entry, err := s.manifest.Lookup(context.Background(), hex.EncodeToString(key)) + if err != nil { + return nil, err + } + ref := entry.Reference() + return ref.Bytes(), nil +} + +func (s *keyValueStore) Put(key []byte, value []byte) error { + return s.manifest.Add(context.Background(), hex.EncodeToString(key), manifest.NewEntry(swarm.NewAddress(value), map[string]string{})) +} + +func (s *keyValueStore) Save() (swarm.Address, error) { + ref, err := s.manifest.Store(context.Background()) + if err != nil { + return swarm.ZeroAddress, err + } + err = s.putter.Done(ref) + if err != nil { + return swarm.ZeroAddress, err + } + return ref, nil } -// func NewDefaultKeyValueStore(storer api.Storer) (KeyValueStore, error) { -// return NewKeyValueStore(storer, memory.KvsTypeMemory) -// } - -func NewKeyValueStore(storer api.Storer, kvsType string) (KeyValueStore, error) { - switch kvsType { - case "": - return memory.NewMemoryKeyValueStore() - case memory.KvsTypeMemory: - return memory.NewMemoryKeyValueStore() - case manifest.KvsTypeManifest: - return manifest.NewManifestKeyValueStore(storer) - default: - return nil, ErrInvalidKvsType +func New(ls file.LoadSaver, putter storer.PutterSession, rootHash swarm.Address) KeyValueStore { + var ( + manif manifest.Interface + err error + ) + if swarm.ZeroAddress.Equal(rootHash) || swarm.EmptyAddress.Equal(rootHash) { + manif, err = manifest.NewSimpleManifest(ls) + } else { + manif, err = manifest.NewSimpleManifestReference(rootHash, ls) + } + if err != nil { + return nil + } + + return &keyValueStore{ + manifest: manif, + putter: putter, } } diff --git a/pkg/kvs/kvs_test.go b/pkg/kvs/kvs_test.go new file mode 100644 index 00000000000..234802de2e3 --- /dev/null +++ b/pkg/kvs/kvs_test.go @@ -0,0 +1,132 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package kvs_test + +import ( + "bytes" + "context" + "encoding/hex" + "testing" + + "github.com/ethersphere/bee/pkg/file" + "github.com/ethersphere/bee/pkg/file/loadsave" + "github.com/ethersphere/bee/pkg/file/pipeline" + "github.com/ethersphere/bee/pkg/file/pipeline/builder" + "github.com/ethersphere/bee/pkg/file/redundancy" + "github.com/ethersphere/bee/pkg/kvs" + "github.com/ethersphere/bee/pkg/storage" + mockstorer "github.com/ethersphere/bee/pkg/storer/mock" + "github.com/ethersphere/bee/pkg/swarm" +) + +var mockStorer = mockstorer.New() + +func requestPipelineFactory(ctx context.Context, s storage.Putter, encrypt bool, rLevel redundancy.Level) func() pipeline.Interface { + return func() pipeline.Interface { + return builder.NewPipelineBuilder(ctx, s, encrypt, rLevel) + } +} + +func createLs() file.LoadSaver { + return loadsave.New(mockStorer.ChunkStore(), mockStorer.Cache(), requestPipelineFactory(context.Background(), mockStorer.Cache(), false, redundancy.NONE)) +} + +func keyValuePair(t *testing.T) ([]byte, []byte) { + return swarm.RandAddress(t).Bytes(), swarm.RandAddress(t).Bytes() +} + +func TestKvsAddLookup(t *testing.T) { + ls := createLs() + + putter := mockStorer.DirectUpload() + s := kvs.New(ls, putter, swarm.ZeroAddress) + + lookupKey, encryptedAccesskey := keyValuePair(t) + + err := s.Put(lookupKey, encryptedAccesskey) + if err != nil { + t.Errorf("Add() should not return an error: %v", err) + } + + key, err := s.Get(lookupKey) + if err != nil { + t.Errorf("Lookup() should not return an error: %v", err) + } + + if !bytes.Equal(key, encryptedAccesskey) { + t.Errorf("Get() value is not the expected %s != %s", hex.EncodeToString(key), hex.EncodeToString(encryptedAccesskey)) + } + +} + +func TestKvsAddLookupWithSave(t *testing.T) { + ls := createLs() + putter := mockStorer.DirectUpload() + s1 := kvs.New(ls, putter, swarm.ZeroAddress) + lookupKey, encryptedAccesskey := keyValuePair(t) + + err := s1.Put(lookupKey, encryptedAccesskey) + if err != nil { + t.Fatalf("Add() should not return an error: %v", err) + } + ref, err := s1.Save() + if err != nil { + t.Fatalf("Save() should not return an error: %v", err) + } + s2 := kvs.New(ls, putter, ref) + key, err := s2.Get(lookupKey) + if err != nil { + t.Fatalf("Lookup() should not return an error: %v", err) + } + + if !bytes.Equal(key, encryptedAccesskey) { + t.Errorf("Get() value is not the expected %s != %s", hex.EncodeToString(key), hex.EncodeToString(encryptedAccesskey)) + } + +} + +func TestKvsAddSaveAdd(t *testing.T) { + ls := createLs() + putter := mockStorer.DirectUpload() + kvs1 := kvs.New(ls, putter, swarm.ZeroAddress) + kvs1key1, kvs1val1 := keyValuePair(t) + + err := kvs1.Put(kvs1key1, kvs1val1) + if err != nil { + t.Fatalf("Add() should not return an error: %v", err) + } + ref, err := kvs1.Save() + if err != nil { + t.Fatalf("Save() should not return an error: %v", err) + } + + // New KVS + kvs2 := kvs.New(ls, putter, ref) + + kvs2key1, kvs2val1 := keyValuePair(t) + + // put after save + kvs2.Put(kvs2key1, kvs2val1) + + // get after save + kvs2get1, err := kvs2.Get(kvs2key1) + if err != nil { + t.Fatalf("Lookup() should not return an error: %v", err) + } + if !bytes.Equal(kvs2get1, kvs2val1) { + t.Errorf("Get() value is not the expected %s != %s", hex.EncodeToString(kvs2key1), hex.EncodeToString(kvs2val1)) + } + + // get before Save + kvs2get2, err := kvs2.Get(kvs1key1) + + if err != nil { + t.Fatalf("Lookup() should not return an error: %v", err) + } + if !bytes.Equal(kvs2get2, kvs1val1) { + t.Errorf("Get() value is not the expected %s != %s", hex.EncodeToString(kvs2get2), hex.EncodeToString(kvs1val1)) + } + +} diff --git a/pkg/kvs/manifest/kvs.go b/pkg/kvs/manifest/kvs.go deleted file mode 100644 index 19ca983d54a..00000000000 --- a/pkg/kvs/manifest/kvs.go +++ /dev/null @@ -1,86 +0,0 @@ -package manifest - -import ( - "context" - "encoding/hex" - - "github.com/ethersphere/bee/pkg/api" - "github.com/ethersphere/bee/pkg/file/loadsave" - "github.com/ethersphere/bee/pkg/file/pipeline" - "github.com/ethersphere/bee/pkg/file/pipeline/builder" - "github.com/ethersphere/bee/pkg/file/redundancy" - "github.com/ethersphere/bee/pkg/manifest" - "github.com/ethersphere/bee/pkg/storage" - "github.com/ethersphere/bee/pkg/swarm" -) - -const ( - // KvsTypeManifest represents - KvsTypeManifest = "Manifest" -) - -type ManifestKeyValueStore interface { - Get(rootHash swarm.Address, key []byte) ([]byte, error) - Put(rootHash swarm.Address, key, value []byte) (swarm.Address, error) -} - -type manifestKeyValueStore struct { - storer api.Storer -} - -// TODO: pass context as dep. -func (m *manifestKeyValueStore) Get(rootHash swarm.Address, key []byte) ([]byte, error) { - ls := loadsave.NewReadonly(m.storer.ChunkStore()) - // existing manif - manif, err := manifest.NewSimpleManifestReference(rootHash, ls) - if err != nil { - return nil, err - } - entry, err := manif.Lookup(context.Background(), hex.EncodeToString(key)) - if err != nil { - return nil, err - } - ref := entry.Reference() - return ref.Bytes(), nil -} - -func (m *manifestKeyValueStore) Put(rootHash swarm.Address, key []byte, value []byte) (swarm.Address, error) { - factory := requestPipelineFactory(context.Background(), m.storer.Cache(), false, redundancy.NONE) - ls := loadsave.New(m.storer.ChunkStore(), m.storer.Cache(), factory) - // existing manif - manif, err := manifest.NewSimpleManifestReference(rootHash, ls) - if err != nil { - // new manif - manif, err = manifest.NewSimpleManifest(ls) - if err != nil { - return swarm.EmptyAddress, err - } - } - err = manif.Add(context.Background(), hex.EncodeToString(key), manifest.NewEntry(swarm.NewAddress(value), map[string]string{})) - if err != nil { - return swarm.EmptyAddress, err - } - manifRef, err := manif.Store(context.Background()) - if err != nil { - return swarm.EmptyAddress, err - } - - putter := m.storer.DirectUpload() - err = putter.Done(manifRef) - if err != nil { - return swarm.EmptyAddress, err - } - return manifRef, nil -} - -func NewManifestKeyValueStore(storer api.Storer) (ManifestKeyValueStore, error) { - return &manifestKeyValueStore{ - storer: storer, - }, nil -} - -func requestPipelineFactory(ctx context.Context, s storage.Putter, encrypt bool, rLevel redundancy.Level) func() pipeline.Interface { - return func() pipeline.Interface { - return builder.NewPipelineBuilder(ctx, s, encrypt, rLevel) - } -} diff --git a/pkg/kvs/memory/kvs.go b/pkg/kvs/mock/kvs.go similarity index 55% rename from pkg/kvs/memory/kvs.go rename to pkg/kvs/mock/kvs.go index 3a1abc69c0e..1c2866137df 100644 --- a/pkg/kvs/memory/kvs.go +++ b/pkg/kvs/mock/kvs.go @@ -1,22 +1,13 @@ -package memory +package mock import ( "encoding/hex" "sync" + "github.com/ethersphere/bee/pkg/kvs" "github.com/ethersphere/bee/pkg/swarm" ) -const ( - // KvsTypeMemory represents - KvsTypeMemory = "Memory" -) - -type MemoryKeyValueStore interface { - Get(rootHash swarm.Address, key []byte) ([]byte, error) - Put(rootHash swarm.Address, key, value []byte) (swarm.Address, error) -} - var lock = &sync.Mutex{} type single struct { @@ -47,21 +38,27 @@ func getMemory() map[string][]byte { return mem.memoryMock } -type memoryKeyValueStore struct { +type mockKeyValueStore struct { } -func (m *memoryKeyValueStore) Get(rootHash swarm.Address, key []byte) ([]byte, error) { +var _ kvs.KeyValueStore = (*mockKeyValueStore)(nil) + +func (m *mockKeyValueStore) Get(key []byte) ([]byte, error) { mem := getMemory() val := mem[hex.EncodeToString(key)] return val, nil } -func (m *memoryKeyValueStore) Put(rootHash swarm.Address, key []byte, value []byte) (swarm.Address, error) { +func (m *mockKeyValueStore) Put(key []byte, value []byte) error { mem := getMemory() mem[hex.EncodeToString(key)] = value - return swarm.EmptyAddress, nil + return nil +} + +func (s *mockKeyValueStore) Save() (swarm.Address, error) { + return swarm.ZeroAddress, nil } -func NewMemoryKeyValueStore() (MemoryKeyValueStore, error) { - return &memoryKeyValueStore{}, nil +func New() kvs.KeyValueStore { + return &mockKeyValueStore{} } From 4a12e299260f11447de52679c653b75429ddbddf Mon Sep 17 00:00:00 2001 From: Kexort Date: Thu, 28 Mar 2024 10:58:49 +0100 Subject: [PATCH 25/41] Add referenced mock kvs (#26) --- pkg/kvs/mock/kvs.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/pkg/kvs/mock/kvs.go b/pkg/kvs/mock/kvs.go index 1c2866137df..fc091a88417 100644 --- a/pkg/kvs/mock/kvs.go +++ b/pkg/kvs/mock/kvs.go @@ -12,7 +12,7 @@ var lock = &sync.Mutex{} type single struct { // TODO string -> []byte ? - memoryMock map[string][]byte + memoryMock map[string]map[string][]byte } var singleInMemorySwarm *single @@ -23,13 +23,13 @@ func getInMemorySwarm() *single { defer lock.Unlock() if singleInMemorySwarm == nil { singleInMemorySwarm = &single{ - memoryMock: make(map[string][]byte)} + memoryMock: make(map[string]map[string][]byte)} } } return singleInMemorySwarm } -func getMemory() map[string][]byte { +func getMemory() map[string]map[string][]byte { ch := make(chan *single) go func() { ch <- getInMemorySwarm() @@ -39,26 +39,34 @@ func getMemory() map[string][]byte { } type mockKeyValueStore struct { + address swarm.Address } var _ kvs.KeyValueStore = (*mockKeyValueStore)(nil) func (m *mockKeyValueStore) Get(key []byte) ([]byte, error) { mem := getMemory() - val := mem[hex.EncodeToString(key)] + val := mem[m.address.String()][hex.EncodeToString(key)] return val, nil } func (m *mockKeyValueStore) Put(key []byte, value []byte) error { mem := getMemory() - mem[hex.EncodeToString(key)] = value + if _, ok := mem[m.address.String()]; !ok { + mem[m.address.String()] = make(map[string][]byte) + } + mem[m.address.String()][hex.EncodeToString(key)] = value return nil } -func (s *mockKeyValueStore) Save() (swarm.Address, error) { - return swarm.ZeroAddress, nil +func (m *mockKeyValueStore) Save() (swarm.Address, error) { + return m.address, nil } func New() kvs.KeyValueStore { - return &mockKeyValueStore{} + return &mockKeyValueStore{address: swarm.EmptyAddress} +} + +func NewReference(address swarm.Address) kvs.KeyValueStore { + return &mockKeyValueStore{address: address} } From fee9296f12dad867803c23f011dd94937efbd925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20S=C3=A1rai?= Date: Thu, 28 Mar 2024 14:10:17 +0100 Subject: [PATCH 26/41] Act kvs test (#27) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (test:) Refactor tests * (fix:) Save reset counter --------- Co-authored-by: Ferenc Sárai --- pkg/kvs/kvs.go | 17 +++- pkg/kvs/kvs_test.go | 221 ++++++++++++++++++++++++++------------------ 2 files changed, 145 insertions(+), 93 deletions(-) diff --git a/pkg/kvs/kvs.go b/pkg/kvs/kvs.go index 91731f24af3..1106f2ceee8 100644 --- a/pkg/kvs/kvs.go +++ b/pkg/kvs/kvs.go @@ -1,8 +1,13 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package kvs import ( "context" "encoding/hex" + "errors" "github.com/ethersphere/bee/pkg/file" "github.com/ethersphere/bee/pkg/manifest" @@ -19,6 +24,7 @@ type KeyValueStore interface { type keyValueStore struct { manifest manifest.Interface putter storer.PutterSession + putCnt int } var _ KeyValueStore = (*keyValueStore)(nil) @@ -34,10 +40,18 @@ func (s *keyValueStore) Get(key []byte) ([]byte, error) { } func (s *keyValueStore) Put(key []byte, value []byte) error { - return s.manifest.Add(context.Background(), hex.EncodeToString(key), manifest.NewEntry(swarm.NewAddress(value), map[string]string{})) + err := s.manifest.Add(context.Background(), hex.EncodeToString(key), manifest.NewEntry(swarm.NewAddress(value), map[string]string{})) + if err != nil { + return err + } + s.putCnt++ + return nil } func (s *keyValueStore) Save() (swarm.Address, error) { + if s.putCnt == 0 { + return swarm.ZeroAddress, errors.New("nothing to save") + } ref, err := s.manifest.Store(context.Background()) if err != nil { return swarm.ZeroAddress, err @@ -46,6 +60,7 @@ func (s *keyValueStore) Save() (swarm.Address, error) { if err != nil { return swarm.ZeroAddress, err } + s.putCnt = 0 return ref, nil } diff --git a/pkg/kvs/kvs_test.go b/pkg/kvs/kvs_test.go index 234802de2e3..309250dacff 100644 --- a/pkg/kvs/kvs_test.go +++ b/pkg/kvs/kvs_test.go @@ -5,9 +5,7 @@ package kvs_test import ( - "bytes" "context" - "encoding/hex" "testing" "github.com/ethersphere/bee/pkg/file" @@ -19,6 +17,7 @@ import ( "github.com/ethersphere/bee/pkg/storage" mockstorer "github.com/ethersphere/bee/pkg/storer/mock" "github.com/ethersphere/bee/pkg/swarm" + "github.com/stretchr/testify/assert" ) var mockStorer = mockstorer.New() @@ -37,96 +36,134 @@ func keyValuePair(t *testing.T) ([]byte, []byte) { return swarm.RandAddress(t).Bytes(), swarm.RandAddress(t).Bytes() } -func TestKvsAddLookup(t *testing.T) { - ls := createLs() - - putter := mockStorer.DirectUpload() - s := kvs.New(ls, putter, swarm.ZeroAddress) - - lookupKey, encryptedAccesskey := keyValuePair(t) - - err := s.Put(lookupKey, encryptedAccesskey) - if err != nil { - t.Errorf("Add() should not return an error: %v", err) - } - - key, err := s.Get(lookupKey) - if err != nil { - t.Errorf("Lookup() should not return an error: %v", err) - } - - if !bytes.Equal(key, encryptedAccesskey) { - t.Errorf("Get() value is not the expected %s != %s", hex.EncodeToString(key), hex.EncodeToString(encryptedAccesskey)) - } - -} - -func TestKvsAddLookupWithSave(t *testing.T) { - ls := createLs() - putter := mockStorer.DirectUpload() - s1 := kvs.New(ls, putter, swarm.ZeroAddress) - lookupKey, encryptedAccesskey := keyValuePair(t) - - err := s1.Put(lookupKey, encryptedAccesskey) - if err != nil { - t.Fatalf("Add() should not return an error: %v", err) - } - ref, err := s1.Save() - if err != nil { - t.Fatalf("Save() should not return an error: %v", err) - } - s2 := kvs.New(ls, putter, ref) - key, err := s2.Get(lookupKey) - if err != nil { - t.Fatalf("Lookup() should not return an error: %v", err) - } - - if !bytes.Equal(key, encryptedAccesskey) { - t.Errorf("Get() value is not the expected %s != %s", hex.EncodeToString(key), hex.EncodeToString(encryptedAccesskey)) - } - +func TestKvs(t *testing.T) { + + s := kvs.New(createLs(), mockStorer.DirectUpload(), swarm.ZeroAddress) + key, val := keyValuePair(t) + + t.Run("Get non-existent key should return error", func(t *testing.T) { + _, err := s.Get([]byte{1}) + assert.Error(t, err) + }) + + t.Run("Multiple Get with same key, no error", func(t *testing.T) { + err := s.Put(key, val) + assert.NoError(t, err) + + // get #1 + v, err := s.Get(key) + assert.NoError(t, err) + assert.Equal(t, val, v) + // get #2 + v, err = s.Get(key) + assert.NoError(t, err) + assert.Equal(t, val, v) + }) + + t.Run("Get should return value equal to put value", func(t *testing.T) { + var ( + key1 []byte = []byte{1} + key2 []byte = []byte{2} + key3 []byte = []byte{3} + ) + testCases := []struct { + name string + key []byte + val []byte + }{ + { + name: "Test key = 1", + key: key1, + val: []byte{11}, + }, + { + name: "Test key = 2", + key: key2, + val: []byte{22}, + }, + { + name: "Test overwrite key = 1", + key: key1, + val: []byte{111}, + }, + { + name: "Test key = 3", + key: key3, + val: []byte{33}, + }, + { + name: "Test key = 3 with same value", + key: key3, + val: []byte{33}, + }, + { + name: "Test key = 3 with value for key1", + key: key3, + val: []byte{11}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := s.Put(tc.key, tc.val) + assert.NoError(t, err) + retVal, err := s.Get(tc.key) + assert.NoError(t, err) + assert.Equal(t, tc.val, retVal) + }) + } + }) } -func TestKvsAddSaveAdd(t *testing.T) { - ls := createLs() - putter := mockStorer.DirectUpload() - kvs1 := kvs.New(ls, putter, swarm.ZeroAddress) - kvs1key1, kvs1val1 := keyValuePair(t) - - err := kvs1.Put(kvs1key1, kvs1val1) - if err != nil { - t.Fatalf("Add() should not return an error: %v", err) - } - ref, err := kvs1.Save() - if err != nil { - t.Fatalf("Save() should not return an error: %v", err) - } - - // New KVS - kvs2 := kvs.New(ls, putter, ref) - - kvs2key1, kvs2val1 := keyValuePair(t) - - // put after save - kvs2.Put(kvs2key1, kvs2val1) - - // get after save - kvs2get1, err := kvs2.Get(kvs2key1) - if err != nil { - t.Fatalf("Lookup() should not return an error: %v", err) - } - if !bytes.Equal(kvs2get1, kvs2val1) { - t.Errorf("Get() value is not the expected %s != %s", hex.EncodeToString(kvs2key1), hex.EncodeToString(kvs2val1)) - } - - // get before Save - kvs2get2, err := kvs2.Get(kvs1key1) - - if err != nil { - t.Fatalf("Lookup() should not return an error: %v", err) - } - if !bytes.Equal(kvs2get2, kvs1val1) { - t.Errorf("Get() value is not the expected %s != %s", hex.EncodeToString(kvs2get2), hex.EncodeToString(kvs1val1)) - } - +func TestKvs_Save(t *testing.T) { + key1, val1 := keyValuePair(t) + key2, val2 := keyValuePair(t) + t.Run("Save empty KVS return error", func(t *testing.T) { + s := kvs.New(createLs(), mockStorer.DirectUpload(), swarm.ZeroAddress) + _, err := s.Save() + assert.Error(t, err) + }) + t.Run("Save not empty KVS return valid swarm address", func(t *testing.T) { + s := kvs.New(createLs(), mockStorer.DirectUpload(), swarm.ZeroAddress) + s.Put(key1, val1) + ref, err := s.Save() + assert.NoError(t, err) + assert.True(t, ref.IsValidNonEmpty()) + }) + t.Run("Save KVS with one item, no error, pre-save value exist", func(t *testing.T) { + ls := createLs() + putter := mockStorer.DirectUpload() + s1 := kvs.New(ls, putter, swarm.ZeroAddress) + + err := s1.Put(key1, val1) + assert.NoError(t, err) + + ref, err := s1.Save() + assert.NoError(t, err) + + s2 := kvs.New(ls, putter, ref) + val, err := s2.Get(key1) + assert.NoError(t, err) + assert.Equal(t, val1, val) + }) + t.Run("Save KVS and add one item, no error, after-save value exist", func(t *testing.T) { + ls := createLs() + putter := mockStorer.DirectUpload() + + kvs1 := kvs.New(ls, putter, swarm.ZeroAddress) + + err := kvs1.Put(key1, val1) + assert.NoError(t, err) + ref, err := kvs1.Save() + assert.NoError(t, err) + + // New KVS + kvs2 := kvs.New(ls, putter, ref) + err = kvs2.Put(key2, val2) + assert.NoError(t, err) + + val, err := kvs2.Get(key2) + assert.NoError(t, err) + assert.Equal(t, val2, val) + }) } From 0d45187bb845a378d2d51284d3b58bbd17cb5e80 Mon Sep 17 00:00:00 2001 From: Kexort Date: Tue, 2 Apr 2024 11:36:26 +0200 Subject: [PATCH 27/41] Small refactor + al test (#28) Adds TestDecryptRefWithGrantee_Success and replaces generateFixPrivateKey with getPrivKey Co-authored-by: Peter Ott --- pkg/dynamicaccess/accesslogic.go | 14 ++-- pkg/dynamicaccess/accesslogic_test.go | 107 +++++++++++++++++++------- pkg/dynamicaccess/publish.go | 12 --- pkg/dynamicaccess/publish_test.go | 11 --- pkg/dynamicaccess/timestamp.go | 10 --- pkg/dynamicaccess/timestamp_test.go | 1 - 6 files changed, 87 insertions(+), 68 deletions(-) delete mode 100644 pkg/dynamicaccess/publish.go delete mode 100644 pkg/dynamicaccess/publish_test.go delete mode 100644 pkg/dynamicaccess/timestamp.go delete mode 100644 pkg/dynamicaccess/timestamp_test.go diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index ad33ba5eb16..6c632a58351 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -14,7 +14,7 @@ var hashFunc = sha3.NewLegacyKeccak256 // Read-only interface for the ACT type Decryptor interface { // DecryptRef will return a decrypted reference, for given encrypted reference and grantee - DecryptRef(storage kvs.KeyValueStore, encryped_ref swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) + DecryptRef(storage kvs.KeyValueStore, encryptedRef swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) // Embedding the Session interface Session } @@ -26,14 +26,14 @@ type Control interface { // Adds a new grantee to the ACT AddGrantee(storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKey *encryption.Key) error // Encrypts a Swarm reference for a given grantee - EncryptRef(storage kvs.KeyValueStore, grantee *ecdsa.PublicKey, ref swarm.Address) error + EncryptRef(storage kvs.KeyValueStore, grantee *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) } type ActLogic struct { Session } -var _ Decryptor = (*ActLogic)(nil) +var _ Control = (*ActLogic)(nil) // Adds a new publisher to an empty act func (al ActLogic) AddPublisher(storage kvs.KeyValueStore, publisher *ecdsa.PublicKey) error { @@ -116,9 +116,9 @@ func (al *ActLogic) getKeys(publicKey *ecdsa.PublicKey) ([][]byte, error) { return al.Session.Key(publicKey, [][]byte{zeroByteArray, oneByteArray}) } -// DecryptRef will return a decrypted reference, for given encrypted reference and grantee -func (al ActLogic) DecryptRef(storage kvs.KeyValueStore, encryped_ref swarm.Address, grantee *ecdsa.PublicKey) (swarm.Address, error) { - keys, err := al.getKeys(grantee) +// DecryptRef will return a decrypted reference, for given encrypted reference and publisher +func (al ActLogic) DecryptRef(storage kvs.KeyValueStore, encryptedRef swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) { + keys, err := al.getKeys(publisher) if err != nil { return swarm.EmptyAddress, err } @@ -140,7 +140,7 @@ func (al ActLogic) DecryptRef(storage kvs.KeyValueStore, encryped_ref swarm.Addr // Decrypt reference refCipher := encryption.New(accessKey, 0, uint32(0), hashFunc) - ref, err := refCipher.Decrypt(encryped_ref.Bytes()) + ref, err := refCipher.Decrypt(encryptedRef.Bytes()) if err != nil { return swarm.EmptyAddress, err } diff --git a/pkg/dynamicaccess/accesslogic_test.go b/pkg/dynamicaccess/accesslogic_test.go index cb5ff0a6c78..8c2f2a6489f 100644 --- a/pkg/dynamicaccess/accesslogic_test.go +++ b/pkg/dynamicaccess/accesslogic_test.go @@ -3,10 +3,11 @@ package dynamicaccess_test import ( "crypto/ecdsa" "crypto/elliptic" + "crypto/rand" "encoding/hex" - "math/big" "testing" + "github.com/ethersphere/bee/pkg/crypto" "github.com/ethersphere/bee/pkg/dynamicaccess" kvsmock "github.com/ethersphere/bee/pkg/kvs/mock" "github.com/ethersphere/bee/pkg/swarm" @@ -14,33 +15,42 @@ import ( // Generates a new test environment with a fix private key func setupAccessLogic2() dynamicaccess.ActLogic { - privateKey := generateFixPrivateKey(1000) - diffieHellman := dynamicaccess.NewDefaultSession(&privateKey) + privateKey := getPrivKey(1) + diffieHellman := dynamicaccess.NewDefaultSession(privateKey) al := dynamicaccess.NewLogic(diffieHellman) return al } -// Generates a fixed identity with private and public key. The private key is generated from the input -func generateFixPrivateKey(input int64) ecdsa.PrivateKey { - fixedD := big.NewInt(input) - curve := elliptic.P256() - x, y := curve.ScalarBaseMult(fixedD.Bytes()) +func getPrivKey(keyNumber int) *ecdsa.PrivateKey { + var keyHex string - privateKey := ecdsa.PrivateKey{ - PublicKey: ecdsa.PublicKey{ - Curve: curve, - X: x, - Y: y, - }, - D: fixedD, + switch keyNumber { + case 0: + keyHex = "a786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfa" + case 1: + keyHex = "b786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfb" + case 2: + keyHex = "c786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfc" + default: + panic("Invalid key number") } - return privateKey + data, err := hex.DecodeString(keyHex) + if err != nil { + panic(err) + } + + privKey, err := crypto.DecodeSecp256k1PrivateKey(data) + if err != nil { + panic(err) + } + + return privKey } func TestDecryptRef_Success(t *testing.T) { - id0 := generateFixPrivateKey(0) + id0 := getPrivKey(0) s := kvsmock.New() al := setupAccessLogic2() err := al.AddPublisher(s, &id0.PublicKey) @@ -72,8 +82,51 @@ func TestDecryptRef_Success(t *testing.T) { } } +func TestDecryptRefWithGrantee_Success(t *testing.T) { + id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + diffieHellman := dynamicaccess.NewDefaultSession(id0) + al := dynamicaccess.NewLogic(diffieHellman) + + s := kvsmock.New() + err := al.AddPublisher(s, &id0.PublicKey) + if err != nil { + t.Errorf("AddPublisher: expected no error, got %v", err) + } + + id1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + err = al.AddGrantee(s, &id0.PublicKey, &id1.PublicKey, nil) + if err != nil { + t.Errorf("AddNewGrantee: expected no error, got %v", err) + } + + byteRef, _ := hex.DecodeString("39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559") + + expectedRef := swarm.NewAddress(byteRef) + t.Logf("encryptedRef: %s", expectedRef.String()) + + encryptedRef, err := al.EncryptRef(s, &id0.PublicKey, expectedRef) + t.Logf("encryptedRef: %s", encryptedRef.String()) + if err != nil { + t.Errorf("There was an error while calling EncryptRef: ") + t.Error(err) + } + + diffieHellman2 := dynamicaccess.NewDefaultSession(id1) + granteeAccessLogic := dynamicaccess.NewLogic(diffieHellman2) + acutalRef, err := granteeAccessLogic.DecryptRef(s, encryptedRef, &id0.PublicKey) + if err != nil { + t.Errorf("There was an error while calling Get: ") + t.Error(err) + } + + if expectedRef.Compare(acutalRef) != 0 { + + t.Errorf("Get gave back wrong Swarm reference!") + } +} + func TestDecryptRef_Error(t *testing.T) { - id0 := generateFixPrivateKey(0) + id0 := getPrivKey(0) s := kvsmock.New() al := setupAccessLogic2() @@ -89,13 +142,13 @@ func TestDecryptRef_Error(t *testing.T) { r, err := al.DecryptRef(s, encryptedRef, nil) if err == nil { t.Logf("r: %s", r.String()) - t.Errorf("Get should give back encrypted access key not found error!") + t.Errorf("Get should return encrypted access key not found error!") } } func TestAddPublisher(t *testing.T) { - id0 := generateFixPrivateKey(0) - savedLookupKey := "bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a" + id0 := getPrivKey(0) + savedLookupKey := "b6ee086390c280eeb9824c331a4427596f0c8510d5564bc1b6168d0059a46e2b" s := kvsmock.New() al := setupAccessLogic2() @@ -127,13 +180,13 @@ func TestAddPublisher(t *testing.T) { func TestAddNewGranteeToContent(t *testing.T) { - id0 := generateFixPrivateKey(0) - id1 := generateFixPrivateKey(1) - id2 := generateFixPrivateKey(2) + id0 := getPrivKey(0) + id1 := getPrivKey(1) + id2 := getPrivKey(2) - publisherLookupKey := "bc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a" - firstAddedGranteeLookupKey := "e221a2abf64357260e8f2c937ee938aed98dce097e537c1a3fd4caf73510dbe4" - secondAddedGranteeLookupKey := "8fe8dff7cd15a6a0095c1b25071a5691e7c901fd0b95857a96c0e4659b48716a" + publisherLookupKey := "b6ee086390c280eeb9824c331a4427596f0c8510d5564bc1b6168d0059a46e2b" + firstAddedGranteeLookupKey := "a13678e81f9d939b9401a3ad7e548d2ceb81c50f8c76424296e83a1ad79c0df0" + secondAddedGranteeLookupKey := "d5e9a6499ca74f5b8b958a4b89b7338045b2baa9420e115443a8050e26986564" s := kvsmock.New() al := setupAccessLogic2() diff --git a/pkg/dynamicaccess/publish.go b/pkg/dynamicaccess/publish.go deleted file mode 100644 index f913288e4d5..00000000000 --- a/pkg/dynamicaccess/publish.go +++ /dev/null @@ -1,12 +0,0 @@ -package dynamicaccess - -type Publish interface { - upload(ref string) (string, error) -} - -type DefaultPublish struct { -} - -func (d *DefaultPublish) upload(ref string) (string, error) { - return "default", nil -} diff --git a/pkg/dynamicaccess/publish_test.go b/pkg/dynamicaccess/publish_test.go deleted file mode 100644 index d31069da4a3..00000000000 --- a/pkg/dynamicaccess/publish_test.go +++ /dev/null @@ -1,11 +0,0 @@ -package dynamicaccess - -import "testing" - -func TestUpload(t *testing.T) { - p := &DefaultPublish{} - _, err := p.upload("test") - if err != nil { - t.Errorf("Error uploading file: %v", err) - } -} diff --git a/pkg/dynamicaccess/timestamp.go b/pkg/dynamicaccess/timestamp.go deleted file mode 100644 index 48347d33a7c..00000000000 --- a/pkg/dynamicaccess/timestamp.go +++ /dev/null @@ -1,10 +0,0 @@ -package dynamicaccess - -// container interface bee-ből a manifest -type Timestamp interface{} - -type defaultTimeStamp struct{} - -func NewTimestamp() Timestamp { - return &defaultTimeStamp{} -} diff --git a/pkg/dynamicaccess/timestamp_test.go b/pkg/dynamicaccess/timestamp_test.go deleted file mode 100644 index e39ccbcf0c5..00000000000 --- a/pkg/dynamicaccess/timestamp_test.go +++ /dev/null @@ -1 +0,0 @@ -package dynamicaccess From f7563b31f9dc1e115effa5b688cc64463986a1e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= <58116288+bosi95@users.noreply.github.com> Date: Wed, 3 Apr 2024 14:05:25 +0200 Subject: [PATCH 28/41] Persist grantee list on swarm (#30) * Persist grantee list on swarm * accesslogic refactor * Refactor grantee list tests Co-authored-by: Roland Seres --- pkg/dynamicaccess/accesslogic.go | 5 +- pkg/dynamicaccess/controller.go | 6 +- pkg/dynamicaccess/controller_test.go | 4 +- pkg/dynamicaccess/grantee.go | 130 ++++++++++-- pkg/dynamicaccess/grantee_manager.go | 25 +-- pkg/dynamicaccess/grantee_manager_test.go | 6 +- pkg/dynamicaccess/grantee_test.go | 236 +++++++++++++++------- pkg/dynamicaccess/mock/container.go | 20 -- pkg/dynamicaccess/mock/grantee.go | 51 +++++ 9 files changed, 352 insertions(+), 131 deletions(-) delete mode 100644 pkg/dynamicaccess/mock/container.go create mode 100644 pkg/dynamicaccess/mock/grantee.go diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index 6c632a58351..593e02d630c 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -76,10 +76,11 @@ func (al ActLogic) AddGrantee(storage kvs.KeyValueStore, publisherPubKey, grante return err } lookupKey := keys[0] - accessKeyEncryptionKey := keys[1] + // accessKeyDecryptionKey is used for encryption of the access key + accessKeyDecryptionKey := keys[1] // Encrypt the access key for the new Grantee - cipher := encryption.New(encryption.Key(accessKeyEncryptionKey), 0, uint32(0), hashFunc) + cipher := encryption.New(encryption.Key(accessKeyDecryptionKey), 0, uint32(0), hashFunc) granteeEncryptedAccessKey, err := cipher.Encrypt(accessKey) if err != nil { return err diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index d118e22489f..df39fa23c32 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -9,7 +9,7 @@ import ( type Controller interface { DownloadHandler(timestamp int64, enryptedRef swarm.Address, publisher *ecdsa.PublicKey, tag string) (swarm.Address, error) - UploadHandler(ref swarm.Address, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error) + UploadHandler(ref swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) } type defaultController struct { @@ -27,7 +27,7 @@ func (c *defaultController) DownloadHandler(timestamp int64, enryptedRef swarm.A return addr, err } -func (c *defaultController) UploadHandler(ref swarm.Address, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error) { +func (c *defaultController) UploadHandler(ref swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) { kvs, err := c.history.Lookup(0) if err != nil { return swarm.EmptyAddress, err @@ -36,7 +36,7 @@ func (c *defaultController) UploadHandler(ref swarm.Address, publisher *ecdsa.Pu // new feed // TODO: putter session to create kvs kvs = kvsmock.New() - _, err = c.granteeManager.Publish(kvs, publisher, topic) + _, err = c.granteeManager.Publish(kvs, publisher) if err != nil { return swarm.EmptyAddress, err } diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index 63f80059e08..24107123ab5 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -72,9 +72,9 @@ func TestEncrypt(t *testing.T) { eref, ref := prepareEncryptedChunkReference(ak) key1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - gm.Add("topic", []*ecdsa.PublicKey{&key1.PublicKey}) + gm.Add([]*ecdsa.PublicKey{&key1.PublicKey}) - addr, _ := c.UploadHandler(ref, &pk.PublicKey, "topic") + addr, _ := c.UploadHandler(ref, &pk.PublicKey) if !addr.Equal(eref) { t.Fatalf("Decrypted chunk address: %s is not the expected: %s", addr, eref) } diff --git a/pkg/dynamicaccess/grantee.go b/pkg/dynamicaccess/grantee.go index d85ebf217ea..afbe5349818 100644 --- a/pkg/dynamicaccess/grantee.go +++ b/pkg/dynamicaccess/grantee.go @@ -1,44 +1,134 @@ package dynamicaccess import ( + "context" "crypto/ecdsa" + "crypto/elliptic" + "fmt" + + "github.com/ethersphere/bee/pkg/file" + "github.com/ethersphere/bee/pkg/storer" + "github.com/ethersphere/bee/pkg/swarm" +) + +const ( + publicKeyLen = 65 ) type GranteeList interface { - Add(topic string, addList []*ecdsa.PublicKey) error - Remove(topic string, removeList []*ecdsa.PublicKey) error - Get(topic string) []*ecdsa.PublicKey + Add(addList []*ecdsa.PublicKey) error + Remove(removeList []*ecdsa.PublicKey) error + Get() []*ecdsa.PublicKey + Save() (swarm.Address, error) } type GranteeListStruct struct { - grantees map[string][]*ecdsa.PublicKey + grantees []byte + loadSave file.LoadSaver + putter storer.PutterSession +} + +var _ GranteeList = (*GranteeListStruct)(nil) + +func (g *GranteeListStruct) Get() []*ecdsa.PublicKey { + return g.deserialize(g.grantees) +} + +func (g *GranteeListStruct) serialize(publicKeys []*ecdsa.PublicKey) []byte { + b := make([]byte, 0, len(publicKeys)*publicKeyLen) + for _, key := range publicKeys { + b = append(b, g.serializePublicKey(key)...) + } + return b +} + +func (g *GranteeListStruct) serializePublicKey(pub *ecdsa.PublicKey) []byte { + return elliptic.Marshal(pub.Curve, pub.X, pub.Y) } -func (g *GranteeListStruct) Get(topic string) []*ecdsa.PublicKey { - grantees := g.grantees[topic] - keys := make([]*ecdsa.PublicKey, len(grantees)) - copy(keys, grantees) - return keys +func (g *GranteeListStruct) deserialize(data []byte) []*ecdsa.PublicKey { + if len(data) == 0 { + return nil + } + + p := make([]*ecdsa.PublicKey, 0, len(data)/publicKeyLen) + for i := 0; i < len(data); i += publicKeyLen { + pubKey := g.deserializeBytes(data[i : i+publicKeyLen]) + if pubKey == nil { + return nil + } + p = append(p, pubKey) + } + return p } -func (g *GranteeListStruct) Add(topic string, addList []*ecdsa.PublicKey) error { - g.grantees[topic] = append(g.grantees[topic], addList...) +func (g *GranteeListStruct) deserializeBytes(data []byte) *ecdsa.PublicKey { + curve := elliptic.P256() + x, y := elliptic.Unmarshal(curve, data) + return &ecdsa.PublicKey{Curve: curve, X: x, Y: y} +} + +func (g *GranteeListStruct) Add(addList []*ecdsa.PublicKey) error { + if len(addList) == 0 { + return fmt.Errorf("no public key provided") + } + + data := g.serialize(addList) + g.grantees = append(g.grantees, data...) return nil } -func (g *GranteeListStruct) Remove(topic string, removeList []*ecdsa.PublicKey) error { - for _, remove := range removeList { - for i, grantee := range g.grantees[topic] { - if *grantee == *remove { - g.grantees[topic][i] = g.grantees[topic][len(g.grantees[topic])-1] - g.grantees[topic] = g.grantees[topic][:len(g.grantees[topic])-1] +func (g *GranteeListStruct) Save() (swarm.Address, error) { + refBytes, err := g.loadSave.Save(context.Background(), g.grantees) + if err != nil { + return swarm.ZeroAddress, fmt.Errorf("grantee save error: %w", err) + } + address := swarm.NewAddress(refBytes) + err = g.putter.Done(address) + if err != nil { + return swarm.ZeroAddress, err + } + return address, nil +} + +func (g *GranteeListStruct) Remove(keysToRemove []*ecdsa.PublicKey) error { + if len(keysToRemove) == 0 { + return fmt.Errorf("nothing to remove") + } + grantees := g.deserialize(g.grantees) + if grantees == nil { + return fmt.Errorf("no grantee found") + } + + for _, remove := range keysToRemove { + for i, grantee := range grantees { + if grantee.Equal(remove) { + grantees[i] = grantees[len(grantees)-1] + grantees = grantees[:len(grantees)-1] } } } - + g.grantees = g.serialize(grantees) return nil } -func NewGrantee() *GranteeListStruct { - return &GranteeListStruct{grantees: make(map[string][]*ecdsa.PublicKey)} +func NewGranteeList(ls file.LoadSaver, putter storer.PutterSession, reference swarm.Address) GranteeList { + var ( + data []byte + err error + ) + if swarm.ZeroAddress.Equal(reference) || swarm.EmptyAddress.Equal(reference) { + data = []byte{} + } else { + data, err = ls.Load(context.Background(), reference.Bytes()) + } + if err != nil { + return nil + } + + return &GranteeListStruct{ + grantees: data, + loadSave: ls, + putter: putter, + } } diff --git a/pkg/dynamicaccess/grantee_manager.go b/pkg/dynamicaccess/grantee_manager.go index 004a933b006..eaa9cc9ffd9 100644 --- a/pkg/dynamicaccess/grantee_manager.go +++ b/pkg/dynamicaccess/grantee_manager.go @@ -3,16 +3,17 @@ package dynamicaccess import ( "crypto/ecdsa" + "github.com/ethersphere/bee/pkg/dynamicaccess/mock" "github.com/ethersphere/bee/pkg/kvs" "github.com/ethersphere/bee/pkg/swarm" ) type GranteeManager interface { - Get(topic string) []*ecdsa.PublicKey - Add(topic string, addList []*ecdsa.PublicKey) error - Publish(kvs kvs.KeyValueStore, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error) + Get() []*ecdsa.PublicKey + Add(addList []*ecdsa.PublicKey) error + Publish(kvs kvs.KeyValueStore, publisher *ecdsa.PublicKey) (swarm.Address, error) - // HandleGrantees(topic string, addList, removeList []*ecdsa.PublicKey) *Act + // HandleGrantees(addList, removeList []*ecdsa.PublicKey) *Act // Load(grantee Grantee) // Save() @@ -22,24 +23,24 @@ var _ GranteeManager = (*granteeManager)(nil) type granteeManager struct { accessLogic ActLogic - granteeList GranteeList + granteeList *mock.GranteeListStructMock } func NewGranteeManager(al ActLogic) *granteeManager { - return &granteeManager{accessLogic: al, granteeList: NewGrantee()} + return &granteeManager{accessLogic: al, granteeList: mock.NewGranteeList()} } -func (gm *granteeManager) Get(topic string) []*ecdsa.PublicKey { - return gm.granteeList.Get(topic) +func (gm *granteeManager) Get() []*ecdsa.PublicKey { + return gm.granteeList.Get() } -func (gm *granteeManager) Add(topic string, addList []*ecdsa.PublicKey) error { - return gm.granteeList.Add(topic, addList) +func (gm *granteeManager) Add(addList []*ecdsa.PublicKey) error { + return gm.granteeList.Add(addList) } -func (gm *granteeManager) Publish(kvs kvs.KeyValueStore, publisher *ecdsa.PublicKey, topic string) (swarm.Address, error) { +func (gm *granteeManager) Publish(kvs kvs.KeyValueStore, publisher *ecdsa.PublicKey) (swarm.Address, error) { err := gm.accessLogic.AddPublisher(kvs, publisher) - for _, grantee := range gm.granteeList.Get(topic) { + for _, grantee := range gm.granteeList.Get() { err = gm.accessLogic.AddGrantee(kvs, publisher, grantee, nil) } return swarm.EmptyAddress, err diff --git a/pkg/dynamicaccess/grantee_manager_test.go b/pkg/dynamicaccess/grantee_manager_test.go index 7ac43425e10..d458e007088 100644 --- a/pkg/dynamicaccess/grantee_manager_test.go +++ b/pkg/dynamicaccess/grantee_manager_test.go @@ -24,15 +24,15 @@ func TestAdd(t *testing.T) { id1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) id2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - err := m.Add("topic", []*ecdsa.PublicKey{&id1.PublicKey}) + err := m.Add([]*ecdsa.PublicKey{&id1.PublicKey}) if err != nil { t.Errorf("Add() returned an error") } - err = m.Add("topic", []*ecdsa.PublicKey{&id2.PublicKey}) + err = m.Add([]*ecdsa.PublicKey{&id2.PublicKey}) if err != nil { t.Errorf("Add() returned an error") } s := kvsmock.New() - m.Publish(s, &pub.PublicKey, "topic") + m.Publish(s, &pub.PublicKey) fmt.Println("") } diff --git a/pkg/dynamicaccess/grantee_test.go b/pkg/dynamicaccess/grantee_test.go index 7962690ccb4..5b7a9bc5402 100644 --- a/pkg/dynamicaccess/grantee_test.go +++ b/pkg/dynamicaccess/grantee_test.go @@ -1,104 +1,202 @@ package dynamicaccess_test import ( + "context" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" - "reflect" "testing" "github.com/ethersphere/bee/pkg/dynamicaccess" + "github.com/ethersphere/bee/pkg/file" + "github.com/ethersphere/bee/pkg/file/loadsave" + "github.com/ethersphere/bee/pkg/file/pipeline" + "github.com/ethersphere/bee/pkg/file/pipeline/builder" + "github.com/ethersphere/bee/pkg/file/redundancy" + "github.com/ethersphere/bee/pkg/storage" + mockstorer "github.com/ethersphere/bee/pkg/storer/mock" + "github.com/ethersphere/bee/pkg/swarm" + "github.com/stretchr/testify/assert" ) -var _ dynamicaccess.GranteeList = (*dynamicaccess.GranteeListStruct)(nil) +var mockStorer = mockstorer.New() -func TestGranteeAddGrantees(t *testing.T) { - grantee := dynamicaccess.NewGrantee() - - key1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - t.Errorf("Expected no error, got %v", err) - } - - key2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - t.Errorf("Expected no error, got %v", err) - } - - addList := []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey} - exampleTopic := "topic" - err = grantee.Add(exampleTopic, addList) - - if err != nil { - t.Errorf("Expected no error, got %v", err) - } - - grantees := grantee.Get(exampleTopic) - if !reflect.DeepEqual(grantees, addList) { - t.Errorf("Expected grantees %v, got %v", addList, grantees) +func requestPipelineFactory(ctx context.Context, s storage.Putter, encrypt bool, rLevel redundancy.Level) func() pipeline.Interface { + return func() pipeline.Interface { + return builder.NewPipelineBuilder(ctx, s, encrypt, rLevel) } } -func TestRemoveGrantees(t *testing.T) { - grantee := dynamicaccess.NewGrantee() +func createLs() file.LoadSaver { + return loadsave.New(mockStorer.ChunkStore(), mockStorer.Cache(), requestPipelineFactory(context.Background(), mockStorer.Cache(), false, redundancy.NONE)) +} +func generateKeyListFixture() ([]*ecdsa.PublicKey, error) { key1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - t.Errorf("Expected no error, got %v", err) - } - key2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + key3, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { - t.Errorf("Expected no error, got %v", err) - } - - addList := []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey} - exampleTopic := "topic" - err = grantee.Add(exampleTopic, addList) - if err != nil { - t.Errorf("Expected no error, got %v", err) + return nil, err } + return []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey, &key3.PublicKey}, nil +} - removeList := []*ecdsa.PublicKey{&key1.PublicKey} - err = grantee.Remove(exampleTopic, removeList) +func TestGranteeAddGet(t *testing.T) { + putter := mockStorer.DirectUpload() + gl := dynamicaccess.NewGranteeList(createLs(), putter, swarm.ZeroAddress) + keys, err := generateKeyListFixture() if err != nil { - t.Errorf("Expected no error, got %v", err) + t.Errorf("key generation error: %v", err) } - grantees := grantee.Get(exampleTopic) - expectedGrantees := []*ecdsa.PublicKey{&key2.PublicKey} + t.Run("Get empty grantee list should return error", func(t *testing.T) { + val := gl.Get() + assert.Nil(t, val) + }) + + t.Run("Get should return value equal to put value", func(t *testing.T) { + var ( + addList1 []*ecdsa.PublicKey = []*ecdsa.PublicKey{keys[0]} + addList2 []*ecdsa.PublicKey = []*ecdsa.PublicKey{keys[1], keys[0]} + addList3 []*ecdsa.PublicKey = keys + ) + testCases := []struct { + name string + list []*ecdsa.PublicKey + }{ + { + name: "Test list = 1", + list: addList1, + }, + { + name: "Test list = 2", + list: addList2, + }, + { + name: "Test list = 3", + list: addList3, + }, + { + name: "Test empty add list", + list: nil, + }, + } - for i, grantee := range grantees { - if grantee != expectedGrantees[i] { - t.Errorf("Expected grantee %v, got %v", expectedGrantees[i], grantee) + expList := []*ecdsa.PublicKey{} + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := gl.Add(tc.list) + if tc.list == nil { + assert.Error(t, err) + } else { + assert.NoError(t, err) + expList = append(expList, tc.list...) + retVal := gl.Get() + assert.Equal(t, expList, retVal) + } + }) } - } + }) } -func TestGetGrantees(t *testing.T) { - grantee := dynamicaccess.NewGrantee() - - key1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) +func TestGranteeRemove(t *testing.T) { + putter := mockStorer.DirectUpload() + gl := dynamicaccess.NewGranteeList(createLs(), putter, swarm.ZeroAddress) + keys, err := generateKeyListFixture() if err != nil { - t.Errorf("Expected no error, got %v", err) + t.Errorf("key generation error: %v", err) } - key2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - t.Errorf("Expected no error, got %v", err) - } + t.Run("Add should NOT return error", func(t *testing.T) { + err := gl.Add(keys) + assert.NoError(t, err) + retVal := gl.Get() + assert.Equal(t, keys, retVal) + }) + removeList1 := []*ecdsa.PublicKey{keys[0]} + removeList2 := []*ecdsa.PublicKey{keys[2], keys[1]} + t.Run("Remove the first item should return NO error", func(t *testing.T) { + err := gl.Remove(removeList1) + assert.NoError(t, err) + retVal := gl.Get() + assert.Equal(t, removeList2, retVal) + }) + t.Run("Remove non-existent item should return NO error", func(t *testing.T) { + err := gl.Remove(removeList1) + assert.NoError(t, err) + retVal := gl.Get() + assert.Equal(t, removeList2, retVal) + }) + t.Run("Remove second and third item should return NO error", func(t *testing.T) { + err := gl.Remove(removeList2) + assert.NoError(t, err) + retVal := gl.Get() + assert.Nil(t, retVal) + }) + t.Run("Remove from empty grantee list should return error", func(t *testing.T) { + err := gl.Remove(removeList1) + assert.Error(t, err) + retVal := gl.Get() + assert.Nil(t, retVal) + }) + t.Run("Remove empty remove list should return error", func(t *testing.T) { + err := gl.Remove(nil) + assert.Error(t, err) + retVal := gl.Get() + assert.Nil(t, retVal) + }) +} - addList := []*ecdsa.PublicKey{&key1.PublicKey, &key2.PublicKey} - exampleTopic := "topic" - err = grantee.Add(exampleTopic, addList) +func TestGranteeSave(t *testing.T) { + keys, err := generateKeyListFixture() if err != nil { - t.Errorf("Expected no error, got %v", err) - } - - grantees := grantee.Get(exampleTopic) - for i, grantee := range grantees { - if grantee != addList[i] { - t.Errorf("Expected grantee %v, got %v", addList[i], grantee) - } + t.Errorf("key generation error: %v", err) } + t.Run("Save empty grantee list return NO error", func(t *testing.T) { + gl := dynamicaccess.NewGranteeList(createLs(), mockStorer.DirectUpload(), swarm.ZeroAddress) + _, err := gl.Save() + assert.NoError(t, err) + }) + t.Run("Save not empty grantee list return valid swarm address", func(t *testing.T) { + gl := dynamicaccess.NewGranteeList(createLs(), mockStorer.DirectUpload(), swarm.ZeroAddress) + err = gl.Add(keys) + ref, err := gl.Save() + assert.NoError(t, err) + assert.True(t, ref.IsValidNonEmpty()) + }) + t.Run("Save grantee list with one item, no error, pre-save value exist", func(t *testing.T) { + ls := createLs() + putter := mockStorer.DirectUpload() + gl1 := dynamicaccess.NewGranteeList(ls, putter, swarm.ZeroAddress) + + err := gl1.Add(keys) + assert.NoError(t, err) + + ref, err := gl1.Save() + assert.NoError(t, err) + + gl2 := dynamicaccess.NewGranteeList(ls, putter, ref) + val := gl2.Get() + assert.NoError(t, err) + assert.Equal(t, keys, val) + }) + t.Run("Save grantee list and add one item, no error, after-save value exist", func(t *testing.T) { + ls := createLs() + putter := mockStorer.DirectUpload() + + gl1 := dynamicaccess.NewGranteeList(ls, putter, swarm.ZeroAddress) + + err := gl1.Add(keys) + assert.NoError(t, err) + ref, err := gl1.Save() + assert.NoError(t, err) + + // New KVS + gl2 := dynamicaccess.NewGranteeList(ls, putter, ref) + err = gl2.Add(keys) + assert.NoError(t, err) + + val := gl2.Get() + assert.Equal(t, append(keys, keys...), val) + }) } diff --git a/pkg/dynamicaccess/mock/container.go b/pkg/dynamicaccess/mock/container.go deleted file mode 100644 index 3cad9badd39..00000000000 --- a/pkg/dynamicaccess/mock/container.go +++ /dev/null @@ -1,20 +0,0 @@ -package mock - -type ContainerMock struct { - AddFunc func(string, string, string) error - GetFunc func(string, string, string) (string, error) -} - -func (ma *ContainerMock) Add(ref string, publisher string, tag string) error { - if ma.AddFunc == nil { - return nil - } - return ma.AddFunc(ref, publisher, tag) -} - -func (ma *ContainerMock) Get(ref string, publisher string, tag string) (string, error) { - if ma.GetFunc == nil { - return "", nil - } - return ma.GetFunc(ref, publisher, tag) -} diff --git a/pkg/dynamicaccess/mock/grantee.go b/pkg/dynamicaccess/mock/grantee.go new file mode 100644 index 00000000000..f4bd90740ab --- /dev/null +++ b/pkg/dynamicaccess/mock/grantee.go @@ -0,0 +1,51 @@ +package mock + +import ( + "crypto/ecdsa" + + "github.com/ethersphere/bee/pkg/swarm" +) + +type GranteeListMock interface { + Add(publicKeys []*ecdsa.PublicKey) error + Remove(removeList []*ecdsa.PublicKey) error + Get() []*ecdsa.PublicKey + Save() (swarm.Address, error) +} + +type GranteeListStructMock struct { + grantees []*ecdsa.PublicKey +} + +func (g *GranteeListStructMock) Get() []*ecdsa.PublicKey { + grantees := g.grantees + keys := make([]*ecdsa.PublicKey, len(grantees)) + copy(keys, grantees) + return keys +} + +func (g *GranteeListStructMock) Add(addList []*ecdsa.PublicKey) error { + g.grantees = append(g.grantees, addList...) + return nil +} + +func (g *GranteeListStructMock) Remove(removeList []*ecdsa.PublicKey) error { + for _, remove := range removeList { + for i, grantee := range g.grantees { + if *grantee == *remove { + g.grantees[i] = g.grantees[len(g.grantees)-1] + g.grantees = g.grantees[:len(g.grantees)-1] + } + } + } + + return nil +} + +func (g *GranteeListStructMock) Save() (swarm.Address, error) { + return swarm.EmptyAddress, nil +} + +func NewGranteeList() *GranteeListStructMock { + return &GranteeListStructMock{grantees: []*ecdsa.PublicKey{}} +} From d84fca5a935465a75f44f7eb53441c7a543dce90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ferenc=20S=C3=A1rai?= Date: Thu, 4 Apr 2024 11:41:44 +0200 Subject: [PATCH 29/41] Update package imports to use the v2 version of the modules (#33) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ferenc Sárai --- pkg/dynamicaccess/accesslogic.go | 6 +++--- pkg/dynamicaccess/accesslogic_test.go | 8 ++++---- pkg/dynamicaccess/controller.go | 4 ++-- pkg/dynamicaccess/controller_test.go | 12 ++++++------ pkg/dynamicaccess/grantee.go | 6 +++--- pkg/dynamicaccess/grantee_manager.go | 6 +++--- pkg/dynamicaccess/grantee_manager_test.go | 4 ++-- pkg/dynamicaccess/grantee_test.go | 18 +++++++++--------- pkg/dynamicaccess/history.go | 2 +- pkg/dynamicaccess/history_test.go | 6 +++--- pkg/dynamicaccess/mock/grantee.go | 2 +- pkg/dynamicaccess/mock/history.go | 10 +++++----- pkg/dynamicaccess/mock/session.go | 4 ++-- pkg/dynamicaccess/session.go | 4 ++-- pkg/dynamicaccess/session_test.go | 8 ++++---- pkg/kvs/kvs.go | 8 ++++---- pkg/kvs/kvs_test.go | 18 +++++++++--------- pkg/kvs/mock/kvs.go | 4 ++-- 18 files changed, 65 insertions(+), 65 deletions(-) diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index 593e02d630c..a2a6fbdce24 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -3,9 +3,9 @@ package dynamicaccess import ( "crypto/ecdsa" - encryption "github.com/ethersphere/bee/pkg/encryption" - "github.com/ethersphere/bee/pkg/kvs" - "github.com/ethersphere/bee/pkg/swarm" + encryption "github.com/ethersphere/bee/v2/pkg/encryption" + "github.com/ethersphere/bee/v2/pkg/kvs" + "github.com/ethersphere/bee/v2/pkg/swarm" "golang.org/x/crypto/sha3" ) diff --git a/pkg/dynamicaccess/accesslogic_test.go b/pkg/dynamicaccess/accesslogic_test.go index 8c2f2a6489f..be115f424d1 100644 --- a/pkg/dynamicaccess/accesslogic_test.go +++ b/pkg/dynamicaccess/accesslogic_test.go @@ -7,10 +7,10 @@ import ( "encoding/hex" "testing" - "github.com/ethersphere/bee/pkg/crypto" - "github.com/ethersphere/bee/pkg/dynamicaccess" - kvsmock "github.com/ethersphere/bee/pkg/kvs/mock" - "github.com/ethersphere/bee/pkg/swarm" + "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/dynamicaccess" + kvsmock "github.com/ethersphere/bee/v2/pkg/kvs/mock" + "github.com/ethersphere/bee/v2/pkg/swarm" ) // Generates a new test environment with a fix private key diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index df39fa23c32..ccdc8c2d7f4 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -3,8 +3,8 @@ package dynamicaccess import ( "crypto/ecdsa" - kvsmock "github.com/ethersphere/bee/pkg/kvs/mock" - "github.com/ethersphere/bee/pkg/swarm" + kvsmock "github.com/ethersphere/bee/v2/pkg/kvs/mock" + "github.com/ethersphere/bee/v2/pkg/swarm" ) type Controller interface { diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index 24107123ab5..efbfc8d4e42 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -8,12 +8,12 @@ import ( "testing" "time" - "github.com/ethersphere/bee/pkg/crypto" - "github.com/ethersphere/bee/pkg/dynamicaccess" - "github.com/ethersphere/bee/pkg/dynamicaccess/mock" - "github.com/ethersphere/bee/pkg/encryption" - kvsmock "github.com/ethersphere/bee/pkg/kvs/mock" - "github.com/ethersphere/bee/pkg/swarm" + "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/dynamicaccess" + "github.com/ethersphere/bee/v2/pkg/dynamicaccess/mock" + "github.com/ethersphere/bee/v2/pkg/encryption" + kvsmock "github.com/ethersphere/bee/v2/pkg/kvs/mock" + "github.com/ethersphere/bee/v2/pkg/swarm" "golang.org/x/crypto/sha3" ) diff --git a/pkg/dynamicaccess/grantee.go b/pkg/dynamicaccess/grantee.go index afbe5349818..d850fc047cc 100644 --- a/pkg/dynamicaccess/grantee.go +++ b/pkg/dynamicaccess/grantee.go @@ -6,9 +6,9 @@ import ( "crypto/elliptic" "fmt" - "github.com/ethersphere/bee/pkg/file" - "github.com/ethersphere/bee/pkg/storer" - "github.com/ethersphere/bee/pkg/swarm" + "github.com/ethersphere/bee/v2/pkg/file" + "github.com/ethersphere/bee/v2/pkg/storer" + "github.com/ethersphere/bee/v2/pkg/swarm" ) const ( diff --git a/pkg/dynamicaccess/grantee_manager.go b/pkg/dynamicaccess/grantee_manager.go index eaa9cc9ffd9..1fac35a38bd 100644 --- a/pkg/dynamicaccess/grantee_manager.go +++ b/pkg/dynamicaccess/grantee_manager.go @@ -3,9 +3,9 @@ package dynamicaccess import ( "crypto/ecdsa" - "github.com/ethersphere/bee/pkg/dynamicaccess/mock" - "github.com/ethersphere/bee/pkg/kvs" - "github.com/ethersphere/bee/pkg/swarm" + "github.com/ethersphere/bee/v2/pkg/dynamicaccess/mock" + "github.com/ethersphere/bee/v2/pkg/kvs" + "github.com/ethersphere/bee/v2/pkg/swarm" ) type GranteeManager interface { diff --git a/pkg/dynamicaccess/grantee_manager_test.go b/pkg/dynamicaccess/grantee_manager_test.go index d458e007088..bb01c13cd85 100644 --- a/pkg/dynamicaccess/grantee_manager_test.go +++ b/pkg/dynamicaccess/grantee_manager_test.go @@ -7,8 +7,8 @@ import ( "fmt" "testing" - "github.com/ethersphere/bee/pkg/dynamicaccess" - kvsmock "github.com/ethersphere/bee/pkg/kvs/mock" + "github.com/ethersphere/bee/v2/pkg/dynamicaccess" + kvsmock "github.com/ethersphere/bee/v2/pkg/kvs/mock" ) func setupAccessLogic(privateKey *ecdsa.PrivateKey) dynamicaccess.ActLogic { diff --git a/pkg/dynamicaccess/grantee_test.go b/pkg/dynamicaccess/grantee_test.go index 5b7a9bc5402..b644f5896c5 100644 --- a/pkg/dynamicaccess/grantee_test.go +++ b/pkg/dynamicaccess/grantee_test.go @@ -7,15 +7,15 @@ import ( "crypto/rand" "testing" - "github.com/ethersphere/bee/pkg/dynamicaccess" - "github.com/ethersphere/bee/pkg/file" - "github.com/ethersphere/bee/pkg/file/loadsave" - "github.com/ethersphere/bee/pkg/file/pipeline" - "github.com/ethersphere/bee/pkg/file/pipeline/builder" - "github.com/ethersphere/bee/pkg/file/redundancy" - "github.com/ethersphere/bee/pkg/storage" - mockstorer "github.com/ethersphere/bee/pkg/storer/mock" - "github.com/ethersphere/bee/pkg/swarm" + "github.com/ethersphere/bee/v2/pkg/dynamicaccess" + "github.com/ethersphere/bee/v2/pkg/file" + "github.com/ethersphere/bee/v2/pkg/file/loadsave" + "github.com/ethersphere/bee/v2/pkg/file/pipeline" + "github.com/ethersphere/bee/v2/pkg/file/pipeline/builder" + "github.com/ethersphere/bee/v2/pkg/file/redundancy" + "github.com/ethersphere/bee/v2/pkg/storage" + mockstorer "github.com/ethersphere/bee/v2/pkg/storer/mock" + "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/stretchr/testify/assert" ) diff --git a/pkg/dynamicaccess/history.go b/pkg/dynamicaccess/history.go index 63ec00847a6..82d18f5ef8a 100644 --- a/pkg/dynamicaccess/history.go +++ b/pkg/dynamicaccess/history.go @@ -2,7 +2,7 @@ package dynamicaccess import ( "github.com/ethereum/go-ethereum/common" - "github.com/ethersphere/bee/pkg/kvs" + "github.com/ethersphere/bee/v2/pkg/kvs" ) type History interface { diff --git a/pkg/dynamicaccess/history_test.go b/pkg/dynamicaccess/history_test.go index c2164df7d39..6c0a48a4fd6 100644 --- a/pkg/dynamicaccess/history_test.go +++ b/pkg/dynamicaccess/history_test.go @@ -5,9 +5,9 @@ import ( "testing" "time" - "github.com/ethersphere/bee/pkg/dynamicaccess" - "github.com/ethersphere/bee/pkg/dynamicaccess/mock" - kvsmock "github.com/ethersphere/bee/pkg/kvs/mock" + "github.com/ethersphere/bee/v2/pkg/dynamicaccess" + "github.com/ethersphere/bee/v2/pkg/dynamicaccess/mock" + kvsmock "github.com/ethersphere/bee/v2/pkg/kvs/mock" "github.com/stretchr/testify/assert" ) diff --git a/pkg/dynamicaccess/mock/grantee.go b/pkg/dynamicaccess/mock/grantee.go index f4bd90740ab..50689ade08f 100644 --- a/pkg/dynamicaccess/mock/grantee.go +++ b/pkg/dynamicaccess/mock/grantee.go @@ -3,7 +3,7 @@ package mock import ( "crypto/ecdsa" - "github.com/ethersphere/bee/pkg/swarm" + "github.com/ethersphere/bee/v2/pkg/swarm" ) type GranteeListMock interface { diff --git a/pkg/dynamicaccess/mock/history.go b/pkg/dynamicaccess/mock/history.go index 91b544a0760..e7747434d59 100644 --- a/pkg/dynamicaccess/mock/history.go +++ b/pkg/dynamicaccess/mock/history.go @@ -5,11 +5,11 @@ import ( "sort" "time" - "github.com/ethersphere/bee/pkg/crypto" - "github.com/ethersphere/bee/pkg/feeds" - "github.com/ethersphere/bee/pkg/kvs" - "github.com/ethersphere/bee/pkg/storage" - "github.com/ethersphere/bee/pkg/swarm" + "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/feeds" + "github.com/ethersphere/bee/v2/pkg/kvs" + "github.com/ethersphere/bee/v2/pkg/storage" + "github.com/ethersphere/bee/v2/pkg/swarm" ) type historyMock struct { diff --git a/pkg/dynamicaccess/mock/session.go b/pkg/dynamicaccess/mock/session.go index ba3e3f8c8f2..9613aacedfa 100644 --- a/pkg/dynamicaccess/mock/session.go +++ b/pkg/dynamicaccess/mock/session.go @@ -3,8 +3,8 @@ package mock import ( "crypto/ecdsa" - "github.com/ethersphere/bee/pkg/crypto" - "github.com/ethersphere/bee/pkg/keystore" + "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/keystore" ) type SessionMock struct { diff --git a/pkg/dynamicaccess/session.go b/pkg/dynamicaccess/session.go index b2718aedd67..0b58c1aa3dd 100644 --- a/pkg/dynamicaccess/session.go +++ b/pkg/dynamicaccess/session.go @@ -4,8 +4,8 @@ import ( "crypto/ecdsa" "errors" - "github.com/ethersphere/bee/pkg/crypto" - "github.com/ethersphere/bee/pkg/keystore" + "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/keystore" ) // Session represents an interface for a Diffie-Helmann key derivation diff --git a/pkg/dynamicaccess/session_test.go b/pkg/dynamicaccess/session_test.go index 501d1abd2b6..493b998a805 100644 --- a/pkg/dynamicaccess/session_test.go +++ b/pkg/dynamicaccess/session_test.go @@ -8,10 +8,10 @@ import ( "io" "testing" - "github.com/ethersphere/bee/pkg/crypto" - "github.com/ethersphere/bee/pkg/dynamicaccess" - "github.com/ethersphere/bee/pkg/dynamicaccess/mock" - memkeystore "github.com/ethersphere/bee/pkg/keystore/mem" + "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/dynamicaccess" + "github.com/ethersphere/bee/v2/pkg/dynamicaccess/mock" + memkeystore "github.com/ethersphere/bee/v2/pkg/keystore/mem" ) func mockKeyFunc(publicKey *ecdsa.PublicKey, nonces [][]byte) ([][]byte, error) { diff --git a/pkg/kvs/kvs.go b/pkg/kvs/kvs.go index 1106f2ceee8..fcb5fc668bb 100644 --- a/pkg/kvs/kvs.go +++ b/pkg/kvs/kvs.go @@ -9,10 +9,10 @@ import ( "encoding/hex" "errors" - "github.com/ethersphere/bee/pkg/file" - "github.com/ethersphere/bee/pkg/manifest" - "github.com/ethersphere/bee/pkg/storer" - "github.com/ethersphere/bee/pkg/swarm" + "github.com/ethersphere/bee/v2/pkg/file" + "github.com/ethersphere/bee/v2/pkg/manifest" + "github.com/ethersphere/bee/v2/pkg/storer" + "github.com/ethersphere/bee/v2/pkg/swarm" ) type KeyValueStore interface { diff --git a/pkg/kvs/kvs_test.go b/pkg/kvs/kvs_test.go index 309250dacff..9edfe48062c 100644 --- a/pkg/kvs/kvs_test.go +++ b/pkg/kvs/kvs_test.go @@ -8,15 +8,15 @@ import ( "context" "testing" - "github.com/ethersphere/bee/pkg/file" - "github.com/ethersphere/bee/pkg/file/loadsave" - "github.com/ethersphere/bee/pkg/file/pipeline" - "github.com/ethersphere/bee/pkg/file/pipeline/builder" - "github.com/ethersphere/bee/pkg/file/redundancy" - "github.com/ethersphere/bee/pkg/kvs" - "github.com/ethersphere/bee/pkg/storage" - mockstorer "github.com/ethersphere/bee/pkg/storer/mock" - "github.com/ethersphere/bee/pkg/swarm" + "github.com/ethersphere/bee/v2/pkg/file" + "github.com/ethersphere/bee/v2/pkg/file/loadsave" + "github.com/ethersphere/bee/v2/pkg/file/pipeline" + "github.com/ethersphere/bee/v2/pkg/file/pipeline/builder" + "github.com/ethersphere/bee/v2/pkg/file/redundancy" + "github.com/ethersphere/bee/v2/pkg/kvs" + "github.com/ethersphere/bee/v2/pkg/storage" + mockstorer "github.com/ethersphere/bee/v2/pkg/storer/mock" + "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/stretchr/testify/assert" ) diff --git a/pkg/kvs/mock/kvs.go b/pkg/kvs/mock/kvs.go index fc091a88417..78282934bf2 100644 --- a/pkg/kvs/mock/kvs.go +++ b/pkg/kvs/mock/kvs.go @@ -4,8 +4,8 @@ import ( "encoding/hex" "sync" - "github.com/ethersphere/bee/pkg/kvs" - "github.com/ethersphere/bee/pkg/swarm" + "github.com/ethersphere/bee/v2/pkg/kvs" + "github.com/ethersphere/bee/v2/pkg/swarm" ) var lock = &sync.Mutex{} From 1056212538ef8b12cc710b6917ed6473303d3881 Mon Sep 17 00:00:00 2001 From: Levente Kiss Date: Tue, 9 Apr 2024 16:23:36 +0200 Subject: [PATCH 30/41] chore(mantaray): merge mantaray fix for rebase --- pkg/dynamicaccess/history.go | 147 +++++++++++++++++++++++++--- pkg/dynamicaccess/history_test.go | 153 +++++++++++++++++++++--------- pkg/dynamicaccess/mock/history.go | 90 ------------------ pkg/manifest/mantaray.go | 24 +++-- 4 files changed, 255 insertions(+), 159 deletions(-) delete mode 100644 pkg/dynamicaccess/mock/history.go diff --git a/pkg/dynamicaccess/history.go b/pkg/dynamicaccess/history.go index 82d18f5ef8a..85a62085f36 100644 --- a/pkg/dynamicaccess/history.go +++ b/pkg/dynamicaccess/history.go @@ -1,36 +1,155 @@ package dynamicaccess import ( - "github.com/ethereum/go-ethereum/common" - "github.com/ethersphere/bee/v2/pkg/kvs" + "context" + "errors" + "fmt" + "math" + "strconv" + "time" + + "github.com/ethersphere/bee/v2/pkg/file" + "github.com/ethersphere/bee/v2/pkg/manifest" + "github.com/ethersphere/bee/v2/pkg/manifest/mantaray" + "github.com/ethersphere/bee/v2/pkg/swarm" ) type History interface { - Add(timestamp int64, kvs kvs.KeyValueStore) error - Get(timestamp int64) (kvs.KeyValueStore, error) - Lookup(at int64) (kvs.KeyValueStore, error) + Add(ctx context.Context, ref swarm.Address, timestamp *int64) error + Lookup(ctx context.Context, timestamp int64) (swarm.Address, error) + Store(ctx context.Context) (swarm.Address, error) } var _ History = (*history)(nil) +var ErrEndIteration = errors.New("end iteration") + type history struct { - history map[int64]*kvs.KeyValueStore + manifest *manifest.MantarayManifest + ls file.LoadSaver } -func NewHistory(topic []byte, owner common.Address) *history { - return &history{history: make(map[int64]*kvs.KeyValueStore)} +func NewHistory(ls file.LoadSaver, ref *swarm.Address) (*history, error) { + var err error + var m manifest.Interface + + if ref != nil { + m, err = manifest.NewDefaultManifestReference(*ref, ls) + } else { + m, err = manifest.NewDefaultManifest(ls, false) + } + if err != nil { + return nil, err + } + + mm, ok := m.(*manifest.MantarayManifest) + if !ok { + return nil, fmt.Errorf("expected MantarayManifest, got %T", m) + } + + return &history{manifest: mm, ls: ls}, nil } -func (h *history) Add(timestamp int64, kvs kvs.KeyValueStore) error { +func (h *history) Add(ctx context.Context, ref swarm.Address, timestamp *int64) error { + // Do we need any extra meta/act? + meta := map[string]string{} + // add timestamps transformed so that the latests timestamp becomes the smallest key + var unixTime int64 + if timestamp != nil { + unixTime = *timestamp + } else { + unixTime = time.Now().Unix() + } - return nil + key := strconv.FormatInt(math.MaxInt64-unixTime, 10) + return h.manifest.Add(ctx, key, manifest.NewEntry(ref, meta)) } -func (h *history) Lookup(at int64) (kvs.KeyValueStore, error) { - return nil, nil +// Lookup finds the entry for a path or returns error if not found +func (h *history) Lookup(ctx context.Context, timestamp int64) (swarm.Address, error) { + if timestamp <= 0 { + return swarm.ZeroAddress, errors.New("invalid timestamp") + } + + reversedTimestamp := math.MaxInt64 - timestamp + node, err := h.LookupNode(ctx, reversedTimestamp) + if err != nil { + return swarm.ZeroAddress, err + } + + if node != nil { + return swarm.NewAddress(node.Entry()), nil + } + + return swarm.ZeroAddress, nil } -func (h *history) Get(timestamp int64) (kvs.KeyValueStore, error) { - // get the feed +func (h *history) LookupNode(ctx context.Context, searchedTimestamp int64) (*mantaray.Node, error) { + // before node's timestamp is the closest one that is less than or equal to the searched timestamp + // for instance: 2030, 2020, 1994 -> search for 2021 -> before is 2020 + var beforeNode *mantaray.Node + // after node's timestamp is after the latest + // for instance: 2030, 2020, 1994 -> search for 1980 -> after is 1994 + var afterNode *mantaray.Node + + walker := func(pathTimestamp []byte, currNode *mantaray.Node, err error) error { + if err != nil { + return err + } + + if currNode.IsValueType() && len(currNode.Entry()) > 0 { + afterNode = currNode + + match, err := isBeforeMatch(pathTimestamp, searchedTimestamp) + if match { + beforeNode = currNode + // return error to stop the walk, this is how WalkNode works... + return ErrEndIteration + } + + return err + } + + return nil + } + + rootNode := h.manifest.Root() + err := rootNode.WalkNode(ctx, []byte{}, h.ls, walker) + + if err != nil && !errors.Is(err, ErrEndIteration) { + return nil, fmt.Errorf("history lookup node error: %w", err) + } + + if beforeNode != nil { + return beforeNode, nil + } + if afterNode != nil { + return afterNode, nil + + } return nil, nil } + +func (h *history) Store(ctx context.Context) (swarm.Address, error) { + return h.manifest.Store(ctx) +} + +func bytesToInt64(b []byte) (int64, error) { + num, err := strconv.ParseInt(string(b), 10, 64) + if err != nil { + return -1, err + } + + return num, nil +} + +func isBeforeMatch(pathTimestamp []byte, searchedTimestamp int64) (bool, error) { + targetTimestamp, err := bytesToInt64(pathTimestamp) + if err != nil { + return false, err + } + if targetTimestamp == 0 { + return false, nil + } + return searchedTimestamp <= targetTimestamp, nil +} diff --git a/pkg/dynamicaccess/history_test.go b/pkg/dynamicaccess/history_test.go index 6c0a48a4fd6..4b353823b00 100644 --- a/pkg/dynamicaccess/history_test.go +++ b/pkg/dynamicaccess/history_test.go @@ -1,59 +1,122 @@ package dynamicaccess_test import ( - "encoding/hex" + "context" "testing" "time" "github.com/ethersphere/bee/v2/pkg/dynamicaccess" - "github.com/ethersphere/bee/v2/pkg/dynamicaccess/mock" - kvsmock "github.com/ethersphere/bee/v2/pkg/kvs/mock" + "github.com/ethersphere/bee/v2/pkg/file/loadsave" + "github.com/ethersphere/bee/v2/pkg/file/pipeline" + "github.com/ethersphere/bee/v2/pkg/file/pipeline/builder" + "github.com/ethersphere/bee/v2/pkg/storage" + mockstorer "github.com/ethersphere/bee/v2/pkg/storer/mock" + "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/stretchr/testify/assert" ) -func TestHistoryLookup(t *testing.T) { - h := prepareTestHistory() - now := time.Now() - - tests := []struct { - input int64 - expected string - }{ - {input: 0, expected: "value3"}, - {input: now.Unix(), expected: "value3"}, - {input: now.AddDate(0, -5, 0).Unix(), expected: "value3"}, - {input: now.AddDate(0, -6, 0).Unix(), expected: "value3"}, - {input: now.AddDate(-1, 0, 0).Unix(), expected: "value3"}, - {input: now.AddDate(-1, -6, 0).Unix(), expected: "value2"}, - {input: now.AddDate(-2, -0, 0).Unix(), expected: "value2"}, - {input: now.AddDate(-2, -6, 0).Unix(), expected: "value1"}, - {input: now.AddDate(-3, -0, 0).Unix(), expected: "value1"}, - } +func TestHistoryAdd(t *testing.T) { + h, err := dynamicaccess.NewHistory(nil, nil) + assert.NoError(t, err) - for _, tt := range tests { - t.Run("", func(t *testing.T) { - sAt, _ := h.Lookup(tt.input) - output, _ := sAt.Get([]byte("key1")) - assert.Equal(t, output, hex.EncodeToString([]byte(tt.expected))) - }) - } + addr := swarm.NewAddress([]byte("addr")) + + ctx := context.Background() + + err = h.Add(ctx, addr, nil) + assert.NoError(t, err) +} + +func TestSingleNodeHistoryLookup(t *testing.T) { + storer := mockstorer.New() + ctx := context.Background() + ls := loadsave.New(storer.ChunkStore(), storer.Cache(), pipelineFactory(storer.Cache(), false)) + + h, err := dynamicaccess.NewHistory(ls, nil) + assert.NoError(t, err) + + testActRef := swarm.RandAddress(t) + err = h.Add(ctx, testActRef, nil) + assert.NoError(t, err) + + _, err = h.Store(ctx) + assert.NoError(t, err) + + searchedTime := time.Now().Unix() + actRef, err := h.Lookup(ctx, searchedTime) + assert.NoError(t, err) + assert.True(t, actRef.Equal(testActRef)) +} + +func TestMultiNodeHistoryLookup(t *testing.T) { + storer := mockstorer.New() + ctx := context.Background() + ls := loadsave.New(storer.ChunkStore(), storer.Cache(), pipelineFactory(storer.Cache(), false)) + + h, _ := dynamicaccess.NewHistory(ls, nil) + + testActRef1 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd891")) + firstTime := time.Date(1994, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + h.Add(ctx, testActRef1, &firstTime) + + testActRef2 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd892")) + secondTime := time.Date(2000, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + h.Add(ctx, testActRef2, &secondTime) + + testActRef3 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd893")) + thirdTime := time.Date(2015, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + h.Add(ctx, testActRef3, &thirdTime) + + testActRef4 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd894")) + fourthTime := time.Date(2020, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + h.Add(ctx, testActRef4, &fourthTime) + + testActRef5 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd895")) + fifthTime := time.Date(2030, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + h.Add(ctx, testActRef5, &fifthTime) + + // latest + searchedTime := time.Date(1980, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + actRef, err := h.Lookup(ctx, searchedTime) + assert.NoError(t, err) + assert.True(t, actRef.Equal(testActRef1)) + + // before first time + searchedTime = time.Date(2021, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + actRef, err = h.Lookup(ctx, searchedTime) + assert.NoError(t, err) + assert.True(t, actRef.Equal(testActRef4)) + + // same time + searchedTime = time.Date(2000, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + actRef, err = h.Lookup(ctx, searchedTime) + assert.NoError(t, err) + assert.True(t, actRef.Equal(testActRef2)) + + // after time + searchedTime = time.Date(2045, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + actRef, err = h.Lookup(ctx, searchedTime) + assert.NoError(t, err) + assert.True(t, actRef.Equal(testActRef5)) } -func prepareTestHistory() dynamicaccess.History { - var ( - h = mock.NewHistory() - now = time.Now() - s1 = kvsmock.New() - s2 = kvsmock.New() - s3 = kvsmock.New() - ) - s1.Put([]byte("key1"), []byte("value1")) - s2.Put([]byte("key1"), []byte("value2")) - s3.Put([]byte("key1"), []byte("value3")) - - h.Insert(now.AddDate(-3, 0, 0).Unix(), s1) - h.Insert(now.AddDate(-2, 0, 0).Unix(), s2) - h.Insert(now.AddDate(-1, 0, 0).Unix(), s3) - - return h +func TestHistoryStore(t *testing.T) { + storer := mockstorer.New() + ctx := context.Background() + ls := loadsave.New(storer.ChunkStore(), storer.Cache(), pipelineFactory(storer.Cache(), false)) + + h, _ := dynamicaccess.NewHistory(ls, nil) + + testActRef1 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd891")) + firstTime := time.Date(1994, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + h.Add(ctx, testActRef1, &firstTime) + + _, err := h.Store(ctx) + assert.NoError(t, err) +} + +func pipelineFactory(s storage.Putter, encrypt bool) func() pipeline.Interface { + return func() pipeline.Interface { + return builder.NewPipelineBuilder(context.Background(), s, encrypt, 0) + } } diff --git a/pkg/dynamicaccess/mock/history.go b/pkg/dynamicaccess/mock/history.go deleted file mode 100644 index e7747434d59..00000000000 --- a/pkg/dynamicaccess/mock/history.go +++ /dev/null @@ -1,90 +0,0 @@ -package mock - -import ( - "context" - "sort" - "time" - - "github.com/ethersphere/bee/v2/pkg/crypto" - "github.com/ethersphere/bee/v2/pkg/feeds" - "github.com/ethersphere/bee/v2/pkg/kvs" - "github.com/ethersphere/bee/v2/pkg/storage" - "github.com/ethersphere/bee/v2/pkg/swarm" -) - -type historyMock struct { - history map[int64]kvs.KeyValueStore -} - -func NewHistory() *historyMock { - return &historyMock{history: make(map[int64]kvs.KeyValueStore)} -} - -func (h *historyMock) Add(timestamp int64, act kvs.KeyValueStore) error { - h.history[timestamp] = act - return nil -} - -func (h *historyMock) Insert(timestamp int64, act kvs.KeyValueStore) *historyMock { - h.Add(timestamp, act) - return h -} - -func (h *historyMock) Lookup(at int64) (kvs.KeyValueStore, error) { - keys := []int64{} - for k := range h.history { - keys = append(keys, k) - } - - sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) - - timestamp := time.Now() - if at != 0 { - timestamp = time.Unix(at, 0) - } - - for i := len(keys) - 1; i >= 0; i-- { - update := time.Unix(keys[i], 0) - if update.Before(timestamp) || update.Equal(timestamp) { - return h.history[keys[i]], nil - } - } - return nil, nil -} - -func (h *historyMock) Get(timestamp int64) (kvs.KeyValueStore, error) { - return h.history[timestamp], nil -} - -type finder struct { - getter *feeds.Getter -} - -type updater struct { - *feeds.Putter - next uint64 -} - -func (f *finder) At(ctx context.Context, at int64, after uint64) (chunk swarm.Chunk, currentIndex, nextIndex feeds.Index, err error) { - return nil, nil, nil, nil -} - -func HistoryFinder(getter storage.Getter, feed *feeds.Feed) feeds.Lookup { - return &finder{feeds.NewGetter(getter, feed)} -} - -func (u *updater) Update(ctx context.Context, at int64, payload []byte) error { - return nil -} - -func (u *updater) Feed() *feeds.Feed { - return nil -} - -func HistoryUpdater(putter storage.Putter, signer crypto.Signer, topic []byte) (feeds.Updater, error) { - p, err := feeds.NewPutter(putter, signer, topic) - if err != nil { - return nil, err - } - return &updater{Putter: p}, nil -} diff --git a/pkg/manifest/mantaray.go b/pkg/manifest/mantaray.go index f3b86e06b66..009e3eab055 100644 --- a/pkg/manifest/mantaray.go +++ b/pkg/manifest/mantaray.go @@ -20,7 +20,7 @@ const ( ManifestMantarayContentType = "application/bzz-manifest-mantaray+octet-stream" ) -type mantarayManifest struct { +type MantarayManifest struct { trie *mantaray.Node ls file.LoadSaver @@ -31,7 +31,7 @@ func NewMantarayManifest( ls file.LoadSaver, encrypted bool, ) (Interface, error) { - mm := &mantarayManifest{ + mm := &MantarayManifest{ trie: mantaray.New(), ls: ls, } @@ -48,24 +48,28 @@ func NewMantarayManifestReference( reference swarm.Address, ls file.LoadSaver, ) (Interface, error) { - return &mantarayManifest{ + return &MantarayManifest{ trie: mantaray.NewNodeRef(reference.Bytes()), ls: ls, }, nil } -func (m *mantarayManifest) Type() string { +func (m *MantarayManifest) Root() *mantaray.Node { + return m.trie +} + +func (m *MantarayManifest) Type() string { return ManifestMantarayContentType } -func (m *mantarayManifest) Add(ctx context.Context, path string, entry Entry) error { +func (m *MantarayManifest) Add(ctx context.Context, path string, entry Entry) error { p := []byte(path) e := entry.Reference().Bytes() return m.trie.Add(ctx, p, e, entry.Metadata(), m.ls) } -func (m *mantarayManifest) Remove(ctx context.Context, path string) error { +func (m *MantarayManifest) Remove(ctx context.Context, path string) error { p := []byte(path) err := m.trie.Remove(ctx, p, m.ls) @@ -79,7 +83,7 @@ func (m *mantarayManifest) Remove(ctx context.Context, path string) error { return nil } -func (m *mantarayManifest) Lookup(ctx context.Context, path string) (Entry, error) { +func (m *MantarayManifest) Lookup(ctx context.Context, path string) (Entry, error) { p := []byte(path) node, err := m.trie.LookupNode(ctx, p, m.ls) @@ -100,13 +104,13 @@ func (m *mantarayManifest) Lookup(ctx context.Context, path string) (Entry, erro return entry, nil } -func (m *mantarayManifest) HasPrefix(ctx context.Context, prefix string) (bool, error) { +func (m *MantarayManifest) HasPrefix(ctx context.Context, prefix string) (bool, error) { p := []byte(prefix) return m.trie.HasPrefix(ctx, p, m.ls) } -func (m *mantarayManifest) Store(ctx context.Context, storeSizeFn ...StoreSizeFunc) (swarm.Address, error) { +func (m *MantarayManifest) Store(ctx context.Context, storeSizeFn ...StoreSizeFunc) (swarm.Address, error) { var ls mantaray.LoadSaver if len(storeSizeFn) > 0 { ls = &mantarayLoadSaver{ @@ -127,7 +131,7 @@ func (m *mantarayManifest) Store(ctx context.Context, storeSizeFn ...StoreSizeFu return address, nil } -func (m *mantarayManifest) IterateAddresses(ctx context.Context, fn swarm.AddressIterFunc) error { +func (m *MantarayManifest) IterateAddresses(ctx context.Context, fn swarm.AddressIterFunc) error { reference := swarm.NewAddress(m.trie.Reference()) if swarm.ZeroAddress.Equal(reference) { From f6fc3c48519a0085efa13c6e0cfab62a7ae0682e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= <58116288+bosi95@users.noreply.github.com> Date: Fri, 19 Apr 2024 13:03:25 +0200 Subject: [PATCH 31/41] chore: rebase router after API auth changes --- cmd/bee/cmd/start.go | 7 +- pkg/api/api.go | 10 + pkg/api/api_test.go | 7 + pkg/api/bytes.go | 47 +- pkg/api/bzz.go | 54 +- pkg/api/chunk.go | 28 +- pkg/api/chunk_address.go | 9 +- pkg/api/dirs.go | 13 +- pkg/api/dynamicaccess.go | 108 +++ pkg/api/dynamicaccess_test.go | 804 ++++++++++++++++++++++ pkg/api/export_test.go | 2 + pkg/api/feed.go | 19 +- pkg/api/router.go | 14 +- pkg/api/soc.go | 17 +- pkg/dynamicaccess/accesslogic.go | 40 +- pkg/dynamicaccess/accesslogic_test.go | 52 +- pkg/dynamicaccess/controller.go | 208 +++++- pkg/dynamicaccess/controller_test.go | 129 ++-- pkg/dynamicaccess/grantee.go | 10 +- pkg/dynamicaccess/grantee_manager.go | 47 -- pkg/dynamicaccess/grantee_manager_test.go | 38 - pkg/dynamicaccess/grantee_test.go | 9 +- pkg/dynamicaccess/mock/service.go | 162 +++++ pkg/dynamicaccess/service.go | 39 ++ pkg/kvs/kvs.go | 28 +- pkg/kvs/kvs_test.go | 51 +- pkg/kvs/mock/kvs.go | 7 +- pkg/node/devnode.go | 13 + pkg/node/node.go | 13 + pkg/soc/testing/soc.go | 36 + 30 files changed, 1703 insertions(+), 318 deletions(-) create mode 100644 pkg/api/dynamicaccess.go create mode 100644 pkg/api/dynamicaccess_test.go delete mode 100644 pkg/dynamicaccess/grantee_manager.go delete mode 100644 pkg/dynamicaccess/grantee_manager_test.go create mode 100644 pkg/dynamicaccess/mock/service.go create mode 100644 pkg/dynamicaccess/service.go diff --git a/cmd/bee/cmd/start.go b/cmd/bee/cmd/start.go index 56dda6a2591..8b429a6fdad 100644 --- a/cmd/bee/cmd/start.go +++ b/cmd/bee/cmd/start.go @@ -27,6 +27,7 @@ import ( chaincfg "github.com/ethersphere/bee/v2/pkg/config" "github.com/ethersphere/bee/v2/pkg/crypto" "github.com/ethersphere/bee/v2/pkg/crypto/clef" + "github.com/ethersphere/bee/v2/pkg/dynamicaccess" "github.com/ethersphere/bee/v2/pkg/keystore" filekeystore "github.com/ethersphere/bee/v2/pkg/keystore/file" memkeystore "github.com/ethersphere/bee/v2/pkg/keystore/mem" @@ -287,7 +288,7 @@ func buildBeeNode(ctx context.Context, c *command, cmd *cobra.Command, logger lo neighborhoodSuggester = c.config.GetString(optionNameNeighborhoodSuggester) } - b, err := node.NewBee(ctx, c.config.GetString(optionNameP2PAddr), signerConfig.publicKey, signerConfig.signer, networkID, logger, signerConfig.libp2pPrivateKey, signerConfig.pssPrivateKey, &node.Options{ + b, err := node.NewBee(ctx, c.config.GetString(optionNameP2PAddr), signerConfig.publicKey, signerConfig.signer, networkID, logger, signerConfig.libp2pPrivateKey, signerConfig.pssPrivateKey, signerConfig.session, &node.Options{ DataDir: c.config.GetString(optionNameDataDir), CacheCapacity: c.config.GetUint64(optionNameCacheCapacity), DBOpenFilesLimit: c.config.GetUint64(optionNameDBOpenFilesLimit), @@ -363,6 +364,7 @@ type signerConfig struct { publicKey *ecdsa.PublicKey libp2pPrivateKey *ecdsa.PrivateKey pssPrivateKey *ecdsa.PrivateKey + session dynamicaccess.Session } func waitForClef(logger log.Logger, maxRetries uint64, endpoint string) (externalSigner *external.ExternalSigner, err error) { @@ -393,6 +395,7 @@ func (c *command) configureSigner(cmd *cobra.Command, logger log.Logger) (config var signer crypto.Signer var password string var publicKey *ecdsa.PublicKey + var session dynamicaccess.Session if p := c.config.GetString(optionNamePassword); p != "" { password = p } else if pf := c.config.GetString(optionNamePasswordFile); pf != "" { @@ -465,6 +468,7 @@ func (c *command) configureSigner(cmd *cobra.Command, logger log.Logger) (config } signer = crypto.NewDefaultSigner(swarmPrivateKey) publicKey = &swarmPrivateKey.PublicKey + session = dynamicaccess.NewDefaultSession(swarmPrivateKey) } logger.Info("swarm public key", "public_key", hex.EncodeToString(crypto.EncodeSecp256k1PublicKey(publicKey))) @@ -503,6 +507,7 @@ func (c *command) configureSigner(cmd *cobra.Command, logger log.Logger) (config publicKey: publicKey, libp2pPrivateKey: libp2pPrivateKey, pssPrivateKey: pssPrivateKey, + session: session, }, nil } diff --git a/pkg/api/api.go b/pkg/api/api.go index a7eea80cb30..2566037873e 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethersphere/bee/v2/pkg/accounting" "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/dynamicaccess" "github.com/ethersphere/bee/v2/pkg/feeds" "github.com/ethersphere/bee/v2/pkg/file/pipeline" "github.com/ethersphere/bee/v2/pkg/file/pipeline/builder" @@ -83,6 +84,10 @@ const ( SwarmRedundancyFallbackModeHeader = "Swarm-Redundancy-Fallback-Mode" SwarmChunkRetrievalTimeoutHeader = "Swarm-Chunk-Retrieval-Timeout" SwarmLookAheadBufferSizeHeader = "Swarm-Lookahead-Buffer-Size" + SwarmActHeader = "Swarm-Act" + SwarmActTimestampHeader = "Swarm-Act-Timestamp" + SwarmActPublisherHeader = "Swarm-Act-Publisher" + SwarmActHistoryAddressHeader = "Swarm-Act-History-Address" ImmutableHeader = "Immutable" GasPriceHeader = "Gas-Price" @@ -115,6 +120,8 @@ var ( errBatchUnusable = errors.New("batch not usable") errUnsupportedDevNodeOperation = errors.New("operation not supported in dev mode") errOperationSupportedOnlyInFullMode = errors.New("operation is supported only in full mode") + errActDownload = errors.New("act download failed") + errActUpload = errors.New("act upload failed") ) // Storer interface provides the functionality required from the local storage @@ -144,6 +151,7 @@ type Service struct { feedFactory feeds.Factory signer crypto.Signer post postage.Service + dac dynamicaccess.Service postageContract postagecontract.Interface probe *Probe metricsRegistry *prometheus.Registry @@ -240,6 +248,7 @@ type ExtraOptions struct { Pss pss.Interface FeedFactory feeds.Factory Post postage.Service + Dac dynamicaccess.Service PostageContract postagecontract.Interface Staking staking.Contract Steward steward.Interface @@ -322,6 +331,7 @@ func (s *Service) Configure(signer crypto.Signer, tracer *tracing.Tracer, o Opti s.pss = e.Pss s.feedFactory = e.FeedFactory s.post = e.Post + s.dac = e.Dac s.postageContract = e.PostageContract s.steward = e.Steward s.stakingContract = e.Staking diff --git a/pkg/api/api_test.go b/pkg/api/api_test.go index b8c8044998a..3e107d15824 100644 --- a/pkg/api/api_test.go +++ b/pkg/api/api_test.go @@ -26,6 +26,8 @@ import ( accountingmock "github.com/ethersphere/bee/v2/pkg/accounting/mock" "github.com/ethersphere/bee/v2/pkg/api" "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/dynamicaccess" + mockdac "github.com/ethersphere/bee/v2/pkg/dynamicaccess/mock" "github.com/ethersphere/bee/v2/pkg/feeds" "github.com/ethersphere/bee/v2/pkg/file/pipeline" "github.com/ethersphere/bee/v2/pkg/file/pipeline/builder" @@ -100,6 +102,7 @@ type testServerOptions struct { PostageContract postagecontract.Interface StakingContract staking.Contract Post postage.Service + Dac dynamicaccess.Service Steward steward.Interface WsHeaders http.Header DirectUpload bool @@ -147,6 +150,9 @@ func newTestServer(t *testing.T, o testServerOptions) (*http.Client, *websocket. if o.Post == nil { o.Post = mockpost.New() } + if o.Dac == nil { + o.Dac = mockdac.New() + } if o.BatchStore == nil { o.BatchStore = mockbatchstore.New(mockbatchstore.WithAcceptAllExistsFunc()) // default is with accept-all Exists() func } @@ -187,6 +193,7 @@ func newTestServer(t *testing.T, o testServerOptions) (*http.Client, *websocket. Pss: o.Pss, FeedFactory: o.Feeds, Post: o.Post, + Dac: o.Dac, PostageContract: o.PostageContract, Steward: o.Steward, SyncStatus: o.SyncStatus, diff --git a/pkg/api/bytes.go b/pkg/api/bytes.go index 9b5d9b902d6..dc3735a497b 100644 --- a/pkg/api/bytes.go +++ b/pkg/api/bytes.go @@ -33,12 +33,14 @@ func (s *Service) bytesUploadHandler(w http.ResponseWriter, r *http.Request) { defer span.Finish() headers := struct { - BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` - SwarmTag uint64 `map:"Swarm-Tag"` - Pin bool `map:"Swarm-Pin"` - Deferred *bool `map:"Swarm-Deferred-Upload"` - Encrypt bool `map:"Swarm-Encrypt"` - RLevel redundancy.Level `map:"Swarm-Redundancy-Level"` + BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` + SwarmTag uint64 `map:"Swarm-Tag"` + Pin bool `map:"Swarm-Pin"` + Deferred *bool `map:"Swarm-Deferred-Upload"` + Encrypt bool `map:"Swarm-Encrypt"` + RLevel redundancy.Level `map:"Swarm-Redundancy-Level"` + Act bool `map:"Swarm-Act"` + HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address"` }{} if response := s.mapStructure(r.Header, &headers); response != nil { response("invalid header params", logger, w) @@ -100,7 +102,7 @@ func (s *Service) bytesUploadHandler(w http.ResponseWriter, r *http.Request) { } p := requestPipelineFn(putter, headers.Encrypt, headers.RLevel) - address, err := p(ctx, r.Body) + reference, err := p(ctx, r.Body) if err != nil { logger.Debug("split write all failed", "error", err) logger.Error(nil, "split write all failed") @@ -114,9 +116,18 @@ func (s *Service) bytesUploadHandler(w http.ResponseWriter, r *http.Request) { return } - span.SetTag("root_address", address) + encryptedReference := reference + if headers.Act { + encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, reference, headers.HistoryAddress) + if err != nil { + jsonhttp.InternalServerError(w, errActUpload) + return + } + } + // TODO: what should be the root_address ? (eref vs ref) + span.SetTag("root_address", reference) - err = putter.Done(address) + err = putter.Done(reference) if err != nil { logger.Debug("done split failed", "error", err) logger.Error(nil, "done split failed") @@ -133,7 +144,7 @@ func (s *Service) bytesUploadHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Expose-Headers", SwarmTagHeader) jsonhttp.Created(w, bytesPostResponse{ - Reference: address, + Reference: encryptedReference, }) } @@ -149,11 +160,16 @@ func (s *Service) bytesGetHandler(w http.ResponseWriter, r *http.Request) { return } + address := paths.Address + if v := getAddressFromContext(r.Context()); !v.Equal(swarm.ZeroAddress) { + address = v + } + additionalHeaders := http.Header{ ContentTypeHeader: {"application/octet-stream"}, } - s.downloadHandler(logger, w, r, paths.Address, additionalHeaders, true, false) + s.downloadHandler(logger, w, r, address, additionalHeaders, true, false) } func (s *Service) bytesHeadHandler(w http.ResponseWriter, r *http.Request) { @@ -167,11 +183,16 @@ func (s *Service) bytesHeadHandler(w http.ResponseWriter, r *http.Request) { return } + address := paths.Address + if v := getAddressFromContext(r.Context()); !v.Equal(swarm.ZeroAddress) { + address = v + } + getter := s.storer.Download(true) - ch, err := getter.Get(r.Context(), paths.Address) + ch, err := getter.Get(r.Context(), address) if err != nil { - logger.Debug("get root chunk failed", "chunk_address", paths.Address, "error", err) + logger.Debug("get root chunk failed", "chunk_address", address, "error", err) logger.Error(nil, "get rook chunk failed") w.WriteHeader(http.StatusNotFound) return diff --git a/pkg/api/bzz.go b/pkg/api/bzz.go index 65ded851f12..241e30cf165 100644 --- a/pkg/api/bzz.go +++ b/pkg/api/bzz.go @@ -63,14 +63,16 @@ func (s *Service) bzzUploadHandler(w http.ResponseWriter, r *http.Request) { defer span.Finish() headers := struct { - ContentType string `map:"Content-Type,mimeMediaType" validate:"required"` - BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` - SwarmTag uint64 `map:"Swarm-Tag"` - Pin bool `map:"Swarm-Pin"` - Deferred *bool `map:"Swarm-Deferred-Upload"` - Encrypt bool `map:"Swarm-Encrypt"` - IsDir bool `map:"Swarm-Collection"` - RLevel redundancy.Level `map:"Swarm-Redundancy-Level"` + ContentType string `map:"Content-Type,mimeMediaType" validate:"required"` + BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` + SwarmTag uint64 `map:"Swarm-Tag"` + Pin bool `map:"Swarm-Pin"` + Deferred *bool `map:"Swarm-Deferred-Upload"` + Encrypt bool `map:"Swarm-Encrypt"` + IsDir bool `map:"Swarm-Collection"` + RLevel redundancy.Level `map:"Swarm-Redundancy-Level"` + Act bool `map:"Swarm-Act"` + HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address"` }{} if response := s.mapStructure(r.Header, &headers); response != nil { response("invalid header params", logger, w) @@ -132,10 +134,10 @@ func (s *Service) bzzUploadHandler(w http.ResponseWriter, r *http.Request) { } if headers.IsDir || headers.ContentType == multiPartFormData { - s.dirUploadHandler(ctx, logger, span, ow, r, putter, r.Header.Get(ContentTypeHeader), headers.Encrypt, tag, headers.RLevel) + s.dirUploadHandler(ctx, logger, span, ow, r, putter, r.Header.Get(ContentTypeHeader), headers.Encrypt, tag, headers.RLevel, headers.Act, headers.HistoryAddress) return } - s.fileUploadHandler(ctx, logger, span, ow, r, putter, headers.Encrypt, tag, headers.RLevel) + s.fileUploadHandler(ctx, logger, span, ow, r, putter, headers.Encrypt, tag, headers.RLevel, headers.Act, headers.HistoryAddress) } // fileUploadResponse is returned when an HTTP request to upload a file is successful @@ -155,6 +157,8 @@ func (s *Service) fileUploadHandler( encrypt bool, tagID uint64, rLevel redundancy.Level, + act bool, + historyAddress *swarm.Address, ) { queries := struct { FileName string `map:"name" validate:"startsnotwith=/"` @@ -260,6 +264,15 @@ func (s *Service) fileUploadHandler( } logger.Debug("store", "manifest_reference", manifestReference) + encryptedReference := manifestReference + if act { + encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, manifestReference, historyAddress) + if err != nil { + jsonhttp.InternalServerError(w, errActUpload) + return + } + } + err = putter.Done(manifestReference) if err != nil { logger.Debug("done split failed", "error", err) @@ -268,7 +281,7 @@ func (s *Service) fileUploadHandler( ext.LogError(span, err, olog.String("action", "putter.Done")) return } - + // TODO: what should be the root_address ? (eref vs ref) span.LogFields(olog.Bool("success", true)) span.SetTag("root_address", manifestReference) @@ -276,10 +289,11 @@ func (s *Service) fileUploadHandler( w.Header().Set(SwarmTagHeader, fmt.Sprint(tagID)) span.SetTag("tagID", tagID) } - w.Header().Set(ETagHeader, fmt.Sprintf("%q", manifestReference.String())) + w.Header().Set(ETagHeader, fmt.Sprintf("%q", encryptedReference.String())) w.Header().Set("Access-Control-Expose-Headers", SwarmTagHeader) + // TODO: do we need to return reference as well ? jsonhttp.Created(w, bzzUploadResponse{ - Reference: manifestReference, + Reference: encryptedReference, }) } @@ -295,11 +309,16 @@ func (s *Service) bzzDownloadHandler(w http.ResponseWriter, r *http.Request) { return } + address := paths.Address + if v := getAddressFromContext(r.Context()); !v.Equal(swarm.ZeroAddress) { + address = v + } + if strings.HasSuffix(paths.Path, "/") { paths.Path = strings.TrimRight(paths.Path, "/") + "/" // NOTE: leave one slash if there was some. } - s.serveReference(logger, paths.Address, paths.Path, w, r, false) + s.serveReference(logger, address, paths.Path, w, r, false) } func (s *Service) bzzHeadHandler(w http.ResponseWriter, r *http.Request) { @@ -314,11 +333,16 @@ func (s *Service) bzzHeadHandler(w http.ResponseWriter, r *http.Request) { return } + address := paths.Address + if v := getAddressFromContext(r.Context()); !v.Equal(swarm.ZeroAddress) { + address = v + } + if strings.HasSuffix(paths.Path, "/") { paths.Path = strings.TrimRight(paths.Path, "/") + "/" // NOTE: leave one slash if there was some. } - s.serveReference(logger, paths.Address, paths.Path, w, r, true) + s.serveReference(logger, address, paths.Path, w, r, true) } func (s *Service) serveReference(logger log.Logger, address swarm.Address, pathVar string, w http.ResponseWriter, r *http.Request, headerOnly bool) { diff --git a/pkg/api/chunk.go b/pkg/api/chunk.go index a572cacdcdf..13738f70d39 100644 --- a/pkg/api/chunk.go +++ b/pkg/api/chunk.go @@ -30,8 +30,10 @@ func (s *Service) chunkUploadHandler(w http.ResponseWriter, r *http.Request) { logger := s.logger.WithName("post_chunk").Build() headers := struct { - BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` - SwarmTag uint64 `map:"Swarm-Tag"` + BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` + SwarmTag uint64 `map:"Swarm-Tag"` + Act bool `map:"Swarm-Act"` + HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address"` }{} if response := s.mapStructure(r.Header, &headers); response != nil { response("invalid header params", logger, w) @@ -139,6 +141,15 @@ func (s *Service) chunkUploadHandler(w http.ResponseWriter, r *http.Request) { } } + encryptedReference := chunk.Address() + if headers.Act { + encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, chunk.Address(), headers.HistoryAddress) + if err != nil { + jsonhttp.InternalServerError(w, errActUpload) + return + } + } + err = putter.Put(r.Context(), chunk) if err != nil { logger.Debug("chunk upload: write chunk failed", "chunk_address", chunk.Address(), "error", err) @@ -165,7 +176,7 @@ func (s *Service) chunkUploadHandler(w http.ResponseWriter, r *http.Request) { } w.Header().Set("Access-Control-Expose-Headers", SwarmTagHeader) - jsonhttp.Created(w, chunkAddressResponse{Reference: chunk.Address()}) + jsonhttp.Created(w, chunkAddressResponse{Reference: encryptedReference}) } func (s *Service) chunkGetHandler(w http.ResponseWriter, r *http.Request) { @@ -192,15 +203,20 @@ func (s *Service) chunkGetHandler(w http.ResponseWriter, r *http.Request) { return } - chunk, err := s.storer.Download(cache).Get(r.Context(), paths.Address) + address := paths.Address + if v := getAddressFromContext(r.Context()); !v.Equal(swarm.ZeroAddress) { + address = v + } + + chunk, err := s.storer.Download(cache).Get(r.Context(), address) if err != nil { if errors.Is(err, storage.ErrNotFound) { - loggerV1.Debug("chunk not found", "address", paths.Address) + loggerV1.Debug("chunk not found", "address", address) jsonhttp.NotFound(w, "chunk not found") return } - logger.Debug("read chunk failed", "chunk_address", paths.Address, "error", err) + logger.Debug("read chunk failed", "chunk_address", address, "error", err) logger.Error(nil, "read chunk failed") jsonhttp.InternalServerError(w, "read chunk failed") return diff --git a/pkg/api/chunk_address.go b/pkg/api/chunk_address.go index 6f214a0ea03..c49c980c2e7 100644 --- a/pkg/api/chunk_address.go +++ b/pkg/api/chunk_address.go @@ -23,9 +23,14 @@ func (s *Service) hasChunkHandler(w http.ResponseWriter, r *http.Request) { return } - has, err := s.storer.ChunkStore().Has(r.Context(), paths.Address) + address := paths.Address + if v := getAddressFromContext(r.Context()); !v.Equal(swarm.ZeroAddress) { + address = v + } + + has, err := s.storer.ChunkStore().Has(r.Context(), address) if err != nil { - logger.Debug("has chunk failed", "chunk_address", paths.Address, "error", err) + logger.Debug("has chunk failed", "chunk_address", address, "error", err) jsonhttp.BadRequest(w, err) return } diff --git a/pkg/api/dirs.go b/pkg/api/dirs.go index f54a02807c9..1ec5e6cde40 100644 --- a/pkg/api/dirs.go +++ b/pkg/api/dirs.go @@ -47,6 +47,8 @@ func (s *Service) dirUploadHandler( encrypt bool, tag uint64, rLevel redundancy.Level, + act bool, + historyAddress *swarm.Address, ) { if r.Body == http.NoBody { logger.Error(nil, "request has no body") @@ -98,6 +100,15 @@ func (s *Service) dirUploadHandler( return } + encryptedReference := reference + if act { + encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, reference, historyAddress) + if err != nil { + jsonhttp.InternalServerError(w, errActUpload) + return + } + } + err = putter.Done(reference) if err != nil { logger.Debug("store dir failed", "error", err) @@ -113,7 +124,7 @@ func (s *Service) dirUploadHandler( } w.Header().Set("Access-Control-Expose-Headers", SwarmTagHeader) jsonhttp.Created(w, bzzUploadResponse{ - Reference: reference, + Reference: encryptedReference, }) } diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go new file mode 100644 index 00000000000..7a05b1ad38c --- /dev/null +++ b/pkg/api/dynamicaccess.go @@ -0,0 +1,108 @@ +package api + +import ( + "context" + "crypto/ecdsa" + "net/http" + + "github.com/ethersphere/bee/v2/pkg/jsonhttp" + "github.com/ethersphere/bee/v2/pkg/log" + storer "github.com/ethersphere/bee/v2/pkg/storer" + "github.com/ethersphere/bee/v2/pkg/swarm" + "github.com/gorilla/mux" +) + +type addressKey struct{} + +// getAddressFromContext is a helper function to extract the address from the context +func getAddressFromContext(ctx context.Context) swarm.Address { + v, ok := ctx.Value(addressKey{}).(swarm.Address) + if ok { + return v + } + return swarm.ZeroAddress +} + +// setAddress sets the swarm address in the context +func setAddressInContext(ctx context.Context, address swarm.Address) context.Context { + return context.WithValue(ctx, addressKey{}, address) +} + +func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { + return func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + logger := s.logger.WithName("acthandler").Build() + paths := struct { + Address swarm.Address `map:"address,resolve" validate:"required"` + }{} + if response := s.mapStructure(mux.Vars(r), &paths); response != nil { + response("invalid path params", logger, w) + return + } + + headers := struct { + Timestamp *int64 `map:"Swarm-Act-Timestamp"` + Publisher *ecdsa.PublicKey `map:"Swarm-Act-Publisher"` + HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address"` + }{} + if response := s.mapStructure(r.Header, &headers); response != nil { + response("invalid header params", logger, w) + return + } + + // Try to download the file wihtout decryption, if the act headers are not present + if headers.Publisher == nil || headers.Timestamp == nil || headers.HistoryAddress == nil { + h.ServeHTTP(w, r) + return + } + ctx := r.Context() + reference, err := s.dac.DownloadHandler(ctx, *headers.Timestamp, paths.Address, headers.Publisher, *headers.HistoryAddress) + if err != nil { + jsonhttp.InternalServerError(w, errActDownload) + return + } + h.ServeHTTP(w, r.WithContext(setAddressInContext(ctx, reference))) + }) + } + +} + +// TODO: is ctx needed in ctrl upload ? +func (s *Service) actEncryptionHandler( + ctx context.Context, + logger log.Logger, + w http.ResponseWriter, + putter storer.PutterSession, + reference swarm.Address, + historyAddress *swarm.Address, +) (swarm.Address, error) { + publisherPublicKey := &s.publicKey + kvsReference, historyReference, encryptedReference, err := s.dac.UploadHandler(ctx, reference, publisherPublicKey, historyAddress) + if err != nil { + logger.Debug("act failed to encrypt reference", "error", err) + logger.Error(nil, "act failed to encrypt reference") + return swarm.ZeroAddress, err + } + err = putter.Done(historyReference) + if err != nil { + logger.Debug("done split history failed", "error", err) + logger.Error(nil, "done split history failed") + return swarm.ZeroAddress, err + } + err = putter.Done(encryptedReference) + if err != nil { + logger.Debug("done split encrypted reference failed", "error", err) + logger.Error(nil, "done split encrypted reference failed") + return swarm.ZeroAddress, err + } + err = putter.Done(kvsReference) + if err != nil { + logger.Debug("done split kvs reference failed", "error", err) + logger.Error(nil, "done split kvs reference failed") + return swarm.ZeroAddress, err + } + + w.Header().Set(SwarmActHistoryAddressHeader, historyReference.String()) + + return encryptedReference, nil +} diff --git a/pkg/api/dynamicaccess_test.go b/pkg/api/dynamicaccess_test.go new file mode 100644 index 00000000000..4657b1f8bbe --- /dev/null +++ b/pkg/api/dynamicaccess_test.go @@ -0,0 +1,804 @@ +// Copyright 2020 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package api_test + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "io" + "net/http" + "strconv" + "strings" + "testing" + "time" + + "github.com/ethersphere/bee/v2/pkg/api" + "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/dynamicaccess" + mockdac "github.com/ethersphere/bee/v2/pkg/dynamicaccess/mock" + "github.com/ethersphere/bee/v2/pkg/file/loadsave" + "github.com/ethersphere/bee/v2/pkg/file/redundancy" + "github.com/ethersphere/bee/v2/pkg/jsonhttp" + "github.com/ethersphere/bee/v2/pkg/jsonhttp/jsonhttptest" + "github.com/ethersphere/bee/v2/pkg/log" + mockpost "github.com/ethersphere/bee/v2/pkg/postage/mock" + testingsoc "github.com/ethersphere/bee/v2/pkg/soc/testing" + mockstorer "github.com/ethersphere/bee/v2/pkg/storer/mock" + "github.com/ethersphere/bee/v2/pkg/swarm" + "gitlab.com/nolash/go-mockbytes" +) + +func prepareHistoryFixture(storer api.Storer) (dynamicaccess.History, swarm.Address) { + ctx := context.Background() + ls := loadsave.New(storer.ChunkStore(), storer.Cache(), pipelineFactory(storer.Cache(), false, redundancy.NONE)) + + h, _ := dynamicaccess.NewHistory(ls, nil) + + testActRef1 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd891")) + firstTime := time.Date(1994, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + h.Add(ctx, testActRef1, &firstTime) + + testActRef2 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd892")) + secondTime := time.Date(2000, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + h.Add(ctx, testActRef2, &secondTime) + + testActRef3 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd893")) + thirdTime := time.Date(2015, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + h.Add(ctx, testActRef3, &thirdTime) + + testActRef4 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd894")) + fourthTime := time.Date(2020, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + h.Add(ctx, testActRef4, &fourthTime) + + testActRef5 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd895")) + fifthTime := time.Date(2030, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + h.Add(ctx, testActRef5, &fifthTime) + + ref, _ := h.Store(ctx) + return h, ref +} + +// TODO: feed test +// nolint:paralleltest,tparallel +// TestDacWithoutActHeader [positive tests]: +// On each endpoint: upload w/ "Swarm-Act" header then download and check the decrypted data +func TestDacEachEndpointWithAct(t *testing.T) { + t.Parallel() + var ( + spk, _ = hex.DecodeString("a786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfa") + pk, _ = crypto.DecodeSecp256k1PrivateKey(spk) + publicKeyBytes = crypto.EncodeSecp256k1PublicKey(&pk.PublicKey) + publisher = hex.EncodeToString(publicKeyBytes) + testfile = "testfile1" + storerMock = mockstorer.New() + logger = log.Noop + now = time.Now().Unix() + chunk = swarm.NewChunk( + swarm.MustParseHexAddress("0025737be11979e91654dffd2be817ac1e52a2dadb08c97a7cef12f937e707bc"), + []byte{72, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 149, 179, 31, 244, 146, 247, 129, 123, 132, 248, 215, 77, 44, 47, 91, 248, 229, 215, 89, 156, 210, 243, 3, 110, 204, 74, 101, 119, 53, 53, 145, 188, 193, 153, 130, 197, 83, 152, 36, 140, 150, 209, 191, 214, 193, 4, 144, 121, 32, 45, 205, 220, 59, 227, 28, 43, 161, 51, 108, 14, 106, 180, 135, 2}, + ) + g = mockbytes.New(0, mockbytes.MockTypeStandard).WithModulus(255) + bytedata, _ = g.SequentialBytes(swarm.ChunkSize * 2) + tag, _ = storerMock.NewSession() + sch = testingsoc.GenerateMockSOCWithKey(t, []byte("foo"), pk) + dirdata = []byte("Lorem ipsum dolor sit amet") + socResource = func(owner, id, sig string) string { return fmt.Sprintf("/soc/%s/%s?sig=%s", owner, id, sig) } + ) + + tc := []struct { + name string + downurl string + upurl string + exphash string + data io.Reader + expdata []byte + contenttype string + resp struct { + Reference swarm.Address `json:"reference"` + } + }{ + { + name: "bzz", + upurl: "/bzz?name=sample.html", + downurl: "/bzz", + exphash: "a5df670544eaea29e61b19d8739faa4573b19e4426e58a173e51ed0b5e7e2ade", + resp: api.BzzUploadResponse{Reference: swarm.MustParseHexAddress("a5df670544eaea29e61b19d8739faa4573b19e4426e58a173e51ed0b5e7e2ade")}, + data: strings.NewReader(testfile), + expdata: []byte(testfile), + contenttype: "text/html; charset=utf-8", + }, + { + name: "bzz-dir", + upurl: "/bzz?name=ipsum/lorem.txt", + downurl: "/bzz", + exphash: "6561b2a744d2a8f276270585da22e092c07c56624af83ac9969d52b54e87cee6/ipsum/lorem.txt", + resp: api.BzzUploadResponse{Reference: swarm.MustParseHexAddress("6561b2a744d2a8f276270585da22e092c07c56624af83ac9969d52b54e87cee6")}, + data: tarFiles(t, []f{ + { + data: dirdata, + name: "lorem.txt", + dir: "ipsum", + header: http.Header{ + api.ContentTypeHeader: {"text/plain; charset=utf-8"}, + }, + }, + }), + expdata: dirdata, + contenttype: api.ContentTypeTar, + }, + { + name: "bytes", + upurl: "/bytes", + downurl: "/bytes", + exphash: "e30da540bb9e1901169977fcf617f28b7f8df4537de978784f6d47491619a630", + resp: api.BytesPostResponse{Reference: swarm.MustParseHexAddress("e30da540bb9e1901169977fcf617f28b7f8df4537de978784f6d47491619a630")}, + data: bytes.NewReader(bytedata), + expdata: bytedata, + contenttype: "application/octet-stream", + }, + { + name: "chunks", + upurl: "/chunks", + downurl: "/chunks", + exphash: "ca8d2d29466e017cba46d383e7e0794d99a141185ec525086037f25fc2093155", + resp: api.ChunkAddressResponse{Reference: swarm.MustParseHexAddress("ca8d2d29466e017cba46d383e7e0794d99a141185ec525086037f25fc2093155")}, + data: bytes.NewReader(chunk.Data()), + expdata: chunk.Data(), + contenttype: "binary/octet-stream", + }, + { + name: "soc", + upurl: socResource(hex.EncodeToString(sch.Owner), hex.EncodeToString(sch.ID), hex.EncodeToString(sch.Signature)), + downurl: "/chunks", + exphash: "b100d7ce487426b17b98ff779fad4f2dd471d04ab1c8949dd2a1a78fe4a1524e", + resp: api.ChunkAddressResponse{Reference: swarm.MustParseHexAddress("b100d7ce487426b17b98ff779fad4f2dd471d04ab1c8949dd2a1a78fe4a1524e")}, + data: bytes.NewReader(sch.WrappedChunk.Data()), + expdata: sch.Chunk().Data(), + contenttype: "binary/octet-stream", + }, + } + + for _, v := range tc { + upTestOpts := []jsonhttptest.Option{ + jsonhttptest.WithRequestHeader(api.SwarmActHeader, "true"), + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestHeader(api.SwarmPinHeader, "true"), + jsonhttptest.WithRequestHeader(api.SwarmTagHeader, fmt.Sprintf("%d", tag.TagID)), + jsonhttptest.WithRequestBody(v.data), + jsonhttptest.WithExpectedJSONResponse(v.resp), + jsonhttptest.WithRequestHeader(api.ContentTypeHeader, v.contenttype), + } + if v.name == "soc" { + upTestOpts = append(upTestOpts, jsonhttptest.WithRequestHeader(api.SwarmPinHeader, "true")) + } else { + upTestOpts = append(upTestOpts, jsonhttptest.WithNonEmptyResponseHeader(api.SwarmTagHeader)) + } + expcontenttype := v.contenttype + if v.name == "bzz-dir" { + expcontenttype = "text/plain; charset=utf-8" + upTestOpts = append(upTestOpts, jsonhttptest.WithRequestHeader(api.SwarmCollectionHeader, "True")) + } + t.Run(v.name, func(t *testing.T) { + client, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(), + }) + header := jsonhttptest.Request(t, client, http.MethodPost, v.upurl, http.StatusCreated, + upTestOpts..., + ) + + historyRef := header.Get(api.SwarmActHistoryAddressHeader) + jsonhttptest.Request(t, client, http.MethodGet, v.downurl+"/"+v.exphash, http.StatusOK, + jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(now, 10)), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, historyRef), + jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, publisher), + jsonhttptest.WithExpectedResponse(v.expdata), + jsonhttptest.WithExpectedContentLength(len(v.expdata)), + jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, expcontenttype), + ) + + if v.name != "bzz-dir" && v.name != "soc" && v.name != "chunks" { + t.Run("head", func(t *testing.T) { + jsonhttptest.Request(t, client, http.MethodHead, v.downurl+"/"+v.exphash, http.StatusOK, + jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(now, 10)), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, historyRef), + jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, publisher), + jsonhttptest.WithRequestBody(v.data), + jsonhttptest.WithExpectedContentLength(len(v.expdata)), + jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, expcontenttype), + ) + }) + } + }) + } +} + +// nolint:paralleltest,tparallel +// TestDacWithoutActHeader [negative tests]: +// 1. upload w/ "Swarm-Act" header then try to dowload w/o the header. +// 2. upload w/o "Swarm-Act" header then try to dowload w/ the header. +func TestDacWithoutAct(t *testing.T) { + t.Parallel() + var ( + spk, _ = hex.DecodeString("a786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfa") + pk, _ = crypto.DecodeSecp256k1PrivateKey(spk) + publicKeyBytes = crypto.EncodeSecp256k1PublicKey(&pk.PublicKey) + publisher = hex.EncodeToString(publicKeyBytes) + fileUploadResource = "/bzz" + fileDownloadResource = func(addr string) string { return "/bzz/" + addr } + storerMock = mockstorer.New() + h, fixtureHref = prepareHistoryFixture(storerMock) + logger = log.Noop + fileName = "sample.html" + now = time.Now().Unix() + ) + + t.Run("upload-w/-act-then-download-w/o-act", func(t *testing.T) { + client, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(mockdac.WithHistory(h, fixtureHref.String())), + }) + var ( + testfile = "testfile1" + encryptedRef = "a5df670544eaea29e61b19d8739faa4573b19e4426e58a173e51ed0b5e7e2ade" + ) + jsonhttptest.Request(t, client, http.MethodPost, fileUploadResource+"?name="+fileName, http.StatusCreated, + jsonhttptest.WithRequestHeader(api.SwarmActHeader, "true"), + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestBody(strings.NewReader(testfile)), + jsonhttptest.WithExpectedJSONResponse(api.BzzUploadResponse{ + Reference: swarm.MustParseHexAddress(encryptedRef), + }), + jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), + jsonhttptest.WithNonEmptyResponseHeader(api.SwarmTagHeader), + jsonhttptest.WithExpectedResponseHeader(api.ETagHeader, fmt.Sprintf("%q", encryptedRef)), + ) + + jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusNotFound, + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Message: "address not found or incorrect", + Code: http.StatusNotFound, + }), + jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, "application/json; charset=utf-8"), + ) + }) + + t.Run("upload-w/o-act-then-download-w/-act", func(t *testing.T) { + client, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(), + }) + var ( + rootHash = "0cb947ccbc410c43139ba4409d83bf89114cb0d79556a651c06c888cf73f4d7e" + sampleHtml = ` + + + +

My First Heading

+ +

My first paragraph.

+ + + ` + ) + + jsonhttptest.Request(t, client, http.MethodPost, fileUploadResource+"?name="+fileName, http.StatusCreated, + jsonhttptest.WithRequestHeader(api.SwarmDeferredUploadHeader, "true"), + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestBody(strings.NewReader(sampleHtml)), + jsonhttptest.WithExpectedJSONResponse(api.BzzUploadResponse{ + Reference: swarm.MustParseHexAddress(rootHash), + }), + jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), + jsonhttptest.WithNonEmptyResponseHeader(api.SwarmTagHeader), + jsonhttptest.WithExpectedResponseHeader(api.ETagHeader, fmt.Sprintf("%q", rootHash)), + ) + + jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(rootHash), http.StatusInternalServerError, + jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(now, 10)), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, fixtureHref.String()), + jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, publisher), + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Message: api.ErrActDownload.Error(), + Code: http.StatusInternalServerError, + }), + jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, "application/json; charset=utf-8"), + ) + }) +} + +// nolint:paralleltest,tparallel +// TestDacInvalidPath [negative test]: Expect Bad request when the path address is invalid. +func TestDacInvalidPath(t *testing.T) { + t.Parallel() + var ( + spk, _ = hex.DecodeString("a786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfa") + pk, _ = crypto.DecodeSecp256k1PrivateKey(spk) + publicKeyBytes = crypto.EncodeSecp256k1PublicKey(&pk.PublicKey) + publisher = hex.EncodeToString(publicKeyBytes) + fileDownloadResource = func(addr string) string { return "/bzz/" + addr } + storerMock = mockstorer.New() + _, fixtureHref = prepareHistoryFixture(storerMock) + logger = log.Noop + now = time.Now().Unix() + ) + + t.Run("invalid-path-params", func(t *testing.T) { + client, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(), + }) + var ( + encryptedRef = "asd" + ) + + jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusBadRequest, + jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(now, 10)), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, fixtureHref.String()), + jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, publisher), + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "address", + Error: api.HexInvalidByteError('s').Error(), + }, + }}), + jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), + ) + }) +} + +// nolint:paralleltest,tparallel +// TestDacHistory tests: +// [positive tests] 1., 2.: uploading a file w/ and w/o history address then downloading it and checking the data. +// [negative test] 3. uploading a file then downloading it with a wrong history address. +// [negative test] 4. uploading a file to a wrong history address. +// [negative test] 4. downloading a file to w/o history address. +func TestDacHistory(t *testing.T) { + t.Parallel() + var ( + spk, _ = hex.DecodeString("a786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfa") + pk, _ = crypto.DecodeSecp256k1PrivateKey(spk) + publicKeyBytes = crypto.EncodeSecp256k1PublicKey(&pk.PublicKey) + publisher = hex.EncodeToString(publicKeyBytes) + fileUploadResource = "/bzz" + fileDownloadResource = func(addr string) string { return "/bzz/" + addr } + storerMock = mockstorer.New() + h, fixtureHref = prepareHistoryFixture(storerMock) + logger = log.Noop + fileName = "sample.html" + now = time.Now().Unix() + ) + + t.Run("empty-history-upload-then-download-and-check-data", func(t *testing.T) { + client, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(), + }) + var ( + testfile = "testfile1" + encryptedRef = "a5df670544eaea29e61b19d8739faa4573b19e4426e58a173e51ed0b5e7e2ade" + ) + header := jsonhttptest.Request(t, client, http.MethodPost, fileUploadResource+"?name="+fileName, http.StatusCreated, + jsonhttptest.WithRequestHeader(api.SwarmActHeader, "true"), + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestBody(strings.NewReader(testfile)), + jsonhttptest.WithExpectedJSONResponse(api.BzzUploadResponse{ + Reference: swarm.MustParseHexAddress(encryptedRef), + }), + jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), + jsonhttptest.WithNonEmptyResponseHeader(api.SwarmTagHeader), + jsonhttptest.WithExpectedResponseHeader(api.ETagHeader, fmt.Sprintf("%q", encryptedRef)), + ) + + historyRef := header.Get(api.SwarmActHistoryAddressHeader) + jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusOK, + jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(now, 10)), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, historyRef), + jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, publisher), + jsonhttptest.WithExpectedResponse([]byte(testfile)), + jsonhttptest.WithExpectedContentLength(len(testfile)), + jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), + jsonhttptest.WithExpectedResponseHeader(api.ContentDispositionHeader, fmt.Sprintf(`inline; filename="%s"`, fileName)), + ) + }) + + t.Run("with-history-upload-then-download-and-check-data", func(t *testing.T) { + client, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(mockdac.WithHistory(h, fixtureHref.String())), + }) + var ( + encryptedRef = "c611199e1b3674d6bf89a83e518bd16896bf5315109b4a23dcb4682a02d17b97" + testfile = ` + + + +

My First Heading

+ +

My first paragraph.

+ + + ` + ) + + jsonhttptest.Request(t, client, http.MethodPost, fileUploadResource+"?name="+fileName, http.StatusCreated, + jsonhttptest.WithRequestHeader(api.SwarmActHeader, "true"), + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, fixtureHref.String()), + jsonhttptest.WithRequestBody(strings.NewReader(testfile)), + jsonhttptest.WithExpectedJSONResponse(api.BzzUploadResponse{ + Reference: swarm.MustParseHexAddress(encryptedRef), + }), + jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), + jsonhttptest.WithNonEmptyResponseHeader(api.SwarmTagHeader), + jsonhttptest.WithExpectedResponseHeader(api.ETagHeader, fmt.Sprintf("%q", encryptedRef)), + ) + + jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusOK, + jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(now, 10)), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, fixtureHref.String()), + jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, publisher), + jsonhttptest.WithExpectedResponse([]byte(testfile)), + jsonhttptest.WithExpectedContentLength(len(testfile)), + jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), + jsonhttptest.WithExpectedResponseHeader(api.ContentDispositionHeader, fmt.Sprintf(`inline; filename="%s"`, fileName)), + ) + }) + + t.Run("upload-then-download-wrong-history", func(t *testing.T) { + client, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(mockdac.WithHistory(h, fixtureHref.String())), + }) + var ( + testfile = "testfile1" + encryptedRef = "a5df670544eaea29e61b19d8739faa4573b19e4426e58a173e51ed0b5e7e2ade" + ) + jsonhttptest.Request(t, client, http.MethodPost, fileUploadResource+"?name="+fileName, http.StatusCreated, + jsonhttptest.WithRequestHeader(api.SwarmActHeader, "true"), + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestBody(strings.NewReader(testfile)), + jsonhttptest.WithExpectedJSONResponse(api.BzzUploadResponse{ + Reference: swarm.MustParseHexAddress(encryptedRef), + }), + jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), + jsonhttptest.WithNonEmptyResponseHeader(api.SwarmTagHeader), + jsonhttptest.WithExpectedResponseHeader(api.ETagHeader, fmt.Sprintf("%q", encryptedRef)), + ) + + jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusInternalServerError, + jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(now, 10)), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, "fc4e9fe978991257b897d987bc4ff13058b66ef45a53189a0b4fe84bb3346396"), + jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, publisher), + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Message: api.ErrActDownload.Error(), + Code: http.StatusInternalServerError, + }), + jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, "application/json; charset=utf-8"), + ) + }) + + t.Run("upload-wrong-history", func(t *testing.T) { + client, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(), + }) + var ( + testfile = "testfile1" + ) + + jsonhttptest.Request(t, client, http.MethodPost, fileUploadResource+"?name="+fileName, http.StatusInternalServerError, + jsonhttptest.WithRequestHeader(api.SwarmActHeader, "true"), + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, fixtureHref.String()), + jsonhttptest.WithRequestBody(strings.NewReader(testfile)), + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Message: api.ErrActUpload.Error(), + Code: http.StatusInternalServerError, + }), + jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), + ) + }) + + t.Run("download-w/o-history", func(t *testing.T) { + client, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(mockdac.WithHistory(h, fixtureHref.String())), + }) + var ( + encryptedRef = "a5df670544eaea29e61b19d8739faa4573b19e4426e58a173e51ed0b5e7e2ade" + ) + + jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusNotFound, + jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(now, 10)), + jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, publisher), + jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, "application/json; charset=utf-8"), + ) + }) +} + +// nolint:paralleltest,tparallel +// TestDacTimestamp doc. comment +// [positive test] 1.: uploading a file w/ ACT then download it w/ timestamp and check the data. +// [negative test] 2.: try to download a file w/o timestamp. +func TestDacTimestamp(t *testing.T) { + t.Parallel() + var ( + spk, _ = hex.DecodeString("a786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfa") + pk, _ = crypto.DecodeSecp256k1PrivateKey(spk) + publicKeyBytes = crypto.EncodeSecp256k1PublicKey(&pk.PublicKey) + publisher = hex.EncodeToString(publicKeyBytes) + fileUploadResource = "/bzz" + fileDownloadResource = func(addr string) string { return "/bzz/" + addr } + storerMock = mockstorer.New() + h, fixtureHref = prepareHistoryFixture(storerMock) + logger = log.Noop + fileName = "sample.html" + ) + t.Run("upload-then-download-with-timestamp-and-check-data", func(t *testing.T) { + client, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(mockdac.WithHistory(h, fixtureHref.String())), + }) + var ( + thirdTime = time.Date(2015, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + encryptedRef = "c611199e1b3674d6bf89a83e518bd16896bf5315109b4a23dcb4682a02d17b97" + testfile = ` + + + +

My First Heading

+ +

My first paragraph.

+ + + ` + ) + + jsonhttptest.Request(t, client, http.MethodPost, fileUploadResource+"?name="+fileName, http.StatusCreated, + jsonhttptest.WithRequestHeader(api.SwarmActHeader, "true"), + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, fixtureHref.String()), + jsonhttptest.WithRequestBody(strings.NewReader(testfile)), + jsonhttptest.WithExpectedJSONResponse(api.BzzUploadResponse{ + Reference: swarm.MustParseHexAddress(encryptedRef), + }), + jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), + jsonhttptest.WithNonEmptyResponseHeader(api.SwarmTagHeader), + jsonhttptest.WithExpectedResponseHeader(api.ETagHeader, fmt.Sprintf("%q", encryptedRef)), + ) + + jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusOK, + jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(thirdTime, 10)), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, fixtureHref.String()), + jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, publisher), + jsonhttptest.WithExpectedResponse([]byte(testfile)), + jsonhttptest.WithExpectedContentLength(len(testfile)), + jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), + jsonhttptest.WithExpectedResponseHeader(api.ContentDispositionHeader, fmt.Sprintf(`inline; filename="%s"`, fileName)), + ) + }) + + t.Run("download-w/o-timestamp", func(t *testing.T) { + var ( + encryptedRef = "a5df670544eaea29e61b19d8739faa4573b19e4426e58a173e51ed0b5e7e2ade" + ) + client, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(mockdac.WithHistory(h, fixtureHref.String())), + }) + + jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusNotFound, + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, fixtureHref.String()), + jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, publisher), + jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, "application/json; charset=utf-8"), + ) + }) +} + +// nolint:paralleltest,tparallel +// TestDacPublisher doc. comment +// [positive test] 1.: uploading a file w/ ACT then download it w/ the publisher address and check the data. +// [negative test] 2.: expect Bad request when the public key is invalid. +// [negative test] 3.: try to download a file w/ an incorrect publisher address. +// [negative test] 3.: try to download a file w/o a publisher address. +func TestDacPublisher(t *testing.T) { + t.Parallel() + var ( + spk, _ = hex.DecodeString("a786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfa") + pk, _ = crypto.DecodeSecp256k1PrivateKey(spk) + publicKeyBytes = crypto.EncodeSecp256k1PublicKey(&pk.PublicKey) + publisher = hex.EncodeToString(publicKeyBytes) + fileUploadResource = "/bzz" + fileDownloadResource = func(addr string) string { return "/bzz/" + addr } + storerMock = mockstorer.New() + h, fixtureHref = prepareHistoryFixture(storerMock) + logger = log.Noop + fileName = "sample.html" + now = time.Now().Unix() + ) + + t.Run("upload-then-download-w/-publisher-and-check-data", func(t *testing.T) { + client, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(mockdac.WithHistory(h, fixtureHref.String()), mockdac.WithPublisher(publisher)), + }) + var ( + encryptedRef = "a5a26b4915d7ce1622f9ca52252092cf2445f98d359dabaf52588c05911aaf4f" + testfile = ` + + + +

My First Heading

+ +

My first paragraph.

+ + + ` + ) + + jsonhttptest.Request(t, client, http.MethodPost, fileUploadResource+"?name="+fileName, http.StatusCreated, + jsonhttptest.WithRequestHeader(api.SwarmActHeader, "true"), + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, fixtureHref.String()), + jsonhttptest.WithRequestBody(strings.NewReader(testfile)), + jsonhttptest.WithExpectedJSONResponse(api.BzzUploadResponse{ + Reference: swarm.MustParseHexAddress(encryptedRef), + }), + jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), + jsonhttptest.WithNonEmptyResponseHeader(api.SwarmTagHeader), + jsonhttptest.WithExpectedResponseHeader(api.ETagHeader, fmt.Sprintf("%q", encryptedRef)), + ) + + jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusOK, + jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(now, 10)), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, fixtureHref.String()), + jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, publisher), + jsonhttptest.WithExpectedResponse([]byte(testfile)), + jsonhttptest.WithExpectedContentLength(len(testfile)), + jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), + jsonhttptest.WithExpectedResponseHeader(api.ContentDispositionHeader, fmt.Sprintf(`inline; filename="%s"`, fileName)), + ) + }) + + t.Run("upload-then-download-invalid-publickey", func(t *testing.T) { + client, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(mockdac.WithPublisher(publisher)), + }) + var ( + publickey = "b786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfb" + encryptedRef = "a5a26b4915d7ce1622f9ca52252092cf2445f98d359dabaf52588c05911aaf4f" + testfile = ` + + + +

My First Heading

+ +

My first paragraph.

+ + + ` + ) + + header := jsonhttptest.Request(t, client, http.MethodPost, fileUploadResource+"?name="+fileName, http.StatusCreated, + jsonhttptest.WithRequestHeader(api.SwarmActHeader, "true"), + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestBody(strings.NewReader(testfile)), + jsonhttptest.WithExpectedJSONResponse(api.BzzUploadResponse{ + Reference: swarm.MustParseHexAddress(encryptedRef), + }), + jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), + jsonhttptest.WithNonEmptyResponseHeader(api.SwarmTagHeader), + jsonhttptest.WithExpectedResponseHeader(api.ETagHeader, fmt.Sprintf("%q", encryptedRef)), + ) + + historyRef := header.Get(api.SwarmActHistoryAddressHeader) + jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusBadRequest, + jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(now, 10)), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, historyRef), + jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, publickey), + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid header params", + Reasons: []jsonhttp.Reason{ + { + Field: "Swarm-Act-Publisher", + Error: "malformed public key: invalid length: 32", + }, + }}), + jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), + ) + }) + + t.Run("download-w/-wrong-publisher", func(t *testing.T) { + var ( + downloader = "03c712a7e29bc792ac8d8ae49793d28d5bda27ed70f0d90697b2fb456c0a168bd2" + encryptedRef = "a5df670544eaea29e61b19d8739faa4573b19e4426e58a173e51ed0b5e7e2ade" + ) + client, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(mockdac.WithHistory(h, fixtureHref.String()), mockdac.WithPublisher(publisher)), + }) + + jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusInternalServerError, + jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(now, 10)), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, fixtureHref.String()), + jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, downloader), + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Message: api.ErrActDownload.Error(), + Code: http.StatusInternalServerError, + }), + jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, "application/json; charset=utf-8"), + ) + }) + + t.Run("download-w/o-publisher", func(t *testing.T) { + var ( + encryptedRef = "a5df670544eaea29e61b19d8739faa4573b19e4426e58a173e51ed0b5e7e2ade" + ) + client, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(mockdac.WithHistory(h, fixtureHref.String()), mockdac.WithPublisher(publisher)), + }) + + jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusNotFound, + jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(now, 10)), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, fixtureHref.String()), + jsonhttptest.WithRequestHeader(api.SwarmActPublisherHeader, publisher), + jsonhttptest.WithExpectedResponseHeader(api.ContentTypeHeader, "application/json; charset=utf-8"), + ) + }) +} diff --git a/pkg/api/export_test.go b/pkg/api/export_test.go index 6eba8a56841..6c07c260479 100644 --- a/pkg/api/export_test.go +++ b/pkg/api/export_test.go @@ -36,6 +36,8 @@ var ( ErrInvalidNameOrAddress = errInvalidNameOrAddress ErrUnsupportedDevNodeOperation = errUnsupportedDevNodeOperation ErrOperationSupportedOnlyInFullMode = errOperationSupportedOnlyInFullMode + ErrActDownload = errActDownload + ErrActUpload = errActUpload ) var ( diff --git a/pkg/api/feed.go b/pkg/api/feed.go index 09fdf6515ec..eed03febe31 100644 --- a/pkg/api/feed.go +++ b/pkg/api/feed.go @@ -137,9 +137,11 @@ func (s *Service) feedPostHandler(w http.ResponseWriter, r *http.Request) { } headers := struct { - BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` - Pin bool `map:"Swarm-Pin"` - Deferred *bool `map:"Swarm-Deferred-Upload"` + BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` + Pin bool `map:"Swarm-Pin"` + Deferred *bool `map:"Swarm-Deferred-Upload"` + Act bool `map:"Swarm-Act"` + HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address"` }{} if response := s.mapStructure(r.Header, &headers); response != nil { response("invalid header params", logger, w) @@ -244,6 +246,15 @@ func (s *Service) feedPostHandler(w http.ResponseWriter, r *http.Request) { } return } + // TODO: do we want to allow feed act upload/ download? + encryptedReference := ref + if headers.Act { + encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, ref, headers.HistoryAddress) + if err != nil { + jsonhttp.InternalServerError(w, errActUpload) + return + } + } err = putter.Done(ref) if err != nil { @@ -253,7 +264,7 @@ func (s *Service) feedPostHandler(w http.ResponseWriter, r *http.Request) { return } - jsonhttp.Created(w, feedReferenceResponse{Reference: ref}) + jsonhttp.Created(w, feedReferenceResponse{Reference: encryptedReference}) } func parseFeedUpdate(ch swarm.Chunk) (swarm.Address, int64, error) { diff --git a/pkg/api/router.go b/pkg/api/router.go index e07f406bec7..e8d963a4d6f 100644 --- a/pkg/api/router.go +++ b/pkg/api/router.go @@ -209,10 +209,12 @@ func (s *Service) mountAPI() { "GET": web.ChainHandlers( s.contentLengthMetricMiddleware(), s.newTracingHandler("bytes-download"), + s.actDecryptionHandler(), web.FinalHandlerFunc(s.bytesGetHandler), ), "HEAD": web.ChainHandlers( s.newTracingHandler("bytes-head"), + s.actDecryptionHandler(), web.FinalHandlerFunc(s.bytesHeadHandler), ), }) @@ -230,8 +232,14 @@ func (s *Service) mountAPI() { )) handle("/chunks/{address}", jsonhttp.MethodHandler{ - "GET": http.HandlerFunc(s.chunkGetHandler), - "HEAD": http.HandlerFunc(s.hasChunkHandler), + "GET": web.ChainHandlers( + s.actDecryptionHandler(), + web.FinalHandlerFunc(s.chunkGetHandler), + ), + "HEAD": web.ChainHandlers( + s.actDecryptionHandler(), + web.FinalHandlerFunc(s.hasChunkHandler), + ), }) handle("/soc/{owner}/{id}", jsonhttp.MethodHandler{ @@ -267,9 +275,11 @@ func (s *Service) mountAPI() { "GET": web.ChainHandlers( s.contentLengthMetricMiddleware(), s.newTracingHandler("bzz-download"), + s.actDecryptionHandler(), web.FinalHandlerFunc(s.bzzDownloadHandler), ), "HEAD": web.ChainHandlers( + s.actDecryptionHandler(), web.FinalHandlerFunc(s.bzzHeadHandler), ), }) diff --git a/pkg/api/soc.go b/pkg/api/soc.go index 0abf338deb9..b4ce98e91eb 100644 --- a/pkg/api/soc.go +++ b/pkg/api/soc.go @@ -43,8 +43,10 @@ func (s *Service) socUploadHandler(w http.ResponseWriter, r *http.Request) { } headers := struct { - BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` - Pin bool `map:"Swarm-Pin"` + BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` + Pin bool `map:"Swarm-Pin"` + Act bool `map:"Swarm-Act"` + HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address"` }{} if response := s.mapStructure(r.Header, &headers); response != nil { response("invalid header params", logger, w) @@ -155,6 +157,15 @@ func (s *Service) socUploadHandler(w http.ResponseWriter, r *http.Request) { return } + encryptedReference := sch.Address() + if headers.Act { + encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, sch.Address(), headers.HistoryAddress) + if err != nil { + jsonhttp.InternalServerError(w, errActUpload) + return + } + } + err = putter.Put(r.Context(), sch) if err != nil { logger.Debug("write chunk failed", "chunk_address", sch.Address(), "error", err) @@ -171,5 +182,5 @@ func (s *Service) socUploadHandler(w http.ResponseWriter, r *http.Request) { return } - jsonhttp.Created(w, chunkAddressResponse{Reference: sch.Address()}) + jsonhttp.Created(w, socPostResponse{Reference: encryptedReference}) } diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index a2a6fbdce24..be9e8b194f1 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -1,6 +1,7 @@ package dynamicaccess import ( + "context" "crypto/ecdsa" encryption "github.com/ethersphere/bee/v2/pkg/encryption" @@ -14,7 +15,7 @@ var hashFunc = sha3.NewLegacyKeccak256 // Read-only interface for the ACT type Decryptor interface { // DecryptRef will return a decrypted reference, for given encrypted reference and grantee - DecryptRef(storage kvs.KeyValueStore, encryptedRef swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) + DecryptRef(ctx context.Context, storage kvs.KeyValueStore, encryptedRef swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) // Embedding the Session interface Session } @@ -24,9 +25,9 @@ type Control interface { // Embedding the Decryptor interface Decryptor // Adds a new grantee to the ACT - AddGrantee(storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKey *encryption.Key) error + AddGrantee(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKey *encryption.Key) error // Encrypts a Swarm reference for a given grantee - EncryptRef(storage kvs.KeyValueStore, grantee *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) + EncryptRef(ctx context.Context, storage kvs.KeyValueStore, grantee *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) } type ActLogic struct { @@ -36,17 +37,17 @@ type ActLogic struct { var _ Control = (*ActLogic)(nil) // Adds a new publisher to an empty act -func (al ActLogic) AddPublisher(storage kvs.KeyValueStore, publisher *ecdsa.PublicKey) error { +func (al ActLogic) AddPublisher(ctx context.Context, storage kvs.KeyValueStore, publisher *ecdsa.PublicKey) error { accessKey := encryption.GenerateRandomKey(encryption.KeyLength) - return al.AddGrantee(storage, publisher, publisher, &accessKey) + return al.AddGrantee(ctx, storage, publisher, publisher, &accessKey) } // Encrypts a SWARM reference for a publisher -func (al ActLogic) EncryptRef(storage kvs.KeyValueStore, publisherPubKey *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) { - accessKey, err := al.getAccessKey(storage, publisherPubKey) +func (al ActLogic) EncryptRef(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) { + accessKey, err := al.getAccessKey(ctx, storage, publisherPubKey) if err != nil { - return swarm.EmptyAddress, err + return swarm.ZeroAddress, err } refCipher := encryption.New(accessKey, 0, uint32(0), hashFunc) encryptedRef, _ := refCipher.Encrypt(ref.Bytes()) @@ -55,13 +56,13 @@ func (al ActLogic) EncryptRef(storage kvs.KeyValueStore, publisherPubKey *ecdsa. } // Adds a new grantee to the ACT -func (al ActLogic) AddGrantee(storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKeyPointer *encryption.Key) error { +func (al ActLogic) AddGrantee(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKeyPointer *encryption.Key) error { var accessKey encryption.Key var err error // Declare the "err" variable if accessKeyPointer == nil { // Get previously generated access key - accessKey, err = al.getAccessKey(storage, publisherPubKey) + accessKey, err = al.getAccessKey(ctx, storage, publisherPubKey) if err != nil { return err } @@ -87,11 +88,11 @@ func (al ActLogic) AddGrantee(storage kvs.KeyValueStore, publisherPubKey, grante } // Add the new encrypted access key for the Act - return storage.Put(lookupKey, granteeEncryptedAccessKey) + return storage.Put(ctx, lookupKey, granteeEncryptedAccessKey) } // Will return the access key for a publisher (public key) -func (al *ActLogic) getAccessKey(storage kvs.KeyValueStore, publisherPubKey *ecdsa.PublicKey) ([]byte, error) { +func (al *ActLogic) getAccessKey(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey *ecdsa.PublicKey) ([]byte, error) { keys, err := al.getKeys(publisherPubKey) if err != nil { return nil, err @@ -100,13 +101,12 @@ func (al *ActLogic) getAccessKey(storage kvs.KeyValueStore, publisherPubKey *ecd publisherAKDecryptionKey := keys[1] // no need to constructor call if value not found in act accessKeyDecryptionCipher := encryption.New(encryption.Key(publisherAKDecryptionKey), 0, uint32(0), hashFunc) - encryptedAK, err := storage.Get(publisherLookupKey) + encryptedAK, err := storage.Get(ctx, publisherLookupKey) if err != nil { return nil, err } return accessKeyDecryptionCipher.Decrypt(encryptedAK) - } var oneByteArray = []byte{1} @@ -118,32 +118,32 @@ func (al *ActLogic) getKeys(publicKey *ecdsa.PublicKey) ([][]byte, error) { } // DecryptRef will return a decrypted reference, for given encrypted reference and publisher -func (al ActLogic) DecryptRef(storage kvs.KeyValueStore, encryptedRef swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) { +func (al ActLogic) DecryptRef(ctx context.Context, storage kvs.KeyValueStore, encryptedRef swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) { keys, err := al.getKeys(publisher) if err != nil { - return swarm.EmptyAddress, err + return swarm.ZeroAddress, err } lookupKey := keys[0] accessKeyDecryptionKey := keys[1] // Lookup encrypted access key from the ACT manifest - encryptedAccessKey, err := storage.Get(lookupKey) + encryptedAccessKey, err := storage.Get(ctx, lookupKey) if err != nil { - return swarm.EmptyAddress, err + return swarm.ZeroAddress, err } // Decrypt access key accessKeyCipher := encryption.New(encryption.Key(accessKeyDecryptionKey), 0, uint32(0), hashFunc) accessKey, err := accessKeyCipher.Decrypt(encryptedAccessKey) if err != nil { - return swarm.EmptyAddress, err + return swarm.ZeroAddress, err } // Decrypt reference refCipher := encryption.New(accessKey, 0, uint32(0), hashFunc) ref, err := refCipher.Decrypt(encryptedRef.Bytes()) if err != nil { - return swarm.EmptyAddress, err + return swarm.ZeroAddress, err } return swarm.NewAddress(ref), nil diff --git a/pkg/dynamicaccess/accesslogic_test.go b/pkg/dynamicaccess/accesslogic_test.go index be115f424d1..66a3f8930e3 100644 --- a/pkg/dynamicaccess/accesslogic_test.go +++ b/pkg/dynamicaccess/accesslogic_test.go @@ -1,6 +1,7 @@ package dynamicaccess_test import ( + "context" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" @@ -14,7 +15,7 @@ import ( ) // Generates a new test environment with a fix private key -func setupAccessLogic2() dynamicaccess.ActLogic { +func setupAccessLogic() dynamicaccess.ActLogic { privateKey := getPrivKey(1) diffieHellman := dynamicaccess.NewDefaultSession(privateKey) al := dynamicaccess.NewLogic(diffieHellman) @@ -50,10 +51,11 @@ func getPrivKey(keyNumber int) *ecdsa.PrivateKey { } func TestDecryptRef_Success(t *testing.T) { + ctx := context.Background() id0 := getPrivKey(0) s := kvsmock.New() - al := setupAccessLogic2() - err := al.AddPublisher(s, &id0.PublicKey) + al := setupAccessLogic() + err := al.AddPublisher(ctx, s, &id0.PublicKey) if err != nil { t.Errorf("AddPublisher: expected no error, got %v", err) } @@ -63,14 +65,14 @@ func TestDecryptRef_Success(t *testing.T) { expectedRef := swarm.NewAddress(byteRef) t.Logf("encryptedRef: %s", expectedRef.String()) - encryptedRef, err := al.EncryptRef(s, &id0.PublicKey, expectedRef) + encryptedRef, err := al.EncryptRef(ctx, s, &id0.PublicKey, expectedRef) t.Logf("encryptedRef: %s", encryptedRef.String()) if err != nil { t.Errorf("There was an error while calling EncryptRef: ") t.Error(err) } - acutalRef, err := al.DecryptRef(s, encryptedRef, &id0.PublicKey) + acutalRef, err := al.DecryptRef(ctx, s, encryptedRef, &id0.PublicKey) if err != nil { t.Errorf("There was an error while calling Get: ") t.Error(err) @@ -83,18 +85,19 @@ func TestDecryptRef_Success(t *testing.T) { } func TestDecryptRefWithGrantee_Success(t *testing.T) { + ctx := context.Background() id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) diffieHellman := dynamicaccess.NewDefaultSession(id0) al := dynamicaccess.NewLogic(diffieHellman) s := kvsmock.New() - err := al.AddPublisher(s, &id0.PublicKey) + err := al.AddPublisher(ctx, s, &id0.PublicKey) if err != nil { t.Errorf("AddPublisher: expected no error, got %v", err) } id1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - err = al.AddGrantee(s, &id0.PublicKey, &id1.PublicKey, nil) + err = al.AddGrantee(ctx, s, &id0.PublicKey, &id1.PublicKey, nil) if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } @@ -104,7 +107,7 @@ func TestDecryptRefWithGrantee_Success(t *testing.T) { expectedRef := swarm.NewAddress(byteRef) t.Logf("encryptedRef: %s", expectedRef.String()) - encryptedRef, err := al.EncryptRef(s, &id0.PublicKey, expectedRef) + encryptedRef, err := al.EncryptRef(ctx, s, &id0.PublicKey, expectedRef) t.Logf("encryptedRef: %s", encryptedRef.String()) if err != nil { t.Errorf("There was an error while calling EncryptRef: ") @@ -113,7 +116,7 @@ func TestDecryptRefWithGrantee_Success(t *testing.T) { diffieHellman2 := dynamicaccess.NewDefaultSession(id1) granteeAccessLogic := dynamicaccess.NewLogic(diffieHellman2) - acutalRef, err := granteeAccessLogic.DecryptRef(s, encryptedRef, &id0.PublicKey) + acutalRef, err := granteeAccessLogic.DecryptRef(ctx, s, encryptedRef, &id0.PublicKey) if err != nil { t.Errorf("There was an error while calling Get: ") t.Error(err) @@ -128,18 +131,19 @@ func TestDecryptRefWithGrantee_Success(t *testing.T) { func TestDecryptRef_Error(t *testing.T) { id0 := getPrivKey(0) + ctx := context.Background() s := kvsmock.New() - al := setupAccessLogic2() - err := al.AddPublisher(s, &id0.PublicKey) + al := setupAccessLogic() + err := al.AddPublisher(ctx, s, &id0.PublicKey) if err != nil { t.Errorf("AddPublisher: expected no error, got %v", err) } expectedRef := "39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559" - encryptedRef, _ := al.EncryptRef(s, &id0.PublicKey, swarm.NewAddress([]byte(expectedRef))) + encryptedRef, _ := al.EncryptRef(ctx, s, &id0.PublicKey, swarm.NewAddress([]byte(expectedRef))) - r, err := al.DecryptRef(s, encryptedRef, nil) + r, err := al.DecryptRef(ctx, s, encryptedRef, nil) if err == nil { t.Logf("r: %s", r.String()) t.Errorf("Get should return encrypted access key not found error!") @@ -150,9 +154,10 @@ func TestAddPublisher(t *testing.T) { id0 := getPrivKey(0) savedLookupKey := "b6ee086390c280eeb9824c331a4427596f0c8510d5564bc1b6168d0059a46e2b" s := kvsmock.New() + ctx := context.Background() - al := setupAccessLogic2() - err := al.AddPublisher(s, &id0.PublicKey) + al := setupAccessLogic() + err := al.AddPublisher(ctx, s, &id0.PublicKey) if err != nil { t.Errorf("AddPublisher: expected no error, got %v", err) } @@ -162,7 +167,7 @@ func TestAddPublisher(t *testing.T) { t.Errorf("DecodeString: expected no error, got %v", err) } - encryptedAccessKey, err := s.Get(decodedSavedLookupKey) + encryptedAccessKey, err := s.Get(ctx, decodedSavedLookupKey) if err != nil { t.Errorf("Lookup: expected no error, got %v", err) } @@ -183,24 +188,25 @@ func TestAddNewGranteeToContent(t *testing.T) { id0 := getPrivKey(0) id1 := getPrivKey(1) id2 := getPrivKey(2) + ctx := context.Background() publisherLookupKey := "b6ee086390c280eeb9824c331a4427596f0c8510d5564bc1b6168d0059a46e2b" firstAddedGranteeLookupKey := "a13678e81f9d939b9401a3ad7e548d2ceb81c50f8c76424296e83a1ad79c0df0" secondAddedGranteeLookupKey := "d5e9a6499ca74f5b8b958a4b89b7338045b2baa9420e115443a8050e26986564" s := kvsmock.New() - al := setupAccessLogic2() - err := al.AddPublisher(s, &id0.PublicKey) + al := setupAccessLogic() + err := al.AddPublisher(ctx, s, &id0.PublicKey) if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } - err = al.AddGrantee(s, &id0.PublicKey, &id1.PublicKey, nil) + err = al.AddGrantee(ctx, s, &id0.PublicKey, &id1.PublicKey, nil) if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } - err = al.AddGrantee(s, &id0.PublicKey, &id2.PublicKey, nil) + err = al.AddGrantee(ctx, s, &id0.PublicKey, &id2.PublicKey, nil) if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } @@ -209,7 +215,7 @@ func TestAddNewGranteeToContent(t *testing.T) { if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } - result, _ := s.Get(lookupKeyAsByte) + result, _ := s.Get(ctx, lookupKeyAsByte) hexEncodedEncryptedAK := hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { t.Errorf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) @@ -219,7 +225,7 @@ func TestAddNewGranteeToContent(t *testing.T) { if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } - result, _ = s.Get(lookupKeyAsByte) + result, _ = s.Get(ctx, lookupKeyAsByte) hexEncodedEncryptedAK = hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { t.Errorf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) @@ -229,7 +235,7 @@ func TestAddNewGranteeToContent(t *testing.T) { if err != nil { t.Errorf("AddNewGrantee: expected no error, got %v", err) } - result, _ = s.Get(lookupKeyAsByte) + result, _ = s.Get(ctx, lookupKeyAsByte) hexEncodedEncryptedAK = hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { t.Errorf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index ccdc8c2d7f4..87a24e707b2 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -1,54 +1,208 @@ package dynamicaccess import ( + "context" "crypto/ecdsa" + "time" + "github.com/ethersphere/bee/v2/pkg/file/loadsave" + "github.com/ethersphere/bee/v2/pkg/file/pipeline" + "github.com/ethersphere/bee/v2/pkg/file/pipeline/builder" + "github.com/ethersphere/bee/v2/pkg/file/redundancy" + "github.com/ethersphere/bee/v2/pkg/kvs" kvsmock "github.com/ethersphere/bee/v2/pkg/kvs/mock" + "github.com/ethersphere/bee/v2/pkg/storage" "github.com/ethersphere/bee/v2/pkg/swarm" ) +type GranteeManager interface { + //PUT /grantees/{grantee} + //body: {publisher?, grantee root hash ,grantee} + Grant(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error + //DELETE /grantees/{grantee} + //body: {publisher?, grantee root hash , grantee} + Revoke(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error + //[ ] + //POST /grantees + //body: {publisher, historyRootHash} + Commit(ctx context.Context, granteesAddress swarm.Address, actRootHash swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, swarm.Address, error) + + //Post /grantees + //{publisher, addList, removeList} + HandleGrantees(ctx context.Context, rootHash swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) error + + //GET /grantees/{history root hash} + GetGrantees(ctx context.Context, rootHash swarm.Address) ([]*ecdsa.PublicKey, error) +} + +// TODO: ądd granteeList ref to history metadata to solve inconsistency type Controller interface { - DownloadHandler(timestamp int64, enryptedRef swarm.Address, publisher *ecdsa.PublicKey, tag string) (swarm.Address, error) - UploadHandler(ref swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) + GranteeManager + DownloadHandler(ctx context.Context, timestamp int64, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, error) + UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash *swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) } -type defaultController struct { - history History - granteeManager GranteeManager - accessLogic ActLogic +type controller struct { + accessLogic ActLogic + granteeList GranteeList + //[ ]: do we need to protect this with a mutex? + revokeFlag []swarm.Address + getter storage.Getter + putter storage.Putter } -func (c *defaultController) DownloadHandler(timestamp int64, enryptedRef swarm.Address, publisher *ecdsa.PublicKey, tag string) (swarm.Address, error) { - kvs, err := c.history.Lookup(timestamp) +var _ Controller = (*controller)(nil) + +func (c *controller) DownloadHandler( + ctx context.Context, + timestamp int64, + encryptedRef swarm.Address, + publisher *ecdsa.PublicKey, + historyRootHash swarm.Address, +) (swarm.Address, error) { + ls := loadsave.New(c.getter, c.putter, requestPipelineFactory(ctx, c.putter, false, redundancy.NONE)) + history, err := NewHistory(ls, &historyRootHash) if err != nil { - return swarm.EmptyAddress, err + return swarm.ZeroAddress, err } - addr, err := c.accessLogic.DecryptRef(kvs, enryptedRef, publisher) - return addr, err + + kvsRef, err := history.Lookup(ctx, timestamp) + if err != nil { + return swarm.ZeroAddress, err + } + kvs := kvs.New(ls, kvsRef) + return c.accessLogic.DecryptRef(ctx, kvs, encryptedRef, publisher) } -func (c *defaultController) UploadHandler(ref swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) { - kvs, err := c.history.Lookup(0) +// TODO: review return params: how to get back history ref ? +func (c *controller) UploadHandler( + ctx context.Context, + refrefence swarm.Address, + publisher *ecdsa.PublicKey, + historyRootHash *swarm.Address, +) (swarm.Address, swarm.Address, swarm.Address, error) { + ls := loadsave.New(c.getter, c.putter, requestPipelineFactory(ctx, c.putter, false, redundancy.NONE)) + history, err := NewHistory(ls, historyRootHash) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + now := time.Now().Unix() + kvsRef, err := history.Lookup(ctx, now) if err != nil { - return swarm.EmptyAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - if kvs == nil { - // new feed - // TODO: putter session to create kvs - kvs = kvsmock.New() - _, err = c.granteeManager.Publish(kvs, publisher) + kvs := kvs.New(ls, kvsRef) + historyRef := swarm.ZeroAddress + if historyRootHash != nil { + historyRef = *historyRootHash + } + if kvsRef.Equal(swarm.ZeroAddress) { + err = c.accessLogic.AddPublisher(ctx, kvs, publisher) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + kvsRef, err = kvs.Save(ctx) if err != nil { - return swarm.EmptyAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + err = history.Add(ctx, kvsRef, &now) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + historyRef, err = history.Store(ctx) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + } + encryptedRef, err := c.accessLogic.EncryptRef(ctx, kvs, publisher, refrefence) + return kvsRef, historyRef, encryptedRef, err +} + +func NewController(ctx context.Context, accessLogic ActLogic, getter storage.Getter, putter storage.Putter) Controller { + return &controller{ + granteeList: nil, + accessLogic: accessLogic, + getter: getter, + putter: putter, + } +} + +func (c *controller) Grant(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error { + return c.granteeList.Add([]*ecdsa.PublicKey{grantee}) +} + +func (c *controller) Revoke(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error { + if !c.isRevokeFlagged(granteesAddress) { + c.setRevokeFlag(granteesAddress, true) + } + return c.granteeList.Remove([]*ecdsa.PublicKey{grantee}) +} + +func (c *controller) Commit(ctx context.Context, granteesAddress swarm.Address, actRootHash swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, swarm.Address, error) { + var act kvs.KeyValueStore + if c.isRevokeFlagged(granteesAddress) { + act = kvsmock.New() + c.accessLogic.AddPublisher(ctx, act, publisher) + } else { + act = kvsmock.NewReference(actRootHash) + } + + grantees := c.granteeList.Get() + for _, grantee := range grantees { + c.accessLogic.AddGrantee(ctx, act, publisher, grantee, nil) + } + + granteeref, err := c.granteeList.Save(ctx) + if err != nil { + return swarm.EmptyAddress, swarm.EmptyAddress, err + } + + actref, err := act.Save(ctx) + if err != nil { + return swarm.EmptyAddress, swarm.EmptyAddress, err + } + + c.setRevokeFlag(granteesAddress, false) + return granteeref, actref, err +} + +func (c *controller) HandleGrantees(ctx context.Context, granteesAddress swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) error { + act := kvsmock.New() + + c.accessLogic.AddPublisher(ctx, act, publisher) + for _, grantee := range addList { + c.accessLogic.AddGrantee(ctx, act, publisher, grantee, nil) + } + return nil +} + +func (c *controller) GetGrantees(ctx context.Context, granteeRootHash swarm.Address) ([]*ecdsa.PublicKey, error) { + return c.granteeList.Get(), nil +} + +func (c *controller) isRevokeFlagged(granteeRootHash swarm.Address) bool { + for _, revoke := range c.revokeFlag { + if revoke.Equal(granteeRootHash) { + return true + } + } + return false +} + +func (c *controller) setRevokeFlag(granteeRootHash swarm.Address, set bool) { + if set { + c.revokeFlag = append(c.revokeFlag, granteeRootHash) + } else { + for i, revoke := range c.revokeFlag { + if revoke.Equal(granteeRootHash) { + c.revokeFlag = append(c.revokeFlag[:i], c.revokeFlag[i+1:]...) + } } } - //FIXME: check if kvs is consistent with the grantee list - return c.accessLogic.EncryptRef(kvs, publisher, ref) } -func NewController(history History, granteeManager GranteeManager, accessLogic ActLogic) Controller { - return &defaultController{ - history: history, - granteeManager: granteeManager, - accessLogic: accessLogic, +func requestPipelineFactory(ctx context.Context, s storage.Putter, encrypt bool, rLevel redundancy.Level) func() pipeline.Interface { + return func() pipeline.Interface { + return builder.NewPipelineBuilder(ctx, s, encrypt, rLevel) } } diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index efbfc8d4e42..0512e6481f9 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -1,83 +1,93 @@ package dynamicaccess_test import ( + "context" "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" "encoding/hex" "testing" "time" - "github.com/ethersphere/bee/v2/pkg/crypto" "github.com/ethersphere/bee/v2/pkg/dynamicaccess" - "github.com/ethersphere/bee/v2/pkg/dynamicaccess/mock" "github.com/ethersphere/bee/v2/pkg/encryption" - kvsmock "github.com/ethersphere/bee/v2/pkg/kvs/mock" + "github.com/ethersphere/bee/v2/pkg/file" + "github.com/ethersphere/bee/v2/pkg/kvs" "github.com/ethersphere/bee/v2/pkg/swarm" + "github.com/stretchr/testify/assert" "golang.org/x/crypto/sha3" ) var hashFunc = sha3.NewLegacyKeccak256 -func mockTestHistory(key, val []byte) dynamicaccess.History { - var ( - h = mock.NewHistory() - now = time.Now() - s = kvsmock.New() - ) - _ = s.Put(key, val) - h.Insert(now.AddDate(-3, 0, 0).Unix(), s) - return h +func getHistoryFixture(ctx context.Context, ls file.LoadSaver, al dynamicaccess.ActLogic, publisher *ecdsa.PublicKey) (swarm.Address, error) { + h, err := dynamicaccess.NewHistory(ls, nil) + if err != nil { + return swarm.ZeroAddress, nil + } + pk1 := getPrivKey(1) + pk2 := getPrivKey(2) + + kvs0 := kvs.New(ls, swarm.ZeroAddress) + al.AddPublisher(ctx, kvs0, publisher) + kvs0Ref, _ := kvs0.Save(ctx) + kvs1 := kvs.New(ls, swarm.ZeroAddress) + al.AddGrantee(ctx, kvs1, publisher, &pk1.PublicKey, nil) + al.AddPublisher(ctx, kvs1, publisher) + kvs1Ref, _ := kvs1.Save(ctx) + kvs2 := kvs.New(ls, swarm.ZeroAddress) + al.AddGrantee(ctx, kvs2, publisher, &pk2.PublicKey, nil) + al.AddPublisher(ctx, kvs2, publisher) + kvs2Ref, _ := kvs2.Save(ctx) + firstTime := time.Date(1994, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + secondTime := time.Date(2000, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + thirdTime := time.Date(2015, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + + h.Add(ctx, kvs0Ref, &thirdTime) + h.Add(ctx, kvs1Ref, &firstTime) + h.Add(ctx, kvs2Ref, &secondTime) + return h.Store(ctx) } -func TestDecrypt(t *testing.T) { - pk := getPrivateKey() - ak := encryption.Key([]byte("cica")) - - si := dynamicaccess.NewDefaultSession(pk) - aek, _ := si.Key(&pk.PublicKey, [][]byte{{0}, {1}}) - e2 := encryption.New(aek[1], 0, uint32(0), hashFunc) - peak, _ := e2.Encrypt(ak) - - h := mockTestHistory(aek[0], peak) - al := setupAccessLogic(pk) - gm := dynamicaccess.NewGranteeManager(al) - c := dynamicaccess.NewController(h, gm, al) - eref, ref := prepareEncryptedChunkReference(ak) - // ech := al.EncryptRef(ch, "tag") +// TODO: separate up down test with fixture, now these just check if the flow works at all +func TestController_NewUploadDownload(t *testing.T) { + ctx := context.Background() + publisher := getPrivKey(1) + diffieHellman := dynamicaccess.NewDefaultSession(publisher) + al := dynamicaccess.NewLogic(diffieHellman) + c := dynamicaccess.NewController(ctx, al, mockStorer.ChunkStore(), mockStorer.Cache()) + ref := swarm.RandAddress(t) + _, hRef, encryptedRef, err := c.UploadHandler(ctx, ref, &publisher.PublicKey, nil) + assert.NoError(t, err) + dref, err := c.DownloadHandler(ctx, time.Now().Unix(), encryptedRef, &publisher.PublicKey, hRef) + assert.NoError(t, err) + assert.Equal(t, ref, dref) +} - ts := int64(0) - addr, err := c.DownloadHandler(ts, eref, &pk.PublicKey, "tag") - if err != nil { - t.Fatalf("DownloadHandler() returned an error: %v", err) - } - if !addr.Equal(ref) { - t.Fatalf("Decrypted chunk address: %s is not the expected: %s", addr, ref) - } +func TestController_ExistingUploadDownload(t *testing.T) { + ls := createLs() + ctx := context.Background() + publisher := getPrivKey(0) + diffieHellman := dynamicaccess.NewDefaultSession(publisher) + al := dynamicaccess.NewLogic(diffieHellman) + c := dynamicaccess.NewController(ctx, al, mockStorer.ChunkStore(), mockStorer.Cache()) + ref := swarm.RandAddress(t) + hRef, err := getHistoryFixture(ctx, ls, al, &publisher.PublicKey) + assert.NoError(t, err) + _, hRef, encryptedRef, err := c.UploadHandler(ctx, ref, &publisher.PublicKey, &hRef) + assert.NoError(t, err) + dref, err := c.DownloadHandler(ctx, time.Now().Unix(), encryptedRef, &publisher.PublicKey, hRef) + assert.NoError(t, err) + assert.Equal(t, ref, dref) } -func TestEncrypt(t *testing.T) { - pk := getPrivateKey() - ak := encryption.Key([]byte("cica")) +func TestControllerGrant(t *testing.T) { +} - si := dynamicaccess.NewDefaultSession(pk) - aek, _ := si.Key(&pk.PublicKey, [][]byte{{0}, {1}}) - e2 := encryption.New(aek[1], 0, uint32(0), hashFunc) - peak, _ := e2.Encrypt(ak) +func TestControllerRevoke(t *testing.T) { - h := mockTestHistory(aek[0], peak) - al := setupAccessLogic(pk) - gm := dynamicaccess.NewGranteeManager(al) - c := dynamicaccess.NewController(h, gm, al) - eref, ref := prepareEncryptedChunkReference(ak) +} - key1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - gm.Add([]*ecdsa.PublicKey{&key1.PublicKey}) +func TestControllerCommit(t *testing.T) { - addr, _ := c.UploadHandler(ref, &pk.PublicKey) - if !addr.Equal(eref) { - t.Fatalf("Decrypted chunk address: %s is not the expected: %s", addr, eref) - } } func prepareEncryptedChunkReference(ak []byte) (swarm.Address, swarm.Address) { @@ -85,14 +95,7 @@ func prepareEncryptedChunkReference(ak []byte) (swarm.Address, swarm.Address) { e1 := encryption.New(ak, 0, uint32(0), hashFunc) ech, err := e1.Encrypt(addr) if err != nil { - return swarm.EmptyAddress, swarm.NewAddress(addr) + return swarm.EmptyAddress, swarm.EmptyAddress } return swarm.NewAddress(ech), swarm.NewAddress(addr) } - -func getPrivateKey() *ecdsa.PrivateKey { - data, _ := hex.DecodeString("c786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfa") - - privKey, _ := crypto.DecodeSecp256k1PrivateKey(data) - return privKey -} diff --git a/pkg/dynamicaccess/grantee.go b/pkg/dynamicaccess/grantee.go index d850fc047cc..8499cfc4f04 100644 --- a/pkg/dynamicaccess/grantee.go +++ b/pkg/dynamicaccess/grantee.go @@ -19,7 +19,7 @@ type GranteeList interface { Add(addList []*ecdsa.PublicKey) error Remove(removeList []*ecdsa.PublicKey) error Get() []*ecdsa.PublicKey - Save() (swarm.Address, error) + Save(ctx context.Context) (swarm.Address, error) } type GranteeListStruct struct { @@ -78,8 +78,8 @@ func (g *GranteeListStruct) Add(addList []*ecdsa.PublicKey) error { return nil } -func (g *GranteeListStruct) Save() (swarm.Address, error) { - refBytes, err := g.loadSave.Save(context.Background(), g.grantees) +func (g *GranteeListStruct) Save(ctx context.Context) (swarm.Address, error) { + refBytes, err := g.loadSave.Save(ctx, g.grantees) if err != nil { return swarm.ZeroAddress, fmt.Errorf("grantee save error: %w", err) } @@ -132,3 +132,7 @@ func NewGranteeList(ls file.LoadSaver, putter storer.PutterSession, reference sw putter: putter, } } + +func (g *GranteeListStruct) Store() (swarm.Address, error) { + return swarm.EmptyAddress, nil +} diff --git a/pkg/dynamicaccess/grantee_manager.go b/pkg/dynamicaccess/grantee_manager.go deleted file mode 100644 index 1fac35a38bd..00000000000 --- a/pkg/dynamicaccess/grantee_manager.go +++ /dev/null @@ -1,47 +0,0 @@ -package dynamicaccess - -import ( - "crypto/ecdsa" - - "github.com/ethersphere/bee/v2/pkg/dynamicaccess/mock" - "github.com/ethersphere/bee/v2/pkg/kvs" - "github.com/ethersphere/bee/v2/pkg/swarm" -) - -type GranteeManager interface { - Get() []*ecdsa.PublicKey - Add(addList []*ecdsa.PublicKey) error - Publish(kvs kvs.KeyValueStore, publisher *ecdsa.PublicKey) (swarm.Address, error) - - // HandleGrantees(addList, removeList []*ecdsa.PublicKey) *Act - - // Load(grantee Grantee) - // Save() -} - -var _ GranteeManager = (*granteeManager)(nil) - -type granteeManager struct { - accessLogic ActLogic - granteeList *mock.GranteeListStructMock -} - -func NewGranteeManager(al ActLogic) *granteeManager { - return &granteeManager{accessLogic: al, granteeList: mock.NewGranteeList()} -} - -func (gm *granteeManager) Get() []*ecdsa.PublicKey { - return gm.granteeList.Get() -} - -func (gm *granteeManager) Add(addList []*ecdsa.PublicKey) error { - return gm.granteeList.Add(addList) -} - -func (gm *granteeManager) Publish(kvs kvs.KeyValueStore, publisher *ecdsa.PublicKey) (swarm.Address, error) { - err := gm.accessLogic.AddPublisher(kvs, publisher) - for _, grantee := range gm.granteeList.Get() { - err = gm.accessLogic.AddGrantee(kvs, publisher, grantee, nil) - } - return swarm.EmptyAddress, err -} diff --git a/pkg/dynamicaccess/grantee_manager_test.go b/pkg/dynamicaccess/grantee_manager_test.go deleted file mode 100644 index bb01c13cd85..00000000000 --- a/pkg/dynamicaccess/grantee_manager_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package dynamicaccess_test - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "fmt" - "testing" - - "github.com/ethersphere/bee/v2/pkg/dynamicaccess" - kvsmock "github.com/ethersphere/bee/v2/pkg/kvs/mock" -) - -func setupAccessLogic(privateKey *ecdsa.PrivateKey) dynamicaccess.ActLogic { - si := dynamicaccess.NewDefaultSession(privateKey) - al := dynamicaccess.NewLogic(si) - - return al -} - -func TestAdd(t *testing.T) { - m := dynamicaccess.NewGranteeManager(setupAccessLogic(getPrivateKey())) - pub, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - - id1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - id2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - err := m.Add([]*ecdsa.PublicKey{&id1.PublicKey}) - if err != nil { - t.Errorf("Add() returned an error") - } - err = m.Add([]*ecdsa.PublicKey{&id2.PublicKey}) - if err != nil { - t.Errorf("Add() returned an error") - } - s := kvsmock.New() - m.Publish(s, &pub.PublicKey) - fmt.Println("") -} diff --git a/pkg/dynamicaccess/grantee_test.go b/pkg/dynamicaccess/grantee_test.go index b644f5896c5..5730911ad74 100644 --- a/pkg/dynamicaccess/grantee_test.go +++ b/pkg/dynamicaccess/grantee_test.go @@ -148,19 +148,20 @@ func TestGranteeRemove(t *testing.T) { } func TestGranteeSave(t *testing.T) { + ctx := context.Background() keys, err := generateKeyListFixture() if err != nil { t.Errorf("key generation error: %v", err) } t.Run("Save empty grantee list return NO error", func(t *testing.T) { gl := dynamicaccess.NewGranteeList(createLs(), mockStorer.DirectUpload(), swarm.ZeroAddress) - _, err := gl.Save() + _, err := gl.Save(ctx) assert.NoError(t, err) }) t.Run("Save not empty grantee list return valid swarm address", func(t *testing.T) { gl := dynamicaccess.NewGranteeList(createLs(), mockStorer.DirectUpload(), swarm.ZeroAddress) err = gl.Add(keys) - ref, err := gl.Save() + ref, err := gl.Save(ctx) assert.NoError(t, err) assert.True(t, ref.IsValidNonEmpty()) }) @@ -172,7 +173,7 @@ func TestGranteeSave(t *testing.T) { err := gl1.Add(keys) assert.NoError(t, err) - ref, err := gl1.Save() + ref, err := gl1.Save(ctx) assert.NoError(t, err) gl2 := dynamicaccess.NewGranteeList(ls, putter, ref) @@ -188,7 +189,7 @@ func TestGranteeSave(t *testing.T) { err := gl1.Add(keys) assert.NoError(t, err) - ref, err := gl1.Save() + ref, err := gl1.Save(ctx) assert.NoError(t, err) // New KVS diff --git a/pkg/dynamicaccess/mock/service.go b/pkg/dynamicaccess/mock/service.go new file mode 100644 index 00000000000..2d19c3ed4e3 --- /dev/null +++ b/pkg/dynamicaccess/mock/service.go @@ -0,0 +1,162 @@ +// Copyright 2020 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mock + +import ( + "context" + "crypto/ecdsa" + "encoding/hex" + "fmt" + "time" + + "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/dynamicaccess" + "github.com/ethersphere/bee/v2/pkg/encryption" + "github.com/ethersphere/bee/v2/pkg/file" + "github.com/ethersphere/bee/v2/pkg/file/loadsave" + "github.com/ethersphere/bee/v2/pkg/file/pipeline" + "github.com/ethersphere/bee/v2/pkg/file/pipeline/builder" + "github.com/ethersphere/bee/v2/pkg/file/redundancy" + "github.com/ethersphere/bee/v2/pkg/storage" + mockstorer "github.com/ethersphere/bee/v2/pkg/storer/mock" + "github.com/ethersphere/bee/v2/pkg/swarm" + "golang.org/x/crypto/sha3" +) + +type mockDacService struct { + historyMap map[string]dynamicaccess.History + refMap map[string]swarm.Address + acceptAll bool + publisher string + encrypter encryption.Interface + ls file.LoadSaver +} + +type optionFunc func(*mockDacService) + +// Option is an option passed to a mock dynamicaccess Service. +type Option interface { + apply(*mockDacService) +} + +func (f optionFunc) apply(r *mockDacService) { f(r) } + +// New creates a new mock dynamicaccess service. +func New(o ...Option) dynamicaccess.Service { + storer := mockstorer.New() + m := &mockDacService{ + historyMap: make(map[string]dynamicaccess.History), + refMap: make(map[string]swarm.Address), + publisher: "", + encrypter: encryption.New(encryption.Key("b6ee086390c280eeb9824c331a4427596f0c8510d5564bc1b6168d0059a46e2b"), 0, uint32(0), sha3.NewLegacyKeccak256), + ls: loadsave.New(storer.ChunkStore(), storer.Cache(), requestPipelineFactory(context.Background(), storer.Cache(), false, redundancy.NONE)), + } + for _, v := range o { + v.apply(m) + } + + return m +} + +// WithAcceptAll sets the mock to return fixed references on every call to DownloadHandler. +func WithAcceptAll() Option { + return optionFunc(func(m *mockDacService) { m.acceptAll = true }) +} + +func WithHistory(h dynamicaccess.History, ref string) Option { + return optionFunc(func(m *mockDacService) { + m.historyMap = map[string]dynamicaccess.History{ref: h} + }) +} + +func WithPublisher(ref string) Option { + return optionFunc(func(m *mockDacService) { + m.publisher = ref + m.encrypter = encryption.New(encryption.Key(ref), 0, uint32(0), sha3.NewLegacyKeccak256) + }) +} + +func (m *mockDacService) DownloadHandler(ctx context.Context, timestamp int64, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, error) { + if m.acceptAll { + return swarm.ParseHexAddress("36e6c1bbdfee6ac21485d5f970479fd1df458d36df9ef4e8179708ed46da557f") + } + + publicKeyBytes := crypto.EncodeSecp256k1PublicKey(publisher) + p := hex.EncodeToString(publicKeyBytes) + if m.publisher != "" && m.publisher != p { + return swarm.ZeroAddress, fmt.Errorf("incorrect publisher") + } + + h, exists := m.historyMap[historyRootHash.String()] + if !exists { + return swarm.ZeroAddress, fmt.Errorf("history not found") + } + kvsRef, err := h.Lookup(ctx, timestamp) + if kvsRef.Equal(swarm.ZeroAddress) || err != nil { + return swarm.ZeroAddress, fmt.Errorf("kvs not found") + } + return m.refMap[encryptedRef.String()], nil +} + +func (m *mockDacService) UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash *swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) { + historyRef, _ := swarm.ParseHexAddress("67bdf80a9bbea8eca9c8480e43fdceb485d2d74d5708e45144b8c4adacd13d9c") + kvsRef, _ := swarm.ParseHexAddress("3339613565613837623134316665343461613630396333333237656364383934") + if m.acceptAll { + encryptedRef, _ := swarm.ParseHexAddress("fc4e9fe978991257b897d987bc4ff13058b66ef45a53189a0b4fe84bb3346396") + return kvsRef, historyRef, encryptedRef, nil + } + var ( + h dynamicaccess.History + exists bool + ) + now := time.Now().Unix() + if historyRootHash != nil { + historyRef = *historyRootHash + h, exists = m.historyMap[historyRef.String()] + if !exists { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, fmt.Errorf("history not found") + } + kvsRef, _ = h.Lookup(ctx, now) + } else { + h, _ = dynamicaccess.NewHistory(m.ls, nil) + h.Add(ctx, kvsRef, &now) + historyRef, _ = h.Store(ctx) + m.historyMap[historyRef.String()] = h + } + if kvsRef.Equal(swarm.ZeroAddress) { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, fmt.Errorf("kvs not found") + } + encryptedRef, _ := m.encrypter.Encrypt(reference.Bytes()) + m.refMap[(hex.EncodeToString(encryptedRef))] = reference + return kvsRef, historyRef, swarm.NewAddress(encryptedRef), nil +} + +func (m *mockDacService) Close() error { + return nil +} + +func (m *mockDacService) Grant(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error { + return nil +} +func (m *mockDacService) Revoke(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error { + return nil +} +func (m *mockDacService) Commit(ctx context.Context, granteesAddress swarm.Address, actRootHash swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, swarm.Address, error) { + return swarm.ZeroAddress, swarm.ZeroAddress, nil +} +func (m *mockDacService) HandleGrantees(ctx context.Context, rootHash swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) error { + return nil +} +func (m *mockDacService) GetGrantees(ctx context.Context, rootHash swarm.Address) ([]*ecdsa.PublicKey, error) { + return nil, nil +} + +func requestPipelineFactory(ctx context.Context, s storage.Putter, encrypt bool, rLevel redundancy.Level) func() pipeline.Interface { + return func() pipeline.Interface { + return builder.NewPipelineBuilder(ctx, s, encrypt, rLevel) + } +} + +var _ dynamicaccess.Controller = (*mockDacService)(nil) diff --git a/pkg/dynamicaccess/service.go b/pkg/dynamicaccess/service.go new file mode 100644 index 00000000000..c87b74ee60c --- /dev/null +++ b/pkg/dynamicaccess/service.go @@ -0,0 +1,39 @@ +package dynamicaccess + +import ( + "context" + "crypto/ecdsa" + "io" + + "github.com/ethersphere/bee/v2/pkg/swarm" +) + +type Service interface { + DownloadHandler(ctx context.Context, timestamp int64, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, error) + UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash *swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) + io.Closer +} + +// TODO: is service needed at all? -> it is just a wrapper around controller +type service struct { + controller Controller +} + +func (s *service) DownloadHandler(ctx context.Context, timestamp int64, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, error) { + return s.controller.DownloadHandler(ctx, timestamp, encryptedRef, publisher, historyRootHash) +} + +func (s *service) UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash *swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) { + return s.controller.UploadHandler(ctx, reference, publisher, historyRootHash) +} + +// TODO: what to do in close ? +func (s *service) Close() error { + return nil +} + +func NewService(controller Controller) (Service, error) { + return &service{ + controller: controller, + }, nil +} diff --git a/pkg/kvs/kvs.go b/pkg/kvs/kvs.go index fcb5fc668bb..42a07da6738 100644 --- a/pkg/kvs/kvs.go +++ b/pkg/kvs/kvs.go @@ -11,27 +11,24 @@ import ( "github.com/ethersphere/bee/v2/pkg/file" "github.com/ethersphere/bee/v2/pkg/manifest" - "github.com/ethersphere/bee/v2/pkg/storer" "github.com/ethersphere/bee/v2/pkg/swarm" ) type KeyValueStore interface { - Get(key []byte) ([]byte, error) - Put(key, value []byte) error - Save() (swarm.Address, error) + Get(ctx context.Context, key []byte) ([]byte, error) + Put(ctx context.Context, key, value []byte) error + Save(ctx context.Context) (swarm.Address, error) } type keyValueStore struct { manifest manifest.Interface - putter storer.PutterSession putCnt int } var _ KeyValueStore = (*keyValueStore)(nil) -// TODO: pass context as dep. -func (s *keyValueStore) Get(key []byte) ([]byte, error) { - entry, err := s.manifest.Lookup(context.Background(), hex.EncodeToString(key)) +func (s *keyValueStore) Get(ctx context.Context, key []byte) ([]byte, error) { + entry, err := s.manifest.Lookup(ctx, hex.EncodeToString(key)) if err != nil { return nil, err } @@ -39,8 +36,8 @@ func (s *keyValueStore) Get(key []byte) ([]byte, error) { return ref.Bytes(), nil } -func (s *keyValueStore) Put(key []byte, value []byte) error { - err := s.manifest.Add(context.Background(), hex.EncodeToString(key), manifest.NewEntry(swarm.NewAddress(value), map[string]string{})) +func (s *keyValueStore) Put(ctx context.Context, key []byte, value []byte) error { + err := s.manifest.Add(ctx, hex.EncodeToString(key), manifest.NewEntry(swarm.NewAddress(value), map[string]string{})) if err != nil { return err } @@ -48,15 +45,11 @@ func (s *keyValueStore) Put(key []byte, value []byte) error { return nil } -func (s *keyValueStore) Save() (swarm.Address, error) { +func (s *keyValueStore) Save(ctx context.Context) (swarm.Address, error) { if s.putCnt == 0 { return swarm.ZeroAddress, errors.New("nothing to save") } - ref, err := s.manifest.Store(context.Background()) - if err != nil { - return swarm.ZeroAddress, err - } - err = s.putter.Done(ref) + ref, err := s.manifest.Store(ctx) if err != nil { return swarm.ZeroAddress, err } @@ -64,7 +57,7 @@ func (s *keyValueStore) Save() (swarm.Address, error) { return ref, nil } -func New(ls file.LoadSaver, putter storer.PutterSession, rootHash swarm.Address) KeyValueStore { +func New(ls file.LoadSaver, rootHash swarm.Address) KeyValueStore { var ( manif manifest.Interface err error @@ -80,6 +73,5 @@ func New(ls file.LoadSaver, putter storer.PutterSession, rootHash swarm.Address) return &keyValueStore{ manifest: manif, - putter: putter, } } diff --git a/pkg/kvs/kvs_test.go b/pkg/kvs/kvs_test.go index 9edfe48062c..5c6f75e2379 100644 --- a/pkg/kvs/kvs_test.go +++ b/pkg/kvs/kvs_test.go @@ -38,24 +38,25 @@ func keyValuePair(t *testing.T) ([]byte, []byte) { func TestKvs(t *testing.T) { - s := kvs.New(createLs(), mockStorer.DirectUpload(), swarm.ZeroAddress) + s := kvs.New(createLs(), swarm.ZeroAddress) key, val := keyValuePair(t) + ctx := context.Background() t.Run("Get non-existent key should return error", func(t *testing.T) { - _, err := s.Get([]byte{1}) + _, err := s.Get(ctx, []byte{1}) assert.Error(t, err) }) t.Run("Multiple Get with same key, no error", func(t *testing.T) { - err := s.Put(key, val) + err := s.Put(ctx, key, val) assert.NoError(t, err) // get #1 - v, err := s.Get(key) + v, err := s.Get(ctx, key) assert.NoError(t, err) assert.Equal(t, val, v) // get #2 - v, err = s.Get(key) + v, err = s.Get(ctx, key) assert.NoError(t, err) assert.Equal(t, val, v) }) @@ -105,9 +106,9 @@ func TestKvs(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - err := s.Put(tc.key, tc.val) + err := s.Put(ctx, tc.key, tc.val) assert.NoError(t, err) - retVal, err := s.Get(tc.key) + retVal, err := s.Get(ctx, tc.key) assert.NoError(t, err) assert.Equal(t, tc.val, retVal) }) @@ -116,53 +117,53 @@ func TestKvs(t *testing.T) { } func TestKvs_Save(t *testing.T) { + ctx := context.Background() + key1, val1 := keyValuePair(t) key2, val2 := keyValuePair(t) t.Run("Save empty KVS return error", func(t *testing.T) { - s := kvs.New(createLs(), mockStorer.DirectUpload(), swarm.ZeroAddress) - _, err := s.Save() + s := kvs.New(createLs(), swarm.ZeroAddress) + _, err := s.Save(ctx) assert.Error(t, err) }) t.Run("Save not empty KVS return valid swarm address", func(t *testing.T) { - s := kvs.New(createLs(), mockStorer.DirectUpload(), swarm.ZeroAddress) - s.Put(key1, val1) - ref, err := s.Save() + s := kvs.New(createLs(), swarm.ZeroAddress) + s.Put(ctx, key1, val1) + ref, err := s.Save(ctx) assert.NoError(t, err) assert.True(t, ref.IsValidNonEmpty()) }) t.Run("Save KVS with one item, no error, pre-save value exist", func(t *testing.T) { ls := createLs() - putter := mockStorer.DirectUpload() - s1 := kvs.New(ls, putter, swarm.ZeroAddress) + s1 := kvs.New(ls, swarm.ZeroAddress) - err := s1.Put(key1, val1) + err := s1.Put(ctx, key1, val1) assert.NoError(t, err) - ref, err := s1.Save() + ref, err := s1.Save(ctx) assert.NoError(t, err) - s2 := kvs.New(ls, putter, ref) - val, err := s2.Get(key1) + s2 := kvs.New(ls, ref) + val, err := s2.Get(ctx, key1) assert.NoError(t, err) assert.Equal(t, val1, val) }) t.Run("Save KVS and add one item, no error, after-save value exist", func(t *testing.T) { ls := createLs() - putter := mockStorer.DirectUpload() - kvs1 := kvs.New(ls, putter, swarm.ZeroAddress) + kvs1 := kvs.New(ls, swarm.ZeroAddress) - err := kvs1.Put(key1, val1) + err := kvs1.Put(ctx, key1, val1) assert.NoError(t, err) - ref, err := kvs1.Save() + ref, err := kvs1.Save(ctx) assert.NoError(t, err) // New KVS - kvs2 := kvs.New(ls, putter, ref) - err = kvs2.Put(key2, val2) + kvs2 := kvs.New(ls, ref) + err = kvs2.Put(ctx, key2, val2) assert.NoError(t, err) - val, err := kvs2.Get(key2) + val, err := kvs2.Get(ctx, key2) assert.NoError(t, err) assert.Equal(t, val2, val) }) diff --git a/pkg/kvs/mock/kvs.go b/pkg/kvs/mock/kvs.go index 78282934bf2..0203cc52ce6 100644 --- a/pkg/kvs/mock/kvs.go +++ b/pkg/kvs/mock/kvs.go @@ -1,6 +1,7 @@ package mock import ( + "context" "encoding/hex" "sync" @@ -44,13 +45,13 @@ type mockKeyValueStore struct { var _ kvs.KeyValueStore = (*mockKeyValueStore)(nil) -func (m *mockKeyValueStore) Get(key []byte) ([]byte, error) { +func (m *mockKeyValueStore) Get(_ context.Context, key []byte) ([]byte, error) { mem := getMemory() val := mem[m.address.String()][hex.EncodeToString(key)] return val, nil } -func (m *mockKeyValueStore) Put(key []byte, value []byte) error { +func (m *mockKeyValueStore) Put(_ context.Context, key []byte, value []byte) error { mem := getMemory() if _, ok := mem[m.address.String()]; !ok { mem[m.address.String()] = make(map[string][]byte) @@ -59,7 +60,7 @@ func (m *mockKeyValueStore) Put(key []byte, value []byte) error { return nil } -func (m *mockKeyValueStore) Save() (swarm.Address, error) { +func (m *mockKeyValueStore) Save(ctx context.Context) (swarm.Address, error) { return m.address, nil } diff --git a/pkg/node/devnode.go b/pkg/node/devnode.go index fe9476d9d00..9e51cf562b9 100644 --- a/pkg/node/devnode.go +++ b/pkg/node/devnode.go @@ -21,6 +21,7 @@ import ( "github.com/ethersphere/bee/v2/pkg/api" "github.com/ethersphere/bee/v2/pkg/bzz" "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/dynamicaccess" "github.com/ethersphere/bee/v2/pkg/feeds/factory" "github.com/ethersphere/bee/v2/pkg/log" mockP2P "github.com/ethersphere/bee/v2/pkg/p2p/mock" @@ -65,6 +66,7 @@ type DevBee struct { localstoreCloser io.Closer apiCloser io.Closer pssCloser io.Closer + dacCloser io.Closer errorLogWriter io.Writer apiServer *http.Server } @@ -188,6 +190,15 @@ func NewDevBee(logger log.Logger, o *DevOptions) (b *DevBee, err error) { } b.localstoreCloser = localStore + session := dynamicaccess.NewDefaultSession(mockKey) + actLogic := dynamicaccess.NewLogic(session) + ctrl := dynamicaccess.NewController(context.Background(), actLogic, localStore.ChunkStore(), localStore.Cache()) + dac, err := dynamicaccess.NewService(ctrl) + if err != nil { + return nil, fmt.Errorf("dac service: %w", err) + } + b.dacCloser = dac + pssService := pss.New(mockKey, logger) b.pssCloser = pssService @@ -337,6 +348,7 @@ func NewDevBee(logger log.Logger, o *DevOptions) (b *DevBee, err error) { Pss: pssService, FeedFactory: mockFeeds, Post: post, + Dac: dac, PostageContract: postageContract, Staking: mockStaking, Steward: mockSteward, @@ -423,6 +435,7 @@ func (b *DevBee) Shutdown() error { } tryClose(b.pssCloser, "pss") + tryClose(b.dacCloser, "dac") tryClose(b.tracerCloser, "tracer") tryClose(b.stateStoreCloser, "statestore") tryClose(b.localstoreCloser, "localstore") diff --git a/pkg/node/node.go b/pkg/node/node.go index 5f799298164..faaf308ecba 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -29,6 +29,7 @@ import ( "github.com/ethersphere/bee/v2/pkg/api" "github.com/ethersphere/bee/v2/pkg/config" "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/dynamicaccess" "github.com/ethersphere/bee/v2/pkg/feeds/factory" "github.com/ethersphere/bee/v2/pkg/hive" "github.com/ethersphere/bee/v2/pkg/log" @@ -115,6 +116,7 @@ type Bee struct { shutdownInProgress bool shutdownMutex sync.Mutex syncingStopped *syncutil.Signaler + dacCloser io.Closer } type Options struct { @@ -196,6 +198,7 @@ func NewBee( logger log.Logger, libp2pPrivateKey, pssPrivateKey *ecdsa.PrivateKey, + session dynamicaccess.Session, o *Options, ) (b *Bee, err error) { tracer, tracerCloser, err := tracing.NewTracer(&tracing.Options{ @@ -706,6 +709,14 @@ func NewBee( b.localstoreCloser = localStore evictFn = func(id []byte) error { return localStore.EvictBatch(context.Background(), id) } + actLogic := dynamicaccess.NewLogic(session) + ctrl := dynamicaccess.NewController(ctx, actLogic, localStore.ChunkStore(), localStore.Cache()) + dac, err := dynamicaccess.NewService(ctrl) + if err != nil { + return nil, fmt.Errorf("dac service: %w", err) + } + b.dacCloser = dac + var ( syncErr atomic.Value syncStatus atomic.Value @@ -1021,6 +1032,7 @@ func NewBee( Pss: pssService, FeedFactory: feedFactory, Post: post, + Dac: dac, PostageContract: postageStampContractService, Staking: stakingContract, Steward: steward, @@ -1213,6 +1225,7 @@ func (b *Bee) Shutdown() error { c() } + tryClose(b.dacCloser, "dac") tryClose(b.tracerCloser, "tracer") tryClose(b.topologyCloser, "topology driver") tryClose(b.storageIncetivesCloser, "storage incentives agent") diff --git a/pkg/soc/testing/soc.go b/pkg/soc/testing/soc.go index e5325f464b1..9516a721d83 100644 --- a/pkg/soc/testing/soc.go +++ b/pkg/soc/testing/soc.go @@ -5,6 +5,7 @@ package testing import ( + "crypto/ecdsa" "testing" "github.com/ethersphere/bee/v2/pkg/cac" @@ -70,3 +71,38 @@ func GenerateMockSOC(t *testing.T, data []byte) *MockSOC { WrappedChunk: ch, } } + +// GenerateMockSOC generates a valid mocked SOC from given data and key. +func GenerateMockSOCWithKey(t *testing.T, data []byte, privKey *ecdsa.PrivateKey) *MockSOC { + t.Helper() + + signer := crypto.NewDefaultSigner(privKey) + owner, err := signer.EthereumAddress() + if err != nil { + t.Fatal(err) + } + + ch, err := cac.New(data) + if err != nil { + t.Fatal(err) + } + + id := make([]byte, swarm.HashSize) + hasher := swarm.NewHasher() + _, err = hasher.Write(append(id, ch.Address().Bytes()...)) + if err != nil { + t.Fatal(err) + } + + signature, err := signer.Sign(hasher.Sum(nil)) + if err != nil { + t.Fatal(err) + } + + return &MockSOC{ + ID: id, + Owner: owner.Bytes(), + Signature: signature, + WrappedChunk: ch, + } +} From 96d4a4ec2f143ad036738cf68e9e6825fa4478b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= <58116288+bosi95@users.noreply.github.com> Date: Fri, 19 Apr 2024 16:10:24 +0200 Subject: [PATCH 32/41] Act refactor api and ctrl (#36) * Refactor: newreference pattern for history, kvs, grantee * CHG: history metadata to store granteelist reference * History test with metadata * Remove putter from grantee --- pkg/api/bytes.go | 6 +-- pkg/api/bzz.go | 8 +-- pkg/api/chunk.go | 10 ++-- pkg/api/chunk_address.go | 2 +- pkg/api/dirs.go | 2 +- pkg/api/dynamicaccess.go | 37 +++++++------ pkg/api/dynamicaccess_test.go | 18 +++---- pkg/api/feed.go | 10 ++-- pkg/api/soc.go | 8 +-- pkg/dynamicaccess/accesslogic.go | 4 +- pkg/dynamicaccess/controller.go | 81 ++++++++++++++++++---------- pkg/dynamicaccess/controller_test.go | 22 ++++---- pkg/dynamicaccess/grantee.go | 32 ++++------- pkg/dynamicaccess/grantee_test.go | 22 +++----- pkg/dynamicaccess/history.go | 49 ++++++++++------- pkg/dynamicaccess/history_test.go | 61 ++++++++++++++------- pkg/dynamicaccess/mock/service.go | 28 +++++----- pkg/dynamicaccess/service.go | 10 ++-- pkg/kvs/kvs.go | 25 +++++---- pkg/kvs/kvs_test.go | 19 ++++--- 20 files changed, 256 insertions(+), 198 deletions(-) diff --git a/pkg/api/bytes.go b/pkg/api/bytes.go index dc3735a497b..a84ec2936de 100644 --- a/pkg/api/bytes.go +++ b/pkg/api/bytes.go @@ -40,7 +40,7 @@ func (s *Service) bytesUploadHandler(w http.ResponseWriter, r *http.Request) { Encrypt bool `map:"Swarm-Encrypt"` RLevel redundancy.Level `map:"Swarm-Redundancy-Level"` Act bool `map:"Swarm-Act"` - HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address"` + HistoryAddress swarm.Address `map:"Swarm-Act-History-Address"` }{} if response := s.mapStructure(r.Header, &headers); response != nil { response("invalid header params", logger, w) @@ -161,7 +161,7 @@ func (s *Service) bytesGetHandler(w http.ResponseWriter, r *http.Request) { } address := paths.Address - if v := getAddressFromContext(r.Context()); !v.Equal(swarm.ZeroAddress) { + if v := getAddressFromContext(r.Context()); !v.IsZero() { address = v } @@ -184,7 +184,7 @@ func (s *Service) bytesHeadHandler(w http.ResponseWriter, r *http.Request) { } address := paths.Address - if v := getAddressFromContext(r.Context()); !v.Equal(swarm.ZeroAddress) { + if v := getAddressFromContext(r.Context()); !v.IsZero() { address = v } diff --git a/pkg/api/bzz.go b/pkg/api/bzz.go index 241e30cf165..6319624a17d 100644 --- a/pkg/api/bzz.go +++ b/pkg/api/bzz.go @@ -72,7 +72,7 @@ func (s *Service) bzzUploadHandler(w http.ResponseWriter, r *http.Request) { IsDir bool `map:"Swarm-Collection"` RLevel redundancy.Level `map:"Swarm-Redundancy-Level"` Act bool `map:"Swarm-Act"` - HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address"` + HistoryAddress swarm.Address `map:"Swarm-Act-History-Address"` }{} if response := s.mapStructure(r.Header, &headers); response != nil { response("invalid header params", logger, w) @@ -158,7 +158,7 @@ func (s *Service) fileUploadHandler( tagID uint64, rLevel redundancy.Level, act bool, - historyAddress *swarm.Address, + historyAddress swarm.Address, ) { queries := struct { FileName string `map:"name" validate:"startsnotwith=/"` @@ -310,7 +310,7 @@ func (s *Service) bzzDownloadHandler(w http.ResponseWriter, r *http.Request) { } address := paths.Address - if v := getAddressFromContext(r.Context()); !v.Equal(swarm.ZeroAddress) { + if v := getAddressFromContext(r.Context()); !v.IsZero() { address = v } @@ -334,7 +334,7 @@ func (s *Service) bzzHeadHandler(w http.ResponseWriter, r *http.Request) { } address := paths.Address - if v := getAddressFromContext(r.Context()); !v.Equal(swarm.ZeroAddress) { + if v := getAddressFromContext(r.Context()); !v.IsZero() { address = v } diff --git a/pkg/api/chunk.go b/pkg/api/chunk.go index 13738f70d39..496f7f8d306 100644 --- a/pkg/api/chunk.go +++ b/pkg/api/chunk.go @@ -30,10 +30,10 @@ func (s *Service) chunkUploadHandler(w http.ResponseWriter, r *http.Request) { logger := s.logger.WithName("post_chunk").Build() headers := struct { - BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` - SwarmTag uint64 `map:"Swarm-Tag"` - Act bool `map:"Swarm-Act"` - HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address"` + BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` + SwarmTag uint64 `map:"Swarm-Tag"` + Act bool `map:"Swarm-Act"` + HistoryAddress swarm.Address `map:"Swarm-Act-History-Address"` }{} if response := s.mapStructure(r.Header, &headers); response != nil { response("invalid header params", logger, w) @@ -204,7 +204,7 @@ func (s *Service) chunkGetHandler(w http.ResponseWriter, r *http.Request) { } address := paths.Address - if v := getAddressFromContext(r.Context()); !v.Equal(swarm.ZeroAddress) { + if v := getAddressFromContext(r.Context()); !v.IsZero() { address = v } diff --git a/pkg/api/chunk_address.go b/pkg/api/chunk_address.go index c49c980c2e7..838c9ce6e8e 100644 --- a/pkg/api/chunk_address.go +++ b/pkg/api/chunk_address.go @@ -24,7 +24,7 @@ func (s *Service) hasChunkHandler(w http.ResponseWriter, r *http.Request) { } address := paths.Address - if v := getAddressFromContext(r.Context()); !v.Equal(swarm.ZeroAddress) { + if v := getAddressFromContext(r.Context()); !v.IsZero() { address = v } diff --git a/pkg/api/dirs.go b/pkg/api/dirs.go index 1ec5e6cde40..c85c68e4edb 100644 --- a/pkg/api/dirs.go +++ b/pkg/api/dirs.go @@ -48,7 +48,7 @@ func (s *Service) dirUploadHandler( tag uint64, rLevel redundancy.Level, act bool, - historyAddress *swarm.Address, + historyAddress swarm.Address, ) { if r.Body == http.NoBody { logger.Error(nil, "request has no body") diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index 7a05b1ad38c..c7c1279609e 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -28,6 +28,8 @@ func setAddressInContext(ctx context.Context, address swarm.Address) context.Con return context.WithValue(ctx, addressKey{}, address) } +// actDecryptionHandler is a middleware that looks up and decrypts the given address, +// if the act headers are present func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { return func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -56,7 +58,7 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { return } ctx := r.Context() - reference, err := s.dac.DownloadHandler(ctx, *headers.Timestamp, paths.Address, headers.Publisher, *headers.HistoryAddress) + reference, err := s.dac.DownloadHandler(ctx, paths.Address, headers.Publisher, *headers.HistoryAddress, *headers.Timestamp) if err != nil { jsonhttp.InternalServerError(w, errActDownload) return @@ -67,27 +69,38 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { } -// TODO: is ctx needed in ctrl upload ? +// actEncryptionHandler is a middleware that encrypts the given address using the publisher's public key +// Uploads the encrypted reference, history and kvs to the store func (s *Service) actEncryptionHandler( ctx context.Context, logger log.Logger, w http.ResponseWriter, putter storer.PutterSession, reference swarm.Address, - historyAddress *swarm.Address, + historyRootHash swarm.Address, ) (swarm.Address, error) { publisherPublicKey := &s.publicKey - kvsReference, historyReference, encryptedReference, err := s.dac.UploadHandler(ctx, reference, publisherPublicKey, historyAddress) + storageReference, historyReference, encryptedReference, err := s.dac.UploadHandler(ctx, reference, publisherPublicKey, historyRootHash) if err != nil { logger.Debug("act failed to encrypt reference", "error", err) logger.Error(nil, "act failed to encrypt reference") return swarm.ZeroAddress, err } - err = putter.Done(historyReference) - if err != nil { - logger.Debug("done split history failed", "error", err) - logger.Error(nil, "done split history failed") - return swarm.ZeroAddress, err + // only need to upload history and kvs if a new history is created, + // meaning that the publsher uploaded to the history for the first time + if !historyReference.Equal(historyRootHash) { + err = putter.Done(storageReference) + if err != nil { + logger.Debug("done split keyvaluestore failed", "error", err) + logger.Error(nil, "done split keyvaluestore failed") + return swarm.ZeroAddress, err + } + err = putter.Done(historyReference) + if err != nil { + logger.Debug("done split history failed", "error", err) + logger.Error(nil, "done split history failed") + return swarm.ZeroAddress, err + } } err = putter.Done(encryptedReference) if err != nil { @@ -95,12 +108,6 @@ func (s *Service) actEncryptionHandler( logger.Error(nil, "done split encrypted reference failed") return swarm.ZeroAddress, err } - err = putter.Done(kvsReference) - if err != nil { - logger.Debug("done split kvs reference failed", "error", err) - logger.Error(nil, "done split kvs reference failed") - return swarm.ZeroAddress, err - } w.Header().Set(SwarmActHistoryAddressHeader, historyReference.String()) diff --git a/pkg/api/dynamicaccess_test.go b/pkg/api/dynamicaccess_test.go index 4657b1f8bbe..480714444e0 100644 --- a/pkg/api/dynamicaccess_test.go +++ b/pkg/api/dynamicaccess_test.go @@ -36,27 +36,27 @@ func prepareHistoryFixture(storer api.Storer) (dynamicaccess.History, swarm.Addr ctx := context.Background() ls := loadsave.New(storer.ChunkStore(), storer.Cache(), pipelineFactory(storer.Cache(), false, redundancy.NONE)) - h, _ := dynamicaccess.NewHistory(ls, nil) + h, _ := dynamicaccess.NewHistory(ls) testActRef1 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd891")) firstTime := time.Date(1994, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - h.Add(ctx, testActRef1, &firstTime) + h.Add(ctx, testActRef1, &firstTime, nil) testActRef2 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd892")) secondTime := time.Date(2000, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - h.Add(ctx, testActRef2, &secondTime) + h.Add(ctx, testActRef2, &secondTime, nil) testActRef3 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd893")) thirdTime := time.Date(2015, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - h.Add(ctx, testActRef3, &thirdTime) + h.Add(ctx, testActRef3, &thirdTime, nil) testActRef4 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd894")) fourthTime := time.Date(2020, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - h.Add(ctx, testActRef4, &fourthTime) + h.Add(ctx, testActRef4, &fourthTime, nil) testActRef5 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd895")) fifthTime := time.Date(2030, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - h.Add(ctx, testActRef5, &fifthTime) + h.Add(ctx, testActRef5, &fifthTime, nil) ref, _ := h.Store(ctx) return h, ref @@ -371,7 +371,7 @@ func TestDacInvalidPath(t *testing.T) { // [positive tests] 1., 2.: uploading a file w/ and w/o history address then downloading it and checking the data. // [negative test] 3. uploading a file then downloading it with a wrong history address. // [negative test] 4. uploading a file to a wrong history address. -// [negative test] 4. downloading a file to w/o history address. +// [negative test] 5. downloading a file to w/o history address. func TestDacHistory(t *testing.T) { t.Parallel() var ( @@ -552,7 +552,7 @@ func TestDacHistory(t *testing.T) { } // nolint:paralleltest,tparallel -// TestDacTimestamp doc. comment +// TestDacTimestamp // [positive test] 1.: uploading a file w/ ACT then download it w/ timestamp and check the data. // [negative test] 2.: try to download a file w/o timestamp. func TestDacTimestamp(t *testing.T) { @@ -637,7 +637,7 @@ func TestDacTimestamp(t *testing.T) { } // nolint:paralleltest,tparallel -// TestDacPublisher doc. comment +// TestDacPublisher // [positive test] 1.: uploading a file w/ ACT then download it w/ the publisher address and check the data. // [negative test] 2.: expect Bad request when the public key is invalid. // [negative test] 3.: try to download a file w/ an incorrect publisher address. diff --git a/pkg/api/feed.go b/pkg/api/feed.go index eed03febe31..a2679547478 100644 --- a/pkg/api/feed.go +++ b/pkg/api/feed.go @@ -137,11 +137,11 @@ func (s *Service) feedPostHandler(w http.ResponseWriter, r *http.Request) { } headers := struct { - BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` - Pin bool `map:"Swarm-Pin"` - Deferred *bool `map:"Swarm-Deferred-Upload"` - Act bool `map:"Swarm-Act"` - HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address"` + BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` + Pin bool `map:"Swarm-Pin"` + Deferred *bool `map:"Swarm-Deferred-Upload"` + Act bool `map:"Swarm-Act"` + HistoryAddress swarm.Address `map:"Swarm-Act-History-Address"` }{} if response := s.mapStructure(r.Header, &headers); response != nil { response("invalid header params", logger, w) diff --git a/pkg/api/soc.go b/pkg/api/soc.go index b4ce98e91eb..c2f11d6e05c 100644 --- a/pkg/api/soc.go +++ b/pkg/api/soc.go @@ -43,10 +43,10 @@ func (s *Service) socUploadHandler(w http.ResponseWriter, r *http.Request) { } headers := struct { - BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` - Pin bool `map:"Swarm-Pin"` - Act bool `map:"Swarm-Act"` - HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address"` + BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` + Pin bool `map:"Swarm-Pin"` + Act bool `map:"Swarm-Act"` + HistoryAddress swarm.Address `map:"Swarm-Act-History-Address"` }{} if response := s.mapStructure(r.Header, &headers); response != nil { response("invalid header params", logger, w) diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index be9e8b194f1..b3b808e2b74 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -149,8 +149,8 @@ func (al ActLogic) DecryptRef(ctx context.Context, storage kvs.KeyValueStore, en return swarm.NewAddress(ref), nil } -func NewLogic(S Session) ActLogic { +func NewLogic(s Session) ActLogic { return ActLogic{ - Session: S, + Session: s, } } diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index 87a24e707b2..fdef1c4971a 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -35,11 +35,14 @@ type GranteeManager interface { GetGrantees(ctx context.Context, rootHash swarm.Address) ([]*ecdsa.PublicKey, error) } -// TODO: ądd granteeList ref to history metadata to solve inconsistency +// TODO: add granteeList ref to history metadata to solve inconsistency type Controller interface { GranteeManager - DownloadHandler(ctx context.Context, timestamp int64, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, error) - UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash *swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) + // DownloadHandler decrypts the encryptedRef using the lookupkey based on the history and timestamp. + DownloadHandler(ctx context.Context, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) + // TODO: history encryption + // UploadHandler encrypts the reference and stores it in the history as the latest update. + UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) } type controller struct { @@ -55,57 +58,61 @@ var _ Controller = (*controller)(nil) func (c *controller) DownloadHandler( ctx context.Context, - timestamp int64, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, + timestamp int64, ) (swarm.Address, error) { ls := loadsave.New(c.getter, c.putter, requestPipelineFactory(ctx, c.putter, false, redundancy.NONE)) - history, err := NewHistory(ls, &historyRootHash) + history, err := NewHistoryReference(ls, historyRootHash) if err != nil { return swarm.ZeroAddress, err } - - kvsRef, err := history.Lookup(ctx, timestamp) + entry, err := history.Lookup(ctx, timestamp) + if err != nil { + return swarm.ZeroAddress, err + } + // TODO: hanlde granteelist ref in mtdt + kvs, err := kvs.NewReference(ls, entry.Reference()) if err != nil { return swarm.ZeroAddress, err } - kvs := kvs.New(ls, kvsRef) + return c.accessLogic.DecryptRef(ctx, kvs, encryptedRef, publisher) } -// TODO: review return params: how to get back history ref ? func (c *controller) UploadHandler( ctx context.Context, refrefence swarm.Address, publisher *ecdsa.PublicKey, - historyRootHash *swarm.Address, + historyRootHash swarm.Address, ) (swarm.Address, swarm.Address, swarm.Address, error) { ls := loadsave.New(c.getter, c.putter, requestPipelineFactory(ctx, c.putter, false, redundancy.NONE)) - history, err := NewHistory(ls, historyRootHash) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } + historyRef := historyRootHash + var ( + storage kvs.KeyValueStore + storageRef swarm.Address + ) now := time.Now().Unix() - kvsRef, err := history.Lookup(ctx, now) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - kvs := kvs.New(ls, kvsRef) - historyRef := swarm.ZeroAddress - if historyRootHash != nil { - historyRef = *historyRootHash - } - if kvsRef.Equal(swarm.ZeroAddress) { - err = c.accessLogic.AddPublisher(ctx, kvs, publisher) + if historyRef.IsZero() { + history, err := NewHistory(ls) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + storage, err = kvs.New(ls) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + err = c.accessLogic.AddPublisher(ctx, storage, publisher) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - kvsRef, err = kvs.Save(ctx) + storageRef, err = storage.Save(ctx) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - err = history.Add(ctx, kvsRef, &now) + // TODO: pass granteelist ref as mtdt + err = history.Add(ctx, storageRef, &now, nil) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } @@ -113,9 +120,25 @@ func (c *controller) UploadHandler( if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } + } else { + history, err := NewHistoryReference(ls, historyRef) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + // TODO: hanlde granteelist ref in mtdt + entry, err := history.Lookup(ctx, now) + storageRef = entry.Reference() + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + storage, err = kvs.NewReference(ls, storageRef) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } } - encryptedRef, err := c.accessLogic.EncryptRef(ctx, kvs, publisher, refrefence) - return kvsRef, historyRef, encryptedRef, err + + encryptedRef, err := c.accessLogic.EncryptRef(ctx, storage, publisher, refrefence) + return storageRef, historyRef, encryptedRef, err } func NewController(ctx context.Context, accessLogic ActLogic, getter storage.Getter, putter storage.Putter) Controller { diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index 0512e6481f9..a48d426466e 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -19,21 +19,21 @@ import ( var hashFunc = sha3.NewLegacyKeccak256 func getHistoryFixture(ctx context.Context, ls file.LoadSaver, al dynamicaccess.ActLogic, publisher *ecdsa.PublicKey) (swarm.Address, error) { - h, err := dynamicaccess.NewHistory(ls, nil) + h, err := dynamicaccess.NewHistory(ls) if err != nil { return swarm.ZeroAddress, nil } pk1 := getPrivKey(1) pk2 := getPrivKey(2) - kvs0 := kvs.New(ls, swarm.ZeroAddress) + kvs0, _ := kvs.New(ls) al.AddPublisher(ctx, kvs0, publisher) kvs0Ref, _ := kvs0.Save(ctx) - kvs1 := kvs.New(ls, swarm.ZeroAddress) + kvs1, _ := kvs.New(ls) al.AddGrantee(ctx, kvs1, publisher, &pk1.PublicKey, nil) al.AddPublisher(ctx, kvs1, publisher) kvs1Ref, _ := kvs1.Save(ctx) - kvs2 := kvs.New(ls, swarm.ZeroAddress) + kvs2, _ := kvs.New(ls) al.AddGrantee(ctx, kvs2, publisher, &pk2.PublicKey, nil) al.AddPublisher(ctx, kvs2, publisher) kvs2Ref, _ := kvs2.Save(ctx) @@ -41,9 +41,9 @@ func getHistoryFixture(ctx context.Context, ls file.LoadSaver, al dynamicaccess. secondTime := time.Date(2000, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() thirdTime := time.Date(2015, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - h.Add(ctx, kvs0Ref, &thirdTime) - h.Add(ctx, kvs1Ref, &firstTime) - h.Add(ctx, kvs2Ref, &secondTime) + h.Add(ctx, kvs0Ref, &thirdTime, nil) + h.Add(ctx, kvs1Ref, &firstTime, nil) + h.Add(ctx, kvs2Ref, &secondTime, nil) return h.Store(ctx) } @@ -55,9 +55,9 @@ func TestController_NewUploadDownload(t *testing.T) { al := dynamicaccess.NewLogic(diffieHellman) c := dynamicaccess.NewController(ctx, al, mockStorer.ChunkStore(), mockStorer.Cache()) ref := swarm.RandAddress(t) - _, hRef, encryptedRef, err := c.UploadHandler(ctx, ref, &publisher.PublicKey, nil) + _, hRef, encryptedRef, err := c.UploadHandler(ctx, ref, &publisher.PublicKey, swarm.ZeroAddress) assert.NoError(t, err) - dref, err := c.DownloadHandler(ctx, time.Now().Unix(), encryptedRef, &publisher.PublicKey, hRef) + dref, err := c.DownloadHandler(ctx, encryptedRef, &publisher.PublicKey, hRef, time.Now().Unix()) assert.NoError(t, err) assert.Equal(t, ref, dref) } @@ -72,9 +72,9 @@ func TestController_ExistingUploadDownload(t *testing.T) { ref := swarm.RandAddress(t) hRef, err := getHistoryFixture(ctx, ls, al, &publisher.PublicKey) assert.NoError(t, err) - _, hRef, encryptedRef, err := c.UploadHandler(ctx, ref, &publisher.PublicKey, &hRef) + _, hRef, encryptedRef, err := c.UploadHandler(ctx, ref, &publisher.PublicKey, hRef) assert.NoError(t, err) - dref, err := c.DownloadHandler(ctx, time.Now().Unix(), encryptedRef, &publisher.PublicKey, hRef) + dref, err := c.DownloadHandler(ctx, encryptedRef, &publisher.PublicKey, hRef, time.Now().Unix()) assert.NoError(t, err) assert.Equal(t, ref, dref) } diff --git a/pkg/dynamicaccess/grantee.go b/pkg/dynamicaccess/grantee.go index 8499cfc4f04..6724b718e4f 100644 --- a/pkg/dynamicaccess/grantee.go +++ b/pkg/dynamicaccess/grantee.go @@ -7,7 +7,6 @@ import ( "fmt" "github.com/ethersphere/bee/v2/pkg/file" - "github.com/ethersphere/bee/v2/pkg/storer" "github.com/ethersphere/bee/v2/pkg/swarm" ) @@ -25,7 +24,6 @@ type GranteeList interface { type GranteeListStruct struct { grantees []byte loadSave file.LoadSaver - putter storer.PutterSession } var _ GranteeList = (*GranteeListStruct)(nil) @@ -83,12 +81,8 @@ func (g *GranteeListStruct) Save(ctx context.Context) (swarm.Address, error) { if err != nil { return swarm.ZeroAddress, fmt.Errorf("grantee save error: %w", err) } - address := swarm.NewAddress(refBytes) - err = g.putter.Done(address) - if err != nil { - return swarm.ZeroAddress, err - } - return address, nil + + return swarm.NewAddress(refBytes), nil } func (g *GranteeListStruct) Remove(keysToRemove []*ecdsa.PublicKey) error { @@ -112,16 +106,15 @@ func (g *GranteeListStruct) Remove(keysToRemove []*ecdsa.PublicKey) error { return nil } -func NewGranteeList(ls file.LoadSaver, putter storer.PutterSession, reference swarm.Address) GranteeList { - var ( - data []byte - err error - ) - if swarm.ZeroAddress.Equal(reference) || swarm.EmptyAddress.Equal(reference) { - data = []byte{} - } else { - data, err = ls.Load(context.Background(), reference.Bytes()) +func NewGranteeList(ls file.LoadSaver) GranteeList { + return &GranteeListStruct{ + grantees: []byte{}, + loadSave: ls, } +} + +func NewGranteeListReference(ls file.LoadSaver, reference swarm.Address) GranteeList { + data, err := ls.Load(context.Background(), reference.Bytes()) if err != nil { return nil } @@ -129,10 +122,5 @@ func NewGranteeList(ls file.LoadSaver, putter storer.PutterSession, reference sw return &GranteeListStruct{ grantees: data, loadSave: ls, - putter: putter, } } - -func (g *GranteeListStruct) Store() (swarm.Address, error) { - return swarm.EmptyAddress, nil -} diff --git a/pkg/dynamicaccess/grantee_test.go b/pkg/dynamicaccess/grantee_test.go index 5730911ad74..0578be28ff4 100644 --- a/pkg/dynamicaccess/grantee_test.go +++ b/pkg/dynamicaccess/grantee_test.go @@ -15,7 +15,6 @@ import ( "github.com/ethersphere/bee/v2/pkg/file/redundancy" "github.com/ethersphere/bee/v2/pkg/storage" mockstorer "github.com/ethersphere/bee/v2/pkg/storer/mock" - "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/stretchr/testify/assert" ) @@ -42,8 +41,7 @@ func generateKeyListFixture() ([]*ecdsa.PublicKey, error) { } func TestGranteeAddGet(t *testing.T) { - putter := mockStorer.DirectUpload() - gl := dynamicaccess.NewGranteeList(createLs(), putter, swarm.ZeroAddress) + gl := dynamicaccess.NewGranteeList(createLs()) keys, err := generateKeyListFixture() if err != nil { t.Errorf("key generation error: %v", err) @@ -100,8 +98,7 @@ func TestGranteeAddGet(t *testing.T) { } func TestGranteeRemove(t *testing.T) { - putter := mockStorer.DirectUpload() - gl := dynamicaccess.NewGranteeList(createLs(), putter, swarm.ZeroAddress) + gl := dynamicaccess.NewGranteeList(createLs()) keys, err := generateKeyListFixture() if err != nil { t.Errorf("key generation error: %v", err) @@ -154,12 +151,12 @@ func TestGranteeSave(t *testing.T) { t.Errorf("key generation error: %v", err) } t.Run("Save empty grantee list return NO error", func(t *testing.T) { - gl := dynamicaccess.NewGranteeList(createLs(), mockStorer.DirectUpload(), swarm.ZeroAddress) + gl := dynamicaccess.NewGranteeList(createLs()) _, err := gl.Save(ctx) assert.NoError(t, err) }) t.Run("Save not empty grantee list return valid swarm address", func(t *testing.T) { - gl := dynamicaccess.NewGranteeList(createLs(), mockStorer.DirectUpload(), swarm.ZeroAddress) + gl := dynamicaccess.NewGranteeList(createLs()) err = gl.Add(keys) ref, err := gl.Save(ctx) assert.NoError(t, err) @@ -167,8 +164,7 @@ func TestGranteeSave(t *testing.T) { }) t.Run("Save grantee list with one item, no error, pre-save value exist", func(t *testing.T) { ls := createLs() - putter := mockStorer.DirectUpload() - gl1 := dynamicaccess.NewGranteeList(ls, putter, swarm.ZeroAddress) + gl1 := dynamicaccess.NewGranteeList(ls) err := gl1.Add(keys) assert.NoError(t, err) @@ -176,24 +172,22 @@ func TestGranteeSave(t *testing.T) { ref, err := gl1.Save(ctx) assert.NoError(t, err) - gl2 := dynamicaccess.NewGranteeList(ls, putter, ref) + gl2 := dynamicaccess.NewGranteeListReference(ls, ref) val := gl2.Get() assert.NoError(t, err) assert.Equal(t, keys, val) }) t.Run("Save grantee list and add one item, no error, after-save value exist", func(t *testing.T) { ls := createLs() - putter := mockStorer.DirectUpload() - gl1 := dynamicaccess.NewGranteeList(ls, putter, swarm.ZeroAddress) + gl1 := dynamicaccess.NewGranteeList(ls) err := gl1.Add(keys) assert.NoError(t, err) ref, err := gl1.Save(ctx) assert.NoError(t, err) - // New KVS - gl2 := dynamicaccess.NewGranteeList(ls, putter, ref) + gl2 := dynamicaccess.NewGranteeListReference(ls, ref) err = gl2.Add(keys) assert.NoError(t, err) diff --git a/pkg/dynamicaccess/history.go b/pkg/dynamicaccess/history.go index 85a62085f36..25193adf886 100644 --- a/pkg/dynamicaccess/history.go +++ b/pkg/dynamicaccess/history.go @@ -15,8 +15,8 @@ import ( ) type History interface { - Add(ctx context.Context, ref swarm.Address, timestamp *int64) error - Lookup(ctx context.Context, timestamp int64) (swarm.Address, error) + Add(ctx context.Context, ref swarm.Address, timestamp *int64, metadata *map[string]string) error + Lookup(ctx context.Context, timestamp int64) (manifest.Entry, error) Store(ctx context.Context) (swarm.Address, error) } @@ -29,15 +29,22 @@ type history struct { ls file.LoadSaver } -func NewHistory(ls file.LoadSaver, ref *swarm.Address) (*history, error) { - var err error - var m manifest.Interface +func NewHistory(ls file.LoadSaver) (History, error) { + m, err := manifest.NewDefaultManifest(ls, false) + if err != nil { + return nil, err + } - if ref != nil { - m, err = manifest.NewDefaultManifestReference(*ref, ls) - } else { - m, err = manifest.NewDefaultManifest(ls, false) + mm, ok := m.(*manifest.MantarayManifest) + if !ok { + return nil, fmt.Errorf("expected MantarayManifest, got %T", m) } + + return &history{manifest: mm, ls: ls}, nil +} + +func NewHistoryReference(ls file.LoadSaver, ref swarm.Address) (History, error) { + m, err := manifest.NewDefaultManifestReference(ref, ls) if err != nil { return nil, err } @@ -50,9 +57,11 @@ func NewHistory(ls file.LoadSaver, ref *swarm.Address) (*history, error) { return &history{manifest: mm, ls: ls}, nil } -func (h *history) Add(ctx context.Context, ref swarm.Address, timestamp *int64) error { - // Do we need any extra meta/act? - meta := map[string]string{} +func (h *history) Add(ctx context.Context, ref swarm.Address, timestamp *int64, metadata *map[string]string) error { + mtdt := map[string]string{} + if metadata != nil { + mtdt = *metadata + } // add timestamps transformed so that the latests timestamp becomes the smallest key var unixTime int64 if timestamp != nil { @@ -62,29 +71,29 @@ func (h *history) Add(ctx context.Context, ref swarm.Address, timestamp *int64) } key := strconv.FormatInt(math.MaxInt64-unixTime, 10) - return h.manifest.Add(ctx, key, manifest.NewEntry(ref, meta)) + return h.manifest.Add(ctx, key, manifest.NewEntry(ref, mtdt)) } // Lookup finds the entry for a path or returns error if not found -func (h *history) Lookup(ctx context.Context, timestamp int64) (swarm.Address, error) { +func (h *history) Lookup(ctx context.Context, timestamp int64) (manifest.Entry, error) { if timestamp <= 0 { - return swarm.ZeroAddress, errors.New("invalid timestamp") + return manifest.NewEntry(swarm.ZeroAddress, map[string]string{}), errors.New("invalid timestamp") } reversedTimestamp := math.MaxInt64 - timestamp - node, err := h.LookupNode(ctx, reversedTimestamp) + node, err := h.lookupNode(ctx, reversedTimestamp) if err != nil { - return swarm.ZeroAddress, err + return manifest.NewEntry(swarm.ZeroAddress, map[string]string{}), err } if node != nil { - return swarm.NewAddress(node.Entry()), nil + return manifest.NewEntry(swarm.NewAddress(node.Entry()), node.Metadata()), nil } - return swarm.ZeroAddress, nil + return manifest.NewEntry(swarm.ZeroAddress, map[string]string{}), nil } -func (h *history) LookupNode(ctx context.Context, searchedTimestamp int64) (*mantaray.Node, error) { +func (h *history) lookupNode(ctx context.Context, searchedTimestamp int64) (*mantaray.Node, error) { // before node's timestamp is the closest one that is less than or equal to the searched timestamp // for instance: 2030, 2020, 1994 -> search for 2021 -> before is 2020 var beforeNode *mantaray.Node diff --git a/pkg/dynamicaccess/history_test.go b/pkg/dynamicaccess/history_test.go index 4b353823b00..5c7f5670cf6 100644 --- a/pkg/dynamicaccess/history_test.go +++ b/pkg/dynamicaccess/history_test.go @@ -2,6 +2,7 @@ package dynamicaccess_test import ( "context" + "reflect" "testing" "time" @@ -16,14 +17,14 @@ import ( ) func TestHistoryAdd(t *testing.T) { - h, err := dynamicaccess.NewHistory(nil, nil) + h, err := dynamicaccess.NewHistory(nil) assert.NoError(t, err) addr := swarm.NewAddress([]byte("addr")) ctx := context.Background() - err = h.Add(ctx, addr, nil) + err = h.Add(ctx, addr, nil, nil) assert.NoError(t, err) } @@ -32,20 +33,22 @@ func TestSingleNodeHistoryLookup(t *testing.T) { ctx := context.Background() ls := loadsave.New(storer.ChunkStore(), storer.Cache(), pipelineFactory(storer.Cache(), false)) - h, err := dynamicaccess.NewHistory(ls, nil) + h, err := dynamicaccess.NewHistory(ls) assert.NoError(t, err) testActRef := swarm.RandAddress(t) - err = h.Add(ctx, testActRef, nil) + err = h.Add(ctx, testActRef, nil, nil) assert.NoError(t, err) _, err = h.Store(ctx) assert.NoError(t, err) searchedTime := time.Now().Unix() - actRef, err := h.Lookup(ctx, searchedTime) + entry, err := h.Lookup(ctx, searchedTime) + actRef := entry.Reference() assert.NoError(t, err) assert.True(t, actRef.Equal(testActRef)) + assert.Nil(t, entry.Metadata()) } func TestMultiNodeHistoryLookup(t *testing.T) { @@ -53,51 +56,64 @@ func TestMultiNodeHistoryLookup(t *testing.T) { ctx := context.Background() ls := loadsave.New(storer.ChunkStore(), storer.Cache(), pipelineFactory(storer.Cache(), false)) - h, _ := dynamicaccess.NewHistory(ls, nil) + h, _ := dynamicaccess.NewHistory(ls) testActRef1 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd891")) firstTime := time.Date(1994, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - h.Add(ctx, testActRef1, &firstTime) + mtdt1 := map[string]string{"firstTime": "1994-04-01"} + h.Add(ctx, testActRef1, &firstTime, &mtdt1) testActRef2 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd892")) secondTime := time.Date(2000, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - h.Add(ctx, testActRef2, &secondTime) + mtdt2 := map[string]string{"secondTime": "2000-04-01"} + h.Add(ctx, testActRef2, &secondTime, &mtdt2) testActRef3 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd893")) thirdTime := time.Date(2015, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - h.Add(ctx, testActRef3, &thirdTime) + mtdt3 := map[string]string{"thirdTime": "2015-04-01"} + h.Add(ctx, testActRef3, &thirdTime, &mtdt3) testActRef4 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd894")) fourthTime := time.Date(2020, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - h.Add(ctx, testActRef4, &fourthTime) + mtdt4 := map[string]string{"fourthTime": "2020-04-01"} + h.Add(ctx, testActRef4, &fourthTime, &mtdt4) testActRef5 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd895")) fifthTime := time.Date(2030, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - h.Add(ctx, testActRef5, &fifthTime) + mtdt5 := map[string]string{"fifthTime": "2030-04-01"} + h.Add(ctx, testActRef5, &fifthTime, &mtdt5) // latest searchedTime := time.Date(1980, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - actRef, err := h.Lookup(ctx, searchedTime) + entry, err := h.Lookup(ctx, searchedTime) + actRef := entry.Reference() assert.NoError(t, err) assert.True(t, actRef.Equal(testActRef1)) + assert.True(t, reflect.DeepEqual(mtdt1, entry.Metadata())) // before first time searchedTime = time.Date(2021, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - actRef, err = h.Lookup(ctx, searchedTime) + entry, err = h.Lookup(ctx, searchedTime) + actRef = entry.Reference() assert.NoError(t, err) assert.True(t, actRef.Equal(testActRef4)) + assert.True(t, reflect.DeepEqual(mtdt4, entry.Metadata())) // same time searchedTime = time.Date(2000, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - actRef, err = h.Lookup(ctx, searchedTime) + entry, err = h.Lookup(ctx, searchedTime) + actRef = entry.Reference() assert.NoError(t, err) assert.True(t, actRef.Equal(testActRef2)) + assert.True(t, reflect.DeepEqual(mtdt2, entry.Metadata())) // after time searchedTime = time.Date(2045, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - actRef, err = h.Lookup(ctx, searchedTime) + entry, err = h.Lookup(ctx, searchedTime) + actRef = entry.Reference() assert.NoError(t, err) assert.True(t, actRef.Equal(testActRef5)) + assert.True(t, reflect.DeepEqual(mtdt5, entry.Metadata())) } func TestHistoryStore(t *testing.T) { @@ -105,14 +121,23 @@ func TestHistoryStore(t *testing.T) { ctx := context.Background() ls := loadsave.New(storer.ChunkStore(), storer.Cache(), pipelineFactory(storer.Cache(), false)) - h, _ := dynamicaccess.NewHistory(ls, nil) + h1, _ := dynamicaccess.NewHistory(ls) testActRef1 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd891")) firstTime := time.Date(1994, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - h.Add(ctx, testActRef1, &firstTime) + mtdt1 := map[string]string{"firstTime": "1994-04-01"} + h1.Add(ctx, testActRef1, &firstTime, &mtdt1) - _, err := h.Store(ctx) + href1, err := h1.Store(ctx) assert.NoError(t, err) + + h2, err := dynamicaccess.NewHistoryReference(ls, href1) + assert.NoError(t, err) + + entry1, err := h2.Lookup(ctx, firstTime) + actRef1 := entry1.Reference() + assert.True(t, actRef1.Equal(testActRef1)) + assert.True(t, reflect.DeepEqual(mtdt1, entry1.Metadata())) } func pipelineFactory(s storage.Putter, encrypt bool) func() pipeline.Interface { diff --git a/pkg/dynamicaccess/mock/service.go b/pkg/dynamicaccess/mock/service.go index 2d19c3ed4e3..6f4d5f09e72 100644 --- a/pkg/dynamicaccess/mock/service.go +++ b/pkg/dynamicaccess/mock/service.go @@ -78,7 +78,7 @@ func WithPublisher(ref string) Option { }) } -func (m *mockDacService) DownloadHandler(ctx context.Context, timestamp int64, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, error) { +func (m *mockDacService) DownloadHandler(ctx context.Context, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) { if m.acceptAll { return swarm.ParseHexAddress("36e6c1bbdfee6ac21485d5f970479fd1df458d36df9ef4e8179708ed46da557f") } @@ -93,14 +93,15 @@ func (m *mockDacService) DownloadHandler(ctx context.Context, timestamp int64, e if !exists { return swarm.ZeroAddress, fmt.Errorf("history not found") } - kvsRef, err := h.Lookup(ctx, timestamp) - if kvsRef.Equal(swarm.ZeroAddress) || err != nil { + entry, err := h.Lookup(ctx, timestamp) + kvsRef := entry.Reference() + if kvsRef.IsZero() || err != nil { return swarm.ZeroAddress, fmt.Errorf("kvs not found") } return m.refMap[encryptedRef.String()], nil } -func (m *mockDacService) UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash *swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) { +func (m *mockDacService) UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) { historyRef, _ := swarm.ParseHexAddress("67bdf80a9bbea8eca9c8480e43fdceb485d2d74d5708e45144b8c4adacd13d9c") kvsRef, _ := swarm.ParseHexAddress("3339613565613837623134316665343461613630396333333237656364383934") if m.acceptAll { @@ -112,22 +113,25 @@ func (m *mockDacService) UploadHandler(ctx context.Context, reference swarm.Addr exists bool ) now := time.Now().Unix() - if historyRootHash != nil { - historyRef = *historyRootHash + if !historyRootHash.IsZero() { + historyRef = historyRootHash h, exists = m.historyMap[historyRef.String()] if !exists { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, fmt.Errorf("history not found") } - kvsRef, _ = h.Lookup(ctx, now) + entry, _ := h.Lookup(ctx, now) + kvsRef := entry.Reference() + if kvsRef.IsZero() { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, fmt.Errorf("kvs not found") + } } else { - h, _ = dynamicaccess.NewHistory(m.ls, nil) - h.Add(ctx, kvsRef, &now) + h, _ = dynamicaccess.NewHistory(m.ls) + // TODO: pass granteelist ref as mtdt + h.Add(ctx, kvsRef, &now, nil) historyRef, _ = h.Store(ctx) m.historyMap[historyRef.String()] = h } - if kvsRef.Equal(swarm.ZeroAddress) { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, fmt.Errorf("kvs not found") - } + encryptedRef, _ := m.encrypter.Encrypt(reference.Bytes()) m.refMap[(hex.EncodeToString(encryptedRef))] = reference return kvsRef, historyRef, swarm.NewAddress(encryptedRef), nil diff --git a/pkg/dynamicaccess/service.go b/pkg/dynamicaccess/service.go index c87b74ee60c..b9cedbcf82b 100644 --- a/pkg/dynamicaccess/service.go +++ b/pkg/dynamicaccess/service.go @@ -9,8 +9,8 @@ import ( ) type Service interface { - DownloadHandler(ctx context.Context, timestamp int64, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, error) - UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash *swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) + DownloadHandler(ctx context.Context, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) + UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) io.Closer } @@ -19,11 +19,11 @@ type service struct { controller Controller } -func (s *service) DownloadHandler(ctx context.Context, timestamp int64, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, error) { - return s.controller.DownloadHandler(ctx, timestamp, encryptedRef, publisher, historyRootHash) +func (s *service) DownloadHandler(ctx context.Context, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) { + return s.controller.DownloadHandler(ctx, encryptedRef, publisher, historyRootHash, timestamp) } -func (s *service) UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash *swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) { +func (s *service) UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) { return s.controller.UploadHandler(ctx, reference, publisher, historyRootHash) } diff --git a/pkg/kvs/kvs.go b/pkg/kvs/kvs.go index 42a07da6738..5f2381a8b24 100644 --- a/pkg/kvs/kvs.go +++ b/pkg/kvs/kvs.go @@ -57,21 +57,24 @@ func (s *keyValueStore) Save(ctx context.Context) (swarm.Address, error) { return ref, nil } -func New(ls file.LoadSaver, rootHash swarm.Address) KeyValueStore { - var ( - manif manifest.Interface - err error - ) - if swarm.ZeroAddress.Equal(rootHash) || swarm.EmptyAddress.Equal(rootHash) { - manif, err = manifest.NewSimpleManifest(ls) - } else { - manif, err = manifest.NewSimpleManifestReference(rootHash, ls) - } +func New(ls file.LoadSaver) (KeyValueStore, error) { + manif, err := manifest.NewSimpleManifest(ls) if err != nil { - return nil + return nil, err } return &keyValueStore{ manifest: manif, + }, nil +} + +func NewReference(ls file.LoadSaver, rootHash swarm.Address) (KeyValueStore, error) { + manif, err := manifest.NewSimpleManifestReference(rootHash, ls) + if err != nil { + return nil, err } + + return &keyValueStore{ + manifest: manif, + }, nil } diff --git a/pkg/kvs/kvs_test.go b/pkg/kvs/kvs_test.go index 5c6f75e2379..462e092d532 100644 --- a/pkg/kvs/kvs_test.go +++ b/pkg/kvs/kvs_test.go @@ -38,7 +38,9 @@ func keyValuePair(t *testing.T) ([]byte, []byte) { func TestKvs(t *testing.T) { - s := kvs.New(createLs(), swarm.ZeroAddress) + s, err := kvs.New(createLs()) + assert.NoError(t, err) + key, val := keyValuePair(t) ctx := context.Background() @@ -122,12 +124,12 @@ func TestKvs_Save(t *testing.T) { key1, val1 := keyValuePair(t) key2, val2 := keyValuePair(t) t.Run("Save empty KVS return error", func(t *testing.T) { - s := kvs.New(createLs(), swarm.ZeroAddress) + s, _ := kvs.New(createLs()) _, err := s.Save(ctx) assert.Error(t, err) }) t.Run("Save not empty KVS return valid swarm address", func(t *testing.T) { - s := kvs.New(createLs(), swarm.ZeroAddress) + s, _ := kvs.New(createLs()) s.Put(ctx, key1, val1) ref, err := s.Save(ctx) assert.NoError(t, err) @@ -135,7 +137,7 @@ func TestKvs_Save(t *testing.T) { }) t.Run("Save KVS with one item, no error, pre-save value exist", func(t *testing.T) { ls := createLs() - s1 := kvs.New(ls, swarm.ZeroAddress) + s1, _ := kvs.New(ls) err := s1.Put(ctx, key1, val1) assert.NoError(t, err) @@ -143,7 +145,9 @@ func TestKvs_Save(t *testing.T) { ref, err := s1.Save(ctx) assert.NoError(t, err) - s2 := kvs.New(ls, ref) + s2, err := kvs.NewReference(ls, ref) + assert.NoError(t, err) + val, err := s2.Get(ctx, key1) assert.NoError(t, err) assert.Equal(t, val1, val) @@ -151,7 +155,7 @@ func TestKvs_Save(t *testing.T) { t.Run("Save KVS and add one item, no error, after-save value exist", func(t *testing.T) { ls := createLs() - kvs1 := kvs.New(ls, swarm.ZeroAddress) + kvs1, _ := kvs.New(ls) err := kvs1.Put(ctx, key1, val1) assert.NoError(t, err) @@ -159,7 +163,8 @@ func TestKvs_Save(t *testing.T) { assert.NoError(t, err) // New KVS - kvs2 := kvs.New(ls, ref) + kvs2, err := kvs.NewReference(ls, ref) + assert.NoError(t, err) err = kvs2.Put(ctx, key2, val2) assert.NoError(t, err) From 270788426d0026977ce5e9fdf7e368b6f190d2da Mon Sep 17 00:00:00 2001 From: Kexort Date: Mon, 13 May 2024 15:09:10 +0200 Subject: [PATCH 33/41] ACT grantee management (#37) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * implement grantee management * Add POST endpoint + fixes * Save grantees as pubkey list and fix remove error; CHG: act-handler logger names * Refactor: pass getter, putter to controller functions * Refactor: error handling in dynamicaccess; Read cache header only for download handlers * CHG: grantees ref is encrypted and added to history ref + tests * Fix nil pointer dereference panic * CHG: put actref in handlegrantees; Add: pin, tag,deferred headers * CHG: pass loadsave to handlers; check if history address is nil * FIX: re-init history so that it can be saved; only add publisher if histroy is zero * make act timestamp optional * fix revoke grantees * Fix: Act timestamp header nil check; Uploadhandler UT * Fix controller nil pointer deref --------- Co-authored-by: Bálint Ujvári --- pkg/api/api.go | 4 +- pkg/api/api_test.go | 2 +- pkg/api/bytes.go | 2 +- pkg/api/bzz.go | 2 +- pkg/api/chunk.go | 2 +- pkg/api/dirs.go | 2 +- pkg/api/dynamicaccess.go | 430 +++++++++++++++++- pkg/api/dynamicaccess_test.go | 137 ++++++ pkg/api/feed.go | 2 +- pkg/api/router.go | 15 + pkg/api/soc.go | 2 +- pkg/dynamicaccess/accesslogic.go | 16 +- pkg/dynamicaccess/controller.go | 270 +++++++---- pkg/dynamicaccess/controller_test.go | 219 +++++++-- pkg/dynamicaccess/grantee.go | 130 ++++-- pkg/dynamicaccess/grantee_test.go | 68 ++- .../mock/{service.go => controller.go} | 51 ++- pkg/dynamicaccess/service.go | 39 -- pkg/node/devnode.go | 6 +- pkg/node/node.go | 6 +- 20 files changed, 1113 insertions(+), 292 deletions(-) rename pkg/dynamicaccess/mock/{service.go => controller.go} (69%) delete mode 100644 pkg/dynamicaccess/service.go diff --git a/pkg/api/api.go b/pkg/api/api.go index 2566037873e..6dfd004d131 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -151,7 +151,7 @@ type Service struct { feedFactory feeds.Factory signer crypto.Signer post postage.Service - dac dynamicaccess.Service + dac dynamicaccess.Controller postageContract postagecontract.Interface probe *Probe metricsRegistry *prometheus.Registry @@ -248,7 +248,7 @@ type ExtraOptions struct { Pss pss.Interface FeedFactory feeds.Factory Post postage.Service - Dac dynamicaccess.Service + Dac dynamicaccess.Controller PostageContract postagecontract.Interface Staking staking.Contract Steward steward.Interface diff --git a/pkg/api/api_test.go b/pkg/api/api_test.go index 3e107d15824..67bd9145dc7 100644 --- a/pkg/api/api_test.go +++ b/pkg/api/api_test.go @@ -102,7 +102,7 @@ type testServerOptions struct { PostageContract postagecontract.Interface StakingContract staking.Contract Post postage.Service - Dac dynamicaccess.Service + Dac dynamicaccess.Controller Steward steward.Interface WsHeaders http.Header DirectUpload bool diff --git a/pkg/api/bytes.go b/pkg/api/bytes.go index a84ec2936de..2afb1db99c0 100644 --- a/pkg/api/bytes.go +++ b/pkg/api/bytes.go @@ -118,7 +118,7 @@ func (s *Service) bytesUploadHandler(w http.ResponseWriter, r *http.Request) { encryptedReference := reference if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, reference, headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, reference, headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/bzz.go b/pkg/api/bzz.go index 6319624a17d..b49bc89baea 100644 --- a/pkg/api/bzz.go +++ b/pkg/api/bzz.go @@ -266,7 +266,7 @@ func (s *Service) fileUploadHandler( encryptedReference := manifestReference if act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, manifestReference, historyAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, manifestReference, historyAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/chunk.go b/pkg/api/chunk.go index 496f7f8d306..21daa0d0f57 100644 --- a/pkg/api/chunk.go +++ b/pkg/api/chunk.go @@ -143,7 +143,7 @@ func (s *Service) chunkUploadHandler(w http.ResponseWriter, r *http.Request) { encryptedReference := chunk.Address() if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, chunk.Address(), headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, chunk.Address(), headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/dirs.go b/pkg/api/dirs.go index c85c68e4edb..f187fbde01e 100644 --- a/pkg/api/dirs.go +++ b/pkg/api/dirs.go @@ -102,7 +102,7 @@ func (s *Service) dirUploadHandler( encryptedReference := reference if act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, reference, historyAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, reference, historyAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index c7c1279609e..bbf3abbc69e 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -3,10 +3,20 @@ package api import ( "context" "crypto/ecdsa" + "encoding/hex" + "encoding/json" + "errors" + "io" "net/http" + "time" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/ethersphere/bee/v2/pkg/crypto" + "github.com/ethersphere/bee/v2/pkg/file/loadsave" + "github.com/ethersphere/bee/v2/pkg/file/redundancy" "github.com/ethersphere/bee/v2/pkg/jsonhttp" - "github.com/ethersphere/bee/v2/pkg/log" + "github.com/ethersphere/bee/v2/pkg/postage" + storage "github.com/ethersphere/bee/v2/pkg/storage" storer "github.com/ethersphere/bee/v2/pkg/storer" "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/gorilla/mux" @@ -14,6 +24,8 @@ import ( type addressKey struct{} +const granteeListEncrypt = true + // getAddressFromContext is a helper function to extract the address from the context func getAddressFromContext(ctx context.Context) swarm.Address { v, ok := ctx.Value(addressKey{}).(swarm.Address) @@ -28,12 +40,36 @@ func setAddressInContext(ctx context.Context, address swarm.Address) context.Con return context.WithValue(ctx, addressKey{}, address) } +type GranteesPatchRequest struct { + Addlist []string `json:"add"` + Revokelist []string `json:"revoke"` +} + +type GranteesPatchResponse struct { + Reference swarm.Address `json:"ref"` + HistoryReference swarm.Address `json:"historyref"` +} + +type GranteesPostRequest struct { + GranteeList []string `json:"grantees"` +} + +type GranteesPostResponse struct { + Reference swarm.Address `json:"ref"` + HistoryReference swarm.Address `json:"historyref"` +} + +type GranteesPatch struct { + Addlist []*ecdsa.PublicKey + Revokelist []*ecdsa.PublicKey +} + // actDecryptionHandler is a middleware that looks up and decrypts the given address, // if the act headers are present func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { return func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - logger := s.logger.WithName("acthandler").Build() + logger := s.logger.WithName("act_decryption_handler").Build() paths := struct { Address swarm.Address `map:"address,resolve" validate:"required"` }{} @@ -46,6 +82,7 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { Timestamp *int64 `map:"Swarm-Act-Timestamp"` Publisher *ecdsa.PublicKey `map:"Swarm-Act-Publisher"` HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address"` + Cache *bool `map:"Swarm-Cache"` }{} if response := s.mapStructure(r.Header, &headers); response != nil { response("invalid header params", logger, w) @@ -53,12 +90,23 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { } // Try to download the file wihtout decryption, if the act headers are not present - if headers.Publisher == nil || headers.Timestamp == nil || headers.HistoryAddress == nil { + if headers.Publisher == nil || headers.HistoryAddress == nil { h.ServeHTTP(w, r) return } + + timestamp := time.Now().Unix() + if headers.Timestamp != nil { + timestamp = *headers.Timestamp + } + + cache := true + if headers.Cache != nil { + cache = *headers.Cache + } ctx := r.Context() - reference, err := s.dac.DownloadHandler(ctx, paths.Address, headers.Publisher, *headers.HistoryAddress, *headers.Timestamp) + ls := loadsave.NewReadonly(s.storer.Download(cache)) + reference, err := s.dac.DownloadHandler(ctx, ls, paths.Address, headers.Publisher, *headers.HistoryAddress, timestamp) if err != nil { jsonhttp.InternalServerError(w, errActDownload) return @@ -73,14 +121,15 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { // Uploads the encrypted reference, history and kvs to the store func (s *Service) actEncryptionHandler( ctx context.Context, - logger log.Logger, w http.ResponseWriter, putter storer.PutterSession, reference swarm.Address, historyRootHash swarm.Address, ) (swarm.Address, error) { + logger := s.logger.WithName("act_encryption_handler").Build() publisherPublicKey := &s.publicKey - storageReference, historyReference, encryptedReference, err := s.dac.UploadHandler(ctx, reference, publisherPublicKey, historyRootHash) + ls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, false, redundancy.NONE)) + storageReference, historyReference, encryptedReference, err := s.dac.UploadHandler(ctx, ls, reference, publisherPublicKey, historyRootHash) if err != nil { logger.Debug("act failed to encrypt reference", "error", err) logger.Error(nil, "act failed to encrypt reference") @@ -102,14 +151,371 @@ func (s *Service) actEncryptionHandler( return swarm.ZeroAddress, err } } - err = putter.Done(encryptedReference) + + w.Header().Set(SwarmActHistoryAddressHeader, historyReference.String()) + return encryptedReference, nil +} + +// actListGranteesHandler is a middleware that decrypts the given address and returns the list of grantees, +// only the publisher is authorized to access the list +func (s *Service) actListGranteesHandler(w http.ResponseWriter, r *http.Request) { + logger := s.logger.WithName("act_list_grantees_handler").Build() + paths := struct { + GranteesAddress swarm.Address `map:"address,resolve" validate:"required"` + }{} + if response := s.mapStructure(mux.Vars(r), &paths); response != nil { + response("invalid path params", logger, w) + return + } + + headers := struct { + Cache *bool `map:"Swarm-Cache"` + }{} + if response := s.mapStructure(r.Header, &headers); response != nil { + response("invalid header params", logger, w) + return + } + cache := true + if headers.Cache != nil { + cache = *headers.Cache + } + publisher := &s.publicKey + ls := loadsave.NewReadonly(s.storer.Download(cache)) + grantees, err := s.dac.GetGrantees(r.Context(), ls, publisher, paths.GranteesAddress) if err != nil { - logger.Debug("done split encrypted reference failed", "error", err) - logger.Error(nil, "done split encrypted reference failed") - return swarm.ZeroAddress, err + logger.Debug("could not get grantees", "error", err) + logger.Error(nil, "could not get grantees") + jsonhttp.NotFound(w, "granteelist not found") + return + } + granteeSlice := make([]string, len(grantees)) + for i, grantee := range grantees { + granteeSlice[i] = hex.EncodeToString(crypto.EncodeSecp256k1PublicKey(grantee)) } + jsonhttp.OK(w, granteeSlice) +} - w.Header().Set(SwarmActHistoryAddressHeader, historyReference.String()) +// TODO: actGrantRevokeHandler doc. +func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) { + logger := s.logger.WithName("act_grant_revoke_handler").Build() - return encryptedReference, nil + if r.Body == http.NoBody { + logger.Error(nil, "request has no body") + jsonhttp.BadRequest(w, errInvalidRequest) + return + } + + paths := struct { + GranteesAddress swarm.Address `map:"address,resolve" validate:"required"` + }{} + if response := s.mapStructure(mux.Vars(r), &paths); response != nil { + response("invalid path params", logger, w) + return + } + + headers := struct { + BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` + SwarmTag uint64 `map:"Swarm-Tag"` + Pin bool `map:"Swarm-Pin"` + Deferred *bool `map:"Swarm-Deferred-Upload"` + HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address" validate:"required"` + }{} + if response := s.mapStructure(r.Header, &headers); response != nil { + response("invalid header params", logger, w) + return + } + + historyAddress := swarm.ZeroAddress + if headers.HistoryAddress != nil { + historyAddress = *headers.HistoryAddress + } + + var ( + tag uint64 + err error + deferred = defaultUploadMethod(headers.Deferred) + ) + + if deferred || headers.Pin { + tag, err = s.getOrCreateSessionID(headers.SwarmTag) + if err != nil { + logger.Debug("get or create tag failed", "error", err) + logger.Error(nil, "get or create tag failed") + switch { + case errors.Is(err, storage.ErrNotFound): + jsonhttp.NotFound(w, "tag not found") + default: + jsonhttp.InternalServerError(w, "cannot get or create tag") + } + return + } + } + + body, err := io.ReadAll(r.Body) + if err != nil { + if jsonhttp.HandleBodyReadError(err, w) { + return + } + logger.Debug("read request body failed", "error", err) + logger.Error(nil, "read request body failed") + jsonhttp.InternalServerError(w, "cannot read request") + return + } + + gpr := GranteesPatchRequest{} + if len(body) > 0 { + err = json.Unmarshal(body, &gpr) + if err != nil { + logger.Debug("unmarshal body failed", "error", err) + logger.Error(nil, "unmarshal body failed") + jsonhttp.InternalServerError(w, "error unmarshaling request body") + return + } + } + + grantees := GranteesPatch{} + paresAddlist, err := parseKeys(gpr.Addlist) + if err != nil { + logger.Debug("add list key parse failed", "error", err) + logger.Error(nil, "add list key parse failed") + jsonhttp.InternalServerError(w, "error add list key parsing") + return + } + grantees.Addlist = append(grantees.Addlist, paresAddlist...) + + paresRevokelist, err := parseKeys(gpr.Revokelist) + if err != nil { + logger.Debug("revoke list key parse failed", "error", err) + logger.Error(nil, "revoke list key parse failed") + jsonhttp.InternalServerError(w, "error revoke list key parsing") + return + } + grantees.Revokelist = append(grantees.Revokelist, paresRevokelist...) + + ctx := r.Context() + putter, err := s.newStamperPutter(ctx, putterOptions{ + BatchID: headers.BatchID, + TagID: tag, + Pin: headers.Pin, + Deferred: deferred, + }) + if err != nil { + logger.Debug("putter failed", "error", err) + logger.Error(nil, "putter failed") + switch { + case errors.Is(err, errBatchUnusable) || errors.Is(err, postage.ErrNotUsable): + jsonhttp.UnprocessableEntity(w, "batch not usable yet or does not exist") + case errors.Is(err, postage.ErrNotFound): + jsonhttp.NotFound(w, "batch with id not found") + case errors.Is(err, errInvalidPostageBatch): + jsonhttp.BadRequest(w, "invalid batch id") + case errors.Is(err, errUnsupportedDevNodeOperation): + jsonhttp.BadRequest(w, errUnsupportedDevNodeOperation) + default: + jsonhttp.BadRequest(w, nil) + } + return + } + + granteeref := paths.GranteesAddress + publisher := &s.publicKey + ls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, false, redundancy.NONE)) + gls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, granteeListEncrypt, redundancy.NONE)) + granteeref, encryptedglref, historyref, actref, err := s.dac.HandleGrantees(ctx, ls, gls, granteeref, historyAddress, publisher, grantees.Addlist, grantees.Revokelist) + if err != nil { + logger.Debug("failed to update grantee list", "error", err) + logger.Error(nil, "failed to update grantee list") + jsonhttp.InternalServerError(w, "failed to update grantee list") + return + } + + err = putter.Done(actref) + if err != nil { + logger.Debug("done split act failed", "error", err) + logger.Error(nil, "done split act failed") + jsonhttp.InternalServerError(w, "done split act failed") + return + } + + err = putter.Done(historyref) + if err != nil { + logger.Debug("done split history failed", "error", err) + logger.Error(nil, "done split history failed") + jsonhttp.InternalServerError(w, "done split history failed") + return + } + + err = putter.Done(granteeref) + if err != nil { + logger.Debug("done split grantees failed", "error", err) + logger.Error(nil, "done split grantees failed") + jsonhttp.InternalServerError(w, "done split grantees failed") + return + } + + jsonhttp.OK(w, GranteesPatchResponse{ + Reference: encryptedglref, + HistoryReference: historyref, + }) +} + +// TODO: actCreateGranteesHandler doc. +func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Request) { + logger := s.logger.WithName("acthandler").Build() + + if r.Body == http.NoBody { + logger.Error(nil, "request has no body") + jsonhttp.BadRequest(w, errInvalidRequest) + return + } + + headers := struct { + BatchID []byte `map:"Swarm-Postage-Batch-Id" validate:"required"` + SwarmTag uint64 `map:"Swarm-Tag"` + Pin bool `map:"Swarm-Pin"` + Deferred *bool `map:"Swarm-Deferred-Upload"` + HistoryAddress *swarm.Address `map:"Swarm-Act-History-Address"` + }{} + if response := s.mapStructure(r.Header, &headers); response != nil { + response("invalid header params", logger, w) + return + } + + historyAddress := swarm.ZeroAddress + if headers.HistoryAddress != nil { + historyAddress = *headers.HistoryAddress + } + + var ( + tag uint64 + err error + deferred = defaultUploadMethod(headers.Deferred) + ) + + if deferred || headers.Pin { + tag, err = s.getOrCreateSessionID(headers.SwarmTag) + if err != nil { + logger.Debug("get or create tag failed", "error", err) + logger.Error(nil, "get or create tag failed") + switch { + case errors.Is(err, storage.ErrNotFound): + jsonhttp.NotFound(w, "tag not found") + default: + jsonhttp.InternalServerError(w, "cannot get or create tag") + } + return + } + } + + body, err := io.ReadAll(r.Body) + if err != nil { + if jsonhttp.HandleBodyReadError(err, w) { + return + } + logger.Debug("read request body failed", "error", err) + logger.Error(nil, "read request body failed") + jsonhttp.InternalServerError(w, "cannot read request") + return + } + + gpr := GranteesPostRequest{} + if len(body) > 0 { + err = json.Unmarshal(body, &gpr) + if err != nil { + logger.Debug("unmarshal body failed", "error", err) + logger.Error(nil, "unmarshal body failed") + jsonhttp.InternalServerError(w, "error unmarshaling request body") + return + } + } + + list, err := parseKeys(gpr.GranteeList) + if err != nil { + logger.Debug("create list key parse failed", "error", err) + logger.Error(nil, "create list key parse failed") + jsonhttp.InternalServerError(w, "error create list key parsing") + return + } + + ctx := r.Context() + putter, err := s.newStamperPutter(ctx, putterOptions{ + BatchID: headers.BatchID, + TagID: tag, + Pin: headers.Pin, + Deferred: deferred, + }) + if err != nil { + logger.Debug("putter failed", "error", err) + logger.Error(nil, "putter failed") + switch { + case errors.Is(err, errBatchUnusable) || errors.Is(err, postage.ErrNotUsable): + jsonhttp.UnprocessableEntity(w, "batch not usable yet or does not exist") + case errors.Is(err, postage.ErrNotFound): + jsonhttp.NotFound(w, "batch with id not found") + case errors.Is(err, errInvalidPostageBatch): + jsonhttp.BadRequest(w, "invalid batch id") + case errors.Is(err, errUnsupportedDevNodeOperation): + jsonhttp.BadRequest(w, errUnsupportedDevNodeOperation) + default: + jsonhttp.BadRequest(w, nil) + } + return + } + + publisher := &s.publicKey + ls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, false, redundancy.NONE)) + gls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, granteeListEncrypt, redundancy.NONE)) + granteeref, encryptedglref, historyref, actref, err := s.dac.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, historyAddress, publisher, list, nil) + if err != nil { + logger.Debug("failed to update grantee list", "error", err) + logger.Error(nil, "failed to update grantee list") + jsonhttp.InternalServerError(w, "failed to update grantee list") + return + } + + err = putter.Done(actref) + if err != nil { + logger.Debug("done split act failed", "error", err) + logger.Error(nil, "done split act failed") + jsonhttp.InternalServerError(w, "done split act failed") + return + } + + err = putter.Done(historyref) + if err != nil { + logger.Debug("done split history failed", "error", err) + logger.Error(nil, "done split history failed") + jsonhttp.InternalServerError(w, "done split history failed") + return + } + + err = putter.Done(granteeref) + if err != nil { + logger.Debug("done split grantees failed", "error", err) + logger.Error(nil, "done split grantees failed") + jsonhttp.InternalServerError(w, "done split grantees failed") + return + } + + jsonhttp.Created(w, GranteesPostResponse{ + Reference: encryptedglref, + HistoryReference: historyref, + }) +} + +func parseKeys(list []string) ([]*ecdsa.PublicKey, error) { + parsedList := make([]*ecdsa.PublicKey, 0, len(list)) + for _, g := range list { + h, err := hex.DecodeString(g) + if err != nil { + return []*ecdsa.PublicKey{}, err + } + k, err := btcec.ParsePubKey(h) + if err != nil { + return []*ecdsa.PublicKey{}, err + } + parsedList = append(parsedList, k.ToECDSA()) + } + + return parsedList, nil } diff --git a/pkg/api/dynamicaccess_test.go b/pkg/api/dynamicaccess_test.go index 480714444e0..a2d0cd6e4b2 100644 --- a/pkg/api/dynamicaccess_test.go +++ b/pkg/api/dynamicaccess_test.go @@ -62,6 +62,7 @@ func prepareHistoryFixture(storer api.Storer) (dynamicaccess.History, swarm.Addr return h, ref } +// TODO: test tag, pin, deferred, stamp // TODO: feed test // nolint:paralleltest,tparallel // TestDacWithoutActHeader [positive tests]: @@ -387,6 +388,7 @@ func TestDacHistory(t *testing.T) { fileName = "sample.html" now = time.Now().Unix() ) + fmt.Printf("bagoy now: %d\n", now) t.Run("empty-history-upload-then-download-and-check-data", func(t *testing.T) { client, _, _, _ := newTestServer(t, testServerOptions{ @@ -802,3 +804,138 @@ func TestDacPublisher(t *testing.T) { ) }) } + +func TestDacGrantees(t *testing.T) { + t.Parallel() + var ( + spk, _ = hex.DecodeString("a786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfa") + pk, _ = crypto.DecodeSecp256k1PrivateKey(spk) + storerMock = mockstorer.New() + h, fixtureHref = prepareHistoryFixture(storerMock) + logger = log.Noop + addr = swarm.RandAddress(t) + client, _, _, _ = newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(mockdac.WithHistory(h, fixtureHref.String())), + }) + ) + t.Run("get-grantees", func(t *testing.T) { + var ( + publicKeyBytes = crypto.EncodeSecp256k1PublicKey(&pk.PublicKey) + publisher = hex.EncodeToString(publicKeyBytes) + ) + clientwihtpublisher, _, _, _ := newTestServer(t, testServerOptions{ + Storer: storerMock, + Logger: logger, + Post: mockpost.New(mockpost.WithAcceptAll()), + PublicKey: pk.PublicKey, + Dac: mockdac.New(mockdac.WithHistory(h, fixtureHref.String()), mockdac.WithPublisher(publisher)), + }) + expected := []string{ + "03d7660772cc3142f8a7a2dfac46ce34d12eac1718720cef0e3d94347902aa96a2", + "03c712a7e29bc792ac8d8ae49793d28d5bda27ed70f0d90697b2fb456c0a168bd2", + "032541acf966823bae26c2c16a7102e728ade3e2e29c11a8a17b29d8eb2bd19302", + } + jsonhttptest.Request(t, clientwihtpublisher, http.MethodGet, "/grantee/"+addr.String(), http.StatusOK, + jsonhttptest.WithExpectedJSONResponse(expected), + ) + }) + + t.Run("get-grantees-unauthorized", func(t *testing.T) { + jsonhttptest.Request(t, client, http.MethodGet, "/grantee/fc4e9fe978991257b897d987bc4ff13058b66ef45a53189a0b4fe84bb3346396", http.StatusNotFound, + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Message: "granteelist not found", + Code: http.StatusNotFound, + }), + ) + }) + t.Run("get-grantees-invalid-address", func(t *testing.T) { + jsonhttptest.Request(t, client, http.MethodGet, "/grantee/asd", http.StatusBadRequest, + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "address", + Error: api.HexInvalidByteError('s').Error(), + }, + }}), + ) + }) + t.Run("add-revoke-grantees", func(t *testing.T) { + body := api.GranteesPatchRequest{ + Addlist: []string{"02ab7473879005929d10ce7d4f626412dad9fe56b0a6622038931d26bd79abf0a4"}, + Revokelist: []string{"02ab7473879005929d10ce7d4f626412dad9fe56b0a6622038931d26bd79abf0a4"}, + } + jsonhttptest.Request(t, client, http.MethodPatch, "/grantee/"+addr.String(), http.StatusOK, + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, addr.String()), + jsonhttptest.WithJSONRequestBody(body), + ) + }) + t.Run("add-revoke-grantees-empty-body", func(t *testing.T) { + jsonhttptest.Request(t, client, http.MethodPatch, "/grantee/"+addr.String(), http.StatusBadRequest, + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestBody(bytes.NewReader(nil)), + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Message: "could not validate request", + Code: http.StatusBadRequest, + }), + ) + }) + t.Run("add-grantee-with-history", func(t *testing.T) { + body := api.GranteesPatchRequest{ + Addlist: []string{"02ab7473879005929d10ce7d4f626412dad9fe56b0a6622038931d26bd79abf0a4"}, + } + jsonhttptest.Request(t, client, http.MethodPatch, "/grantee/"+addr.String(), http.StatusOK, + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestHeader(api.SwarmActHistoryAddressHeader, addr.String()), + jsonhttptest.WithJSONRequestBody(body), + ) + }) + t.Run("add-grantee-without-history", func(t *testing.T) { + body := api.GranteesPatchRequest{ + Addlist: []string{"02ab7473879005929d10ce7d4f626412dad9fe56b0a6622038931d26bd79abf0a4"}, + } + jsonhttptest.Request(t, client, http.MethodPatch, "/grantee/"+addr.String(), http.StatusBadRequest, + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithJSONRequestBody(body), + ) + + }) + t.Run("create-granteelist", func(t *testing.T) { + body := api.GranteesPostRequest{ + GranteeList: []string{ + "02ab7473879005929d10ce7d4f626412dad9fe56b0a6622038931d26bd79abf0a4", + "03d7660772cc3142f8a7a2dfac46ce34d12eac1718720cef0e3d94347902aa96a2", + }, + } + jsonhttptest.Request(t, client, http.MethodPost, "/grantee", http.StatusCreated, + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithJSONRequestBody(body), + ) + }) + t.Run("create-granteelist-without-stamp", func(t *testing.T) { + body := api.GranteesPostRequest{ + GranteeList: []string{ + "03d7660772cc3142f8a7a2dfac46ce34d12eac1718720cef0e3d94347902aa96a2", + }, + } + jsonhttptest.Request(t, client, http.MethodPost, "/grantee", http.StatusBadRequest, + jsonhttptest.WithJSONRequestBody(body), + ) + }) + t.Run("create-granteelist-empty-body", func(t *testing.T) { + jsonhttptest.Request(t, client, http.MethodPost, "/grantee", http.StatusBadRequest, + jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), + jsonhttptest.WithRequestBody(bytes.NewReader(nil)), + jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ + Message: "could not validate request", + Code: http.StatusBadRequest, + }), + ) + }) +} diff --git a/pkg/api/feed.go b/pkg/api/feed.go index a2679547478..3d43d3d148e 100644 --- a/pkg/api/feed.go +++ b/pkg/api/feed.go @@ -249,7 +249,7 @@ func (s *Service) feedPostHandler(w http.ResponseWriter, r *http.Request) { // TODO: do we want to allow feed act upload/ download? encryptedReference := ref if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, ref, headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, ref, headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/api/router.go b/pkg/api/router.go index e8d963a4d6f..19f4b4fbd04 100644 --- a/pkg/api/router.go +++ b/pkg/api/router.go @@ -265,6 +265,21 @@ func (s *Service) mountAPI() { ), }) + handle("/grantee", jsonhttp.MethodHandler{ + "POST": web.ChainHandlers( + web.FinalHandlerFunc(s.actCreateGranteesHandler), + ), + }) + + handle("/grantee/{address}", jsonhttp.MethodHandler{ + "GET": web.ChainHandlers( + web.FinalHandlerFunc(s.actListGranteesHandler), + ), + "PATCH": web.ChainHandlers( + web.FinalHandlerFunc(s.actGrantRevokeHandler), + ), + }) + handle("/bzz/{address}", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { u := r.URL u.Path += "/" diff --git a/pkg/api/soc.go b/pkg/api/soc.go index c2f11d6e05c..29777066b10 100644 --- a/pkg/api/soc.go +++ b/pkg/api/soc.go @@ -159,7 +159,7 @@ func (s *Service) socUploadHandler(w http.ResponseWriter, r *http.Request) { encryptedReference := sch.Address() if headers.Act { - encryptedReference, err = s.actEncryptionHandler(r.Context(), logger, w, putter, sch.Address(), headers.HistoryAddress) + encryptedReference, err = s.actEncryptionHandler(r.Context(), w, putter, sch.Address(), headers.HistoryAddress) if err != nil { jsonhttp.InternalServerError(w, errActUpload) return diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index b3b808e2b74..33b8ba819ba 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -11,6 +11,8 @@ import ( ) var hashFunc = sha3.NewLegacyKeccak256 +var oneByteArray = []byte{1} +var zeroByteArray = []byte{0} // Read-only interface for the ACT type Decryptor interface { @@ -50,15 +52,20 @@ func (al ActLogic) EncryptRef(ctx context.Context, storage kvs.KeyValueStore, pu return swarm.ZeroAddress, err } refCipher := encryption.New(accessKey, 0, uint32(0), hashFunc) - encryptedRef, _ := refCipher.Encrypt(ref.Bytes()) + encryptedRef, err := refCipher.Encrypt(ref.Bytes()) + if err != nil { + return swarm.ZeroAddress, err + } return swarm.NewAddress(encryptedRef), nil } // Adds a new grantee to the ACT func (al ActLogic) AddGrantee(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKeyPointer *encryption.Key) error { - var accessKey encryption.Key - var err error // Declare the "err" variable + var ( + accessKey encryption.Key + err error + ) if accessKeyPointer == nil { // Get previously generated access key @@ -109,9 +116,6 @@ func (al *ActLogic) getAccessKey(ctx context.Context, storage kvs.KeyValueStore, return accessKeyDecryptionCipher.Decrypt(encryptedAK) } -var oneByteArray = []byte{1} -var zeroByteArray = []byte{0} - // Generate lookup key and access key decryption key for a given public key func (al *ActLogic) getKeys(publicKey *ecdsa.PublicKey) ([][]byte, error) { return al.Session.Key(publicKey, [][]byte{zeroByteArray, oneByteArray}) diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index fdef1c4971a..a2c9ad50d04 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -3,67 +3,50 @@ package dynamicaccess import ( "context" "crypto/ecdsa" + "io" "time" - "github.com/ethersphere/bee/v2/pkg/file/loadsave" + encryption "github.com/ethersphere/bee/v2/pkg/encryption" + "github.com/ethersphere/bee/v2/pkg/file" "github.com/ethersphere/bee/v2/pkg/file/pipeline" "github.com/ethersphere/bee/v2/pkg/file/pipeline/builder" "github.com/ethersphere/bee/v2/pkg/file/redundancy" "github.com/ethersphere/bee/v2/pkg/kvs" - kvsmock "github.com/ethersphere/bee/v2/pkg/kvs/mock" "github.com/ethersphere/bee/v2/pkg/storage" "github.com/ethersphere/bee/v2/pkg/swarm" ) type GranteeManager interface { - //PUT /grantees/{grantee} - //body: {publisher?, grantee root hash ,grantee} - Grant(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error - //DELETE /grantees/{grantee} - //body: {publisher?, grantee root hash , grantee} - Revoke(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error - //[ ] - //POST /grantees - //body: {publisher, historyRootHash} - Commit(ctx context.Context, granteesAddress swarm.Address, actRootHash swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, swarm.Address, error) - - //Post /grantees - //{publisher, addList, removeList} - HandleGrantees(ctx context.Context, rootHash swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) error - - //GET /grantees/{history root hash} - GetGrantees(ctx context.Context, rootHash swarm.Address) ([]*ecdsa.PublicKey, error) + // TODO: doc + HandleGrantees(ctx context.Context, ls file.LoadSaver, gls file.LoadSaver, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) + // GetGrantees returns the list of grantees for the given publisher. + // The list is accessible only by the publisher. + GetGrantees(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) } -// TODO: add granteeList ref to history metadata to solve inconsistency type Controller interface { GranteeManager // DownloadHandler decrypts the encryptedRef using the lookupkey based on the history and timestamp. - DownloadHandler(ctx context.Context, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) - // TODO: history encryption + DownloadHandler(ctx context.Context, ls file.LoadSaver, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) // UploadHandler encrypts the reference and stores it in the history as the latest update. - UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) + UploadHandler(ctx context.Context, ls file.LoadSaver, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) + io.Closer } type controller struct { accessLogic ActLogic - granteeList GranteeList - //[ ]: do we need to protect this with a mutex? - revokeFlag []swarm.Address - getter storage.Getter - putter storage.Putter } var _ Controller = (*controller)(nil) func (c *controller) DownloadHandler( ctx context.Context, + ls file.LoadSaver, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64, ) (swarm.Address, error) { - ls := loadsave.New(c.getter, c.putter, requestPipelineFactory(ctx, c.putter, false, redundancy.NONE)) history, err := NewHistoryReference(ls, historyRootHash) if err != nil { return swarm.ZeroAddress, err @@ -72,26 +55,25 @@ func (c *controller) DownloadHandler( if err != nil { return swarm.ZeroAddress, err } - // TODO: hanlde granteelist ref in mtdt - kvs, err := kvs.NewReference(ls, entry.Reference()) + act, err := kvs.NewReference(ls, entry.Reference()) if err != nil { return swarm.ZeroAddress, err } - return c.accessLogic.DecryptRef(ctx, kvs, encryptedRef, publisher) + return c.accessLogic.DecryptRef(ctx, act, encryptedRef, publisher) } func (c *controller) UploadHandler( ctx context.Context, + ls file.LoadSaver, refrefence swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, ) (swarm.Address, swarm.Address, swarm.Address, error) { - ls := loadsave.New(c.getter, c.putter, requestPipelineFactory(ctx, c.putter, false, redundancy.NONE)) historyRef := historyRootHash var ( - storage kvs.KeyValueStore - storageRef swarm.Address + storage kvs.KeyValueStore + actRef swarm.Address ) now := time.Now().Unix() if historyRef.IsZero() { @@ -107,12 +89,11 @@ func (c *controller) UploadHandler( if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - storageRef, err = storage.Save(ctx) + actRef, err = storage.Save(ctx) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - // TODO: pass granteelist ref as mtdt - err = history.Add(ctx, storageRef, &now, nil) + err = history.Add(ctx, actRef, &now, nil) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } @@ -125,103 +106,199 @@ func (c *controller) UploadHandler( if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - // TODO: hanlde granteelist ref in mtdt entry, err := history.Lookup(ctx, now) - storageRef = entry.Reference() + actRef = entry.Reference() if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - storage, err = kvs.NewReference(ls, storageRef) + storage, err = kvs.NewReference(ls, actRef) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } } encryptedRef, err := c.accessLogic.EncryptRef(ctx, storage, publisher, refrefence) - return storageRef, historyRef, encryptedRef, err + return actRef, historyRef, encryptedRef, err } -func NewController(ctx context.Context, accessLogic ActLogic, getter storage.Getter, putter storage.Putter) Controller { +func NewController(accessLogic ActLogic) Controller { return &controller{ - granteeList: nil, accessLogic: accessLogic, - getter: getter, - putter: putter, } } -func (c *controller) Grant(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error { - return c.granteeList.Add([]*ecdsa.PublicKey{grantee}) -} - -func (c *controller) Revoke(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error { - if !c.isRevokeFlagged(granteesAddress) { - c.setRevokeFlag(granteesAddress, true) +func (c *controller) HandleGrantees( + ctx context.Context, + ls file.LoadSaver, + gls file.LoadSaver, + encryptedglref swarm.Address, + historyref swarm.Address, + publisher *ecdsa.PublicKey, + addList []*ecdsa.PublicKey, + removeList []*ecdsa.PublicKey, +) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) { + var ( + err error + h History + act kvs.KeyValueStore + granteeref swarm.Address + ) + if !historyref.IsZero() { + h, err = NewHistoryReference(ls, historyref) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + entry, err := h.Lookup(ctx, time.Now().Unix()) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + actref := entry.Reference() + act, err = kvs.NewReference(ls, actref) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + } else { + h, err = NewHistory(ls) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + // generate new access key and new act + act, err = kvs.New(ls) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + err = c.accessLogic.AddPublisher(ctx, act, publisher) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } } - return c.granteeList.Remove([]*ecdsa.PublicKey{grantee}) -} -func (c *controller) Commit(ctx context.Context, granteesAddress swarm.Address, actRootHash swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, swarm.Address, error) { - var act kvs.KeyValueStore - if c.isRevokeFlagged(granteesAddress) { - act = kvsmock.New() - c.accessLogic.AddPublisher(ctx, act, publisher) + var gl GranteeList + if encryptedglref.IsZero() { + gl, err = NewGranteeList(gls) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } } else { - act = kvsmock.NewReference(actRootHash) + granteeref, err = c.decryptRefForPublisher(publisher, encryptedglref) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + + gl, err = NewGranteeListReference(gls, granteeref) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + } + if len(addList) != 0 { + err = gl.Add(addList) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + } + if len(removeList) != 0 { + err = gl.Remove(removeList) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } } - grantees := c.granteeList.Get() - for _, grantee := range grantees { - c.accessLogic.AddGrantee(ctx, act, publisher, grantee, nil) + var granteesToAdd []*ecdsa.PublicKey + if len(removeList) != 0 || encryptedglref.IsZero() { + // generate new access key and new act + act, err = kvs.New(ls) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + err = c.accessLogic.AddPublisher(ctx, act, publisher) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + granteesToAdd = gl.Get() + } else { + granteesToAdd = addList } - granteeref, err := c.granteeList.Save(ctx) - if err != nil { - return swarm.EmptyAddress, swarm.EmptyAddress, err + for _, grantee := range granteesToAdd { + err := c.accessLogic.AddGrantee(ctx, act, publisher, grantee, nil) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } } actref, err := act.Save(ctx) if err != nil { - return swarm.EmptyAddress, swarm.EmptyAddress, err + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - c.setRevokeFlag(granteesAddress, false) - return granteeref, actref, err -} + glref, err := gl.Save(ctx) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } -func (c *controller) HandleGrantees(ctx context.Context, granteesAddress swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) error { - act := kvsmock.New() + eglref, err := c.encryptRefForPublisher(publisher, glref) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + // need to re-initialize history, because Lookup loads the forks causing the manifest save to skip the root node + if !historyref.IsZero() { + h, err = NewHistoryReference(ls, historyref) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + } - c.accessLogic.AddPublisher(ctx, act, publisher) - for _, grantee := range addList { - c.accessLogic.AddGrantee(ctx, act, publisher, grantee, nil) + mtdt := map[string]string{"encryptedglref": eglref.String()} + err = h.Add(ctx, actref, nil, &mtdt) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - return nil + href, err := h.Store(ctx) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + + return glref, eglref, href, actref, nil } -func (c *controller) GetGrantees(ctx context.Context, granteeRootHash swarm.Address) ([]*ecdsa.PublicKey, error) { - return c.granteeList.Get(), nil +func (c *controller) GetGrantees(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { + granteeRef, err := c.decryptRefForPublisher(publisher, encryptedglref) + if err != nil { + return nil, err + } + gl, err := NewGranteeListReference(ls, granteeRef) + if err != nil { + return nil, err + } + return gl.Get(), nil } -func (c *controller) isRevokeFlagged(granteeRootHash swarm.Address) bool { - for _, revoke := range c.revokeFlag { - if revoke.Equal(granteeRootHash) { - return true - } +func (c *controller) encryptRefForPublisher(publisherPubKey *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) { + keys, err := c.accessLogic.Session.Key(publisherPubKey, [][]byte{oneByteArray}) + if err != nil { + return swarm.ZeroAddress, err + } + refCipher := encryption.New(keys[0], 0, uint32(0), hashFunc) + encryptedRef, err := refCipher.Encrypt(ref.Bytes()) + if err != nil { + return swarm.ZeroAddress, err } - return false + + return swarm.NewAddress(encryptedRef), nil } -func (c *controller) setRevokeFlag(granteeRootHash swarm.Address, set bool) { - if set { - c.revokeFlag = append(c.revokeFlag, granteeRootHash) - } else { - for i, revoke := range c.revokeFlag { - if revoke.Equal(granteeRootHash) { - c.revokeFlag = append(c.revokeFlag[:i], c.revokeFlag[i+1:]...) - } - } +func (c *controller) decryptRefForPublisher(publisherPubKey *ecdsa.PublicKey, encryptedRef swarm.Address) (swarm.Address, error) { + keys, err := c.accessLogic.Session.Key(publisherPubKey, [][]byte{oneByteArray}) + if err != nil { + return swarm.ZeroAddress, err + } + refCipher := encryption.New(keys[0], 0, uint32(0), hashFunc) + ref, err := refCipher.Decrypt(encryptedRef.Bytes()) + if err != nil { + return swarm.ZeroAddress, err } + + return swarm.NewAddress(ref), nil } func requestPipelineFactory(ctx context.Context, s storage.Putter, encrypt bool, rLevel redundancy.Level) func() pipeline.Interface { @@ -229,3 +306,8 @@ func requestPipelineFactory(ctx context.Context, s storage.Putter, encrypt bool, return builder.NewPipelineBuilder(ctx, s, encrypt, rLevel) } } + +// TODO: what to do in close ? +func (s *controller) Close() error { + return nil +} diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index a48d426466e..2b3065899a1 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -3,25 +3,25 @@ package dynamicaccess_test import ( "context" "crypto/ecdsa" - "encoding/hex" + "reflect" "testing" "time" "github.com/ethersphere/bee/v2/pkg/dynamicaccess" - "github.com/ethersphere/bee/v2/pkg/encryption" + encryption "github.com/ethersphere/bee/v2/pkg/encryption" "github.com/ethersphere/bee/v2/pkg/file" + "github.com/ethersphere/bee/v2/pkg/file/loadsave" + "github.com/ethersphere/bee/v2/pkg/file/redundancy" "github.com/ethersphere/bee/v2/pkg/kvs" "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/stretchr/testify/assert" "golang.org/x/crypto/sha3" ) -var hashFunc = sha3.NewLegacyKeccak256 - func getHistoryFixture(ctx context.Context, ls file.LoadSaver, al dynamicaccess.ActLogic, publisher *ecdsa.PublicKey) (swarm.Address, error) { h, err := dynamicaccess.NewHistory(ls) if err != nil { - return swarm.ZeroAddress, nil + return swarm.ZeroAddress, err } pk1 := getPrivKey(1) pk2 := getPrivKey(2) @@ -30,12 +30,12 @@ func getHistoryFixture(ctx context.Context, ls file.LoadSaver, al dynamicaccess. al.AddPublisher(ctx, kvs0, publisher) kvs0Ref, _ := kvs0.Save(ctx) kvs1, _ := kvs.New(ls) - al.AddGrantee(ctx, kvs1, publisher, &pk1.PublicKey, nil) al.AddPublisher(ctx, kvs1, publisher) + al.AddGrantee(ctx, kvs1, publisher, &pk1.PublicKey, nil) kvs1Ref, _ := kvs1.Save(ctx) kvs2, _ := kvs.New(ls) - al.AddGrantee(ctx, kvs2, publisher, &pk2.PublicKey, nil) al.AddPublisher(ctx, kvs2, publisher) + al.AddGrantee(ctx, kvs2, publisher, &pk2.PublicKey, nil) kvs2Ref, _ := kvs2.Save(ctx) firstTime := time.Date(1994, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() secondTime := time.Date(2000, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() @@ -47,55 +47,206 @@ func getHistoryFixture(ctx context.Context, ls file.LoadSaver, al dynamicaccess. return h.Store(ctx) } -// TODO: separate up down test with fixture, now these just check if the flow works at all -func TestController_NewUploadDownload(t *testing.T) { +func TestController_UploadHandler(t *testing.T) { ctx := context.Background() - publisher := getPrivKey(1) + publisher := getPrivKey(0) + diffieHellman := dynamicaccess.NewDefaultSession(publisher) + al := dynamicaccess.NewLogic(diffieHellman) + c := dynamicaccess.NewController(al) + ls := createLs() + + t.Run("New upload", func(t *testing.T) { + ref := swarm.RandAddress(t) + _, hRef, encRef, err := c.UploadHandler(ctx, ls, ref, &publisher.PublicKey, swarm.ZeroAddress) + assert.NoError(t, err) + + h, _ := dynamicaccess.NewHistoryReference(ls, hRef) + entry, _ := h.Lookup(ctx, time.Now().Unix()) + actRef := entry.Reference() + act, _ := kvs.NewReference(ls, actRef) + expRef, err := al.EncryptRef(ctx, act, &publisher.PublicKey, ref) + + assert.NoError(t, err) + assert.Equal(t, encRef, expRef) + assert.NotEqual(t, hRef, swarm.ZeroAddress) + }) + + t.Run("Upload to same history", func(t *testing.T) { + ref := swarm.RandAddress(t) + _, hRef1, _, err := c.UploadHandler(ctx, ls, ref, &publisher.PublicKey, swarm.ZeroAddress) + assert.NoError(t, err) + _, hRef2, encRef, err := c.UploadHandler(ctx, ls, ref, &publisher.PublicKey, hRef1) + assert.NoError(t, err) + h, err := dynamicaccess.NewHistoryReference(ls, hRef2) + assert.NoError(t, err) + hRef2, err = h.Store(ctx) + assert.NoError(t, err) + assert.True(t, hRef1.Equal(hRef2)) + + h, _ = dynamicaccess.NewHistoryReference(ls, hRef2) + entry, _ := h.Lookup(ctx, time.Now().Unix()) + actRef := entry.Reference() + act, _ := kvs.NewReference(ls, actRef) + expRef, err := al.EncryptRef(ctx, act, &publisher.PublicKey, ref) + + assert.NoError(t, err) + assert.Equal(t, encRef, expRef) + assert.NotEqual(t, hRef2, swarm.ZeroAddress) + }) +} + +func TestController_PublisherDownload(t *testing.T) { + ctx := context.Background() + publisher := getPrivKey(0) diffieHellman := dynamicaccess.NewDefaultSession(publisher) al := dynamicaccess.NewLogic(diffieHellman) - c := dynamicaccess.NewController(ctx, al, mockStorer.ChunkStore(), mockStorer.Cache()) + c := dynamicaccess.NewController(al) + ls := createLs() ref := swarm.RandAddress(t) - _, hRef, encryptedRef, err := c.UploadHandler(ctx, ref, &publisher.PublicKey, swarm.ZeroAddress) + href, err := getHistoryFixture(ctx, ls, al, &publisher.PublicKey) + h, err := dynamicaccess.NewHistoryReference(ls, href) + entry, err := h.Lookup(ctx, time.Now().Unix()) + actRef := entry.Reference() + act, err := kvs.NewReference(ls, actRef) + encRef, err := al.EncryptRef(ctx, act, &publisher.PublicKey, ref) + assert.NoError(t, err) - dref, err := c.DownloadHandler(ctx, encryptedRef, &publisher.PublicKey, hRef, time.Now().Unix()) + dref, err := c.DownloadHandler(ctx, ls, encRef, &publisher.PublicKey, href, time.Now().Unix()) assert.NoError(t, err) assert.Equal(t, ref, dref) } -func TestController_ExistingUploadDownload(t *testing.T) { - ls := createLs() +func TestController_GranteeDownload(t *testing.T) { ctx := context.Background() publisher := getPrivKey(0) - diffieHellman := dynamicaccess.NewDefaultSession(publisher) + grantee := getPrivKey(2) + publisherDH := dynamicaccess.NewDefaultSession(publisher) + publisherAL := dynamicaccess.NewLogic(publisherDH) + + diffieHellman := dynamicaccess.NewDefaultSession(grantee) al := dynamicaccess.NewLogic(diffieHellman) - c := dynamicaccess.NewController(ctx, al, mockStorer.ChunkStore(), mockStorer.Cache()) + ls := createLs() + c := dynamicaccess.NewController(al) ref := swarm.RandAddress(t) - hRef, err := getHistoryFixture(ctx, ls, al, &publisher.PublicKey) - assert.NoError(t, err) - _, hRef, encryptedRef, err := c.UploadHandler(ctx, ref, &publisher.PublicKey, hRef) + href, err := getHistoryFixture(ctx, ls, publisherAL, &publisher.PublicKey) + h, err := dynamicaccess.NewHistoryReference(ls, href) + ts := time.Date(2001, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() + entry, err := h.Lookup(ctx, ts) + actRef := entry.Reference() + act, err := kvs.NewReference(ls, actRef) + encRef, err := publisherAL.EncryptRef(ctx, act, &publisher.PublicKey, ref) + assert.NoError(t, err) - dref, err := c.DownloadHandler(ctx, encryptedRef, &publisher.PublicKey, hRef, time.Now().Unix()) + dref, err := c.DownloadHandler(ctx, ls, encRef, &publisher.PublicKey, href, ts) assert.NoError(t, err) assert.Equal(t, ref, dref) } -func TestControllerGrant(t *testing.T) { -} +func TestController_HandleGrantees(t *testing.T) { + ctx := context.Background() + publisher := getPrivKey(1) + diffieHellman := dynamicaccess.NewDefaultSession(publisher) + al := dynamicaccess.NewLogic(diffieHellman) + keys, _ := al.Session.Key(&publisher.PublicKey, [][]byte{{1}}) + refCipher := encryption.New(keys[0], 0, uint32(0), sha3.NewLegacyKeccak256) + ls := createLs() + gls := loadsave.New(mockStorer.ChunkStore(), mockStorer.Cache(), requestPipelineFactory(context.Background(), mockStorer.Cache(), true, redundancy.NONE)) + c := dynamicaccess.NewController(al) + href, _ := getHistoryFixture(ctx, ls, al, &publisher.PublicKey) -func TestControllerRevoke(t *testing.T) { + grantee1 := getPrivKey(0) + grantee := getPrivKey(2) -} + t.Run("add to new list", func(t *testing.T) { + addList := []*ecdsa.PublicKey{&grantee.PublicKey} + granteeRef, _, _, _, err := c.HandleGrantees(ctx, ls, ls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + assert.NoError(t, err) + + gl, err := dynamicaccess.NewGranteeListReference(ls, granteeRef) + + assert.NoError(t, err) + assert.Len(t, gl.Get(), 1) + }) + t.Run("add to existing list", func(t *testing.T) { + addList := []*ecdsa.PublicKey{&grantee.PublicKey} + granteeRef, eglref, _, _, err := c.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + assert.NoError(t, err) + + gl, err := dynamicaccess.NewGranteeListReference(ls, granteeRef) + + assert.NoError(t, err) + assert.Len(t, gl.Get(), 1) -func TestControllerCommit(t *testing.T) { + addList = []*ecdsa.PublicKey{&getPrivKey(0).PublicKey} + granteeRef, _, _, _, err = c.HandleGrantees(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil) + gl, err = dynamicaccess.NewGranteeListReference(ls, granteeRef) + assert.NoError(t, err) + assert.Len(t, gl.Get(), 2) + }) + t.Run("add and revoke", func(t *testing.T) { + addList := []*ecdsa.PublicKey{&grantee.PublicKey} + revokeList := []*ecdsa.PublicKey{&grantee1.PublicKey} + gl, _ := dynamicaccess.NewGranteeList(ls) + gl.Add([]*ecdsa.PublicKey{&publisher.PublicKey, &grantee1.PublicKey}) + granteeRef, err := gl.Save(ctx) + eglref, _ := refCipher.Encrypt(granteeRef.Bytes()) + granteeRef, _, _, _, err = c.HandleGrantees(ctx, ls, gls, swarm.NewAddress(eglref), href, &publisher.PublicKey, addList, revokeList) + gl, err = dynamicaccess.NewGranteeListReference(ls, granteeRef) + + assert.NoError(t, err) + assert.Len(t, gl.Get(), 2) + }) + + t.Run("add twice", func(t *testing.T) { + addList := []*ecdsa.PublicKey{&grantee.PublicKey, &grantee.PublicKey} + granteeRef, eglref, _, _, err := c.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + granteeRef, _, _, _, err = c.HandleGrantees(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil) + gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + + assert.NoError(t, err) + assert.Len(t, gl.Get(), 1) + }) + t.Run("revoke non-existing", func(t *testing.T) { + addList := []*ecdsa.PublicKey{&grantee.PublicKey} + granteeRef, _, _, _, err := c.HandleGrantees(ctx, ls, ls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + + assert.NoError(t, err) + assert.Len(t, gl.Get(), 1) + }) } -func prepareEncryptedChunkReference(ak []byte) (swarm.Address, swarm.Address) { - addr, _ := hex.DecodeString("f7b1a45b70ee91d3dbfd98a2a692387f24db7279a9c96c447409e9205cf265baef29bf6aa294264762e33f6a18318562c86383dd8bfea2cec14fae08a8039bf3") - e1 := encryption.New(ak, 0, uint32(0), hashFunc) - ech, err := e1.Encrypt(addr) - if err != nil { - return swarm.EmptyAddress, swarm.EmptyAddress - } - return swarm.NewAddress(ech), swarm.NewAddress(addr) +func TestController_GetGrantees(t *testing.T) { + ctx := context.Background() + publisher := getPrivKey(1) + caller := getPrivKey(0) + grantee := getPrivKey(2) + diffieHellman1 := dynamicaccess.NewDefaultSession(publisher) + diffieHellman2 := dynamicaccess.NewDefaultSession(caller) + al1 := dynamicaccess.NewLogic(diffieHellman1) + al2 := dynamicaccess.NewLogic(diffieHellman2) + ls := createLs() + gls := loadsave.New(mockStorer.ChunkStore(), mockStorer.Cache(), requestPipelineFactory(context.Background(), mockStorer.Cache(), true, redundancy.NONE)) + c1 := dynamicaccess.NewController(al1) + c2 := dynamicaccess.NewController(al2) + + t.Run("get by publisher", func(t *testing.T) { + addList := []*ecdsa.PublicKey{&grantee.PublicKey} + granteeRef, eglRef, _, _, err := c1.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + + grantees, err := c1.GetGrantees(ctx, ls, &publisher.PublicKey, eglRef) + assert.NoError(t, err) + assert.True(t, reflect.DeepEqual(grantees, addList)) + + gl, _ := dynamicaccess.NewGranteeListReference(ls, granteeRef) + assert.True(t, reflect.DeepEqual(gl.Get(), addList)) + }) + t.Run("get by non-publisher", func(t *testing.T) { + addList := []*ecdsa.PublicKey{&grantee.PublicKey} + _, eglRef, _, _, err := c1.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + grantees, err := c2.GetGrantees(ctx, ls, &publisher.PublicKey, eglRef) + assert.Error(t, err) + assert.Nil(t, grantees) + }) } diff --git a/pkg/dynamicaccess/grantee.go b/pkg/dynamicaccess/grantee.go index 6724b718e4f..02cd8099021 100644 --- a/pkg/dynamicaccess/grantee.go +++ b/pkg/dynamicaccess/grantee.go @@ -6,6 +6,7 @@ import ( "crypto/elliptic" "fmt" + "github.com/btcsuite/btcd/btcec/v2" "github.com/ethersphere/bee/v2/pkg/file" "github.com/ethersphere/bee/v2/pkg/swarm" ) @@ -14,70 +15,60 @@ const ( publicKeyLen = 65 ) +// GranteeList manages a list of public keys. type GranteeList interface { + // Add adds a list of public keys to the grantee list. It filters out duplicates. Add(addList []*ecdsa.PublicKey) error + // Remove removes a list of public keys from the grantee list, if there is any. Remove(removeList []*ecdsa.PublicKey) error + // Get simply returns the list of public keys. Get() []*ecdsa.PublicKey + // Save saves the grantee list to the underlying storage and returns the reference. Save(ctx context.Context) (swarm.Address, error) } type GranteeListStruct struct { - grantees []byte + grantees []*ecdsa.PublicKey loadSave file.LoadSaver } var _ GranteeList = (*GranteeListStruct)(nil) func (g *GranteeListStruct) Get() []*ecdsa.PublicKey { - return g.deserialize(g.grantees) -} - -func (g *GranteeListStruct) serialize(publicKeys []*ecdsa.PublicKey) []byte { - b := make([]byte, 0, len(publicKeys)*publicKeyLen) - for _, key := range publicKeys { - b = append(b, g.serializePublicKey(key)...) - } - return b -} - -func (g *GranteeListStruct) serializePublicKey(pub *ecdsa.PublicKey) []byte { - return elliptic.Marshal(pub.Curve, pub.X, pub.Y) -} - -func (g *GranteeListStruct) deserialize(data []byte) []*ecdsa.PublicKey { - if len(data) == 0 { - return nil - } - - p := make([]*ecdsa.PublicKey, 0, len(data)/publicKeyLen) - for i := 0; i < len(data); i += publicKeyLen { - pubKey := g.deserializeBytes(data[i : i+publicKeyLen]) - if pubKey == nil { - return nil - } - p = append(p, pubKey) - } - return p -} - -func (g *GranteeListStruct) deserializeBytes(data []byte) *ecdsa.PublicKey { - curve := elliptic.P256() - x, y := elliptic.Unmarshal(curve, data) - return &ecdsa.PublicKey{Curve: curve, X: x, Y: y} + return g.grantees } func (g *GranteeListStruct) Add(addList []*ecdsa.PublicKey) error { if len(addList) == 0 { return fmt.Errorf("no public key provided") } + filteredList := make([]*ecdsa.PublicKey, 0, len(addList)) + for _, addkey := range addList { + add := true + for _, granteekey := range g.grantees { + if granteekey.Equal(addkey) { + add = false + break + } + } + for _, filteredkey := range filteredList { + if filteredkey.Equal(addkey) { + add = false + break + } + } + if add { + filteredList = append(filteredList, addkey) + } + } + g.grantees = append(g.grantees, filteredList...) - data := g.serialize(addList) - g.grantees = append(g.grantees, data...) return nil } func (g *GranteeListStruct) Save(ctx context.Context) (swarm.Address, error) { - refBytes, err := g.loadSave.Save(ctx, g.grantees) + data := serialize(g.grantees) + refBytes, err := g.loadSave.Save(ctx, data) if err != nil { return swarm.ZeroAddress, fmt.Errorf("grantee save error: %w", err) } @@ -89,38 +80,77 @@ func (g *GranteeListStruct) Remove(keysToRemove []*ecdsa.PublicKey) error { if len(keysToRemove) == 0 { return fmt.Errorf("nothing to remove") } - grantees := g.deserialize(g.grantees) - if grantees == nil { + + if len(g.grantees) == 0 { return fmt.Errorf("no grantee found") } + grantees := g.grantees for _, remove := range keysToRemove { - for i, grantee := range grantees { - if grantee.Equal(remove) { + for i := 0; i < len(grantees); i++ { + if grantees[i].Equal(remove) { grantees[i] = grantees[len(grantees)-1] grantees = grantees[:len(grantees)-1] } } } - g.grantees = g.serialize(grantees) + g.grantees = grantees + return nil } -func NewGranteeList(ls file.LoadSaver) GranteeList { +func NewGranteeList(ls file.LoadSaver) (GranteeList, error) { return &GranteeListStruct{ - grantees: []byte{}, + grantees: []*ecdsa.PublicKey{}, loadSave: ls, - } + }, nil } -func NewGranteeListReference(ls file.LoadSaver, reference swarm.Address) GranteeList { +func NewGranteeListReference(ls file.LoadSaver, reference swarm.Address) (GranteeList, error) { data, err := ls.Load(context.Background(), reference.Bytes()) if err != nil { - return nil + return nil, err } + grantees := deserialize(data) return &GranteeListStruct{ - grantees: data, + grantees: grantees, loadSave: ls, + }, nil +} + +func serialize(publicKeys []*ecdsa.PublicKey) []byte { + b := make([]byte, 0, len(publicKeys)*publicKeyLen) + for _, key := range publicKeys { + b = append(b, serializePublicKey(key)...) + } + return b +} + +func serializePublicKey(pub *ecdsa.PublicKey) []byte { + return elliptic.Marshal(pub.Curve, pub.X, pub.Y) +} + +func deserialize(data []byte) []*ecdsa.PublicKey { + if len(data) == 0 { + return []*ecdsa.PublicKey{} + } + + p := make([]*ecdsa.PublicKey, 0, len(data)/publicKeyLen) + for i := 0; i < len(data); i += publicKeyLen { + pubKey := deserializeBytes(data[i : i+publicKeyLen]) + if pubKey == nil { + return []*ecdsa.PublicKey{} + } + p = append(p, pubKey) + } + return p +} + +func deserializeBytes(data []byte) *ecdsa.PublicKey { + key, err := btcec.ParsePubKey(data) + if err != nil { + return nil } + return key.ToECDSA() } diff --git a/pkg/dynamicaccess/grantee_test.go b/pkg/dynamicaccess/grantee_test.go index 0578be28ff4..c4ce58ac8aa 100644 --- a/pkg/dynamicaccess/grantee_test.go +++ b/pkg/dynamicaccess/grantee_test.go @@ -3,10 +3,10 @@ package dynamicaccess_test import ( "context" "crypto/ecdsa" - "crypto/elliptic" "crypto/rand" "testing" + "github.com/btcsuite/btcd/btcec/v2" "github.com/ethersphere/bee/v2/pkg/dynamicaccess" "github.com/ethersphere/bee/v2/pkg/file" "github.com/ethersphere/bee/v2/pkg/file/loadsave" @@ -15,6 +15,7 @@ import ( "github.com/ethersphere/bee/v2/pkg/file/redundancy" "github.com/ethersphere/bee/v2/pkg/storage" mockstorer "github.com/ethersphere/bee/v2/pkg/storer/mock" + "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/stretchr/testify/assert" ) @@ -31,9 +32,9 @@ func createLs() file.LoadSaver { } func generateKeyListFixture() ([]*ecdsa.PublicKey, error) { - key1, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - key2, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - key3, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + key1, err := ecdsa.GenerateKey(btcec.S256(), rand.Reader) + key2, err := ecdsa.GenerateKey(btcec.S256(), rand.Reader) + key3, err := ecdsa.GenerateKey(btcec.S256(), rand.Reader) if err != nil { return nil, err } @@ -41,7 +42,7 @@ func generateKeyListFixture() ([]*ecdsa.PublicKey, error) { } func TestGranteeAddGet(t *testing.T) { - gl := dynamicaccess.NewGranteeList(createLs()) + gl, _ := dynamicaccess.NewGranteeList(createLs()) keys, err := generateKeyListFixture() if err != nil { t.Errorf("key generation error: %v", err) @@ -49,14 +50,15 @@ func TestGranteeAddGet(t *testing.T) { t.Run("Get empty grantee list should return error", func(t *testing.T) { val := gl.Get() - assert.Nil(t, val) + assert.Empty(t, val) }) t.Run("Get should return value equal to put value", func(t *testing.T) { var ( + keys2, _ = generateKeyListFixture() addList1 []*ecdsa.PublicKey = []*ecdsa.PublicKey{keys[0]} - addList2 []*ecdsa.PublicKey = []*ecdsa.PublicKey{keys[1], keys[0]} - addList3 []*ecdsa.PublicKey = keys + addList2 []*ecdsa.PublicKey = []*ecdsa.PublicKey{keys[1], keys[2]} + addList3 []*ecdsa.PublicKey = keys2 ) testCases := []struct { name string @@ -66,6 +68,10 @@ func TestGranteeAddGet(t *testing.T) { name: "Test list = 1", list: addList1, }, + { + name: "Test list = duplicate1", + list: addList1, + }, { name: "Test list = 2", list: addList2, @@ -88,7 +94,9 @@ func TestGranteeAddGet(t *testing.T) { assert.Error(t, err) } else { assert.NoError(t, err) - expList = append(expList, tc.list...) + if tc.name != "Test list = duplicate1" { + expList = append(expList, tc.list...) + } retVal := gl.Get() assert.Equal(t, expList, retVal) } @@ -98,7 +106,7 @@ func TestGranteeAddGet(t *testing.T) { } func TestGranteeRemove(t *testing.T) { - gl := dynamicaccess.NewGranteeList(createLs()) + gl, _ := dynamicaccess.NewGranteeList(createLs()) keys, err := generateKeyListFixture() if err != nil { t.Errorf("key generation error: %v", err) @@ -128,19 +136,19 @@ func TestGranteeRemove(t *testing.T) { err := gl.Remove(removeList2) assert.NoError(t, err) retVal := gl.Get() - assert.Nil(t, retVal) + assert.Empty(t, retVal) }) t.Run("Remove from empty grantee list should return error", func(t *testing.T) { err := gl.Remove(removeList1) assert.Error(t, err) retVal := gl.Get() - assert.Nil(t, retVal) + assert.Empty(t, retVal) }) t.Run("Remove empty remove list should return error", func(t *testing.T) { err := gl.Remove(nil) assert.Error(t, err) retVal := gl.Get() - assert.Nil(t, retVal) + assert.Empty(t, retVal) }) } @@ -150,13 +158,18 @@ func TestGranteeSave(t *testing.T) { if err != nil { t.Errorf("key generation error: %v", err) } + t.Run("Create grantee list with invalid reference, expect error", func(t *testing.T) { + gl, err := dynamicaccess.NewGranteeListReference(createLs(), swarm.RandAddress(t)) + assert.Error(t, err) + assert.Nil(t, gl) + }) t.Run("Save empty grantee list return NO error", func(t *testing.T) { - gl := dynamicaccess.NewGranteeList(createLs()) + gl, _ := dynamicaccess.NewGranteeList(createLs()) _, err := gl.Save(ctx) assert.NoError(t, err) }) t.Run("Save not empty grantee list return valid swarm address", func(t *testing.T) { - gl := dynamicaccess.NewGranteeList(createLs()) + gl, _ := dynamicaccess.NewGranteeList(createLs()) err = gl.Add(keys) ref, err := gl.Save(ctx) assert.NoError(t, err) @@ -164,7 +177,7 @@ func TestGranteeSave(t *testing.T) { }) t.Run("Save grantee list with one item, no error, pre-save value exist", func(t *testing.T) { ls := createLs() - gl1 := dynamicaccess.NewGranteeList(ls) + gl1, _ := dynamicaccess.NewGranteeList(ls) err := gl1.Add(keys) assert.NoError(t, err) @@ -172,26 +185,39 @@ func TestGranteeSave(t *testing.T) { ref, err := gl1.Save(ctx) assert.NoError(t, err) - gl2 := dynamicaccess.NewGranteeListReference(ls, ref) + gl2, _ := dynamicaccess.NewGranteeListReference(ls, ref) val := gl2.Get() assert.NoError(t, err) assert.Equal(t, keys, val) }) t.Run("Save grantee list and add one item, no error, after-save value exist", func(t *testing.T) { ls := createLs() + keys2, _ := generateKeyListFixture() - gl1 := dynamicaccess.NewGranteeList(ls) + gl1, _ := dynamicaccess.NewGranteeList(ls) err := gl1.Add(keys) assert.NoError(t, err) ref, err := gl1.Save(ctx) assert.NoError(t, err) - gl2 := dynamicaccess.NewGranteeListReference(ls, ref) - err = gl2.Add(keys) + gl2, _ := dynamicaccess.NewGranteeListReference(ls, ref) + err = gl2.Add(keys2) assert.NoError(t, err) val := gl2.Get() - assert.Equal(t, append(keys, keys...), val) + assert.Equal(t, append(keys, keys2...), val) }) } + +func TestGranteeRemoveTwo(t *testing.T) { + gl, _ := dynamicaccess.NewGranteeList(createLs()) + keys, err := generateKeyListFixture() + if err != nil { + t.Errorf("key generation error: %v", err) + } + err = gl.Add([]*ecdsa.PublicKey{keys[0]}) + err = gl.Add([]*ecdsa.PublicKey{keys[0]}) + err = gl.Remove([]*ecdsa.PublicKey{keys[0]}) + assert.NoError(t, err) +} diff --git a/pkg/dynamicaccess/mock/service.go b/pkg/dynamicaccess/mock/controller.go similarity index 69% rename from pkg/dynamicaccess/mock/service.go rename to pkg/dynamicaccess/mock/controller.go index 6f4d5f09e72..09cacb65145 100644 --- a/pkg/dynamicaccess/mock/service.go +++ b/pkg/dynamicaccess/mock/controller.go @@ -44,7 +44,7 @@ type Option interface { func (f optionFunc) apply(r *mockDacService) { f(r) } // New creates a new mock dynamicaccess service. -func New(o ...Option) dynamicaccess.Service { +func New(o ...Option) dynamicaccess.Controller { storer := mockstorer.New() m := &mockDacService{ historyMap: make(map[string]dynamicaccess.History), @@ -78,7 +78,7 @@ func WithPublisher(ref string) Option { }) } -func (m *mockDacService) DownloadHandler(ctx context.Context, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) { +func (m *mockDacService) DownloadHandler(ctx context.Context, ls file.LoadSaver, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) { if m.acceptAll { return swarm.ParseHexAddress("36e6c1bbdfee6ac21485d5f970479fd1df458d36df9ef4e8179708ed46da557f") } @@ -101,7 +101,7 @@ func (m *mockDacService) DownloadHandler(ctx context.Context, encryptedRef swarm return m.refMap[encryptedRef.String()], nil } -func (m *mockDacService) UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) { +func (m *mockDacService) UploadHandler(ctx context.Context, ls file.LoadSaver, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) { historyRef, _ := swarm.ParseHexAddress("67bdf80a9bbea8eca9c8480e43fdceb485d2d74d5708e45144b8c4adacd13d9c") kvsRef, _ := swarm.ParseHexAddress("3339613565613837623134316665343461613630396333333237656364383934") if m.acceptAll { @@ -126,7 +126,6 @@ func (m *mockDacService) UploadHandler(ctx context.Context, reference swarm.Addr } } else { h, _ = dynamicaccess.NewHistory(m.ls) - // TODO: pass granteelist ref as mtdt h.Add(ctx, kvsRef, &now, nil) historyRef, _ = h.Store(ctx) m.historyMap[historyRef.String()] = h @@ -141,20 +140,38 @@ func (m *mockDacService) Close() error { return nil } -func (m *mockDacService) Grant(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error { - return nil -} -func (m *mockDacService) Revoke(ctx context.Context, granteesAddress swarm.Address, grantee *ecdsa.PublicKey) error { - return nil -} -func (m *mockDacService) Commit(ctx context.Context, granteesAddress swarm.Address, actRootHash swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, swarm.Address, error) { - return swarm.ZeroAddress, swarm.ZeroAddress, nil -} -func (m *mockDacService) HandleGrantees(ctx context.Context, rootHash swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) error { - return nil +func (m *mockDacService) HandleGrantees(ctx context.Context, ls file.LoadSaver, gls file.LoadSaver, encryptedglref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList []*ecdsa.PublicKey, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) { + historyRef, _ := swarm.ParseHexAddress("67bdf80a9bbea8eca9c8480e43fdceb485d2d74d5708e45144b8c4adacd13d9c") + glRef, _ := swarm.ParseHexAddress("3339613565613837623134316665343461613630396333333237656364383934") + eglRef, _ := swarm.ParseHexAddress("fc4e9fe978991257b897d987bc4ff13058b66ef45a53189a0b4fe84bb3346396") + actref, _ := swarm.ParseHexAddress("39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559") + return glRef, eglRef, historyRef, actref, nil } -func (m *mockDacService) GetGrantees(ctx context.Context, rootHash swarm.Address) ([]*ecdsa.PublicKey, error) { - return nil, nil + +func (m *mockDacService) GetGrantees(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { + if m.publisher == "" { + return nil, fmt.Errorf("granteelist not found") + } + keys := []string{ + "a786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfa", + "b786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfb", + "c786dd84b61485de12146fd9c4c02d87e8fd95f0542765cb7fc3d2e428c0bcfc", + } + pubkeys := make([]*ecdsa.PublicKey, 0, len(keys)) + for i := range keys { + data, err := hex.DecodeString(keys[i]) + if err != nil { + panic(err) + } + + privKey, err := crypto.DecodeSecp256k1PrivateKey(data) + pubKey := privKey.PublicKey + if err != nil { + panic(err) + } + pubkeys = append(pubkeys, &pubKey) + } + return pubkeys, nil } func requestPipelineFactory(ctx context.Context, s storage.Putter, encrypt bool, rLevel redundancy.Level) func() pipeline.Interface { diff --git a/pkg/dynamicaccess/service.go b/pkg/dynamicaccess/service.go deleted file mode 100644 index b9cedbcf82b..00000000000 --- a/pkg/dynamicaccess/service.go +++ /dev/null @@ -1,39 +0,0 @@ -package dynamicaccess - -import ( - "context" - "crypto/ecdsa" - "io" - - "github.com/ethersphere/bee/v2/pkg/swarm" -) - -type Service interface { - DownloadHandler(ctx context.Context, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) - UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) - io.Closer -} - -// TODO: is service needed at all? -> it is just a wrapper around controller -type service struct { - controller Controller -} - -func (s *service) DownloadHandler(ctx context.Context, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) { - return s.controller.DownloadHandler(ctx, encryptedRef, publisher, historyRootHash, timestamp) -} - -func (s *service) UploadHandler(ctx context.Context, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) { - return s.controller.UploadHandler(ctx, reference, publisher, historyRootHash) -} - -// TODO: what to do in close ? -func (s *service) Close() error { - return nil -} - -func NewService(controller Controller) (Service, error) { - return &service{ - controller: controller, - }, nil -} diff --git a/pkg/node/devnode.go b/pkg/node/devnode.go index 9e51cf562b9..dbee11a9e1a 100644 --- a/pkg/node/devnode.go +++ b/pkg/node/devnode.go @@ -192,11 +192,7 @@ func NewDevBee(logger log.Logger, o *DevOptions) (b *DevBee, err error) { session := dynamicaccess.NewDefaultSession(mockKey) actLogic := dynamicaccess.NewLogic(session) - ctrl := dynamicaccess.NewController(context.Background(), actLogic, localStore.ChunkStore(), localStore.Cache()) - dac, err := dynamicaccess.NewService(ctrl) - if err != nil { - return nil, fmt.Errorf("dac service: %w", err) - } + dac := dynamicaccess.NewController(actLogic) b.dacCloser = dac pssService := pss.New(mockKey, logger) diff --git a/pkg/node/node.go b/pkg/node/node.go index faaf308ecba..fb579c2dbba 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -710,11 +710,7 @@ func NewBee( evictFn = func(id []byte) error { return localStore.EvictBatch(context.Background(), id) } actLogic := dynamicaccess.NewLogic(session) - ctrl := dynamicaccess.NewController(ctx, actLogic, localStore.ChunkStore(), localStore.Cache()) - dac, err := dynamicaccess.NewService(ctrl) - if err != nil { - return nil, fmt.Errorf("dac service: %w", err) - } + dac := dynamicaccess.NewController(actLogic) b.dacCloser = dac var ( From 943b219fe0788d3d1b5ae5f3b1dcee3a15b15bd1 Mon Sep 17 00:00:00 2001 From: kopi-solarpunk <163832130+kopi-solarpunk@users.noreply.github.com> Date: Thu, 16 May 2024 10:03:28 +0200 Subject: [PATCH 34/41] Start refactoring for new linter rules (#39) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor: refactoring to match new linter rules according to https://github.com/Solar-Punk-Ltd/bee/pull/38 Not everything is fixed, just a reference what can and needs to be improved. * implement grantee management * Add POST endpoint + fixes * Save grantees as pubkey list and fix remove error; CHG: act-handler logger names * Refactor: pass getter, putter to controller functions * Refactor: error handling in dynamicaccess; Read cache header only for download handlers * CHG: grantees ref is encrypted and added to history ref + tests * Fix nil pointer dereference panic * CHG: put actref in handlegrantees; Add: pin, tag,deferred headers * CHG: pass loadsave to handlers; check if history address is nil * FIX: re-init history so that it can be saved; only add publisher if histroy is zero * make act timestamp optional * fix revoke grantees * Fix: Act timestamp header nil check; Uploadhandler UT * refactor: start refactoring for now linter rules * refactor: revert non ACT related files * CHG: accesslogic getkeys refactor * refactor: fix errcheck and ineffassign linter errors in most cases * refactor: add headers, and change error handling * refactor: add headers --------- Co-authored-by: Kexort Co-authored-by: Bálint Ujvári --- pkg/api/dynamicaccess.go | 12 +++-- pkg/api/dynamicaccess_test.go | 48 +++++++++----------- pkg/dynamicaccess/accesslogic.go | 65 +++++++++++++++------------ pkg/dynamicaccess/accesslogic_test.go | 52 ++++++++------------- pkg/dynamicaccess/controller.go | 26 ++++++----- pkg/dynamicaccess/controller_test.go | 40 ++++++++++------- pkg/dynamicaccess/grantee.go | 20 ++++++--- pkg/dynamicaccess/grantee_test.go | 14 +++++- pkg/dynamicaccess/history.go | 42 ++++++++++------- pkg/dynamicaccess/history_test.go | 18 +++++--- pkg/dynamicaccess/mock/accesslogic.go | 3 ++ pkg/dynamicaccess/mock/controller.go | 4 +- pkg/dynamicaccess/mock/grantee.go | 4 ++ pkg/dynamicaccess/mock/session.go | 5 ++- pkg/dynamicaccess/session.go | 28 ++++++++---- pkg/dynamicaccess/session_test.go | 6 ++- pkg/kvs/kvs_test.go | 4 +- pkg/kvs/mock/kvs.go | 7 ++- pkg/manifest/mantaray/node.go | 7 +-- 19 files changed, 231 insertions(+), 174 deletions(-) diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index bbf3abbc69e..32447f905e8 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -1,3 +1,7 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package api import ( @@ -6,6 +10,7 @@ import ( "encoding/hex" "encoding/json" "errors" + "fmt" "io" "net/http" "time" @@ -114,7 +119,6 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { h.ServeHTTP(w, r.WithContext(setAddressInContext(ctx, reference))) }) } - } // actEncryptionHandler is a middleware that encrypts the given address using the publisher's public key @@ -133,7 +137,7 @@ func (s *Service) actEncryptionHandler( if err != nil { logger.Debug("act failed to encrypt reference", "error", err) logger.Error(nil, "act failed to encrypt reference") - return swarm.ZeroAddress, err + return swarm.ZeroAddress, fmt.Errorf("act failed to encrypt reference: %w", err) } // only need to upload history and kvs if a new history is created, // meaning that the publsher uploaded to the history for the first time @@ -142,13 +146,13 @@ func (s *Service) actEncryptionHandler( if err != nil { logger.Debug("done split keyvaluestore failed", "error", err) logger.Error(nil, "done split keyvaluestore failed") - return swarm.ZeroAddress, err + return swarm.ZeroAddress, fmt.Errorf("done split keyvaluestore failed: %w", err) } err = putter.Done(historyReference) if err != nil { logger.Debug("done split history failed", "error", err) logger.Error(nil, "done split history failed") - return swarm.ZeroAddress, err + return swarm.ZeroAddress, fmt.Errorf("done split history failed: %w", err) } } diff --git a/pkg/api/dynamicaccess_test.go b/pkg/api/dynamicaccess_test.go index a2d0cd6e4b2..79eac36327b 100644 --- a/pkg/api/dynamicaccess_test.go +++ b/pkg/api/dynamicaccess_test.go @@ -40,23 +40,23 @@ func prepareHistoryFixture(storer api.Storer) (dynamicaccess.History, swarm.Addr testActRef1 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd891")) firstTime := time.Date(1994, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - h.Add(ctx, testActRef1, &firstTime, nil) + _ = h.Add(ctx, testActRef1, &firstTime, nil) testActRef2 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd892")) secondTime := time.Date(2000, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - h.Add(ctx, testActRef2, &secondTime, nil) + _ = h.Add(ctx, testActRef2, &secondTime, nil) testActRef3 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd893")) thirdTime := time.Date(2015, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - h.Add(ctx, testActRef3, &thirdTime, nil) + _ = h.Add(ctx, testActRef3, &thirdTime, nil) testActRef4 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd894")) fourthTime := time.Date(2020, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - h.Add(ctx, testActRef4, &fourthTime, nil) + _ = h.Add(ctx, testActRef4, &fourthTime, nil) testActRef5 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd895")) fifthTime := time.Date(2030, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - h.Add(ctx, testActRef5, &fifthTime, nil) + _ = h.Add(ctx, testActRef5, &fifthTime, nil) ref, _ := h.Store(ctx) return h, ref @@ -221,10 +221,11 @@ func TestDacEachEndpointWithAct(t *testing.T) { } } -// nolint:paralleltest,tparallel // TestDacWithoutActHeader [negative tests]: // 1. upload w/ "Swarm-Act" header then try to dowload w/o the header. // 2. upload w/o "Swarm-Act" header then try to dowload w/ the header. +// +//nolint:paralleltest,tparallel func TestDacWithoutAct(t *testing.T) { t.Parallel() var ( @@ -321,8 +322,9 @@ func TestDacWithoutAct(t *testing.T) { }) } -// nolint:paralleltest,tparallel // TestDacInvalidPath [negative test]: Expect Bad request when the path address is invalid. +// +//nolint:paralleltest,tparallel func TestDacInvalidPath(t *testing.T) { t.Parallel() var ( @@ -345,9 +347,7 @@ func TestDacInvalidPath(t *testing.T) { PublicKey: pk.PublicKey, Dac: mockdac.New(), }) - var ( - encryptedRef = "asd" - ) + encryptedRef := "asd" jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusBadRequest, jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(now, 10)), @@ -361,7 +361,8 @@ func TestDacInvalidPath(t *testing.T) { Field: "address", Error: api.HexInvalidByteError('s').Error(), }, - }}), + }, + }), jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), ) }) @@ -388,7 +389,6 @@ func TestDacHistory(t *testing.T) { fileName = "sample.html" now = time.Now().Unix() ) - fmt.Printf("bagoy now: %d\n", now) t.Run("empty-history-upload-then-download-and-check-data", func(t *testing.T) { client, _, _, _ := newTestServer(t, testServerOptions{ @@ -516,9 +516,7 @@ func TestDacHistory(t *testing.T) { PublicKey: pk.PublicKey, Dac: mockdac.New(), }) - var ( - testfile = "testfile1" - ) + testfile := "testfile1" jsonhttptest.Request(t, client, http.MethodPost, fileUploadResource+"?name="+fileName, http.StatusInternalServerError, jsonhttptest.WithRequestHeader(api.SwarmActHeader, "true"), @@ -541,9 +539,7 @@ func TestDacHistory(t *testing.T) { PublicKey: pk.PublicKey, Dac: mockdac.New(mockdac.WithHistory(h, fixtureHref.String())), }) - var ( - encryptedRef = "a5df670544eaea29e61b19d8739faa4573b19e4426e58a173e51ed0b5e7e2ade" - ) + encryptedRef := "a5df670544eaea29e61b19d8739faa4573b19e4426e58a173e51ed0b5e7e2ade" jsonhttptest.Request(t, client, http.MethodGet, fileDownloadResource(encryptedRef), http.StatusNotFound, jsonhttptest.WithRequestHeader(api.SwarmActTimestampHeader, strconv.FormatInt(now, 10)), @@ -619,9 +615,7 @@ func TestDacTimestamp(t *testing.T) { }) t.Run("download-w/o-timestamp", func(t *testing.T) { - var ( - encryptedRef = "a5df670544eaea29e61b19d8739faa4573b19e4426e58a173e51ed0b5e7e2ade" - ) + encryptedRef := "a5df670544eaea29e61b19d8739faa4573b19e4426e58a173e51ed0b5e7e2ade" client, _, _, _ := newTestServer(t, testServerOptions{ Storer: storerMock, Logger: logger, @@ -754,7 +748,8 @@ func TestDacPublisher(t *testing.T) { Field: "Swarm-Act-Publisher", Error: "malformed public key: invalid length: 32", }, - }}), + }, + }), jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "text/html; charset=utf-8"), ) }) @@ -785,9 +780,7 @@ func TestDacPublisher(t *testing.T) { }) t.Run("download-w/o-publisher", func(t *testing.T) { - var ( - encryptedRef = "a5df670544eaea29e61b19d8739faa4573b19e4426e58a173e51ed0b5e7e2ade" - ) + encryptedRef := "a5df670544eaea29e61b19d8739faa4573b19e4426e58a173e51ed0b5e7e2ade" client, _, _, _ := newTestServer(t, testServerOptions{ Storer: storerMock, Logger: logger, @@ -862,7 +855,8 @@ func TestDacGrantees(t *testing.T) { Field: "address", Error: api.HexInvalidByteError('s').Error(), }, - }}), + }, + }), ) }) t.Run("add-revoke-grantees", func(t *testing.T) { @@ -904,8 +898,8 @@ func TestDacGrantees(t *testing.T) { jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), jsonhttptest.WithJSONRequestBody(body), ) - }) + t.Run("create-granteelist", func(t *testing.T) { body := api.GranteesPostRequest{ GranteeList: []string{ diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index 33b8ba819ba..1cb9db8b5ba 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -1,8 +1,13 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package dynamicaccess import ( "context" "crypto/ecdsa" + "fmt" encryption "github.com/ethersphere/bee/v2/pkg/encryption" "github.com/ethersphere/bee/v2/pkg/kvs" @@ -10,11 +15,14 @@ import ( "golang.org/x/crypto/sha3" ) -var hashFunc = sha3.NewLegacyKeccak256 -var oneByteArray = []byte{1} -var zeroByteArray = []byte{0} +//nolint:gochecknoglobals +var ( + hashFunc = sha3.NewLegacyKeccak256 + oneByteArray = []byte{1} + zeroByteArray = []byte{0} +) -// Read-only interface for the ACT +// Decryptor is a read-only interface for the ACT. type Decryptor interface { // DecryptRef will return a decrypted reference, for given encrypted reference and grantee DecryptRef(ctx context.Context, storage kvs.KeyValueStore, encryptedRef swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) @@ -22,13 +30,13 @@ type Decryptor interface { Session } -// Control interface for the ACT (does write operations) +// Control interface for the ACT (does write operations). type Control interface { // Embedding the Decryptor interface Decryptor - // Adds a new grantee to the ACT + // AddGrantee adds a new grantee to the ACT AddGrantee(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKey *encryption.Key) error - // Encrypts a Swarm reference for a given grantee + // EncryptRef encrypts a Swarm reference for a given grantee EncryptRef(ctx context.Context, storage kvs.KeyValueStore, grantee *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) } @@ -38,14 +46,14 @@ type ActLogic struct { var _ Control = (*ActLogic)(nil) -// Adds a new publisher to an empty act +// AddPublisher adds a new publisher to an empty act. func (al ActLogic) AddPublisher(ctx context.Context, storage kvs.KeyValueStore, publisher *ecdsa.PublicKey) error { accessKey := encryption.GenerateRandomKey(encryption.KeyLength) return al.AddGrantee(ctx, storage, publisher, publisher, &accessKey) } -// Encrypts a SWARM reference for a publisher +// EncryptRef encrypts a SWARM reference for a publisher. func (al ActLogic) EncryptRef(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) { accessKey, err := al.getAccessKey(ctx, storage, publisherPubKey) if err != nil { @@ -54,13 +62,13 @@ func (al ActLogic) EncryptRef(ctx context.Context, storage kvs.KeyValueStore, pu refCipher := encryption.New(accessKey, 0, uint32(0), hashFunc) encryptedRef, err := refCipher.Encrypt(ref.Bytes()) if err != nil { - return swarm.ZeroAddress, err + return swarm.ZeroAddress, fmt.Errorf("failed to encrypt reference: %w", err) } return swarm.NewAddress(encryptedRef), nil } -// Adds a new grantee to the ACT +// AddGrantee adds a new grantee to the ACT. func (al ActLogic) AddGrantee(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKeyPointer *encryption.Key) error { var ( accessKey encryption.Key @@ -79,61 +87,60 @@ func (al ActLogic) AddGrantee(ctx context.Context, storage kvs.KeyValueStore, pu } // Encrypt the access key for the new Grantee - keys, err := al.getKeys(granteePubKey) + lookupKey, accessKeyDecryptionKey, err := al.getKeys(granteePubKey) if err != nil { return err } - lookupKey := keys[0] - // accessKeyDecryptionKey is used for encryption of the access key - accessKeyDecryptionKey := keys[1] // Encrypt the access key for the new Grantee cipher := encryption.New(encryption.Key(accessKeyDecryptionKey), 0, uint32(0), hashFunc) granteeEncryptedAccessKey, err := cipher.Encrypt(accessKey) if err != nil { - return err + return fmt.Errorf("failed to encrypt access key: %w", err) } - // Add the new encrypted access key for the Act + // Add the new encrypted access key to the Act return storage.Put(ctx, lookupKey, granteeEncryptedAccessKey) } -// Will return the access key for a publisher (public key) +// Will return the access key for a publisher (public key). func (al *ActLogic) getAccessKey(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey *ecdsa.PublicKey) ([]byte, error) { - keys, err := al.getKeys(publisherPubKey) + publisherLookupKey, publisherAKDecryptionKey, err := al.getKeys(publisherPubKey) if err != nil { return nil, err } - publisherLookupKey := keys[0] - publisherAKDecryptionKey := keys[1] - // no need to constructor call if value not found in act + // no need for constructor call if value not found in act accessKeyDecryptionCipher := encryption.New(encryption.Key(publisherAKDecryptionKey), 0, uint32(0), hashFunc) encryptedAK, err := storage.Get(ctx, publisherLookupKey) if err != nil { - return nil, err + return nil, fmt.Errorf("failed go get value from KVS: %w", err) } return accessKeyDecryptionCipher.Decrypt(encryptedAK) } // Generate lookup key and access key decryption key for a given public key -func (al *ActLogic) getKeys(publicKey *ecdsa.PublicKey) ([][]byte, error) { - return al.Session.Key(publicKey, [][]byte{zeroByteArray, oneByteArray}) +func (al *ActLogic) getKeys(publicKey *ecdsa.PublicKey) ([]byte, []byte, error) { + nonces := [][]byte{zeroByteArray, oneByteArray} + // keys := make([][]byte, 0, len(nonces)) + keys, err := al.Session.Key(publicKey, nonces) + if keys == nil { + return nil, nil, err + } + return keys[0], keys[1], err } // DecryptRef will return a decrypted reference, for given encrypted reference and publisher func (al ActLogic) DecryptRef(ctx context.Context, storage kvs.KeyValueStore, encryptedRef swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) { - keys, err := al.getKeys(publisher) + lookupKey, accessKeyDecryptionKey, err := al.getKeys(publisher) if err != nil { return swarm.ZeroAddress, err } - lookupKey := keys[0] - accessKeyDecryptionKey := keys[1] // Lookup encrypted access key from the ACT manifest encryptedAccessKey, err := storage.Get(ctx, lookupKey) if err != nil { - return swarm.ZeroAddress, err + return swarm.ZeroAddress, fmt.Errorf("failed to get access key from KVS: %w", err) } // Decrypt access key diff --git a/pkg/dynamicaccess/accesslogic_test.go b/pkg/dynamicaccess/accesslogic_test.go index 66a3f8930e3..8d6c146e468 100644 --- a/pkg/dynamicaccess/accesslogic_test.go +++ b/pkg/dynamicaccess/accesslogic_test.go @@ -1,3 +1,7 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package dynamicaccess_test import ( @@ -12,6 +16,7 @@ import ( "github.com/ethersphere/bee/v2/pkg/dynamicaccess" kvsmock "github.com/ethersphere/bee/v2/pkg/kvs/mock" "github.com/ethersphere/bee/v2/pkg/swarm" + "github.com/stretchr/testify/assert" ) // Generates a new test environment with a fix private key @@ -79,7 +84,6 @@ func TestDecryptRef_Success(t *testing.T) { } if expectedRef.Compare(acutalRef) != 0 { - t.Errorf("Get gave back wrong Swarm reference!") } } @@ -123,7 +127,6 @@ func TestDecryptRefWithGrantee_Success(t *testing.T) { } if expectedRef.Compare(acutalRef) != 0 { - t.Errorf("Get gave back wrong Swarm reference!") } } @@ -135,9 +138,7 @@ func TestDecryptRef_Error(t *testing.T) { s := kvsmock.New() al := setupAccessLogic() err := al.AddPublisher(ctx, s, &id0.PublicKey) - if err != nil { - t.Errorf("AddPublisher: expected no error, got %v", err) - } + assert.NoError(t, err) expectedRef := "39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559" @@ -158,19 +159,14 @@ func TestAddPublisher(t *testing.T) { al := setupAccessLogic() err := al.AddPublisher(ctx, s, &id0.PublicKey) - if err != nil { - t.Errorf("AddPublisher: expected no error, got %v", err) - } + assert.NoError(t, err) decodedSavedLookupKey, err := hex.DecodeString(savedLookupKey) - if err != nil { - t.Errorf("DecodeString: expected no error, got %v", err) - } + assert.NoError(t, err) encryptedAccessKey, err := s.Get(ctx, decodedSavedLookupKey) - if err != nil { - t.Errorf("Lookup: expected no error, got %v", err) - } + assert.NoError(t, err) + decodedEncryptedAccessKey := hex.EncodeToString(encryptedAccessKey) // A random value is returned so it is only possibly to check the length of the returned value @@ -184,7 +180,6 @@ func TestAddPublisher(t *testing.T) { } func TestAddNewGranteeToContent(t *testing.T) { - id0 := getPrivKey(0) id1 := getPrivKey(1) id2 := getPrivKey(2) @@ -197,24 +192,17 @@ func TestAddNewGranteeToContent(t *testing.T) { s := kvsmock.New() al := setupAccessLogic() err := al.AddPublisher(ctx, s, &id0.PublicKey) - if err != nil { - t.Errorf("AddNewGrantee: expected no error, got %v", err) - } + assert.NoError(t, err) err = al.AddGrantee(ctx, s, &id0.PublicKey, &id1.PublicKey, nil) - if err != nil { - t.Errorf("AddNewGrantee: expected no error, got %v", err) - } + assert.NoError(t, err) err = al.AddGrantee(ctx, s, &id0.PublicKey, &id2.PublicKey, nil) - if err != nil { - t.Errorf("AddNewGrantee: expected no error, got %v", err) - } + assert.NoError(t, err) lookupKeyAsByte, err := hex.DecodeString(publisherLookupKey) - if err != nil { - t.Errorf("AddNewGrantee: expected no error, got %v", err) - } + assert.NoError(t, err) + result, _ := s.Get(ctx, lookupKeyAsByte) hexEncodedEncryptedAK := hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { @@ -222,9 +210,8 @@ func TestAddNewGranteeToContent(t *testing.T) { } lookupKeyAsByte, err = hex.DecodeString(firstAddedGranteeLookupKey) - if err != nil { - t.Errorf("AddNewGrantee: expected no error, got %v", err) - } + assert.NoError(t, err) + result, _ = s.Get(ctx, lookupKeyAsByte) hexEncodedEncryptedAK = hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { @@ -232,9 +219,8 @@ func TestAddNewGranteeToContent(t *testing.T) { } lookupKeyAsByte, err = hex.DecodeString(secondAddedGranteeLookupKey) - if err != nil { - t.Errorf("AddNewGrantee: expected no error, got %v", err) - } + assert.NoError(t, err) + result, _ = s.Get(ctx, lookupKeyAsByte) hexEncodedEncryptedAK = hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index a2c9ad50d04..a8998aa12df 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -1,3 +1,7 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package dynamicaccess import ( @@ -33,13 +37,13 @@ type Controller interface { io.Closer } -type controller struct { +type ControllerStruct struct { accessLogic ActLogic } -var _ Controller = (*controller)(nil) +var _ Controller = (*ControllerStruct)(nil) -func (c *controller) DownloadHandler( +func (c *ControllerStruct) DownloadHandler( ctx context.Context, ls file.LoadSaver, encryptedRef swarm.Address, @@ -63,7 +67,7 @@ func (c *controller) DownloadHandler( return c.accessLogic.DecryptRef(ctx, act, encryptedRef, publisher) } -func (c *controller) UploadHandler( +func (c *ControllerStruct) UploadHandler( ctx context.Context, ls file.LoadSaver, refrefence swarm.Address, @@ -121,13 +125,13 @@ func (c *controller) UploadHandler( return actRef, historyRef, encryptedRef, err } -func NewController(accessLogic ActLogic) Controller { - return &controller{ +func NewController(accessLogic ActLogic) *ControllerStruct { + return &ControllerStruct{ accessLogic: accessLogic, } } -func (c *controller) HandleGrantees( +func (c *ControllerStruct) HandleGrantees( ctx context.Context, ls file.LoadSaver, gls file.LoadSaver, @@ -261,7 +265,7 @@ func (c *controller) HandleGrantees( return glref, eglref, href, actref, nil } -func (c *controller) GetGrantees(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { +func (c *ControllerStruct) GetGrantees(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { granteeRef, err := c.decryptRefForPublisher(publisher, encryptedglref) if err != nil { return nil, err @@ -273,7 +277,7 @@ func (c *controller) GetGrantees(ctx context.Context, ls file.LoadSaver, publish return gl.Get(), nil } -func (c *controller) encryptRefForPublisher(publisherPubKey *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) { +func (c *ControllerStruct) encryptRefForPublisher(publisherPubKey *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) { keys, err := c.accessLogic.Session.Key(publisherPubKey, [][]byte{oneByteArray}) if err != nil { return swarm.ZeroAddress, err @@ -287,7 +291,7 @@ func (c *controller) encryptRefForPublisher(publisherPubKey *ecdsa.PublicKey, re return swarm.NewAddress(encryptedRef), nil } -func (c *controller) decryptRefForPublisher(publisherPubKey *ecdsa.PublicKey, encryptedRef swarm.Address) (swarm.Address, error) { +func (c *ControllerStruct) decryptRefForPublisher(publisherPubKey *ecdsa.PublicKey, encryptedRef swarm.Address) (swarm.Address, error) { keys, err := c.accessLogic.Session.Key(publisherPubKey, [][]byte{oneByteArray}) if err != nil { return swarm.ZeroAddress, err @@ -308,6 +312,6 @@ func requestPipelineFactory(ctx context.Context, s storage.Putter, encrypt bool, } // TODO: what to do in close ? -func (s *controller) Close() error { +func (s *ControllerStruct) Close() error { return nil } diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index 2b3065899a1..05b4e76db11 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -1,3 +1,7 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package dynamicaccess_test import ( @@ -18,6 +22,7 @@ import ( "golang.org/x/crypto/sha3" ) +//nolint:errcheck,gosec,wrapcheck func getHistoryFixture(ctx context.Context, ls file.LoadSaver, al dynamicaccess.ActLogic, publisher *ecdsa.PublicKey) (swarm.Address, error) { h, err := dynamicaccess.NewHistory(ls) if err != nil { @@ -103,11 +108,11 @@ func TestController_PublisherDownload(t *testing.T) { c := dynamicaccess.NewController(al) ls := createLs() ref := swarm.RandAddress(t) - href, err := getHistoryFixture(ctx, ls, al, &publisher.PublicKey) - h, err := dynamicaccess.NewHistoryReference(ls, href) - entry, err := h.Lookup(ctx, time.Now().Unix()) + href, _ := getHistoryFixture(ctx, ls, al, &publisher.PublicKey) + h, _ := dynamicaccess.NewHistoryReference(ls, href) + entry, _ := h.Lookup(ctx, time.Now().Unix()) actRef := entry.Reference() - act, err := kvs.NewReference(ls, actRef) + act, _ := kvs.NewReference(ls, actRef) encRef, err := al.EncryptRef(ctx, act, &publisher.PublicKey, ref) assert.NoError(t, err) @@ -128,12 +133,12 @@ func TestController_GranteeDownload(t *testing.T) { ls := createLs() c := dynamicaccess.NewController(al) ref := swarm.RandAddress(t) - href, err := getHistoryFixture(ctx, ls, publisherAL, &publisher.PublicKey) - h, err := dynamicaccess.NewHistoryReference(ls, href) + href, _ := getHistoryFixture(ctx, ls, publisherAL, &publisher.PublicKey) + h, _ := dynamicaccess.NewHistoryReference(ls, href) ts := time.Date(2001, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() - entry, err := h.Lookup(ctx, ts) + entry, _ := h.Lookup(ctx, ts) actRef := entry.Reference() - act, err := kvs.NewReference(ls, actRef) + act, _ := kvs.NewReference(ls, actRef) encRef, err := publisherAL.EncryptRef(ctx, act, &publisher.PublicKey, ref) assert.NoError(t, err) @@ -178,7 +183,7 @@ func TestController_HandleGrantees(t *testing.T) { assert.Len(t, gl.Get(), 1) addList = []*ecdsa.PublicKey{&getPrivKey(0).PublicKey} - granteeRef, _, _, _, err = c.HandleGrantees(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil) + granteeRef, _, _, _, _ = c.HandleGrantees(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil) gl, err = dynamicaccess.NewGranteeListReference(ls, granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 2) @@ -187,12 +192,12 @@ func TestController_HandleGrantees(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} revokeList := []*ecdsa.PublicKey{&grantee1.PublicKey} gl, _ := dynamicaccess.NewGranteeList(ls) - gl.Add([]*ecdsa.PublicKey{&publisher.PublicKey, &grantee1.PublicKey}) - granteeRef, err := gl.Save(ctx) + _ = gl.Add([]*ecdsa.PublicKey{&publisher.PublicKey, &grantee1.PublicKey}) + granteeRef, _ := gl.Save(ctx) eglref, _ := refCipher.Encrypt(granteeRef.Bytes()) - granteeRef, _, _, _, err = c.HandleGrantees(ctx, ls, gls, swarm.NewAddress(eglref), href, &publisher.PublicKey, addList, revokeList) - gl, err = dynamicaccess.NewGranteeListReference(ls, granteeRef) + granteeRef, _, _, _, _ = c.HandleGrantees(ctx, ls, gls, swarm.NewAddress(eglref), href, &publisher.PublicKey, addList, revokeList) + gl, err := dynamicaccess.NewGranteeListReference(ls, granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 2) @@ -200,8 +205,9 @@ func TestController_HandleGrantees(t *testing.T) { t.Run("add twice", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey, &grantee.PublicKey} + //nolint:ineffassign,staticcheck,wastedassign granteeRef, eglref, _, _, err := c.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) - granteeRef, _, _, _, err = c.HandleGrantees(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil) + granteeRef, _, _, _, _ = c.HandleGrantees(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil) gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) @@ -209,7 +215,7 @@ func TestController_HandleGrantees(t *testing.T) { }) t.Run("revoke non-existing", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, _, _, _, err := c.HandleGrantees(ctx, ls, ls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + granteeRef, _, _, _, _ := c.HandleGrantees(ctx, ls, ls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) assert.NoError(t, err) @@ -233,7 +239,7 @@ func TestController_GetGrantees(t *testing.T) { t.Run("get by publisher", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, eglRef, _, _, err := c1.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + granteeRef, eglRef, _, _, _ := c1.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) grantees, err := c1.GetGrantees(ctx, ls, &publisher.PublicKey, eglRef) assert.NoError(t, err) @@ -244,7 +250,7 @@ func TestController_GetGrantees(t *testing.T) { }) t.Run("get by non-publisher", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - _, eglRef, _, _, err := c1.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + _, eglRef, _, _, _ := c1.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) grantees, err := c2.GetGrantees(ctx, ls, &publisher.PublicKey, eglRef) assert.Error(t, err) assert.Nil(t, grantees) diff --git a/pkg/dynamicaccess/grantee.go b/pkg/dynamicaccess/grantee.go index 02cd8099021..57e6429f7da 100644 --- a/pkg/dynamicaccess/grantee.go +++ b/pkg/dynamicaccess/grantee.go @@ -1,9 +1,14 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package dynamicaccess import ( "context" "crypto/ecdsa" "crypto/elliptic" + "errors" "fmt" "github.com/btcsuite/btcd/btcec/v2" @@ -76,13 +81,18 @@ func (g *GranteeListStruct) Save(ctx context.Context) (swarm.Address, error) { return swarm.NewAddress(refBytes), nil } +var ( + ErrNothingToRemove = errors.New("nothing to remove") + ErrNoGranteeFound = errors.New("no grantee found") +) + func (g *GranteeListStruct) Remove(keysToRemove []*ecdsa.PublicKey) error { if len(keysToRemove) == 0 { - return fmt.Errorf("nothing to remove") + return ErrNothingToRemove } if len(g.grantees) == 0 { - return fmt.Errorf("no grantee found") + return ErrNoGranteeFound } grantees := g.grantees @@ -99,17 +109,17 @@ func (g *GranteeListStruct) Remove(keysToRemove []*ecdsa.PublicKey) error { return nil } -func NewGranteeList(ls file.LoadSaver) (GranteeList, error) { +func NewGranteeList(ls file.LoadSaver) (*GranteeListStruct, error) { // Why is the error necessary? return &GranteeListStruct{ grantees: []*ecdsa.PublicKey{}, loadSave: ls, }, nil } -func NewGranteeListReference(ls file.LoadSaver, reference swarm.Address) (GranteeList, error) { +func NewGranteeListReference(ls file.LoadSaver, reference swarm.Address) (*GranteeListStruct, error) { data, err := ls.Load(context.Background(), reference.Bytes()) if err != nil { - return nil, err + return nil, fmt.Errorf("unable to load reference, %w", err) } grantees := deserialize(data) diff --git a/pkg/dynamicaccess/grantee_test.go b/pkg/dynamicaccess/grantee_test.go index c4ce58ac8aa..cd69aaa752c 100644 --- a/pkg/dynamicaccess/grantee_test.go +++ b/pkg/dynamicaccess/grantee_test.go @@ -1,3 +1,7 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package dynamicaccess_test import ( @@ -33,7 +37,13 @@ func createLs() file.LoadSaver { func generateKeyListFixture() ([]*ecdsa.PublicKey, error) { key1, err := ecdsa.GenerateKey(btcec.S256(), rand.Reader) + if err != nil { + return nil, err + } key2, err := ecdsa.GenerateKey(btcec.S256(), rand.Reader) + if err != nil { + return nil, err + } key3, err := ecdsa.GenerateKey(btcec.S256(), rand.Reader) if err != nil { return nil, err @@ -216,8 +226,8 @@ func TestGranteeRemoveTwo(t *testing.T) { if err != nil { t.Errorf("key generation error: %v", err) } - err = gl.Add([]*ecdsa.PublicKey{keys[0]}) - err = gl.Add([]*ecdsa.PublicKey{keys[0]}) + _ = gl.Add([]*ecdsa.PublicKey{keys[0]}) + _ = gl.Add([]*ecdsa.PublicKey{keys[0]}) err = gl.Remove([]*ecdsa.PublicKey{keys[0]}) assert.NoError(t, err) } diff --git a/pkg/dynamicaccess/history.go b/pkg/dynamicaccess/history.go index 25193adf886..27485e95f95 100644 --- a/pkg/dynamicaccess/history.go +++ b/pkg/dynamicaccess/history.go @@ -1,3 +1,7 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package dynamicaccess import ( @@ -20,44 +24,47 @@ type History interface { Store(ctx context.Context) (swarm.Address, error) } -var _ History = (*history)(nil) +var _ History = (*HistoryStruct)(nil) -var ErrEndIteration = errors.New("end iteration") +var ( + ErrEndIteration = errors.New("end iteration") + ErrUnexpectedType = errors.New("unexpected type") +) -type history struct { +type HistoryStruct struct { manifest *manifest.MantarayManifest ls file.LoadSaver } -func NewHistory(ls file.LoadSaver) (History, error) { +func NewHistory(ls file.LoadSaver) (*HistoryStruct, error) { m, err := manifest.NewDefaultManifest(ls, false) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create default manifest: %w", err) } mm, ok := m.(*manifest.MantarayManifest) if !ok { - return nil, fmt.Errorf("expected MantarayManifest, got %T", m) + return nil, fmt.Errorf("%w: expected MantarayManifest, got %T", ErrUnexpectedType, m) } - return &history{manifest: mm, ls: ls}, nil + return &HistoryStruct{manifest: mm, ls: ls}, nil } -func NewHistoryReference(ls file.LoadSaver, ref swarm.Address) (History, error) { +func NewHistoryReference(ls file.LoadSaver, ref swarm.Address) (*HistoryStruct, error) { m, err := manifest.NewDefaultManifestReference(ref, ls) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create default manifest: %w", err) } mm, ok := m.(*manifest.MantarayManifest) if !ok { - return nil, fmt.Errorf("expected MantarayManifest, got %T", m) + return nil, fmt.Errorf("%w: expected MantarayManifest, got %T", ErrUnexpectedType, m) } - return &history{manifest: mm, ls: ls}, nil + return &HistoryStruct{manifest: mm, ls: ls}, nil } -func (h *history) Add(ctx context.Context, ref swarm.Address, timestamp *int64, metadata *map[string]string) error { +func (h *HistoryStruct) Add(ctx context.Context, ref swarm.Address, timestamp *int64, metadata *map[string]string) error { mtdt := map[string]string{} if metadata != nil { mtdt = *metadata @@ -74,10 +81,12 @@ func (h *history) Add(ctx context.Context, ref swarm.Address, timestamp *int64, return h.manifest.Add(ctx, key, manifest.NewEntry(ref, mtdt)) } +var ErrInvalidTimestamp = errors.New("invalid timestamp") + // Lookup finds the entry for a path or returns error if not found -func (h *history) Lookup(ctx context.Context, timestamp int64) (manifest.Entry, error) { +func (h *HistoryStruct) Lookup(ctx context.Context, timestamp int64) (manifest.Entry, error) { if timestamp <= 0 { - return manifest.NewEntry(swarm.ZeroAddress, map[string]string{}), errors.New("invalid timestamp") + return manifest.NewEntry(swarm.ZeroAddress, map[string]string{}), ErrInvalidTimestamp } reversedTimestamp := math.MaxInt64 - timestamp @@ -93,7 +102,7 @@ func (h *history) Lookup(ctx context.Context, timestamp int64) (manifest.Entry, return manifest.NewEntry(swarm.ZeroAddress, map[string]string{}), nil } -func (h *history) lookupNode(ctx context.Context, searchedTimestamp int64) (*mantaray.Node, error) { +func (h *HistoryStruct) lookupNode(ctx context.Context, searchedTimestamp int64) (*mantaray.Node, error) { // before node's timestamp is the closest one that is less than or equal to the searched timestamp // for instance: 2030, 2020, 1994 -> search for 2021 -> before is 2020 var beforeNode *mantaray.Node @@ -134,12 +143,11 @@ func (h *history) lookupNode(ctx context.Context, searchedTimestamp int64) (*man } if afterNode != nil { return afterNode, nil - } return nil, nil } -func (h *history) Store(ctx context.Context) (swarm.Address, error) { +func (h *HistoryStruct) Store(ctx context.Context) (swarm.Address, error) { return h.manifest.Store(ctx) } diff --git a/pkg/dynamicaccess/history_test.go b/pkg/dynamicaccess/history_test.go index 5c7f5670cf6..edc86dc7f22 100644 --- a/pkg/dynamicaccess/history_test.go +++ b/pkg/dynamicaccess/history_test.go @@ -1,3 +1,7 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package dynamicaccess_test import ( @@ -61,27 +65,27 @@ func TestMultiNodeHistoryLookup(t *testing.T) { testActRef1 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd891")) firstTime := time.Date(1994, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() mtdt1 := map[string]string{"firstTime": "1994-04-01"} - h.Add(ctx, testActRef1, &firstTime, &mtdt1) + _ = h.Add(ctx, testActRef1, &firstTime, &mtdt1) testActRef2 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd892")) secondTime := time.Date(2000, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() mtdt2 := map[string]string{"secondTime": "2000-04-01"} - h.Add(ctx, testActRef2, &secondTime, &mtdt2) + _ = h.Add(ctx, testActRef2, &secondTime, &mtdt2) testActRef3 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd893")) thirdTime := time.Date(2015, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() mtdt3 := map[string]string{"thirdTime": "2015-04-01"} - h.Add(ctx, testActRef3, &thirdTime, &mtdt3) + _ = h.Add(ctx, testActRef3, &thirdTime, &mtdt3) testActRef4 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd894")) fourthTime := time.Date(2020, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() mtdt4 := map[string]string{"fourthTime": "2020-04-01"} - h.Add(ctx, testActRef4, &fourthTime, &mtdt4) + _ = h.Add(ctx, testActRef4, &fourthTime, &mtdt4) testActRef5 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd895")) fifthTime := time.Date(2030, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() mtdt5 := map[string]string{"fifthTime": "2030-04-01"} - h.Add(ctx, testActRef5, &fifthTime, &mtdt5) + _ = h.Add(ctx, testActRef5, &fifthTime, &mtdt5) // latest searchedTime := time.Date(1980, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() @@ -126,7 +130,7 @@ func TestHistoryStore(t *testing.T) { testActRef1 := swarm.NewAddress([]byte("39a5ea87b141fe44aa609c3327ecd891")) firstTime := time.Date(1994, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() mtdt1 := map[string]string{"firstTime": "1994-04-01"} - h1.Add(ctx, testActRef1, &firstTime, &mtdt1) + _ = h1.Add(ctx, testActRef1, &firstTime, &mtdt1) href1, err := h1.Store(ctx) assert.NoError(t, err) @@ -134,7 +138,7 @@ func TestHistoryStore(t *testing.T) { h2, err := dynamicaccess.NewHistoryReference(ls, href1) assert.NoError(t, err) - entry1, err := h2.Lookup(ctx, firstTime) + entry1, _ := h2.Lookup(ctx, firstTime) actRef1 := entry1.Reference() assert.True(t, actRef1.Equal(testActRef1)) assert.True(t, reflect.DeepEqual(mtdt1, entry1.Metadata())) diff --git a/pkg/dynamicaccess/mock/accesslogic.go b/pkg/dynamicaccess/mock/accesslogic.go index 8be8e3a07fd..6b48c824eb9 100644 --- a/pkg/dynamicaccess/mock/accesslogic.go +++ b/pkg/dynamicaccess/mock/accesslogic.go @@ -1,3 +1,6 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. package mock type AccessLogicMock struct { diff --git a/pkg/dynamicaccess/mock/controller.go b/pkg/dynamicaccess/mock/controller.go index 09cacb65145..7f77ca2ca32 100644 --- a/pkg/dynamicaccess/mock/controller.go +++ b/pkg/dynamicaccess/mock/controller.go @@ -126,7 +126,7 @@ func (m *mockDacService) UploadHandler(ctx context.Context, ls file.LoadSaver, r } } else { h, _ = dynamicaccess.NewHistory(m.ls) - h.Add(ctx, kvsRef, &now, nil) + _ = h.Add(ctx, kvsRef, &now, nil) historyRef, _ = h.Store(ctx) m.historyMap[historyRef.String()] = h } @@ -140,7 +140,7 @@ func (m *mockDacService) Close() error { return nil } -func (m *mockDacService) HandleGrantees(ctx context.Context, ls file.LoadSaver, gls file.LoadSaver, encryptedglref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList []*ecdsa.PublicKey, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) { +func (m *mockDacService) HandleGrantees(_ context.Context, ls file.LoadSaver, gls file.LoadSaver, encryptedglref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList []*ecdsa.PublicKey, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) { historyRef, _ := swarm.ParseHexAddress("67bdf80a9bbea8eca9c8480e43fdceb485d2d74d5708e45144b8c4adacd13d9c") glRef, _ := swarm.ParseHexAddress("3339613565613837623134316665343461613630396333333237656364383934") eglRef, _ := swarm.ParseHexAddress("fc4e9fe978991257b897d987bc4ff13058b66ef45a53189a0b4fe84bb3346396") diff --git a/pkg/dynamicaccess/mock/grantee.go b/pkg/dynamicaccess/mock/grantee.go index 50689ade08f..6a8f01c87cb 100644 --- a/pkg/dynamicaccess/mock/grantee.go +++ b/pkg/dynamicaccess/mock/grantee.go @@ -1,3 +1,7 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package mock import ( diff --git a/pkg/dynamicaccess/mock/session.go b/pkg/dynamicaccess/mock/session.go index 9613aacedfa..6e5d43a9576 100644 --- a/pkg/dynamicaccess/mock/session.go +++ b/pkg/dynamicaccess/mock/session.go @@ -1,3 +1,7 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package mock import ( @@ -17,7 +21,6 @@ func (s *SessionMock) Key(publicKey *ecdsa.PublicKey, nonces [][]byte) ([][]byte return nil, nil } return s.KeyFunc(publicKey, nonces) - } func NewSessionMock(key *ecdsa.PrivateKey) *SessionMock { diff --git a/pkg/dynamicaccess/session.go b/pkg/dynamicaccess/session.go index 0b58c1aa3dd..fb1f44241be 100644 --- a/pkg/dynamicaccess/session.go +++ b/pkg/dynamicaccess/session.go @@ -1,8 +1,13 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package dynamicaccess import ( "crypto/ecdsa" "errors" + "fmt" "github.com/ethersphere/bee/v2/pkg/crypto" "github.com/ethersphere/bee/v2/pkg/keystore" @@ -14,19 +19,24 @@ type Session interface { Key(publicKey *ecdsa.PublicKey, nonces [][]byte) ([][]byte, error) } -var _ Session = (*session)(nil) +var _ Session = (*SessionStruct)(nil) -type session struct { +type SessionStruct struct { key *ecdsa.PrivateKey } -func (s *session) Key(publicKey *ecdsa.PublicKey, nonces [][]byte) ([][]byte, error) { +var ( + ErrInvalidPublicKey = errors.New("invalid public key") + ErrSecretKeyInfinity = errors.New("shared secret is point at infinity") +) + +func (s *SessionStruct) Key(publicKey *ecdsa.PublicKey, nonces [][]byte) ([][]byte, error) { if publicKey == nil { - return nil, errors.New("invalid public key") + return nil, ErrInvalidPublicKey } x, y := publicKey.Curve.ScalarMult(publicKey.X, publicKey.Y, s.key.D.Bytes()) if x == nil || y == nil { - return nil, errors.New("shared secret is point at infinity") + return nil, ErrSecretKeyInfinity } if len(nonces) == 0 { @@ -37,7 +47,7 @@ func (s *session) Key(publicKey *ecdsa.PublicKey, nonces [][]byte) ([][]byte, er for _, nonce := range nonces { key, err := crypto.LegacyKeccak256(append(x.Bytes(), nonce...)) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to get Keccak256 hash: %w", err) } keys = append(keys, key) } @@ -45,13 +55,13 @@ func (s *session) Key(publicKey *ecdsa.PublicKey, nonces [][]byte) ([][]byte, er return keys, nil } -func NewDefaultSession(key *ecdsa.PrivateKey) Session { - return &session{ +func NewDefaultSession(key *ecdsa.PrivateKey) *SessionStruct { + return &SessionStruct{ key: key, } } // Currently implemented only in mock/session.go -func NewFromKeystore(ks keystore.Service, tag, password string) Session { +func NewFromKeystore(keystore.Service, string, string) Session { return nil } diff --git a/pkg/dynamicaccess/session_test.go b/pkg/dynamicaccess/session_test.go index 493b998a805..701384b93bb 100644 --- a/pkg/dynamicaccess/session_test.go +++ b/pkg/dynamicaccess/session_test.go @@ -1,3 +1,7 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package dynamicaccess_test import ( @@ -14,7 +18,7 @@ import ( memkeystore "github.com/ethersphere/bee/v2/pkg/keystore/mem" ) -func mockKeyFunc(publicKey *ecdsa.PublicKey, nonces [][]byte) ([][]byte, error) { +func mockKeyFunc(*ecdsa.PublicKey, [][]byte) ([][]byte, error) { return [][]byte{{1}}, nil } diff --git a/pkg/kvs/kvs_test.go b/pkg/kvs/kvs_test.go index 462e092d532..e2da7aa7030 100644 --- a/pkg/kvs/kvs_test.go +++ b/pkg/kvs/kvs_test.go @@ -33,11 +33,11 @@ func createLs() file.LoadSaver { } func keyValuePair(t *testing.T) ([]byte, []byte) { + t.Helper() return swarm.RandAddress(t).Bytes(), swarm.RandAddress(t).Bytes() } func TestKvs(t *testing.T) { - s, err := kvs.New(createLs()) assert.NoError(t, err) @@ -130,7 +130,7 @@ func TestKvs_Save(t *testing.T) { }) t.Run("Save not empty KVS return valid swarm address", func(t *testing.T) { s, _ := kvs.New(createLs()) - s.Put(ctx, key1, val1) + _ = s.Put(ctx, key1, val1) ref, err := s.Save(ctx) assert.NoError(t, err) assert.True(t, ref.IsValidNonEmpty()) diff --git a/pkg/kvs/mock/kvs.go b/pkg/kvs/mock/kvs.go index 0203cc52ce6..f5818972004 100644 --- a/pkg/kvs/mock/kvs.go +++ b/pkg/kvs/mock/kvs.go @@ -1,3 +1,7 @@ +// Copyright 2024 The Swarm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + package mock import ( @@ -24,7 +28,8 @@ func getInMemorySwarm() *single { defer lock.Unlock() if singleInMemorySwarm == nil { singleInMemorySwarm = &single{ - memoryMock: make(map[string]map[string][]byte)} + memoryMock: make(map[string]map[string][]byte), + } } } return singleInMemorySwarm diff --git a/pkg/manifest/mantaray/node.go b/pkg/manifest/mantaray/node.go index 5a59c010d52..9561c299db5 100644 --- a/pkg/manifest/mantaray/node.go +++ b/pkg/manifest/mantaray/node.go @@ -16,13 +16,8 @@ const ( ) var ( - ZeroObfuscationKey []byte -) - -// nolint:gochecknoinits -func init() { ZeroObfuscationKey = make([]byte, 32) -} +) // Error used when lookup path does not match var ( From 272ffcb54a4075ae9d14e859fee733499c733e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20Ar=C3=A1nyi?= Date: Thu, 16 May 2024 16:51:32 +0400 Subject: [PATCH 35/41] refactor(act): typos & docs (#40) refactor(act): typos, docs, removed unused import aliases --- pkg/api/bytes.go | 5 ++--- pkg/api/bzz.go | 9 ++++----- pkg/api/dynamicaccess.go | 18 ++++++++++-------- pkg/api/feed.go | 4 ++-- pkg/dynamicaccess/accesslogic.go | 5 +---- pkg/dynamicaccess/accesslogic_test.go | 14 +++++++------- pkg/dynamicaccess/controller.go | 23 +++++++---------------- 7 files changed, 33 insertions(+), 45 deletions(-) diff --git a/pkg/api/bytes.go b/pkg/api/bytes.go index 2afb1db99c0..0eefb581c1b 100644 --- a/pkg/api/bytes.go +++ b/pkg/api/bytes.go @@ -15,7 +15,7 @@ import ( "github.com/ethersphere/bee/v2/pkg/file/redundancy" "github.com/ethersphere/bee/v2/pkg/jsonhttp" "github.com/ethersphere/bee/v2/pkg/postage" - storage "github.com/ethersphere/bee/v2/pkg/storage" + "github.com/ethersphere/bee/v2/pkg/storage" "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/ethersphere/bee/v2/pkg/tracing" "github.com/gorilla/mux" @@ -124,8 +124,7 @@ func (s *Service) bytesUploadHandler(w http.ResponseWriter, r *http.Request) { return } } - // TODO: what should be the root_address ? (eref vs ref) - span.SetTag("root_address", reference) + span.SetTag("root_address", encryptedReference) err = putter.Done(reference) if err != nil { diff --git a/pkg/api/bzz.go b/pkg/api/bzz.go index b49bc89baea..dc26b26667a 100644 --- a/pkg/api/bzz.go +++ b/pkg/api/bzz.go @@ -30,8 +30,8 @@ import ( "github.com/ethersphere/bee/v2/pkg/log" "github.com/ethersphere/bee/v2/pkg/manifest" "github.com/ethersphere/bee/v2/pkg/postage" - storage "github.com/ethersphere/bee/v2/pkg/storage" - storer "github.com/ethersphere/bee/v2/pkg/storer" + "github.com/ethersphere/bee/v2/pkg/storage" + "github.com/ethersphere/bee/v2/pkg/storer" "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/ethersphere/bee/v2/pkg/topology" "github.com/ethersphere/bee/v2/pkg/tracing" @@ -281,9 +281,8 @@ func (s *Service) fileUploadHandler( ext.LogError(span, err, olog.String("action", "putter.Done")) return } - // TODO: what should be the root_address ? (eref vs ref) span.LogFields(olog.Bool("success", true)) - span.SetTag("root_address", manifestReference) + span.SetTag("root_address", encryptedReference) if tagID != 0 { w.Header().Set(SwarmTagHeader, fmt.Sprint(tagID)) @@ -291,7 +290,7 @@ func (s *Service) fileUploadHandler( } w.Header().Set(ETagHeader, fmt.Sprintf("%q", encryptedReference.String())) w.Header().Set("Access-Control-Expose-Headers", SwarmTagHeader) - // TODO: do we need to return reference as well ? + jsonhttp.Created(w, bzzUploadResponse{ Reference: encryptedReference, }) diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index 32447f905e8..b0be09fbcfa 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -21,8 +21,8 @@ import ( "github.com/ethersphere/bee/v2/pkg/file/redundancy" "github.com/ethersphere/bee/v2/pkg/jsonhttp" "github.com/ethersphere/bee/v2/pkg/postage" - storage "github.com/ethersphere/bee/v2/pkg/storage" - storer "github.com/ethersphere/bee/v2/pkg/storer" + "github.com/ethersphere/bee/v2/pkg/storage" + "github.com/ethersphere/bee/v2/pkg/storer" "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/gorilla/mux" ) @@ -40,7 +40,7 @@ func getAddressFromContext(ctx context.Context) swarm.Address { return swarm.ZeroAddress } -// setAddress sets the swarm address in the context +// setAddressInContext sets the swarm address in the context func setAddressInContext(ctx context.Context, address swarm.Address) context.Context { return context.WithValue(ctx, addressKey{}, address) } @@ -121,8 +121,8 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { } } -// actEncryptionHandler is a middleware that encrypts the given address using the publisher's public key -// Uploads the encrypted reference, history and kvs to the store +// actEncryptionHandler is a middleware that encrypts the given address using the publisher's public key, +// uploads the encrypted reference, history and kvs to the store func (s *Service) actEncryptionHandler( ctx context.Context, w http.ResponseWriter, @@ -140,7 +140,7 @@ func (s *Service) actEncryptionHandler( return swarm.ZeroAddress, fmt.Errorf("act failed to encrypt reference: %w", err) } // only need to upload history and kvs if a new history is created, - // meaning that the publsher uploaded to the history for the first time + // meaning that the publisher uploaded to the history for the first time if !historyReference.Equal(historyRootHash) { err = putter.Done(storageReference) if err != nil { @@ -199,7 +199,8 @@ func (s *Service) actListGranteesHandler(w http.ResponseWriter, r *http.Request) jsonhttp.OK(w, granteeSlice) } -// TODO: actGrantRevokeHandler doc. +// actGrantRevokeHandler is a middleware that makes updates to the list of grantees, +// only the publisher is authorized to perform this action func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) { logger := s.logger.WithName("act_grant_revoke_handler").Build() @@ -363,7 +364,8 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) }) } -// TODO: actCreateGranteesHandler doc. +// actCreateGranteesHandler is a middleware that creates a new list of grantees, +// only the publisher is authorized to perform this action func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Request) { logger := s.logger.WithName("acthandler").Build() diff --git a/pkg/api/feed.go b/pkg/api/feed.go index 3d43d3d148e..552cd03ffa4 100644 --- a/pkg/api/feed.go +++ b/pkg/api/feed.go @@ -21,8 +21,8 @@ import ( "github.com/ethersphere/bee/v2/pkg/manifest/simple" "github.com/ethersphere/bee/v2/pkg/postage" "github.com/ethersphere/bee/v2/pkg/soc" - storage "github.com/ethersphere/bee/v2/pkg/storage" - storer "github.com/ethersphere/bee/v2/pkg/storer" + "github.com/ethersphere/bee/v2/pkg/storage" + "github.com/ethersphere/bee/v2/pkg/storer" "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/gorilla/mux" ) diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index 1cb9db8b5ba..aab18aa1174 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -9,7 +9,7 @@ import ( "crypto/ecdsa" "fmt" - encryption "github.com/ethersphere/bee/v2/pkg/encryption" + "github.com/ethersphere/bee/v2/pkg/encryption" "github.com/ethersphere/bee/v2/pkg/kvs" "github.com/ethersphere/bee/v2/pkg/swarm" "golang.org/x/crypto/sha3" @@ -26,13 +26,11 @@ var ( type Decryptor interface { // DecryptRef will return a decrypted reference, for given encrypted reference and grantee DecryptRef(ctx context.Context, storage kvs.KeyValueStore, encryptedRef swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) - // Embedding the Session interface Session } // Control interface for the ACT (does write operations). type Control interface { - // Embedding the Decryptor interface Decryptor // AddGrantee adds a new grantee to the ACT AddGrantee(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKey *encryption.Key) error @@ -122,7 +120,6 @@ func (al *ActLogic) getAccessKey(ctx context.Context, storage kvs.KeyValueStore, // Generate lookup key and access key decryption key for a given public key func (al *ActLogic) getKeys(publicKey *ecdsa.PublicKey) ([]byte, []byte, error) { nonces := [][]byte{zeroByteArray, oneByteArray} - // keys := make([][]byte, 0, len(nonces)) keys, err := al.Session.Key(publicKey, nonces) if keys == nil { return nil, nil, err diff --git a/pkg/dynamicaccess/accesslogic_test.go b/pkg/dynamicaccess/accesslogic_test.go index 8d6c146e468..a894c90e25f 100644 --- a/pkg/dynamicaccess/accesslogic_test.go +++ b/pkg/dynamicaccess/accesslogic_test.go @@ -77,14 +77,14 @@ func TestDecryptRef_Success(t *testing.T) { t.Error(err) } - acutalRef, err := al.DecryptRef(ctx, s, encryptedRef, &id0.PublicKey) + actualRef, err := al.DecryptRef(ctx, s, encryptedRef, &id0.PublicKey) if err != nil { t.Errorf("There was an error while calling Get: ") t.Error(err) } - if expectedRef.Compare(acutalRef) != 0 { - t.Errorf("Get gave back wrong Swarm reference!") + if expectedRef.Compare(actualRef) != 0 { + t.Errorf("Get returned a wrong Swarm reference!") } } @@ -120,14 +120,14 @@ func TestDecryptRefWithGrantee_Success(t *testing.T) { diffieHellman2 := dynamicaccess.NewDefaultSession(id1) granteeAccessLogic := dynamicaccess.NewLogic(diffieHellman2) - acutalRef, err := granteeAccessLogic.DecryptRef(ctx, s, encryptedRef, &id0.PublicKey) + actualRef, err := granteeAccessLogic.DecryptRef(ctx, s, encryptedRef, &id0.PublicKey) if err != nil { t.Errorf("There was an error while calling Get: ") t.Error(err) } - if expectedRef.Compare(acutalRef) != 0 { - t.Errorf("Get gave back wrong Swarm reference!") + if expectedRef.Compare(actualRef) != 0 { + t.Errorf("Get returned a wrong Swarm reference!") } } @@ -169,7 +169,7 @@ func TestAddPublisher(t *testing.T) { decodedEncryptedAccessKey := hex.EncodeToString(encryptedAccessKey) - // A random value is returned so it is only possibly to check the length of the returned value + // A random value is returned, so it is only possible to check the length of the returned value // We know the lookup key because the generated private key is fixed if len(decodedEncryptedAccessKey) != 64 { t.Errorf("AddPublisher: expected encrypted access key length 64, got %d", len(decodedEncryptedAccessKey)) diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index a8998aa12df..911c59f3f27 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -10,18 +10,15 @@ import ( "io" "time" - encryption "github.com/ethersphere/bee/v2/pkg/encryption" + "github.com/ethersphere/bee/v2/pkg/encryption" "github.com/ethersphere/bee/v2/pkg/file" - "github.com/ethersphere/bee/v2/pkg/file/pipeline" - "github.com/ethersphere/bee/v2/pkg/file/pipeline/builder" - "github.com/ethersphere/bee/v2/pkg/file/redundancy" "github.com/ethersphere/bee/v2/pkg/kvs" - "github.com/ethersphere/bee/v2/pkg/storage" "github.com/ethersphere/bee/v2/pkg/swarm" ) type GranteeManager interface { - // TODO: doc + // HandleGrantees manages the grantees for the given publisher, updating the list based on provided public keys to add or remove. + // Only the publisher can make changes to the grantee list. HandleGrantees(ctx context.Context, ls file.LoadSaver, gls file.LoadSaver, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) // GetGrantees returns the list of grantees for the given publisher. // The list is accessible only by the publisher. @@ -70,7 +67,7 @@ func (c *ControllerStruct) DownloadHandler( func (c *ControllerStruct) UploadHandler( ctx context.Context, ls file.LoadSaver, - refrefence swarm.Address, + reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, ) (swarm.Address, swarm.Address, swarm.Address, error) { @@ -121,7 +118,7 @@ func (c *ControllerStruct) UploadHandler( } } - encryptedRef, err := c.accessLogic.EncryptRef(ctx, storage, publisher, refrefence) + encryptedRef, err := c.accessLogic.EncryptRef(ctx, storage, publisher, reference) return actRef, historyRef, encryptedRef, err } @@ -265,7 +262,7 @@ func (c *ControllerStruct) HandleGrantees( return glref, eglref, href, actref, nil } -func (c *ControllerStruct) GetGrantees(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { +func (c *ControllerStruct) GetGrantees(_ context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { granteeRef, err := c.decryptRefForPublisher(publisher, encryptedglref) if err != nil { return nil, err @@ -305,13 +302,7 @@ func (c *ControllerStruct) decryptRefForPublisher(publisherPubKey *ecdsa.PublicK return swarm.NewAddress(ref), nil } -func requestPipelineFactory(ctx context.Context, s storage.Putter, encrypt bool, rLevel redundancy.Level) func() pipeline.Interface { - return func() pipeline.Interface { - return builder.NewPipelineBuilder(ctx, s, encrypt, rLevel) - } -} - // TODO: what to do in close ? -func (s *ControllerStruct) Close() error { +func (c *ControllerStruct) Close() error { return nil } From 6d805ae34222f756596e26e30ed6c3207f4d63d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= <58116288+bosi95@users.noreply.github.com> Date: Thu, 16 May 2024 15:46:43 +0200 Subject: [PATCH 36/41] refactor(act): naming and fix remaining PR comments (#42) * Refactor naming and fix remaining PR comments * use ctx in grantees.get * remove act_ucs.md --- act_ucs.md | 86 --------------------------- pkg/api/dynamicaccess.go | 6 +- pkg/dynamicaccess/accesslogic_test.go | 34 +++++------ pkg/dynamicaccess/controller.go | 20 +++---- pkg/dynamicaccess/controller_test.go | 36 +++++------ pkg/dynamicaccess/grantee.go | 4 +- pkg/dynamicaccess/grantee_test.go | 6 +- pkg/dynamicaccess/mock/controller.go | 4 +- 8 files changed, 53 insertions(+), 143 deletions(-) delete mode 100644 act_ucs.md diff --git a/act_ucs.md b/act_ucs.md deleted file mode 100644 index 14f93f346e4..00000000000 --- a/act_ucs.md +++ /dev/null @@ -1,86 +0,0 @@ -# ACT user stories - -This file contains the SWARM ACT user stories. - -ZenHub Link: [SpDevTeam](https://app.zenhub.com/workspaces/spdevteam-6544d91246b817002d853e69/board) - -### Context & definitions -- Content is created as a 'channel', i.e. as a feed with a specific topic. -- The content is uploaded to the feed, and new version of the content could be uploaded any time. -- The content can be accessed by the publisher and any viewers if it does not have ACT defined. -- The content can be accessed by the publisher and any viewers if it has ACT defined and the viewer is in the grantees list. -- A publisher is a user who is uploading new content. -- A viewer is a user who is accessing content. -- When a viewer is losing access it means that in the newly generated ACT the viewer will no longer be present in the grantee list. -- The publisher should be able to modify the ACT, using the same private key, but on multiple nodes in parallel. -- The publisher should be able to modify the grantee list, using the same private key, but on multiple nodes in parallel (this would potentially require the grantee list to be encrypted as well). - -### Use cases - -- [ ] **1** -- I'm a publisher -- I upload some new content -- I create an ACT for the content, but without additional grantees -- I can access the content -___ - -- [ ] **1/a** -- I'm a publisher -- I have an existing ACT for some content -- I can read and edit the ACT -___ - -- [ ] **2** -- I'm a publisher -- I have an existing ACT for some content, but without additional grantees -- I grant access to the content for some new viewers -- Those viewers can access the content -___ - -- [ ] **2/a** -- I'm a publisher -- I have an existing ACT for some content with some existing grantees -- I grant access to the content to additional viewers -- The existing viewers can access the content still -- The additional viewers can access the content -___ - -- [ ] **2/b** -- I'm a publisher -- I remove viewers from the grantee list of the ACT content -- The content is unchanged -- The removed viewers can access the content still -- The existing viewers can access the content still -___ - -- [ ] **2/c** -- I'm a publisher -- I remove viewers from the grantee list of the ACT content -- The content is updated -- The removed viewers can't access the latest version of content anymore -- The removed viewers can access an older version of the content, the version up until the moment they were removed -- The existing viewers can access the content still, including the latest version -___ - -- [ ] **3** -- I'm a viewer -- I requested access to the content -- If I was granted access, I can access the content -- If I was not granted access, I can't access the content still -___ - -- [ ] **4** -- I'm a viewer -- I had access to the content until now -- My access to the content got revoked -- The content is unchanged -- I can access the content still -___ - -- [ ] **4/a** -- I'm a viewer -- I had access to the content until now -- My access to the content got revoked -- The content is updated -- I can't access versions of the content that were uploaded after I lost access -___ diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index b0be09fbcfa..e776857d190 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -185,7 +185,7 @@ func (s *Service) actListGranteesHandler(w http.ResponseWriter, r *http.Request) } publisher := &s.publicKey ls := loadsave.NewReadonly(s.storer.Download(cache)) - grantees, err := s.dac.GetGrantees(r.Context(), ls, publisher, paths.GranteesAddress) + grantees, err := s.dac.Get(r.Context(), ls, publisher, paths.GranteesAddress) if err != nil { logger.Debug("could not get grantees", "error", err) logger.Error(nil, "could not get grantees") @@ -326,7 +326,7 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) publisher := &s.publicKey ls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, false, redundancy.NONE)) gls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, granteeListEncrypt, redundancy.NONE)) - granteeref, encryptedglref, historyref, actref, err := s.dac.HandleGrantees(ctx, ls, gls, granteeref, historyAddress, publisher, grantees.Addlist, grantees.Revokelist) + granteeref, encryptedglref, historyref, actref, err := s.dac.UpdateHandler(ctx, ls, gls, granteeref, historyAddress, publisher, grantees.Addlist, grantees.Revokelist) if err != nil { logger.Debug("failed to update grantee list", "error", err) logger.Error(nil, "failed to update grantee list") @@ -471,7 +471,7 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques publisher := &s.publicKey ls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, false, redundancy.NONE)) gls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, granteeListEncrypt, redundancy.NONE)) - granteeref, encryptedglref, historyref, actref, err := s.dac.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, historyAddress, publisher, list, nil) + granteeref, encryptedglref, historyref, actref, err := s.dac.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, historyAddress, publisher, list, nil) if err != nil { logger.Debug("failed to update grantee list", "error", err) logger.Error(nil, "failed to update grantee list") diff --git a/pkg/dynamicaccess/accesslogic_test.go b/pkg/dynamicaccess/accesslogic_test.go index a894c90e25f..93b65f1bdaf 100644 --- a/pkg/dynamicaccess/accesslogic_test.go +++ b/pkg/dynamicaccess/accesslogic_test.go @@ -62,7 +62,7 @@ func TestDecryptRef_Success(t *testing.T) { al := setupAccessLogic() err := al.AddPublisher(ctx, s, &id0.PublicKey) if err != nil { - t.Errorf("AddPublisher: expected no error, got %v", err) + t.Fatalf("AddPublisher: expected no error, got %v", err) } byteRef, _ := hex.DecodeString("39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559") @@ -73,18 +73,16 @@ func TestDecryptRef_Success(t *testing.T) { encryptedRef, err := al.EncryptRef(ctx, s, &id0.PublicKey, expectedRef) t.Logf("encryptedRef: %s", encryptedRef.String()) if err != nil { - t.Errorf("There was an error while calling EncryptRef: ") - t.Error(err) + t.Fatalf("There was an error while calling EncryptRef: %v", err) } actualRef, err := al.DecryptRef(ctx, s, encryptedRef, &id0.PublicKey) if err != nil { - t.Errorf("There was an error while calling Get: ") - t.Error(err) + t.Fatalf("There was an error while calling Get: %v", err) } if expectedRef.Compare(actualRef) != 0 { - t.Errorf("Get returned a wrong Swarm reference!") + t.Fatalf("Get gave back wrong Swarm reference!") } } @@ -97,13 +95,13 @@ func TestDecryptRefWithGrantee_Success(t *testing.T) { s := kvsmock.New() err := al.AddPublisher(ctx, s, &id0.PublicKey) if err != nil { - t.Errorf("AddPublisher: expected no error, got %v", err) + t.Fatalf("AddPublisher: expected no error, got %v", err) } id1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) err = al.AddGrantee(ctx, s, &id0.PublicKey, &id1.PublicKey, nil) if err != nil { - t.Errorf("AddNewGrantee: expected no error, got %v", err) + t.Fatalf("AddNewGrantee: expected no error, got %v", err) } byteRef, _ := hex.DecodeString("39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559") @@ -114,20 +112,18 @@ func TestDecryptRefWithGrantee_Success(t *testing.T) { encryptedRef, err := al.EncryptRef(ctx, s, &id0.PublicKey, expectedRef) t.Logf("encryptedRef: %s", encryptedRef.String()) if err != nil { - t.Errorf("There was an error while calling EncryptRef: ") - t.Error(err) + t.Fatalf("There was an error while calling EncryptRef: %v", err) } diffieHellman2 := dynamicaccess.NewDefaultSession(id1) granteeAccessLogic := dynamicaccess.NewLogic(diffieHellman2) actualRef, err := granteeAccessLogic.DecryptRef(ctx, s, encryptedRef, &id0.PublicKey) if err != nil { - t.Errorf("There was an error while calling Get: ") - t.Error(err) + t.Fatalf("There was an error while calling Get: %v", err) } if expectedRef.Compare(actualRef) != 0 { - t.Errorf("Get returned a wrong Swarm reference!") + t.Fatalf("Get gave back wrong Swarm reference!") } } @@ -147,7 +143,7 @@ func TestDecryptRef_Error(t *testing.T) { r, err := al.DecryptRef(ctx, s, encryptedRef, nil) if err == nil { t.Logf("r: %s", r.String()) - t.Errorf("Get should return encrypted access key not found error!") + t.Fatalf("Get should return encrypted access key not found error!") } } @@ -172,10 +168,10 @@ func TestAddPublisher(t *testing.T) { // A random value is returned, so it is only possible to check the length of the returned value // We know the lookup key because the generated private key is fixed if len(decodedEncryptedAccessKey) != 64 { - t.Errorf("AddPublisher: expected encrypted access key length 64, got %d", len(decodedEncryptedAccessKey)) + t.Fatalf("AddPublisher: expected encrypted access key length 64, got %d", len(decodedEncryptedAccessKey)) } if s == nil { - t.Errorf("AddPublisher: expected act, got nil") + t.Fatalf("AddPublisher: expected act, got nil") } } @@ -206,7 +202,7 @@ func TestAddNewGranteeToContent(t *testing.T) { result, _ := s.Get(ctx, lookupKeyAsByte) hexEncodedEncryptedAK := hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { - t.Errorf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) + t.Fatalf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) } lookupKeyAsByte, err = hex.DecodeString(firstAddedGranteeLookupKey) @@ -215,7 +211,7 @@ func TestAddNewGranteeToContent(t *testing.T) { result, _ = s.Get(ctx, lookupKeyAsByte) hexEncodedEncryptedAK = hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { - t.Errorf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) + t.Fatalf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) } lookupKeyAsByte, err = hex.DecodeString(secondAddedGranteeLookupKey) @@ -224,6 +220,6 @@ func TestAddNewGranteeToContent(t *testing.T) { result, _ = s.Get(ctx, lookupKeyAsByte) hexEncodedEncryptedAK = hex.EncodeToString(result) if len(hexEncodedEncryptedAK) != 64 { - t.Errorf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) + t.Fatalf("AddNewGrantee: expected encrypted access key length 64, got %d", len(hexEncodedEncryptedAK)) } } diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index 911c59f3f27..2b3c9e24ace 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -16,17 +16,17 @@ import ( "github.com/ethersphere/bee/v2/pkg/swarm" ) -type GranteeManager interface { - // HandleGrantees manages the grantees for the given publisher, updating the list based on provided public keys to add or remove. +type Grantees interface { + // UpdateHandler manages the grantees for the given publisher, updating the list based on provided public keys to add or remove. // Only the publisher can make changes to the grantee list. - HandleGrantees(ctx context.Context, ls file.LoadSaver, gls file.LoadSaver, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) - // GetGrantees returns the list of grantees for the given publisher. + UpdateHandler(ctx context.Context, ls file.LoadSaver, gls file.LoadSaver, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) + // Get returns the list of grantees for the given publisher. // The list is accessible only by the publisher. - GetGrantees(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) + Get(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) } type Controller interface { - GranteeManager + Grantees // DownloadHandler decrypts the encryptedRef using the lookupkey based on the history and timestamp. DownloadHandler(ctx context.Context, ls file.LoadSaver, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) // UploadHandler encrypts the reference and stores it in the history as the latest update. @@ -128,7 +128,7 @@ func NewController(accessLogic ActLogic) *ControllerStruct { } } -func (c *ControllerStruct) HandleGrantees( +func (c *ControllerStruct) UpdateHandler( ctx context.Context, ls file.LoadSaver, gls file.LoadSaver, @@ -186,7 +186,7 @@ func (c *ControllerStruct) HandleGrantees( return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - gl, err = NewGranteeListReference(gls, granteeref) + gl, err = NewGranteeListReference(ctx, gls, granteeref) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } @@ -262,12 +262,12 @@ func (c *ControllerStruct) HandleGrantees( return glref, eglref, href, actref, nil } -func (c *ControllerStruct) GetGrantees(_ context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { +func (c *ControllerStruct) Get(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { granteeRef, err := c.decryptRefForPublisher(publisher, encryptedglref) if err != nil { return nil, err } - gl, err := NewGranteeListReference(ls, granteeRef) + gl, err := NewGranteeListReference(ctx, ls, granteeRef) if err != nil { return nil, err } diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index 05b4e76db11..6aa7757a5ca 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -164,27 +164,27 @@ func TestController_HandleGrantees(t *testing.T) { t.Run("add to new list", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, _, _, _, err := c.HandleGrantees(ctx, ls, ls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + granteeRef, _, _, _, err := c.UpdateHandler(ctx, ls, ls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) assert.NoError(t, err) - gl, err := dynamicaccess.NewGranteeListReference(ls, granteeRef) + gl, err := dynamicaccess.NewGranteeListReference(ctx, ls, granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 1) }) t.Run("add to existing list", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, eglref, _, _, err := c.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + granteeRef, eglref, _, _, err := c.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) assert.NoError(t, err) - gl, err := dynamicaccess.NewGranteeListReference(ls, granteeRef) + gl, err := dynamicaccess.NewGranteeListReference(ctx, ls, granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 1) addList = []*ecdsa.PublicKey{&getPrivKey(0).PublicKey} - granteeRef, _, _, _, _ = c.HandleGrantees(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil) - gl, err = dynamicaccess.NewGranteeListReference(ls, granteeRef) + granteeRef, _, _, _, _ = c.UpdateHandler(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil) + gl, err = dynamicaccess.NewGranteeListReference(ctx, ls, granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 2) }) @@ -196,8 +196,8 @@ func TestController_HandleGrantees(t *testing.T) { granteeRef, _ := gl.Save(ctx) eglref, _ := refCipher.Encrypt(granteeRef.Bytes()) - granteeRef, _, _, _, _ = c.HandleGrantees(ctx, ls, gls, swarm.NewAddress(eglref), href, &publisher.PublicKey, addList, revokeList) - gl, err := dynamicaccess.NewGranteeListReference(ls, granteeRef) + granteeRef, _, _, _, _ = c.UpdateHandler(ctx, ls, gls, swarm.NewAddress(eglref), href, &publisher.PublicKey, addList, revokeList) + gl, err := dynamicaccess.NewGranteeListReference(ctx, ls, granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 2) @@ -206,17 +206,17 @@ func TestController_HandleGrantees(t *testing.T) { t.Run("add twice", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey, &grantee.PublicKey} //nolint:ineffassign,staticcheck,wastedassign - granteeRef, eglref, _, _, err := c.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) - granteeRef, _, _, _, _ = c.HandleGrantees(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil) - gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + granteeRef, eglref, _, _, err := c.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + granteeRef, _, _, _, _ = c.UpdateHandler(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil) + gl, err := dynamicaccess.NewGranteeListReference(ctx, createLs(), granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 1) }) t.Run("revoke non-existing", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, _, _, _, _ := c.HandleGrantees(ctx, ls, ls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) - gl, err := dynamicaccess.NewGranteeListReference(createLs(), granteeRef) + granteeRef, _, _, _, _ := c.UpdateHandler(ctx, ls, ls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) + gl, err := dynamicaccess.NewGranteeListReference(ctx, createLs(), granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 1) @@ -239,19 +239,19 @@ func TestController_GetGrantees(t *testing.T) { t.Run("get by publisher", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - granteeRef, eglRef, _, _, _ := c1.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + granteeRef, eglRef, _, _, _ := c1.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) - grantees, err := c1.GetGrantees(ctx, ls, &publisher.PublicKey, eglRef) + grantees, err := c1.Get(ctx, ls, &publisher.PublicKey, eglRef) assert.NoError(t, err) assert.True(t, reflect.DeepEqual(grantees, addList)) - gl, _ := dynamicaccess.NewGranteeListReference(ls, granteeRef) + gl, _ := dynamicaccess.NewGranteeListReference(ctx, ls, granteeRef) assert.True(t, reflect.DeepEqual(gl.Get(), addList)) }) t.Run("get by non-publisher", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} - _, eglRef, _, _, _ := c1.HandleGrantees(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) - grantees, err := c2.GetGrantees(ctx, ls, &publisher.PublicKey, eglRef) + _, eglRef, _, _, _ := c1.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, swarm.ZeroAddress, &publisher.PublicKey, addList, nil) + grantees, err := c2.Get(ctx, ls, &publisher.PublicKey, eglRef) assert.Error(t, err) assert.Nil(t, grantees) }) diff --git a/pkg/dynamicaccess/grantee.go b/pkg/dynamicaccess/grantee.go index 57e6429f7da..44832032754 100644 --- a/pkg/dynamicaccess/grantee.go +++ b/pkg/dynamicaccess/grantee.go @@ -116,8 +116,8 @@ func NewGranteeList(ls file.LoadSaver) (*GranteeListStruct, error) { // Why is t }, nil } -func NewGranteeListReference(ls file.LoadSaver, reference swarm.Address) (*GranteeListStruct, error) { - data, err := ls.Load(context.Background(), reference.Bytes()) +func NewGranteeListReference(ctx context.Context, ls file.LoadSaver, reference swarm.Address) (*GranteeListStruct, error) { + data, err := ls.Load(ctx, reference.Bytes()) if err != nil { return nil, fmt.Errorf("unable to load reference, %w", err) } diff --git a/pkg/dynamicaccess/grantee_test.go b/pkg/dynamicaccess/grantee_test.go index cd69aaa752c..91e44569b2a 100644 --- a/pkg/dynamicaccess/grantee_test.go +++ b/pkg/dynamicaccess/grantee_test.go @@ -169,7 +169,7 @@ func TestGranteeSave(t *testing.T) { t.Errorf("key generation error: %v", err) } t.Run("Create grantee list with invalid reference, expect error", func(t *testing.T) { - gl, err := dynamicaccess.NewGranteeListReference(createLs(), swarm.RandAddress(t)) + gl, err := dynamicaccess.NewGranteeListReference(ctx, createLs(), swarm.RandAddress(t)) assert.Error(t, err) assert.Nil(t, gl) }) @@ -195,7 +195,7 @@ func TestGranteeSave(t *testing.T) { ref, err := gl1.Save(ctx) assert.NoError(t, err) - gl2, _ := dynamicaccess.NewGranteeListReference(ls, ref) + gl2, _ := dynamicaccess.NewGranteeListReference(ctx, ls, ref) val := gl2.Get() assert.NoError(t, err) assert.Equal(t, keys, val) @@ -211,7 +211,7 @@ func TestGranteeSave(t *testing.T) { ref, err := gl1.Save(ctx) assert.NoError(t, err) - gl2, _ := dynamicaccess.NewGranteeListReference(ls, ref) + gl2, _ := dynamicaccess.NewGranteeListReference(ctx, ls, ref) err = gl2.Add(keys2) assert.NoError(t, err) diff --git a/pkg/dynamicaccess/mock/controller.go b/pkg/dynamicaccess/mock/controller.go index 7f77ca2ca32..3db86c88020 100644 --- a/pkg/dynamicaccess/mock/controller.go +++ b/pkg/dynamicaccess/mock/controller.go @@ -140,7 +140,7 @@ func (m *mockDacService) Close() error { return nil } -func (m *mockDacService) HandleGrantees(_ context.Context, ls file.LoadSaver, gls file.LoadSaver, encryptedglref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList []*ecdsa.PublicKey, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) { +func (m *mockDacService) UpdateHandler(_ context.Context, ls file.LoadSaver, gls file.LoadSaver, encryptedglref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList []*ecdsa.PublicKey, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) { historyRef, _ := swarm.ParseHexAddress("67bdf80a9bbea8eca9c8480e43fdceb485d2d74d5708e45144b8c4adacd13d9c") glRef, _ := swarm.ParseHexAddress("3339613565613837623134316665343461613630396333333237656364383934") eglRef, _ := swarm.ParseHexAddress("fc4e9fe978991257b897d987bc4ff13058b66ef45a53189a0b4fe84bb3346396") @@ -148,7 +148,7 @@ func (m *mockDacService) HandleGrantees(_ context.Context, ls file.LoadSaver, gl return glRef, eglRef, historyRef, actref, nil } -func (m *mockDacService) GetGrantees(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { +func (m *mockDacService) Get(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { if m.publisher == "" { return nil, fmt.Errorf("granteelist not found") } From f5d8cb149c8c2525a1f402234888e120a67fde46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= <58116288+bosi95@users.noreply.github.com> Date: Fri, 17 May 2024 09:52:51 +0200 Subject: [PATCH 37/41] Refactor accesslogic.AddGrantee and parallelize tests (#43) --- pkg/dynamicaccess/accesslogic.go | 19 ++++------- pkg/dynamicaccess/accesslogic_test.go | 49 +++++++++++++-------------- pkg/dynamicaccess/controller.go | 8 ++--- pkg/dynamicaccess/controller_test.go | 19 +++++++---- pkg/dynamicaccess/grantee_test.go | 3 ++ pkg/dynamicaccess/history_test.go | 4 +++ pkg/kvs/kvs_test.go | 2 ++ 7 files changed, 55 insertions(+), 49 deletions(-) diff --git a/pkg/dynamicaccess/accesslogic.go b/pkg/dynamicaccess/accesslogic.go index aab18aa1174..671537486e9 100644 --- a/pkg/dynamicaccess/accesslogic.go +++ b/pkg/dynamicaccess/accesslogic.go @@ -33,7 +33,7 @@ type Decryptor interface { type Control interface { Decryptor // AddGrantee adds a new grantee to the ACT - AddGrantee(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKey *encryption.Key) error + AddGrantee(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey) error // EncryptRef encrypts a Swarm reference for a given grantee EncryptRef(ctx context.Context, storage kvs.KeyValueStore, grantee *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) } @@ -44,13 +44,6 @@ type ActLogic struct { var _ Control = (*ActLogic)(nil) -// AddPublisher adds a new publisher to an empty act. -func (al ActLogic) AddPublisher(ctx context.Context, storage kvs.KeyValueStore, publisher *ecdsa.PublicKey) error { - accessKey := encryption.GenerateRandomKey(encryption.KeyLength) - - return al.AddGrantee(ctx, storage, publisher, publisher, &accessKey) -} - // EncryptRef encrypts a SWARM reference for a publisher. func (al ActLogic) EncryptRef(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) { accessKey, err := al.getAccessKey(ctx, storage, publisherPubKey) @@ -67,21 +60,21 @@ func (al ActLogic) EncryptRef(ctx context.Context, storage kvs.KeyValueStore, pu } // AddGrantee adds a new grantee to the ACT. -func (al ActLogic) AddGrantee(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey, accessKeyPointer *encryption.Key) error { +func (al ActLogic) AddGrantee(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey) error { var ( accessKey encryption.Key err error ) - if accessKeyPointer == nil { + // Create new access key because grantee is the publisher + if publisherPubKey.Equal(granteePubKey) { + accessKey = encryption.GenerateRandomKey(encryption.KeyLength) + } else { // Get previously generated access key accessKey, err = al.getAccessKey(ctx, storage, publisherPubKey) if err != nil { return err } - } else { - // This is a newly created access key, because grantee is publisher (they are the same) - accessKey = *accessKeyPointer } // Encrypt the access key for the new Grantee diff --git a/pkg/dynamicaccess/accesslogic_test.go b/pkg/dynamicaccess/accesslogic_test.go index 93b65f1bdaf..b8cab2f8fbf 100644 --- a/pkg/dynamicaccess/accesslogic_test.go +++ b/pkg/dynamicaccess/accesslogic_test.go @@ -56,50 +56,48 @@ func getPrivKey(keyNumber int) *ecdsa.PrivateKey { } func TestDecryptRef_Success(t *testing.T) { + t.Parallel() ctx := context.Background() - id0 := getPrivKey(0) + id1 := getPrivKey(1) s := kvsmock.New() al := setupAccessLogic() - err := al.AddPublisher(ctx, s, &id0.PublicKey) + err := al.AddGrantee(ctx, s, &id1.PublicKey, &id1.PublicKey) if err != nil { - t.Fatalf("AddPublisher: expected no error, got %v", err) + t.Fatalf("AddGrantee: expected no error, got %v", err) } byteRef, _ := hex.DecodeString("39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559") - expectedRef := swarm.NewAddress(byteRef) - t.Logf("encryptedRef: %s", expectedRef.String()) - - encryptedRef, err := al.EncryptRef(ctx, s, &id0.PublicKey, expectedRef) - t.Logf("encryptedRef: %s", encryptedRef.String()) + encryptedRef, err := al.EncryptRef(ctx, s, &id1.PublicKey, expectedRef) if err != nil { t.Fatalf("There was an error while calling EncryptRef: %v", err) } - actualRef, err := al.DecryptRef(ctx, s, encryptedRef, &id0.PublicKey) + actualRef, err := al.DecryptRef(ctx, s, encryptedRef, &id1.PublicKey) if err != nil { t.Fatalf("There was an error while calling Get: %v", err) } - if expectedRef.Compare(actualRef) != 0 { - t.Fatalf("Get gave back wrong Swarm reference!") + if !expectedRef.Equal(actualRef) { + t.Fatalf("DecryptRef gave back wrong Swarm reference! Expedted: %v, actual: %v", expectedRef, actualRef) } } func TestDecryptRefWithGrantee_Success(t *testing.T) { + t.Parallel() ctx := context.Background() id0, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) diffieHellman := dynamicaccess.NewDefaultSession(id0) al := dynamicaccess.NewLogic(diffieHellman) s := kvsmock.New() - err := al.AddPublisher(ctx, s, &id0.PublicKey) + err := al.AddGrantee(ctx, s, &id0.PublicKey, &id0.PublicKey) if err != nil { - t.Fatalf("AddPublisher: expected no error, got %v", err) + t.Fatalf("AddGrantee: expected no error, got %v", err) } id1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - err = al.AddGrantee(ctx, s, &id0.PublicKey, &id1.PublicKey, nil) + err = al.AddGrantee(ctx, s, &id0.PublicKey, &id1.PublicKey) if err != nil { t.Fatalf("AddNewGrantee: expected no error, got %v", err) } @@ -107,10 +105,8 @@ func TestDecryptRefWithGrantee_Success(t *testing.T) { byteRef, _ := hex.DecodeString("39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559") expectedRef := swarm.NewAddress(byteRef) - t.Logf("encryptedRef: %s", expectedRef.String()) encryptedRef, err := al.EncryptRef(ctx, s, &id0.PublicKey, expectedRef) - t.Logf("encryptedRef: %s", encryptedRef.String()) if err != nil { t.Fatalf("There was an error while calling EncryptRef: %v", err) } @@ -122,18 +118,19 @@ func TestDecryptRefWithGrantee_Success(t *testing.T) { t.Fatalf("There was an error while calling Get: %v", err) } - if expectedRef.Compare(actualRef) != 0 { - t.Fatalf("Get gave back wrong Swarm reference!") + if !expectedRef.Equal(actualRef) { + t.Fatalf("DecryptRef gave back wrong Swarm reference! Expedted: %v, actual: %v", expectedRef, actualRef) } } func TestDecryptRef_Error(t *testing.T) { + t.Parallel() id0 := getPrivKey(0) ctx := context.Background() s := kvsmock.New() al := setupAccessLogic() - err := al.AddPublisher(ctx, s, &id0.PublicKey) + err := al.AddGrantee(ctx, s, &id0.PublicKey, &id0.PublicKey) assert.NoError(t, err) expectedRef := "39a5ea87b141fe44aa609c3327ecd896c0e2122897f5f4bbacf74db1033c5559" @@ -148,13 +145,14 @@ func TestDecryptRef_Error(t *testing.T) { } func TestAddPublisher(t *testing.T) { + t.Parallel() id0 := getPrivKey(0) savedLookupKey := "b6ee086390c280eeb9824c331a4427596f0c8510d5564bc1b6168d0059a46e2b" s := kvsmock.New() ctx := context.Background() al := setupAccessLogic() - err := al.AddPublisher(ctx, s, &id0.PublicKey) + err := al.AddGrantee(ctx, s, &id0.PublicKey, &id0.PublicKey) assert.NoError(t, err) decodedSavedLookupKey, err := hex.DecodeString(savedLookupKey) @@ -168,14 +166,15 @@ func TestAddPublisher(t *testing.T) { // A random value is returned, so it is only possible to check the length of the returned value // We know the lookup key because the generated private key is fixed if len(decodedEncryptedAccessKey) != 64 { - t.Fatalf("AddPublisher: expected encrypted access key length 64, got %d", len(decodedEncryptedAccessKey)) + t.Fatalf("AddGrantee: expected encrypted access key length 64, got %d", len(decodedEncryptedAccessKey)) } if s == nil { - t.Fatalf("AddPublisher: expected act, got nil") + t.Fatalf("AddGrantee: expected act, got nil") } } func TestAddNewGranteeToContent(t *testing.T) { + t.Parallel() id0 := getPrivKey(0) id1 := getPrivKey(1) id2 := getPrivKey(2) @@ -187,13 +186,13 @@ func TestAddNewGranteeToContent(t *testing.T) { s := kvsmock.New() al := setupAccessLogic() - err := al.AddPublisher(ctx, s, &id0.PublicKey) + err := al.AddGrantee(ctx, s, &id0.PublicKey, &id0.PublicKey) assert.NoError(t, err) - err = al.AddGrantee(ctx, s, &id0.PublicKey, &id1.PublicKey, nil) + err = al.AddGrantee(ctx, s, &id0.PublicKey, &id1.PublicKey) assert.NoError(t, err) - err = al.AddGrantee(ctx, s, &id0.PublicKey, &id2.PublicKey, nil) + err = al.AddGrantee(ctx, s, &id0.PublicKey, &id2.PublicKey) assert.NoError(t, err) lookupKeyAsByte, err := hex.DecodeString(publisherLookupKey) diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index 2b3c9e24ace..030e7c3316d 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -86,7 +86,7 @@ func (c *ControllerStruct) UploadHandler( if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - err = c.accessLogic.AddPublisher(ctx, storage, publisher) + err = c.accessLogic.AddGrantee(ctx, storage, publisher, publisher) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } @@ -168,7 +168,7 @@ func (c *ControllerStruct) UpdateHandler( if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - err = c.accessLogic.AddPublisher(ctx, act, publisher) + err = c.accessLogic.AddGrantee(ctx, act, publisher, publisher) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } @@ -211,7 +211,7 @@ func (c *ControllerStruct) UpdateHandler( if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - err = c.accessLogic.AddPublisher(ctx, act, publisher) + err = c.accessLogic.AddGrantee(ctx, act, publisher, publisher) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } @@ -221,7 +221,7 @@ func (c *ControllerStruct) UpdateHandler( } for _, grantee := range granteesToAdd { - err := c.accessLogic.AddGrantee(ctx, act, publisher, grantee, nil) + err := c.accessLogic.AddGrantee(ctx, act, publisher, grantee) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index 6aa7757a5ca..7118368afa8 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -32,15 +32,15 @@ func getHistoryFixture(ctx context.Context, ls file.LoadSaver, al dynamicaccess. pk2 := getPrivKey(2) kvs0, _ := kvs.New(ls) - al.AddPublisher(ctx, kvs0, publisher) + al.AddGrantee(ctx, kvs0, publisher, publisher) kvs0Ref, _ := kvs0.Save(ctx) kvs1, _ := kvs.New(ls) - al.AddPublisher(ctx, kvs1, publisher) - al.AddGrantee(ctx, kvs1, publisher, &pk1.PublicKey, nil) + al.AddGrantee(ctx, kvs1, publisher, publisher) + al.AddGrantee(ctx, kvs1, publisher, &pk1.PublicKey) kvs1Ref, _ := kvs1.Save(ctx) kvs2, _ := kvs.New(ls) - al.AddPublisher(ctx, kvs2, publisher) - al.AddGrantee(ctx, kvs2, publisher, &pk2.PublicKey, nil) + al.AddGrantee(ctx, kvs2, publisher, publisher) + al.AddGrantee(ctx, kvs2, publisher, &pk2.PublicKey) kvs2Ref, _ := kvs2.Save(ctx) firstTime := time.Date(1994, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() secondTime := time.Date(2000, time.April, 1, 0, 0, 0, 0, time.UTC).Unix() @@ -53,6 +53,7 @@ func getHistoryFixture(ctx context.Context, ls file.LoadSaver, al dynamicaccess. } func TestController_UploadHandler(t *testing.T) { + t.Parallel() ctx := context.Background() publisher := getPrivKey(0) diffieHellman := dynamicaccess.NewDefaultSession(publisher) @@ -101,6 +102,7 @@ func TestController_UploadHandler(t *testing.T) { } func TestController_PublisherDownload(t *testing.T) { + t.Parallel() ctx := context.Background() publisher := getPrivKey(0) diffieHellman := dynamicaccess.NewDefaultSession(publisher) @@ -122,6 +124,7 @@ func TestController_PublisherDownload(t *testing.T) { } func TestController_GranteeDownload(t *testing.T) { + t.Parallel() ctx := context.Background() publisher := getPrivKey(0) grantee := getPrivKey(2) @@ -147,7 +150,8 @@ func TestController_GranteeDownload(t *testing.T) { assert.Equal(t, ref, dref) } -func TestController_HandleGrantees(t *testing.T) { +func TestController_UpdateHandler(t *testing.T) { + t.Parallel() ctx := context.Background() publisher := getPrivKey(1) diffieHellman := dynamicaccess.NewDefaultSession(publisher) @@ -223,7 +227,8 @@ func TestController_HandleGrantees(t *testing.T) { }) } -func TestController_GetGrantees(t *testing.T) { +func TestController_Get(t *testing.T) { + t.Parallel() ctx := context.Background() publisher := getPrivKey(1) caller := getPrivKey(0) diff --git a/pkg/dynamicaccess/grantee_test.go b/pkg/dynamicaccess/grantee_test.go index 91e44569b2a..f9a9b0904f8 100644 --- a/pkg/dynamicaccess/grantee_test.go +++ b/pkg/dynamicaccess/grantee_test.go @@ -52,6 +52,7 @@ func generateKeyListFixture() ([]*ecdsa.PublicKey, error) { } func TestGranteeAddGet(t *testing.T) { + t.Parallel() gl, _ := dynamicaccess.NewGranteeList(createLs()) keys, err := generateKeyListFixture() if err != nil { @@ -116,6 +117,7 @@ func TestGranteeAddGet(t *testing.T) { } func TestGranteeRemove(t *testing.T) { + t.Parallel() gl, _ := dynamicaccess.NewGranteeList(createLs()) keys, err := generateKeyListFixture() if err != nil { @@ -163,6 +165,7 @@ func TestGranteeRemove(t *testing.T) { } func TestGranteeSave(t *testing.T) { + t.Parallel() ctx := context.Background() keys, err := generateKeyListFixture() if err != nil { diff --git a/pkg/dynamicaccess/history_test.go b/pkg/dynamicaccess/history_test.go index edc86dc7f22..871b5241eab 100644 --- a/pkg/dynamicaccess/history_test.go +++ b/pkg/dynamicaccess/history_test.go @@ -21,6 +21,7 @@ import ( ) func TestHistoryAdd(t *testing.T) { + t.Parallel() h, err := dynamicaccess.NewHistory(nil) assert.NoError(t, err) @@ -33,6 +34,7 @@ func TestHistoryAdd(t *testing.T) { } func TestSingleNodeHistoryLookup(t *testing.T) { + t.Parallel() storer := mockstorer.New() ctx := context.Background() ls := loadsave.New(storer.ChunkStore(), storer.Cache(), pipelineFactory(storer.Cache(), false)) @@ -56,6 +58,7 @@ func TestSingleNodeHistoryLookup(t *testing.T) { } func TestMultiNodeHistoryLookup(t *testing.T) { + t.Parallel() storer := mockstorer.New() ctx := context.Background() ls := loadsave.New(storer.ChunkStore(), storer.Cache(), pipelineFactory(storer.Cache(), false)) @@ -121,6 +124,7 @@ func TestMultiNodeHistoryLookup(t *testing.T) { } func TestHistoryStore(t *testing.T) { + t.Parallel() storer := mockstorer.New() ctx := context.Background() ls := loadsave.New(storer.ChunkStore(), storer.Cache(), pipelineFactory(storer.Cache(), false)) diff --git a/pkg/kvs/kvs_test.go b/pkg/kvs/kvs_test.go index e2da7aa7030..2f9af999a95 100644 --- a/pkg/kvs/kvs_test.go +++ b/pkg/kvs/kvs_test.go @@ -38,6 +38,7 @@ func keyValuePair(t *testing.T) ([]byte, []byte) { } func TestKvs(t *testing.T) { + t.Parallel() s, err := kvs.New(createLs()) assert.NoError(t, err) @@ -119,6 +120,7 @@ func TestKvs(t *testing.T) { } func TestKvs_Save(t *testing.T) { + t.Parallel() ctx := context.Background() key1, val1 := keyValuePair(t) From b80c899eefffd8bbd671cc209bceeda840c042af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A1s=20Ar=C3=A1nyi?= Date: Wed, 22 May 2024 12:58:57 +0400 Subject: [PATCH 38/41] docs(act): following changes to openapi with debug API removal --- openapi/Swarm.yaml | 36 +++++++++++++++++++++ openapi/SwarmCommon.yaml | 70 ++++++++++++++++++++++++++++++++++++++++ openapi/SwarmDebug.yaml | 0 3 files changed, 106 insertions(+) create mode 100644 openapi/SwarmDebug.yaml diff --git a/openapi/Swarm.yaml b/openapi/Swarm.yaml index 2ef77a999ef..920710bafc4 100644 --- a/openapi/Swarm.yaml +++ b/openapi/Swarm.yaml @@ -109,6 +109,9 @@ paths: - $ref: "SwarmCommon.yaml#/components/parameters/SwarmRedundancyStrategyParameter" - $ref: "SwarmCommon.yaml#/components/parameters/SwarmRedundancyFallbackModeParameter" - $ref: "SwarmCommon.yaml#/components/parameters/SwarmChunkRetrievalTimeoutParameter" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActTimestamp" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActPublisher" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActHistoryAddress" responses: "200": description: Retrieved content specified by reference @@ -132,6 +135,9 @@ paths: $ref: "SwarmCommon.yaml#/components/schemas/SwarmAddress" required: true description: Swarm address of chunk + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActTimestamp" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActPublisher" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActHistoryAddress" responses: "200": description: Chunk exists @@ -150,6 +156,8 @@ paths: parameters: - $ref: "SwarmCommon.yaml#/components/parameters/SwarmTagParameter" - $ref: "SwarmCommon.yaml#/components/parameters/SwarmPostageBatchId" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmAct" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActHistoryAddress" requestBody: description: Chunk binary data that has to have at least 8 bytes. content: @@ -165,6 +173,8 @@ paths: description: Tag UID if it was passed to the request `swarm-tag` header. schema: $ref: "SwarmCommon.yaml#/components/schemas/Uid" + "swarm-act-history-address": + $ref: "SwarmCommon.yaml#/components/headers/SwarmActHistoryAddress" content: application/json: schema: @@ -221,6 +231,8 @@ paths: - $ref: "SwarmCommon.yaml#/components/parameters/SwarmPostageBatchId" - $ref: "SwarmCommon.yaml#/components/parameters/SwarmDeferredUpload" - $ref: "SwarmCommon.yaml#/components/parameters/SwarmRedundancyLevelParameter" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmAct" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActHistoryAddress" requestBody: content: multipart/form-data: @@ -247,6 +259,8 @@ paths: $ref: "SwarmCommon.yaml#/components/headers/SwarmTag" "etag": $ref: "SwarmCommon.yaml#/components/headers/ETag" + "swarm-act-history-address": + $ref: "SwarmCommon.yaml#/components/headers/SwarmActHistoryAddress" content: application/json: schema: @@ -276,6 +290,9 @@ paths: - $ref: "SwarmCommon.yaml#/components/parameters/SwarmRedundancyStrategyParameter" - $ref: "SwarmCommon.yaml#/components/parameters/SwarmRedundancyFallbackModeParameter" - $ref: "SwarmCommon.yaml#/components/parameters/SwarmChunkRetrievalTimeoutParameter" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActTimestamp" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActPublisher" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActHistoryAddress" responses: "200": description: Ok @@ -305,6 +322,9 @@ paths: $ref: "SwarmCommon.yaml#/components/schemas/SwarmAddress" required: true description: Swarm address of chunk + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActTimestamp" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActPublisher" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActHistoryAddress" responses: "200": description: Chunk exists @@ -691,6 +711,8 @@ paths: $ref: "SwarmCommon.yaml#/components/parameters/SwarmPostageBatchId" name: swarm-postage-batch-id required: true + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmAct" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActHistoryAddress" requestBody: required: true description: The SOC binary data is composed of the span (8 bytes) and the at most 4KB payload. @@ -706,6 +728,9 @@ paths: application/json: schema: $ref: "SwarmCommon.yaml#/components/schemas/ReferenceResponse" + headers: + "swarm-act-history-address": + $ref: "SwarmCommon.yaml#/components/headers/SwarmActHistoryAddress" "400": $ref: "SwarmCommon.yaml#/components/responses/400" "401": @@ -743,6 +768,8 @@ paths: description: "Feed indexing scheme (default: sequence)" - $ref: "SwarmCommon.yaml#/components/parameters/SwarmPinParameter" - $ref: "SwarmCommon.yaml#/components/parameters/SwarmPostageBatchId" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmAct" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActHistoryAddress" responses: "201": description: Created @@ -750,6 +777,9 @@ paths: application/json: schema: $ref: "SwarmCommon.yaml#/components/schemas/ReferenceResponse" + headers: + "swarm-act-history-address": + $ref: "SwarmCommon.yaml#/components/headers/SwarmActHistoryAddress" "400": $ref: "SwarmCommon.yaml#/components/responses/400" "401": @@ -1084,6 +1114,9 @@ paths: $ref: "SwarmCommon.yaml#/components/parameters/SwarmCache" name: swarm-cache required: false + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActTimestamp" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActPublisher" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActHistoryAddress" responses: "200": description: Retrieved chunk content @@ -1111,6 +1144,9 @@ paths: $ref: "SwarmCommon.yaml#/components/schemas/SwarmAddress" required: true description: Swarm address of chunk + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActTimestamp" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActPublisher" + - $ref: "SwarmCommon.yaml#/components/parameters/SwarmActHistoryAddress" responses: "200": description: Chunk exists diff --git a/openapi/SwarmCommon.yaml b/openapi/SwarmCommon.yaml index b32fa88c8a4..9404c9ea2a6 100644 --- a/openapi/SwarmCommon.yaml +++ b/openapi/SwarmCommon.yaml @@ -87,6 +87,36 @@ components: ghostBalance: $ref: "#/components/schemas/BigInt" + ActGranteesCreateRequest: + type: object + properties: + grantees: + type: array + items: + $ref: "#/components/schemas/PublicKey" + + ActGranteesPatchRequest: + type: object + properties: + add: + type: array + items: + $ref: "#/components/schemas/PublicKey" + description: List of grantees to add + revoke: + type: array + items: + $ref: "#/components/schemas/PublicKey" + description: List of grantees to revoke future access from + + ActGranteesOperationResponse: + type: object + properties: + ref: + $ref: "#/components/schemas/SwarmEncryptedReference" + historyref: + $ref: "#/components/schemas/SwarmEncryptedReference" + Balance: type: object properties: @@ -973,6 +1003,12 @@ components: schema: $ref: "#/components/schemas/HexString" + SwarmActHistoryAddress: + description: "Swarm address reference to the new ACT history entry" + schema: + $ref: "#/components/schemas/SwarmAddress" + required: false + ETag: description: | The RFC7232 ETag header field in a response provides the current entity- @@ -1133,6 +1169,40 @@ components: required: false description: "Determines if the download data should be cached on the node. By default the download will be cached" + SwarmAct: + in: header + name: swarm-act + schema: + type: boolean + default: "false" + required: false + description: "Determines if the uploaded data should be treated as ACT content" + + SwarmActPublisher: + in: header + name: swarm-act-publisher + schema: + $ref: "#/components/schemas/PublicKey" + required: false + description: "ACT content publisher's public key" + + SwarmActHistoryAddress: + in: header + name: swarm-act-history-address + schema: + $ref: "#/components/schemas/SwarmAddress" + required: false + description: "ACT history reference address" + + SwarmActTimestamp: + in: header + name: swarm-act-timestamp + schema: + type: integer + format: int64 + required: false + description: "ACT history Unix timestamp" + responses: "200": description: OK. diff --git a/openapi/SwarmDebug.yaml b/openapi/SwarmDebug.yaml new file mode 100644 index 00000000000..e69de29bb2d From 5e377ccacaa11efa1eed5f5c26b50e19bcf8a777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= <58116288+bosi95@users.noreply.github.com> Date: Thu, 23 May 2024 14:19:55 +0200 Subject: [PATCH 39/41] refactor(act): chunk download and granteelist handling (#46) --- pkg/api/dynamicaccess.go | 12 +++++++----- pkg/dynamicaccess/controller.go | 15 ++++----------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index e776857d190..aad9a69d56a 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -113,6 +113,8 @@ func (s *Service) actDecryptionHandler() func(h http.Handler) http.Handler { ls := loadsave.NewReadonly(s.storer.Download(cache)) reference, err := s.dac.DownloadHandler(ctx, ls, paths.Address, headers.Publisher, *headers.HistoryAddress, timestamp) if err != nil { + logger.Debug("act failed to decrypt reference", "error", err) + logger.Error(nil, "act failed to decrypt reference") jsonhttp.InternalServerError(w, errActDownload) return } @@ -132,7 +134,7 @@ func (s *Service) actEncryptionHandler( ) (swarm.Address, error) { logger := s.logger.WithName("act_encryption_handler").Build() publisherPublicKey := &s.publicKey - ls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, false, redundancy.NONE)) + ls := loadsave.New(s.storer.Download(true), s.storer.Cache(), requestPipelineFactory(ctx, putter, false, redundancy.NONE)) storageReference, historyReference, encryptedReference, err := s.dac.UploadHandler(ctx, ls, reference, publisherPublicKey, historyRootHash) if err != nil { logger.Debug("act failed to encrypt reference", "error", err) @@ -324,8 +326,8 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) granteeref := paths.GranteesAddress publisher := &s.publicKey - ls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, false, redundancy.NONE)) - gls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, granteeListEncrypt, redundancy.NONE)) + ls := loadsave.New(s.storer.Download(true), s.storer.Cache(), requestPipelineFactory(ctx, putter, false, redundancy.NONE)) + gls := loadsave.New(s.storer.Download(true), s.storer.Cache(), requestPipelineFactory(ctx, putter, granteeListEncrypt, redundancy.NONE)) granteeref, encryptedglref, historyref, actref, err := s.dac.UpdateHandler(ctx, ls, gls, granteeref, historyAddress, publisher, grantees.Addlist, grantees.Revokelist) if err != nil { logger.Debug("failed to update grantee list", "error", err) @@ -469,8 +471,8 @@ func (s *Service) actCreateGranteesHandler(w http.ResponseWriter, r *http.Reques } publisher := &s.publicKey - ls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, false, redundancy.NONE)) - gls := loadsave.New(s.storer.ChunkStore(), s.storer.Cache(), requestPipelineFactory(ctx, putter, granteeListEncrypt, redundancy.NONE)) + ls := loadsave.New(s.storer.Download(true), s.storer.Cache(), requestPipelineFactory(ctx, putter, false, redundancy.NONE)) + gls := loadsave.New(s.storer.Download(true), s.storer.Cache(), requestPipelineFactory(ctx, putter, granteeListEncrypt, redundancy.NONE)) granteeref, encryptedglref, historyref, actref, err := s.dac.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, historyAddress, publisher, list, nil) if err != nil { logger.Debug("failed to update grantee list", "error", err) diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index 030e7c3316d..7ce650ef348 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -76,7 +76,6 @@ func (c *ControllerStruct) UploadHandler( storage kvs.KeyValueStore actRef swarm.Address ) - now := time.Now().Unix() if historyRef.IsZero() { history, err := NewHistory(ls) if err != nil { @@ -94,7 +93,7 @@ func (c *ControllerStruct) UploadHandler( if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - err = history.Add(ctx, actRef, &now, nil) + err = history.Add(ctx, actRef, nil, nil) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } @@ -107,12 +106,11 @@ func (c *ControllerStruct) UploadHandler( if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - entry, err := history.Lookup(ctx, now) - actRef = entry.Reference() + entry, err := history.Lookup(ctx, time.Now().Unix()) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - storage, err = kvs.NewReference(ls, actRef) + storage, err = kvs.NewReference(ls, entry.Reference()) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } @@ -197,15 +195,12 @@ func (c *ControllerStruct) UpdateHandler( return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } } + granteesToAdd := addList if len(removeList) != 0 { err = gl.Remove(removeList) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - } - - var granteesToAdd []*ecdsa.PublicKey - if len(removeList) != 0 || encryptedglref.IsZero() { // generate new access key and new act act, err = kvs.New(ls) if err != nil { @@ -216,8 +211,6 @@ func (c *ControllerStruct) UpdateHandler( return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } granteesToAdd = gl.Get() - } else { - granteesToAdd = addList } for _, grantee := range granteesToAdd { From 755e03891a13cd41a8943c05594c0aa82523569e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= <58116288+bosi95@users.noreply.github.com> Date: Fri, 24 May 2024 18:55:06 +0200 Subject: [PATCH 40/41] refactor(act): controller logic (#47) --- pkg/dynamicaccess/controller.go | 270 +++++++++++++++----------------- 1 file changed, 129 insertions(+), 141 deletions(-) diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index 7ce650ef348..48e771cddc5 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -19,18 +19,18 @@ import ( type Grantees interface { // UpdateHandler manages the grantees for the given publisher, updating the list based on provided public keys to add or remove. // Only the publisher can make changes to the grantee list. - UpdateHandler(ctx context.Context, ls file.LoadSaver, gls file.LoadSaver, granteeref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) + UpdateHandler(ctx context.Context, ls file.LoadSaver, gls file.LoadSaver, granteeRef swarm.Address, historyRef swarm.Address, publisher *ecdsa.PublicKey, addList, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) // Get returns the list of grantees for the given publisher. // The list is accessible only by the publisher. - Get(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) + Get(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglRef swarm.Address) ([]*ecdsa.PublicKey, error) } type Controller interface { Grantees // DownloadHandler decrypts the encryptedRef using the lookupkey based on the history and timestamp. - DownloadHandler(ctx context.Context, ls file.LoadSaver, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) + DownloadHandler(ctx context.Context, ls file.LoadSaver, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRef swarm.Address, timestamp int64) (swarm.Address, error) // UploadHandler encrypts the reference and stores it in the history as the latest update. - UploadHandler(ctx context.Context, ls file.LoadSaver, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) + UploadHandler(ctx context.Context, ls file.LoadSaver, reference swarm.Address, publisher *ecdsa.PublicKey, historyRef swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) io.Closer } @@ -40,23 +40,21 @@ type ControllerStruct struct { var _ Controller = (*ControllerStruct)(nil) +func NewController(accessLogic ActLogic) *ControllerStruct { + return &ControllerStruct{ + accessLogic: accessLogic, + } +} + func (c *ControllerStruct) DownloadHandler( ctx context.Context, ls file.LoadSaver, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, - historyRootHash swarm.Address, + historyRef swarm.Address, timestamp int64, ) (swarm.Address, error) { - history, err := NewHistoryReference(ls, historyRootHash) - if err != nil { - return swarm.ZeroAddress, err - } - entry, err := history.Lookup(ctx, timestamp) - if err != nil { - return swarm.ZeroAddress, err - } - act, err := kvs.NewReference(ls, entry.Reference()) + _, act, err := c.getHistoryAndAct(ctx, ls, historyRef, publisher, timestamp) if err != nil { return swarm.ZeroAddress, err } @@ -69,125 +67,43 @@ func (c *ControllerStruct) UploadHandler( ls file.LoadSaver, reference swarm.Address, publisher *ecdsa.PublicKey, - historyRootHash swarm.Address, + historyRef swarm.Address, ) (swarm.Address, swarm.Address, swarm.Address, error) { - historyRef := historyRootHash - var ( - storage kvs.KeyValueStore - actRef swarm.Address - ) + history, act, err := c.getHistoryAndAct(ctx, ls, historyRef, publisher, time.Now().Unix()) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } + actRef := swarm.ZeroAddress + newHistoryRef := historyRef if historyRef.IsZero() { - history, err := NewHistory(ls) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - storage, err = kvs.New(ls) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - err = c.accessLogic.AddGrantee(ctx, storage, publisher, publisher) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - actRef, err = storage.Save(ctx) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - err = history.Add(ctx, actRef, nil, nil) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - historyRef, err = history.Store(ctx) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - } else { - history, err := NewHistoryReference(ls, historyRef) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - entry, err := history.Lookup(ctx, time.Now().Unix()) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - storage, err = kvs.NewReference(ls, entry.Reference()) + newHistoryRef, actRef, err = c.saveHistoryAndAct(ctx, history, nil, act) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } } - encryptedRef, err := c.accessLogic.EncryptRef(ctx, storage, publisher, reference) - return actRef, historyRef, encryptedRef, err -} - -func NewController(accessLogic ActLogic) *ControllerStruct { - return &ControllerStruct{ - accessLogic: accessLogic, - } + encryptedRef, err := c.accessLogic.EncryptRef(ctx, act, publisher, reference) + return actRef, newHistoryRef, encryptedRef, err } func (c *ControllerStruct) UpdateHandler( ctx context.Context, ls file.LoadSaver, gls file.LoadSaver, - encryptedglref swarm.Address, - historyref swarm.Address, + encryptedglRef swarm.Address, + historyRef swarm.Address, publisher *ecdsa.PublicKey, addList []*ecdsa.PublicKey, removeList []*ecdsa.PublicKey, ) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) { - var ( - err error - h History - act kvs.KeyValueStore - granteeref swarm.Address - ) - if !historyref.IsZero() { - h, err = NewHistoryReference(ls, historyref) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - entry, err := h.Lookup(ctx, time.Now().Unix()) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - actref := entry.Reference() - act, err = kvs.NewReference(ls, actref) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - } else { - h, err = NewHistory(ls) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - // generate new access key and new act - act, err = kvs.New(ls) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - err = c.accessLogic.AddGrantee(ctx, act, publisher, publisher) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } + history, act, err := c.getHistoryAndAct(ctx, ls, historyRef, publisher, time.Now().Unix()) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - var gl GranteeList - if encryptedglref.IsZero() { - gl, err = NewGranteeList(gls) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - } else { - granteeref, err = c.decryptRefForPublisher(publisher, encryptedglref) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - - gl, err = NewGranteeListReference(ctx, gls, granteeref) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } + gl, err := c.getGranteeList(ctx, gls, encryptedglRef, publisher) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } if len(addList) != 0 { err = gl.Add(addList) @@ -201,14 +117,12 @@ func (c *ControllerStruct) UpdateHandler( if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - // generate new access key and new act - act, err = kvs.New(ls) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - err = c.accessLogic.AddGrantee(ctx, act, publisher, publisher) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + // generate new access key and new act, only if history was not newly created + if !historyRef.IsZero() { + act, err = c.newActWithPublisher(ctx, ls, publisher) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + } } granteesToAdd = gl.Get() } @@ -220,51 +134,125 @@ func (c *ControllerStruct) UpdateHandler( } } - actref, err := act.Save(ctx) + granteeRef, err := gl.Save(ctx) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - glref, err := gl.Save(ctx) - if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err - } - - eglref, err := c.encryptRefForPublisher(publisher, glref) + egranteeRef, err := c.encryptRefForPublisher(publisher, granteeRef) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } // need to re-initialize history, because Lookup loads the forks causing the manifest save to skip the root node - if !historyref.IsZero() { - h, err = NewHistoryReference(ls, historyref) + if !historyRef.IsZero() { + history, err = NewHistoryReference(ls, historyRef) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } } - mtdt := map[string]string{"encryptedglref": eglref.String()} - err = h.Add(ctx, actref, nil, &mtdt) + mtdt := map[string]string{"encryptedglref": egranteeRef.String()} + hRef, actRef, err := c.saveHistoryAndAct(ctx, history, &mtdt, act) if err != nil { return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err } - href, err := h.Store(ctx) + + return granteeRef, egranteeRef, hRef, actRef, nil +} + +func (c *ControllerStruct) Get(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglRef swarm.Address) ([]*ecdsa.PublicKey, error) { + gl, err := c.getGranteeList(ctx, ls, encryptedglRef, publisher) if err != nil { - return swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, swarm.ZeroAddress, err + return nil, err } - - return glref, eglref, href, actref, nil + return gl.Get(), nil } -func (c *ControllerStruct) Get(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { - granteeRef, err := c.decryptRefForPublisher(publisher, encryptedglref) +func (c *ControllerStruct) newActWithPublisher(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey) (kvs.KeyValueStore, error) { + act, err := kvs.New(ls) if err != nil { return nil, err } - gl, err := NewGranteeListReference(ctx, ls, granteeRef) + err = c.accessLogic.AddGrantee(ctx, act, publisher, publisher) if err != nil { return nil, err } - return gl.Get(), nil + + return act, nil +} +func (c *ControllerStruct) getHistoryAndAct(ctx context.Context, ls file.LoadSaver, historyRef swarm.Address, publisher *ecdsa.PublicKey, timestamp int64) (History, kvs.KeyValueStore, error) { + var ( + history History + act kvs.KeyValueStore + err error + ) + if historyRef.IsZero() { + history, err = NewHistory(ls) + if err != nil { + return nil, nil, err + } + act, err = c.newActWithPublisher(ctx, ls, publisher) + if err != nil { + return nil, nil, err + } + } else { + history, err = NewHistoryReference(ls, historyRef) + if err != nil { + return nil, nil, err + } + entry, err := history.Lookup(ctx, timestamp) + if err != nil { + return nil, nil, err + } + act, err = kvs.NewReference(ls, entry.Reference()) + if err != nil { + return nil, nil, err + } + } + + return history, act, nil +} + +func (c *ControllerStruct) saveHistoryAndAct(ctx context.Context, history History, mtdt *map[string]string, act kvs.KeyValueStore) (swarm.Address, swarm.Address, error) { + actRef, err := act.Save(ctx) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, err + } + err = history.Add(ctx, actRef, nil, mtdt) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, err + } + historyRef, err := history.Store(ctx) + if err != nil { + return swarm.ZeroAddress, swarm.ZeroAddress, err + } + + return historyRef, actRef, nil +} + +func (c *ControllerStruct) getGranteeList(ctx context.Context, ls file.LoadSaver, encryptedglRef swarm.Address, publisher *ecdsa.PublicKey) (GranteeList, error) { + var ( + gl GranteeList + err error + ) + if encryptedglRef.IsZero() { + gl, err = NewGranteeList(ls) + if err != nil { + return nil, err + } + } else { + granteeref, err := c.decryptRefForPublisher(publisher, encryptedglRef) + if err != nil { + return nil, err + } + + gl, err = NewGranteeListReference(ctx, ls, granteeref) + if err != nil { + return nil, err + } + } + + return gl, nil } func (c *ControllerStruct) encryptRefForPublisher(publisherPubKey *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) { From 4faf2fc903ffec021acec84d0f03583bb0a5ea90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Ujv=C3=A1ri?= <58116288+bosi95@users.noreply.github.com> Date: Wed, 29 May 2024 16:58:08 +0200 Subject: [PATCH 41/41] test(act): controller add, revoke and get with history + fix typos (#48) --- openapi/SwarmCommon.yaml | 2 +- pkg/api/dynamicaccess.go | 8 ++--- pkg/dynamicaccess/controller.go | 16 ++++------ pkg/dynamicaccess/controller_test.go | 44 ++++++++++++++++++++++++++-- pkg/dynamicaccess/mock/controller.go | 28 +++++++++--------- pkg/kvs/kvs.go | 10 +++---- 6 files changed, 71 insertions(+), 37 deletions(-) diff --git a/openapi/SwarmCommon.yaml b/openapi/SwarmCommon.yaml index 9404c9ea2a6..8559a7c4e53 100644 --- a/openapi/SwarmCommon.yaml +++ b/openapi/SwarmCommon.yaml @@ -879,7 +879,7 @@ components: reserveSize: type: integer reserveSizeWithinRadius: - type: interger + type: integer pullsyncRate: type: number storageRadius: diff --git a/pkg/api/dynamicaccess.go b/pkg/api/dynamicaccess.go index aad9a69d56a..f83f0cca435 100644 --- a/pkg/api/dynamicaccess.go +++ b/pkg/api/dynamicaccess.go @@ -281,23 +281,23 @@ func (s *Service) actGrantRevokeHandler(w http.ResponseWriter, r *http.Request) } grantees := GranteesPatch{} - paresAddlist, err := parseKeys(gpr.Addlist) + parsedAddlist, err := parseKeys(gpr.Addlist) if err != nil { logger.Debug("add list key parse failed", "error", err) logger.Error(nil, "add list key parse failed") jsonhttp.InternalServerError(w, "error add list key parsing") return } - grantees.Addlist = append(grantees.Addlist, paresAddlist...) + grantees.Addlist = append(grantees.Addlist, parsedAddlist...) - paresRevokelist, err := parseKeys(gpr.Revokelist) + parsedRevokelist, err := parseKeys(gpr.Revokelist) if err != nil { logger.Debug("revoke list key parse failed", "error", err) logger.Error(nil, "revoke list key parse failed") jsonhttp.InternalServerError(w, "error revoke list key parsing") return } - grantees.Revokelist = append(grantees.Revokelist, paresRevokelist...) + grantees.Revokelist = append(grantees.Revokelist, parsedRevokelist...) ctx := r.Context() putter, err := s.newStamperPutter(ctx, putterOptions{ diff --git a/pkg/dynamicaccess/controller.go b/pkg/dynamicaccess/controller.go index 48e771cddc5..dffe4bbdf64 100644 --- a/pkg/dynamicaccess/controller.go +++ b/pkg/dynamicaccess/controller.go @@ -86,6 +86,8 @@ func (c *ControllerStruct) UploadHandler( return actRef, newHistoryRef, encryptedRef, err } +// Limitation: If an upadate is called again within a second from the latest upload/update then mantaray save fails with ErrInvalidInput, +// because the key (timestamp) is already present, hence a new fork is not created func (c *ControllerStruct) UpdateHandler( ctx context.Context, ls file.LoadSaver, @@ -180,12 +182,8 @@ func (c *ControllerStruct) newActWithPublisher(ctx context.Context, ls file.Load return act, nil } -func (c *ControllerStruct) getHistoryAndAct(ctx context.Context, ls file.LoadSaver, historyRef swarm.Address, publisher *ecdsa.PublicKey, timestamp int64) (History, kvs.KeyValueStore, error) { - var ( - history History - act kvs.KeyValueStore - err error - ) + +func (c *ControllerStruct) getHistoryAndAct(ctx context.Context, ls file.LoadSaver, historyRef swarm.Address, publisher *ecdsa.PublicKey, timestamp int64) (history History, act kvs.KeyValueStore, err error) { if historyRef.IsZero() { history, err = NewHistory(ls) if err != nil { @@ -230,11 +228,7 @@ func (c *ControllerStruct) saveHistoryAndAct(ctx context.Context, history Histor return historyRef, actRef, nil } -func (c *ControllerStruct) getGranteeList(ctx context.Context, ls file.LoadSaver, encryptedglRef swarm.Address, publisher *ecdsa.PublicKey) (GranteeList, error) { - var ( - gl GranteeList - err error - ) +func (c *ControllerStruct) getGranteeList(ctx context.Context, ls file.LoadSaver, encryptedglRef swarm.Address, publisher *ecdsa.PublicKey) (gl GranteeList, err error) { if encryptedglRef.IsZero() { gl, err = NewGranteeList(ls) if err != nil { diff --git a/pkg/dynamicaccess/controller_test.go b/pkg/dynamicaccess/controller_test.go index 7118368afa8..c25d8079bfa 100644 --- a/pkg/dynamicaccess/controller_test.go +++ b/pkg/dynamicaccess/controller_test.go @@ -19,6 +19,7 @@ import ( "github.com/ethersphere/bee/v2/pkg/kvs" "github.com/ethersphere/bee/v2/pkg/swarm" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "golang.org/x/crypto/sha3" ) @@ -206,13 +207,52 @@ func TestController_UpdateHandler(t *testing.T) { assert.NoError(t, err) assert.Len(t, gl.Get(), 2) }) + t.Run("add and revoke then get from history", func(t *testing.T) { + addRevokeList := []*ecdsa.PublicKey{&grantee.PublicKey} + ref := swarm.RandAddress(t) + _, hRef, encRef, err := c.UploadHandler(ctx, ls, ref, &publisher.PublicKey, swarm.ZeroAddress) + require.NoError(t, err) + + // Need to wait a second before each update call so that a new history mantaray fork is created for the new key(timestamp) entry + time.Sleep(1 * time.Second) + beforeRevokeTS := time.Now().Unix() + _, egranteeRef, hrefUpdate1, _, err := c.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, hRef, &publisher.PublicKey, addRevokeList, nil) + require.NoError(t, err) + + time.Sleep(1 * time.Second) + granteeRef, _, hrefUpdate2, _, err := c.UpdateHandler(ctx, ls, gls, egranteeRef, hrefUpdate1, &publisher.PublicKey, nil, addRevokeList) + require.NoError(t, err) + gl, err := dynamicaccess.NewGranteeListReference(ctx, ls, granteeRef) + require.NoError(t, err) + assert.Empty(t, gl.Get()) + // expect history reference to be different after grantee list update + assert.NotEqual(t, hrefUpdate1, hrefUpdate2) + + granteeDH := dynamicaccess.NewDefaultSession(grantee) + granteeAl := dynamicaccess.NewLogic(granteeDH) + granteeCtrl := dynamicaccess.NewController(granteeAl) + // download with grantee shall still work with the timestamp before the revoke + decRef, err := granteeCtrl.DownloadHandler(ctx, ls, encRef, &publisher.PublicKey, hrefUpdate2, beforeRevokeTS) + require.NoError(t, err) + assert.Equal(t, ref, decRef) + + // download with grantee shall NOT work with the latest timestamp + decRef, err = granteeCtrl.DownloadHandler(ctx, ls, encRef, &publisher.PublicKey, hrefUpdate2, time.Now().Unix()) + require.Error(t, err) + assert.Equal(t, swarm.ZeroAddress, decRef) + + // publisher shall still be able to download with the timestamp before the revoke + decRef, err = c.DownloadHandler(ctx, ls, encRef, &publisher.PublicKey, hrefUpdate2, beforeRevokeTS) + require.NoError(t, err) + assert.Equal(t, ref, decRef) + }) t.Run("add twice", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey, &grantee.PublicKey} //nolint:ineffassign,staticcheck,wastedassign granteeRef, eglref, _, _, err := c.UpdateHandler(ctx, ls, gls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) granteeRef, _, _, _, _ = c.UpdateHandler(ctx, ls, ls, eglref, href, &publisher.PublicKey, addList, nil) - gl, err := dynamicaccess.NewGranteeListReference(ctx, createLs(), granteeRef) + gl, err := dynamicaccess.NewGranteeListReference(ctx, ls, granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 1) @@ -220,7 +260,7 @@ func TestController_UpdateHandler(t *testing.T) { t.Run("revoke non-existing", func(t *testing.T) { addList := []*ecdsa.PublicKey{&grantee.PublicKey} granteeRef, _, _, _, _ := c.UpdateHandler(ctx, ls, ls, swarm.ZeroAddress, href, &publisher.PublicKey, addList, nil) - gl, err := dynamicaccess.NewGranteeListReference(ctx, createLs(), granteeRef) + gl, err := dynamicaccess.NewGranteeListReference(ctx, ls, granteeRef) assert.NoError(t, err) assert.Len(t, gl.Get(), 1) diff --git a/pkg/dynamicaccess/mock/controller.go b/pkg/dynamicaccess/mock/controller.go index 3db86c88020..30f617bfccb 100644 --- a/pkg/dynamicaccess/mock/controller.go +++ b/pkg/dynamicaccess/mock/controller.go @@ -25,7 +25,7 @@ import ( "golang.org/x/crypto/sha3" ) -type mockDacService struct { +type mockController struct { historyMap map[string]dynamicaccess.History refMap map[string]swarm.Address acceptAll bool @@ -34,19 +34,19 @@ type mockDacService struct { ls file.LoadSaver } -type optionFunc func(*mockDacService) +type optionFunc func(*mockController) // Option is an option passed to a mock dynamicaccess Service. type Option interface { - apply(*mockDacService) + apply(*mockController) } -func (f optionFunc) apply(r *mockDacService) { f(r) } +func (f optionFunc) apply(r *mockController) { f(r) } // New creates a new mock dynamicaccess service. func New(o ...Option) dynamicaccess.Controller { storer := mockstorer.New() - m := &mockDacService{ + m := &mockController{ historyMap: make(map[string]dynamicaccess.History), refMap: make(map[string]swarm.Address), publisher: "", @@ -62,23 +62,23 @@ func New(o ...Option) dynamicaccess.Controller { // WithAcceptAll sets the mock to return fixed references on every call to DownloadHandler. func WithAcceptAll() Option { - return optionFunc(func(m *mockDacService) { m.acceptAll = true }) + return optionFunc(func(m *mockController) { m.acceptAll = true }) } func WithHistory(h dynamicaccess.History, ref string) Option { - return optionFunc(func(m *mockDacService) { + return optionFunc(func(m *mockController) { m.historyMap = map[string]dynamicaccess.History{ref: h} }) } func WithPublisher(ref string) Option { - return optionFunc(func(m *mockDacService) { + return optionFunc(func(m *mockController) { m.publisher = ref m.encrypter = encryption.New(encryption.Key(ref), 0, uint32(0), sha3.NewLegacyKeccak256) }) } -func (m *mockDacService) DownloadHandler(ctx context.Context, ls file.LoadSaver, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) { +func (m *mockController) DownloadHandler(ctx context.Context, ls file.LoadSaver, encryptedRef swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address, timestamp int64) (swarm.Address, error) { if m.acceptAll { return swarm.ParseHexAddress("36e6c1bbdfee6ac21485d5f970479fd1df458d36df9ef4e8179708ed46da557f") } @@ -101,7 +101,7 @@ func (m *mockDacService) DownloadHandler(ctx context.Context, ls file.LoadSaver, return m.refMap[encryptedRef.String()], nil } -func (m *mockDacService) UploadHandler(ctx context.Context, ls file.LoadSaver, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) { +func (m *mockController) UploadHandler(ctx context.Context, ls file.LoadSaver, reference swarm.Address, publisher *ecdsa.PublicKey, historyRootHash swarm.Address) (swarm.Address, swarm.Address, swarm.Address, error) { historyRef, _ := swarm.ParseHexAddress("67bdf80a9bbea8eca9c8480e43fdceb485d2d74d5708e45144b8c4adacd13d9c") kvsRef, _ := swarm.ParseHexAddress("3339613565613837623134316665343461613630396333333237656364383934") if m.acceptAll { @@ -136,11 +136,11 @@ func (m *mockDacService) UploadHandler(ctx context.Context, ls file.LoadSaver, r return kvsRef, historyRef, swarm.NewAddress(encryptedRef), nil } -func (m *mockDacService) Close() error { +func (m *mockController) Close() error { return nil } -func (m *mockDacService) UpdateHandler(_ context.Context, ls file.LoadSaver, gls file.LoadSaver, encryptedglref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList []*ecdsa.PublicKey, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) { +func (m *mockController) UpdateHandler(_ context.Context, ls file.LoadSaver, gls file.LoadSaver, encryptedglref swarm.Address, historyref swarm.Address, publisher *ecdsa.PublicKey, addList []*ecdsa.PublicKey, removeList []*ecdsa.PublicKey) (swarm.Address, swarm.Address, swarm.Address, swarm.Address, error) { historyRef, _ := swarm.ParseHexAddress("67bdf80a9bbea8eca9c8480e43fdceb485d2d74d5708e45144b8c4adacd13d9c") glRef, _ := swarm.ParseHexAddress("3339613565613837623134316665343461613630396333333237656364383934") eglRef, _ := swarm.ParseHexAddress("fc4e9fe978991257b897d987bc4ff13058b66ef45a53189a0b4fe84bb3346396") @@ -148,7 +148,7 @@ func (m *mockDacService) UpdateHandler(_ context.Context, ls file.LoadSaver, gls return glRef, eglRef, historyRef, actref, nil } -func (m *mockDacService) Get(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { +func (m *mockController) Get(ctx context.Context, ls file.LoadSaver, publisher *ecdsa.PublicKey, encryptedglref swarm.Address) ([]*ecdsa.PublicKey, error) { if m.publisher == "" { return nil, fmt.Errorf("granteelist not found") } @@ -180,4 +180,4 @@ func requestPipelineFactory(ctx context.Context, s storage.Putter, encrypt bool, } } -var _ dynamicaccess.Controller = (*mockDacService)(nil) +var _ dynamicaccess.Controller = (*mockController)(nil) diff --git a/pkg/kvs/kvs.go b/pkg/kvs/kvs.go index 5f2381a8b24..0c7eae48c08 100644 --- a/pkg/kvs/kvs.go +++ b/pkg/kvs/kvs.go @@ -58,23 +58,23 @@ func (s *keyValueStore) Save(ctx context.Context) (swarm.Address, error) { } func New(ls file.LoadSaver) (KeyValueStore, error) { - manif, err := manifest.NewSimpleManifest(ls) + m, err := manifest.NewSimpleManifest(ls) if err != nil { return nil, err } return &keyValueStore{ - manifest: manif, + manifest: m, }, nil } -func NewReference(ls file.LoadSaver, rootHash swarm.Address) (KeyValueStore, error) { - manif, err := manifest.NewSimpleManifestReference(rootHash, ls) +func NewReference(ls file.LoadSaver, ref swarm.Address) (KeyValueStore, error) { + m, err := manifest.NewSimpleManifestReference(ref, ls) if err != nil { return nil, err } return &keyValueStore{ - manifest: manif, + manifest: m, }, nil }