Compare commits

..
Author SHA1 Message Date
mcrakhman 73a919ff96 Fix mocks 2023-04-04 11:22:48 +02:00
Mikhail Rakhmanov c8c3380a8f Merge pull request #66 from anytypeio/add-master-key 2023-04-03 14:28:23 +02:00
mcrakhman e9d1de7781 Remove master key from account 2023-04-03 13:19:12 +02:00
mcrakhman 357f1c935d Update derivation path comment 2023-04-03 13:01:58 +02:00
mcrakhman 7258df1c2d Update key path 2023-04-03 10:21:28 +02:00
mcrakhman 13df75d0e0 Add old account to coordinator proto 2023-04-02 18:53:02 +02:00
mcrakhman 20a4d85836 Update cryptography and add master key 2023-04-02 18:13:10 +02:00
Mikhail Rakhmanov a4b3430d6e Merge pull request #65 from anytypeio/add-settings-snapshot 2023-03-30 13:06:45 +02:00
Sergey Cherepanov de28c1e3f8 Merge pull request #63 from anytypeio/dependabot/go_modules/github.com/goccy/go-graphviz-0.1.1
Bump github.com/goccy/go-graphviz from 0.1.0 to 0.1.1
2023-03-30 12:13:18 +02:00
Sergey Cherepanov 7135b436ca Merge pull request #62 from anytypeio/dependabot/go_modules/github.com/ipfs/go-block-format-0.1.2
Bump github.com/ipfs/go-block-format from 0.1.1 to 0.1.2
2023-03-30 12:12:47 +02:00
Sergey Cherepanov bb3d1b1d7e Merge pull request #61 from anytypeio/dependabot/go_modules/github.com/ipfs/go-unixfs-0.4.5
Bump github.com/ipfs/go-unixfs from 0.4.4 to 0.4.5
2023-03-30 12:12:16 +02:00
mcrakhman 254fcd5ea8 Change doSnapshot method call 2023-03-29 23:43:04 +02:00
mcrakhman fb4455e16a Add settings changefactory tests 2023-03-29 23:40:49 +02:00
mcrakhman 19fd4e3a56 Add settings snapshot logic 2023-03-29 23:06:51 +02:00
dependabot[bot] 274a83a039 Bump github.com/goccy/go-graphviz from 0.1.0 to 0.1.1
Bumps [github.com/goccy/go-graphviz](https://github.com/goccy/go-graphviz) from 0.1.0 to 0.1.1.
- [Release notes](https://github.com/goccy/go-graphviz/releases)
- [Commits](https://github.com/goccy/go-graphviz/compare/v0.1.0...v0.1.1)

---
updated-dependencies:
- dependency-name: github.com/goccy/go-graphviz
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-29 15:57:04 +00:00
dependabot[bot] 5cabb4fbc3 Bump github.com/ipfs/go-unixfs from 0.4.4 to 0.4.5
Bumps [github.com/ipfs/go-unixfs](https://github.com/ipfs/go-unixfs) from 0.4.4 to 0.4.5.
- [Release notes](https://github.com/ipfs/go-unixfs/releases)
- [Commits](https://github.com/ipfs/go-unixfs/compare/v0.4.4...v0.4.5)

---
updated-dependencies:
- dependency-name: github.com/ipfs/go-unixfs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-29 15:56:32 +00:00
Mikhail Rakhmanov 4b19d06415 Merge pull request #60 from anytypeio/protocol-crypto-update 2023-03-29 17:54:24 +02:00
mcrakhman f472fd83c4 Add some tests 2023-03-28 21:29:12 +02:00
mcrakhman a8ef63eca0 Update space header validation 2023-03-28 00:11:09 +02:00
mcrakhman 13f30aa60f Change sym encryption logic in objecttree 2023-03-27 23:03:02 +02:00
mcrakhman f4bc677d02 Fix private key marshal 2023-03-27 22:28:49 +02:00
mcrakhman 6d70bc3786 Fix mnemonic 2023-03-27 22:08:07 +02:00
mcrakhman b62ad57988 Move peer util package into crypto 2023-03-27 21:22:32 +02:00
dependabot[bot] f074a1fa53 Bump github.com/ipfs/go-block-format from 0.1.1 to 0.1.2
Bumps [github.com/ipfs/go-block-format](https://github.com/ipfs/go-block-format) from 0.1.1 to 0.1.2.
- [Release notes](https://github.com/ipfs/go-block-format/releases)
- [Commits](https://github.com/ipfs/go-block-format/compare/v0.1.1...v0.1.2)

---
updated-dependencies:
- dependency-name: github.com/ipfs/go-block-format
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-27 18:08:17 +00:00
mcrakhman 9679bb3bf3 Add peer id string for keys 2023-03-27 17:04:59 +02:00
mcrakhman eec2b842bc Add accountdata constructor 2023-03-27 16:32:56 +02:00
mcrakhman 2225aca40f Add marshalling of private key 2023-03-27 16:03:58 +02:00
mcrakhman 3d017d1e0c Add individual key derivation for trees 2023-03-27 13:01:35 +02:00
mcrakhman 503c4f339f Remove pointer methods 2023-03-27 01:57:49 +02:00
mcrakhman cdfb88d6a8 Add decoders 2023-03-27 01:55:33 +02:00
mcrakhman 58cc732f3a Add mnemonic 2023-03-27 01:34:51 +02:00
mcrakhman 2d977ed535 Fix tests 2023-03-26 18:47:35 +02:00
mcrakhman 0243ec0fca Fix objecttree tests 2023-03-26 18:36:07 +02:00
mcrakhman ee0f62410f Remove old acl methods and tests, acl builder 2023-03-26 17:53:11 +02:00
mcrakhman 98931433a5 Fix handshake tests 2023-03-26 13:45:24 +02:00
mcrakhman d9868c55c2 Add KeyStorage 2023-03-25 14:06:20 +01:00
mcrakhman fd5bd0b099 More key changes 2023-03-25 13:15:19 +01:00
mcrakhman 5baa6dc856 ACLState refactoring 2023-03-24 22:43:25 +01:00
mcrakhman 2273c94cf4 Merge branch 'update-proto' into protocol-crypto-update 2023-03-24 14:59:14 +01:00
mcrakhman d9cf1feaee Merge branch 'protocol-crypto-update' into update-proto
# Conflicts:
#	commonspace/object/acl/aclrecordproto/aclrecord.pb.go
2023-03-24 14:58:47 +01:00
mcrakhman cc48cf88f3 Update derivation 2023-03-24 14:56:39 +01:00
mcrakhman 33c9a25665 Add sym key 2023-03-24 13:01:53 +01:00
mcrakhman add25062f3 WIP refactor crypto proto 2023-03-24 11:45:39 +01:00
mcrakhman 18240ea5ef Update aclrecordproto 2023-03-22 18:51:01 +01:00
Sergey Cherepanov d20f5fc2b3 Merge pull request #58 from anytypeio/fix-increase-ipfs-chunk-size
increase ipfs chunk size
2023-03-21 20:35:39 +03:00
Roman Khafizianov afae514535 Merge pull request #57 from anytypeio/app-start-logger
app long start/stop logger
2023-03-21 18:35:27 +01:00
Sergey Cherepanov fa24020ab1 make chunk size public 2023-03-21 18:28:45 +01:00
Sergey Cherepanov f34bc4061b increase ipfs chunk size 2023-03-21 18:24:29 +01:00
Roman Khafizianov 8f4a39ebe9 app long start/stop logger 2023-03-21 15:27:15 +01:00
Sergey Cherepanov da1a69932a Merge pull request #55 from anytypeio/update-fileproto
Update fileproto
2023-03-21 13:34:09 +03:00
Sergey Cherepanov 207553f638 update fileproto 2023-03-21 11:23:52 +01:00
Sergey Cherepanov af2026fac8 update fileproto 2023-03-21 11:20:16 +01:00
Sergey Cherepanov 2f21006457 update fileproto 2023-03-21 11:10:50 +01:00
Sergey Cherepanov eed0458794 Merge pull request #53 from anytypeio/dependabot/go_modules/github.com/multiformats/go-multibase-0.2.0
Bump github.com/multiformats/go-multibase from 0.1.1 to 0.2.0
2023-03-20 22:55:11 +03:00
Sergey Cherepanov 42200fddcd Merge pull request #54 from anytypeio/dependabot/go_modules/github.com/ipfs/go-cid-0.4.0
Bump github.com/ipfs/go-cid from 0.3.2 to 0.4.0
2023-03-20 22:54:48 +03:00
Sergey Cherepanov 6ce4c67ac1 Merge pull request #38 from anytypeio/update-fileproto
Update fileproto
2023-03-20 22:54:06 +03:00
dependabot[bot] a523641dff Bump github.com/ipfs/go-cid from 0.3.2 to 0.4.0
Bumps [github.com/ipfs/go-cid](https://github.com/ipfs/go-cid) from 0.3.2 to 0.4.0.
- [Release notes](https://github.com/ipfs/go-cid/releases)
- [Commits](https://github.com/ipfs/go-cid/compare/v0.3.2...v0.4.0)

---
updated-dependencies:
- dependency-name: github.com/ipfs/go-cid
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-20 18:04:01 +00:00
dependabot[bot] b3b1afa307 Bump github.com/multiformats/go-multibase from 0.1.1 to 0.2.0
Bumps [github.com/multiformats/go-multibase](https://github.com/multiformats/go-multibase) from 0.1.1 to 0.2.0.
- [Release notes](https://github.com/multiformats/go-multibase/releases)
- [Commits](https://github.com/multiformats/go-multibase/compare/v0.1.1...v0.2.0)

---
updated-dependencies:
- dependency-name: github.com/multiformats/go-multibase
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-20 18:03:47 +00:00
Sergey Cherepanov 935a95c247 Merge branch 'main' of github.com:anytypeio/any-sync into update-fileproto 2023-03-19 19:23:16 +03:00
Sergey Cherepanov fdb1a59524 Merge pull request #51 from anytypeio/dependabot/go_modules/github.com/ipfs/go-ipfs-blockstore-1.3.0
Bump github.com/ipfs/go-ipfs-blockstore from 1.2.0 to 1.3.0
2023-03-19 19:23:08 +03:00
Sergey Cherepanov 22a597389d Merge branch 'main' of github.com:anytypeio/any-sync into update-fileproto 2023-03-19 19:21:20 +03:00
Sergey Cherepanov 49248b02b3 fileproto forbidden error 2023-03-19 18:40:28 +03:00
Mikhail Rakhmanov feba2c67d4 Merge pull request #52 from anytypeio/derived-id-space 2023-03-16 19:04:17 +01:00
mcrakhman 305c509fc6 Update derive id method name 2023-03-16 19:01:05 +01:00
mcrakhman ed1b597fc5 Add error wrapping in app 2023-03-16 18:46:48 +01:00
mcrakhman 2691da66f7 Update rpc err in pull space 2023-03-16 18:27:28 +01:00
mcrakhman 417b203b09 Derive space id as a separate method 2023-03-16 17:57:10 +01:00
dependabot[bot] 06b6ca22c0 Bump github.com/ipfs/go-ipfs-blockstore from 1.2.0 to 1.3.0
Bumps [github.com/ipfs/go-ipfs-blockstore](https://github.com/ipfs/go-ipfs-blockstore) from 1.2.0 to 1.3.0.
- [Release notes](https://github.com/ipfs/go-ipfs-blockstore/releases)
- [Commits](https://github.com/ipfs/go-ipfs-blockstore/compare/v1.2.0...v1.3.0)

---
updated-dependencies:
- dependency-name: github.com/ipfs/go-ipfs-blockstore
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-13 18:08:51 +00:00
Mikhail Rakhmanov 5ec53522fe Merge pull request #50 from anytypeio/fix-pool-remove-connection 2023-03-13 17:32:34 +01:00
mcrakhman e568f899aa Remove old connection from pool on close (GetOneOf) 2023-03-13 17:18:21 +01:00
Roman Khafizianov 1a01f361ad Merge pull request #48 from anytypeio/fix-suggared-logger
fix replacing existing logger
2023-03-13 14:15:26 +01:00
Roman Khafizianov 86dcc83461 change logger name 2023-03-13 14:14:55 +01:00
Mikhail Rakhmanov 5de8af82bb Merge pull request #49 from anytypeio/fix-counter 2023-03-13 13:55:22 +01:00
mcrakhman 25b0695d3e Add counter for put sync tree 2023-03-13 13:46:36 +01:00
mcrakhman a2efcafb3e Update counters 2023-03-13 13:04:23 +01:00
mcrakhman 6b2667d4dc Add increment/decrement logs 2023-03-13 12:58:37 +01:00
Mikhail Rakhmanov 1fbf87995b Merge pull request #47 from anytypeio/update-proto 2023-03-11 13:48:57 +01:00
mcrakhman 981378c2e3 Ocache remove with ctx 2023-03-11 13:41:26 +01:00
mcrakhman 67ca417cb8 Change timeestamps 2023-03-11 12:52:42 +01:00
Roman Khafizianov df9a5f11ae cleanup logger names 2023-03-10 19:32:20 +01:00
Roman Khafizianov ec0d8a0c07 fix replacing existing logger 2023-03-10 19:18:15 +01:00
mcrakhman bb86e5b7e5 Fix test 2023-03-10 18:22:08 +01:00
mcrakhman a97efd2a5b Merge branch 'ocache-try-close' into update-proto 2023-03-10 18:12:03 +01:00
mcrakhman 01e170ba69 Add newline to treechangeproto 2023-03-10 16:52:30 +01:00
mcrakhman 7d39332a79 Update state builder to fix root change 2023-03-10 16:33:13 +01:00
mcrakhman 19930e6252 Update space reserved constant 2023-03-10 15:07:45 +01:00
mcrakhman ea74562290 Add change type and payload 2023-03-10 14:37:03 +01:00
mcrakhman 74f39239c6 Update tryclose with ttl 2023-03-10 10:17:57 +01:00
mcrakhman 067ad8cf16 Add TryClose to space interface 2023-03-09 23:11:20 +01:00
mcrakhman 0c79741216 Update objecttree with tryclose 2023-03-09 23:09:26 +01:00
mcrakhman 94f28430a3 Remove called records in fuzzy test 2023-03-09 22:58:36 +01:00
mcrakhman 83476a9e84 Lower timeouts 2023-03-09 22:56:59 +01:00
mcrakhman 43a35f4878 Add tryclose everywhere 2023-03-09 22:52:31 +01:00
mcrakhman ca2ea8cef9 Add some fuzzy tests 2023-03-09 22:03:45 +01:00
mcrakhman 63ba0a9fab Update cache logic to remove from entries on close 2023-03-09 20:25:01 +01:00
mcrakhman 4fb34fcb7c Readability improvements 2023-03-09 09:38:45 +01:00
mcrakhman 9836caf2cb More tests and split entry 2023-03-09 09:06:02 +01:00
mcrakhman 65496d247f Test returning to cache after tryclose 2023-03-08 22:52:43 +01:00
mcrakhman 07c6e8000c Add ocache tryClose 2023-03-08 22:07:44 +01:00
Roman Khafizianov 4687b1f805 Merge pull request #45 from anytypeio/fix-logger-console
fix default logger encoding config
2023-03-07 19:47:32 +01:00
Roman Khafizianov 7adae54f87 fix default logger encoding config 2023-03-07 19:37:21 +01:00
Roman Khafizianov f5baccaf08 Merge pull request #44 from anytypeio/logger-json
logger: add Format in config
2023-03-07 18:58:57 +01:00
Roman Khafizianov 589f885b63 logger: add Format in config
support json, color, plaintext
2023-03-07 18:52:28 +01:00
Roman Khafizianov 52278f02f4 Merge pull request #43 from anytypeio/named-logger-levels
Named logger levels
2023-03-07 15:13:58 +01:00
Roman Khafizianov 0d40838196 rename some loggers to the same convention 2023-03-07 10:42:36 +01:00
Roman Khafizianov 3693218fc5 rename logger common.commonspace.settings -> commonspace.settings 2023-03-06 22:26:32 +01:00
Roman Khafizianov d4c19f790d add an ability to set levels separately for named loggers
with glob support
2023-03-06 22:25:43 +01:00
Sergey Cherepanov bc7a1754ad Merge pull request #42 from anytypeio/dependabot/go_modules/github.com/ipfs/go-unixfs-0.4.4
Bump github.com/ipfs/go-unixfs from 0.4.3 to 0.4.4
2023-03-06 21:22:06 +03:00
Sergey Cherepanov f95374bb3e Merge pull request #41 from anytypeio/dependabot/go_modules/golang.org/x/net-0.8.0
Bump golang.org/x/net from 0.7.0 to 0.8.0
2023-03-06 21:21:51 +03:00
Mikhail Rakhmanov 848556200b Merge pull request #40 from anytypeio/add-strkey 2023-03-06 19:15:28 +01:00
dependabot[bot] e043c84cae Bump github.com/ipfs/go-unixfs from 0.4.3 to 0.4.4
Bumps [github.com/ipfs/go-unixfs](https://github.com/ipfs/go-unixfs) from 0.4.3 to 0.4.4.
- [Release notes](https://github.com/ipfs/go-unixfs/releases)
- [Commits](https://github.com/ipfs/go-unixfs/compare/v0.4.3...v0.4.4)

---
updated-dependencies:
- dependency-name: github.com/ipfs/go-unixfs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-06 18:09:49 +00:00
dependabot[bot] 0314ea1103 Bump golang.org/x/net from 0.7.0 to 0.8.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.7.0 to 0.8.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.7.0...v0.8.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-06 18:09:36 +00:00
mcrakhman 6a1c3d27f8 Add strkey for string encoding 2023-03-06 19:08:16 +01:00
Mikhail Rakhmanov d22d8f4f25 Merge pull request #39 from anytypeio/coordinator 2023-03-06 18:59:17 +01:00
mcrakhman 61cd658ab0 Add todo 2023-03-06 14:29:07 +01:00
mcrakhman 998f631ee8 Update diffsyncer to directly sync settings tree 2023-03-06 11:08:27 +01:00
Sergey Cherepanov bee7146a60 fileId ctx funcs 2023-03-06 12:22:29 +03:00
Sergey Cherepanov a497e129c4 Merge branch 'main' of github.com:anytypeio/any-sync into update-fileproto 2023-03-06 12:10:28 +03:00
mcrakhman e1f6dad499 Add error unwrapping 2023-03-06 09:41:45 +01:00
mcrakhman 58f01da459 Add safe access in case no delete change provided 2023-03-06 09:31:27 +01:00
mcrakhman 3895dc8e14 Add errors to coordinator proto 2023-03-04 12:10:32 +01:00
mcrakhman 3ea11abb56 Update status method 2023-03-03 23:10:05 +01:00
Sergey Cherepanov 6524762c09 fileproto: FileInfo 2023-03-03 13:22:37 +03:00
Sergey Cherepanov 70682ff397 fix fileproto 2023-03-03 13:09:55 +03:00
Sergey Cherepanov b37dec42a2 fix fileproto 2023-03-03 13:08:38 +03:00
mcrakhman 3f48831e8b Fix spacestorage 2023-03-02 23:28:46 +01:00
mcrakhman cda401356d Simplify cred provider logic 2023-03-02 22:40:56 +01:00
mcrakhman 897c50e8e6 Add credential provider 2023-03-02 22:04:37 +01:00
mcrakhman 5218eea8ad Fix change builder with no root 2023-03-02 21:08:14 +01:00
mcrakhman 6e5d044251 Update metrics to convert dots 2023-03-02 20:54:52 +01:00
mcrakhman fcb936bca9 Update coordinator client 2023-03-02 19:55:27 +01:00
Sergey Cherepanov 1837d89ef9 fileproto: SpaceInfo method 2023-03-02 19:11:55 +03:00
mcrakhman 9979a820d3 Update coordinator proto 2023-03-02 14:19:32 +01:00
mcrakhman 1ed8f162ae Update space header validation 2023-03-02 13:27:08 +01:00
mcrakhman 31b3fed274 Add space header validation 2023-03-02 13:23:43 +01:00
mcrakhman 3758e9cdf3 Add header to coordinator proto 2023-03-02 12:18:12 +01:00
mcrakhman eae4f0a57d Change verify export 2023-03-01 22:12:07 +01:00
mcrakhman 00c344112e Export VerifyDeleteChange 2023-03-01 22:06:37 +01:00
Roman Khafizianov 6c17d380b7 Merge pull request #37 from anytypeio/fix-graphviz-nocgo
exclude graphviz when CGO_ENABLED=0
2023-03-01 15:13:01 +01:00
Sergey Cherepanov e6352f74ad update fileproto 2023-03-01 13:44:15 +03:00
Roman Khafizianov 3f95941045 exclude graphviz when CGO_ENABLED=0 2023-02-28 17:03:19 +01:00
Sergey Cherepanov c42c4415bf fix fileproto 2023-02-28 12:26:19 +03:00
Sergey Cherepanov cd8dd7a869 extending file protocol 2023-02-28 12:04:05 +03:00
Sergey Cherepanov fef3c7f937 Merge pull request #35 from anytypeio/dependabot/go_modules/github.com/stretchr/testify-1.8.2
Bump github.com/stretchr/testify from 1.8.1 to 1.8.2
2023-02-28 11:45:08 +03:00
mcrakhman 4212138546 Update coordinator proto 2023-02-27 22:50:11 +01:00
dependabot[bot] 61859bbb09 Bump github.com/stretchr/testify from 1.8.1 to 1.8.2
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.1 to 1.8.2.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.1...v1.8.2)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-27 18:15:28 +00:00
Mikhail Rakhmanov 5906091051 Merge pull request #34 from anytypeio/account-deletion 2023-02-27 15:40:58 +01:00
Mikhail Rakhmanov 51a956fd84 Merge pull request #33 from anytypeio/coordinator 2023-02-27 15:40:29 +01:00
Sergey Cherepanov cd5a4abc87 mock coordinatorclient 2023-02-27 17:19:23 +03:00
Sergey Cherepanov 2dd2b309df coordinator client 2023-02-27 16:40:27 +03:00
mcrakhman cf80cdbe7d Change deleter peer logic 2023-02-27 14:24:28 +01:00
mcrakhman 74b0269230 Any-sync remove printlns and fix condition 2023-02-26 18:47:55 +01:00
mcrakhman 679b068dfa Merge remote-tracking branch 'origin/main' into account-deletion 2023-02-26 09:22:18 +01:00
mcrakhman aea36229fb Add deletionmanager tests 2023-02-26 00:05:28 +01:00
mcrakhman 67d97c26ec Add some basic tests for settings 2023-02-25 22:41:12 +01:00
mcrakhman 0c200ba323 Test settings state builder 2023-02-25 21:41:35 +01:00
mcrakhman be253bb01b Add deps 2023-02-24 23:51:45 +01:00
mcrakhman 8d25922f27 Update interfaces 2023-02-24 23:42:05 +01:00
mcrakhman 4ff0d9d42a Add logs and fix building settings state 2023-02-24 23:25:11 +01:00
Sergey Cherepanov 24cc345fbb Merge pull request #31 from anytypeio/coordinator
Coordinator
2023-02-24 16:01:41 +03:00
mcrakhman 0ababcf913 Stop syncing with clients after deletion 2023-02-24 00:10:40 +01:00
mcrakhman c5b47abf8a Remove unnecessary call 2023-02-24 00:04:11 +01:00
mcrakhman 7960297ea2 Change deletion logic in headsync and objectsync 2023-02-23 23:58:09 +01:00
mcrakhman 3cd292db5b Update any-sync new deletion logic 2023-02-23 21:08:31 +01:00
Sergey Cherepanov c708fbd984 remove priv keys from nodes conf 2023-02-23 16:55:10 +03:00
Sergey Cherepanov f5f960bd81 add peerId to space receipt 2023-02-23 14:13:11 +03:00
Sergey Cherepanov 2a76b81a32 Merge branch 'main' of github.com:anytypeio/any-sync into coordinator 2023-02-23 13:58:29 +03:00
Sergey Cherepanov 43e4c121ee coordinator node type 2023-02-23 13:38:58 +03:00
Sergey Cherepanov 6765150ba9 Merge pull request #29 from anytypeio/dependabot/go_modules/github.com/ipfs/go-merkledag-0.10.0
Bump github.com/ipfs/go-merkledag from 0.9.0 to 0.10.0
2023-02-22 16:43:23 +03:00
Sergey Cherepanov ab14cc9b6d Merge pull request #28 from anytypeio/dependabot/go_modules/golang.org/x/net-0.7.0
Bump golang.org/x/net from 0.6.0 to 0.7.0
2023-02-22 16:42:40 +03:00
Sergey Cherepanov 64d9689be5 coordinator proto 2023-02-22 16:35:29 +03:00
mcrakhman 904224447f Add space deleted in storage 2023-02-22 10:05:17 +01:00
mcrakhman 47be32c804 Merge branch 'main' into account-deletion 2023-02-22 09:59:13 +01:00
Mikhail Rakhmanov 98bbdc2e4e Merge pull request #30 from anytypeio/fixes-and-logs 2023-02-21 16:51:40 +01:00
mcrakhman 7da3553db3 Add more logs for space 2023-02-21 16:46:40 +01:00
mcrakhman c37c7ba874 Fix putTree close and handshake params and add logs for trees used debug 2023-02-21 16:36:58 +01:00
mcrakhman def3d7cdd5 Add comments 2023-02-21 14:44:12 +01:00
mcrakhman 1565f7e509 Create methods for deleting space 2023-02-20 23:47:32 +01:00
mcrakhman 75d503b94c Add spaceIsDeleted checks 2023-02-20 22:51:00 +01:00
mcrakhman 63833eb650 Add some deletion logic 2023-02-20 21:43:14 +01:00
dependabot[bot] 7cce8baca6 Bump github.com/ipfs/go-merkledag from 0.9.0 to 0.10.0
Bumps [github.com/ipfs/go-merkledag](https://github.com/ipfs/go-merkledag) from 0.9.0 to 0.10.0.
- [Release notes](https://github.com/ipfs/go-merkledag/releases)
- [Commits](https://github.com/ipfs/go-merkledag/compare/v0.9.0...v0.10.0)

---
updated-dependencies:
- dependency-name: github.com/ipfs/go-merkledag
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-20 18:08:42 +00:00
dependabot[bot] 59e18ca68c Bump golang.org/x/net from 0.6.0 to 0.7.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.6.0 to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.6.0...v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-20 18:08:07 +00:00
Mikhail Rakhmanov 030a7d61ba Merge pull request #27 from anytypeio/add-subscribe-after-push 2023-02-17 22:31:36 +01:00
mcrakhman 40ee06560e Add subscribe after push 2023-02-17 22:27:10 +01:00
Sergey Cherepanov 4ce9ffdf5a Merge pull request #26 from anytypeio/any-handshake
fix listener
2023-02-17 15:39:31 +03:00
Sergey Cherepanov b3e63984bb fix listener 2023-02-17 15:38:42 +03:00
Sergey Cherepanov 96f399293d Merge pull request #24 from anytypeio/any-handshake
Any handshake
2023-02-17 11:15:40 +03:00
Sergey Cherepanov 49b738fb3c remove unused param 2023-02-17 00:24:52 +03:00
Sergey Cherepanov 5b93739487 dial timeout 2023-02-17 00:19:05 +03:00
Sergey Cherepanov 38a74fd195 remove tls listener, move handshake to serve goroutine 2023-02-17 00:09:52 +03:00
Sergey Cherepanov ea9d101622 Merge branch 'main' of github.com:anytypeio/any-sync into any-handshake 2023-02-15 22:29:11 +03:00
Sergey Cherepanov ea80f4591b Merge pull request #25 from anytypeio/drpc-server
Add listen addrs
2023-02-15 22:28:47 +03:00
Sergey Cherepanov f2d729022d secure service test 2023-02-15 22:09:09 +03:00
Sergey Cherepanov 94d33f2c4a handshake with ctx 2023-02-15 21:29:37 +03:00
Sergey Cherepanov 20768266ca Merge branch 'main' of github.com:anytypeio/any-sync into any-handshake 2023-02-15 20:47:25 +03:00
Sergey Cherepanov 1db0e731e0 Merge pull request #23 from anytypeio/handshake-debug
increase dial timeout + debug logs
2023-02-15 14:59:17 +03:00
Sergey Cherepanov 4d356318f0 increase dial timeout + debug logs 2023-02-15 14:58:23 +03:00
mcrakhman cd82b41cf1 Add listen addrs 2023-02-14 23:09:03 +01:00
Sergey Cherepanov 3b837b4b6b Merge pull request #22 from anytypeio/fix-space-gc
Fix space GC
2023-02-14 21:20:35 +03:00
Sergey Cherepanov 189922e534 cleanup thread on error 2023-02-14 21:18:25 +03:00
Sergey Cherepanov 4cde54bdb3 dial timeout 2023-02-14 19:39:57 +03:00
Sergey Cherepanov 00dc0bcca6 fix 2023-02-14 19:00:27 +03:00
Sergey Cherepanov 047bd1c379 handleQueue thread close / onObjectClose method 2023-02-14 16:53:43 +03:00
Sergey Cherepanov ce0a60df67 timeoutconn: remove debug 2023-02-13 22:13:40 +03:00
Sergey Cherepanov 95b7927a4c Merge pull request #21 from anytypeio/fix-ocache-closing-deadlock
Fix ocache closing deadlock
2023-02-13 22:12:22 +03:00
Sergey Cherepanov e6d15c6a72 Merge pull request #19 from anytypeio/dependabot/go_modules/golang.org/x/net-0.6.0
Bump golang.org/x/net from 0.5.0 to 0.6.0
2023-02-13 22:10:21 +03:00
Sergey Cherepanov 0175ffeb3f Merge pull request #20 from anytypeio/dependabot/go_modules/github.com/ipfs/go-unixfs-0.4.3
Bump github.com/ipfs/go-unixfs from 0.4.2 to 0.4.3
2023-02-13 22:10:09 +03:00
Sergey Cherepanov 92f22d04b0 default periodic sync timeout 2023-02-13 22:09:06 +03:00
Sergey Cherepanov 55ad2a536b commongetter: check for space close 2023-02-13 21:53:27 +03:00
Sergey Cherepanov 18451bd4fe listen ctx for closing object 2023-02-13 21:49:56 +03:00
Sergey Cherepanov b34136ce06 wip: secureservice with verifiers 2023-02-13 21:48:41 +03:00
dependabot[bot] 0f89b5a4f5 Bump github.com/ipfs/go-unixfs from 0.4.2 to 0.4.3
Bumps [github.com/ipfs/go-unixfs](https://github.com/ipfs/go-unixfs) from 0.4.2 to 0.4.3.
- [Release notes](https://github.com/ipfs/go-unixfs/releases)
- [Commits](https://github.com/ipfs/go-unixfs/compare/v0.4.2...v0.4.3)

---
updated-dependencies:
- dependency-name: github.com/ipfs/go-unixfs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-13 18:12:32 +00:00
dependabot[bot] 004c8b7f38 Bump golang.org/x/net from 0.5.0 to 0.6.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.5.0 to 0.6.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.5.0...v0.6.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-13 18:12:15 +00:00
Sergey Cherepanov 4c00dba1cf Merge pull request #17 from anytypeio/add-try-lock-iface
RWLocker iface: add TryLock
2023-02-13 19:08:14 +03:00
Sergey Cherepanov 2fe9f8c295 handshake: return identity 2023-02-13 13:45:07 +03:00
Sergey Cherepanov e8a7ad7476 handshake proto and implementation 2023-02-11 22:52:20 +03:00
Roman Khafizianov 0601685040 fix tests 2023-02-10 14:40:45 +01:00
Roman Khafizianov adec5fa746 RWLocker iface: add TryLock 2023-02-10 14:23:45 +01:00
Sergey Cherepanov f6d3a85a7f Merge pull request #11 from anytypeio/dependabot/go_modules/github.com/ipfs/go-unixfs-0.4.2
Bump github.com/ipfs/go-unixfs from 0.4.1 to 0.4.2
2023-02-07 15:35:06 +03:00
Sergey Cherepanov 0eaf9094a1 Merge pull request #13 from anytypeio/dependabot/go_modules/github.com/goccy/go-graphviz-0.1.0
Bump github.com/goccy/go-graphviz from 0.0.9 to 0.1.0
2023-02-07 15:34:50 +03:00
dependabot[bot] 20320f81c3 Bump github.com/ipfs/go-unixfs from 0.4.1 to 0.4.2
Bumps [github.com/ipfs/go-unixfs](https://github.com/ipfs/go-unixfs) from 0.4.1 to 0.4.2.
- [Release notes](https://github.com/ipfs/go-unixfs/releases)
- [Commits](https://github.com/ipfs/go-unixfs/compare/v0.4.1...v0.4.2)

---
updated-dependencies:
- dependency-name: github.com/ipfs/go-unixfs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-07 12:34:43 +00:00
dependabot[bot] cbee800509 Bump github.com/goccy/go-graphviz from 0.0.9 to 0.1.0
Bumps [github.com/goccy/go-graphviz](https://github.com/goccy/go-graphviz) from 0.0.9 to 0.1.0.
- [Release notes](https://github.com/goccy/go-graphviz/releases)
- [Commits](https://github.com/goccy/go-graphviz/compare/v0.0.9...v0.1.0)

---
updated-dependencies:
- dependency-name: github.com/goccy/go-graphviz
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-07 12:34:24 +00:00
Sergey Cherepanov d9baeab63e Merge pull request #12 from anytypeio/dependabot/go_modules/golang.org/x/net-0.5.0
Bump golang.org/x/net from 0.3.0 to 0.5.0
2023-02-07 15:33:34 +03:00
Sergey Cherepanov 7ef55b3c46 Merge pull request #14 from anytypeio/dependabot/go_modules/github.com/ipfs/go-merkledag-0.9.0
Bump github.com/ipfs/go-merkledag from 0.8.1 to 0.9.0
2023-02-07 15:33:22 +03:00
Sergey Cherepanov 6b5b3d2b6b Merge pull request #16 from anytypeio/middleware-compatibility
Middleware fixes etc
2023-02-07 15:33:00 +03:00
Sergey Cherepanov 2d2ad9ee43 Merge pull request #10 from anytypeio/dependabot/go_modules/github.com/ipfs/go-block-format-0.1.1
Bump github.com/ipfs/go-block-format from 0.0.3 to 0.1.1
2023-02-07 15:32:38 +03:00
Sergey Cherepanov b2140f3c30 fix win build 2023-02-05 17:29:55 +03:00
mcrakhman d3108b5e6c Move remote getter to separate entity 2023-02-05 11:31:14 +01:00
Mikhail Rakhmanov e35c5eb5c7 Merge pull request #15 from anytypeio/nodesync 2023-02-04 21:54:37 +01:00
Sergey Cherepanov a4d26ab82d expose chash params 2023-02-04 19:45:11 +03:00
mcrakhman d3362ba85e Update batcher to 3.0.1 2023-02-04 17:41:20 +01:00
mcrakhman 9bb42a2906 Merge branch 'main' into middleware-compatibility 2023-02-04 17:38:48 +01:00
mcrakhman 9c696ad96a Rename sync tree 2023-02-04 17:38:27 +01:00
Mikhail Rakhmanov f2ff10e3f7 Merge pull request #9 from anytypeio/nodesync 2023-02-04 17:33:39 +01:00
Sergey Cherepanov 0f2a0eec8e Update dependabot.yml 2023-02-04 18:19:18 +03:00
dependabot[bot] 9289bb424a Bump github.com/ipfs/go-merkledag from 0.8.1 to 0.9.0
Bumps [github.com/ipfs/go-merkledag](https://github.com/ipfs/go-merkledag) from 0.8.1 to 0.9.0.
- [Release notes](https://github.com/ipfs/go-merkledag/releases)
- [Commits](https://github.com/ipfs/go-merkledag/compare/v0.8.1...v0.9.0)

---
updated-dependencies:
- dependency-name: github.com/ipfs/go-merkledag
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-04 15:07:10 +00:00
dependabot[bot] 859323c5ca Bump golang.org/x/net from 0.3.0 to 0.5.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.3.0 to 0.5.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/compare/v0.3.0...v0.5.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-04 15:06:55 +00:00
dependabot[bot] 61b0a24b28 Bump github.com/ipfs/go-block-format from 0.0.3 to 0.1.1
Bumps [github.com/ipfs/go-block-format](https://github.com/ipfs/go-block-format) from 0.0.3 to 0.1.1.
- [Release notes](https://github.com/ipfs/go-block-format/releases)
- [Commits](https://github.com/ipfs/go-block-format/compare/v0.0.3...v0.1.1)

---
updated-dependencies:
- dependency-name: github.com/ipfs/go-block-format
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-04 15:06:40 +00:00
Sergey Cherepanov 8624b672e2 Create dependabot.yml 2023-02-04 17:28:23 +03:00
Sergey Cherepanov 163c0e4058 testrpc server implement app.Component 2023-02-04 15:20:34 +03:00
mcrakhman 27b1c948e9 Remove SendResponsible 2023-02-03 20:15:55 +01:00
mcrakhman ea48365d2f Simplify ping tree 2023-02-03 20:12:02 +01:00
mcrakhman a193906d94 Add wait for available peers to appear 2023-02-03 15:36:12 +01:00
mcrakhman fad981daee Update responsible peers 2023-02-03 14:55:30 +01:00
mcrakhman 2da9f84907 Switch peers when getting tree 2023-02-03 14:49:05 +01:00
mcrakhman 9ba0620cab Fix peer getting in sync tree 2023-02-03 12:55:44 +01:00
Sergey Cherepanov cc2a98bbad fix 2023-02-02 21:21:25 +03:00
Sergey Cherepanov 75c660916d fix 2023-02-02 21:16:59 +03:00
Sergey Cherepanov d2a19d21fe fix ldiff mock 2023-02-02 13:19:20 +03:00
Sergey Cherepanov 6d2178dfc9 testPool: add peers 2023-02-02 13:18:30 +03:00
Sergey Cherepanov 3fcbe3aba9 testPool: implement pool.Service 2023-02-02 13:12:52 +03:00
mcrakhman 3630744086 Helper methods for local streams etc 2023-02-01 23:21:02 +01:00
Sergey Cherepanov f4c6679b0e ldiff: Len 2023-02-01 21:38:47 +03:00
Sergey Cherepanov aff2ecbca0 ldiff: Element by id 2023-02-01 20:58:20 +03:00
Sergey Cherepanov 38f831ab6b Merge pull request #8 from anytypeio/middleware-compatibility
Middleware compatibility
2023-01-31 19:36:52 +03:00
Sergey Cherepanov 8ff7cc9e16 fix the test blinking 2023-01-31 19:34:27 +03:00
Sergey Cherepanov 2ef1970479 Merge branch 'main' of github.com:anytypeio/any-sync into middleware-compatibility 2023-01-31 19:31:02 +03:00
Sergey Cherepanov 4cdbecd705 Merge pull request #7 from anytypeio/update-fileproto
Update fileproto
2023-01-31 18:40:55 +03:00
Sergey Cherepanov 0b977c2e1c fileproto: allowWrite flag 2023-01-31 15:48:53 +03:00
Sergey Cherepanov 25a51b2cc8 update fileproto: BlocksGet -> BlockGet 2023-01-31 14:27:41 +03:00
Sergey Cherepanov 1e9960752f update fileproto: unary method instead stream for blocksGet 2023-01-31 14:24:39 +03:00
Sergey Cherepanov 22ee12e28c rename proto pkg 2023-01-30 20:19:52 +03:00
Sergey Cherepanov 86aadf9d6c update fileproto 2023-01-30 20:17:41 +03:00
mcrakhman c8f6e39199 Merge branch 'main' into middleware-compatibility 2023-01-30 17:40:10 +01:00
Mikhail Rakhmanov 48e55d4827 Merge pull request #6 from anytypeio/async-stream-dial 2023-01-30 17:37:49 +01:00
Sergey Cherepanov 73762e460a streammanger -> peermanager, remove confconnector 2023-01-30 19:18:53 +03:00
Sergey Cherepanov d04d1f3a3b streampool: close dial queue 2023-01-30 18:40:25 +03:00
Sergey Cherepanov 9dc3c807fb stream config 2023-01-30 17:03:31 +03:00
Sergey Cherepanov 78067eaa4c dial pool 2023-01-30 16:43:52 +03:00
Sergey Cherepanov b2578a19ba async dial 2023-01-30 13:16:07 +03:00
mcrakhman 06bb0f7496 Add dialer to inner pool 2023-01-28 23:58:47 +01:00
mcrakhman e1565f60d6 Merge branch 'main' into middleware-compatibility 2023-01-28 20:24:40 +01:00
Mikhail Rakhmanov c6910bc2fe Merge pull request #5 from anytypeio/stream-manager 2023-01-28 20:22:29 +01:00
Sergey Cherepanov dccb5a9826 fix streampool tags 2023-01-27 20:34:48 +03:00
Sergey Cherepanov 8f1cba3770 streampool fixes 2023-01-27 16:15:50 +03:00
Sergey Cherepanov 44ffc39729 fix test 2023-01-27 14:52:08 +03:00
Sergey Cherepanov d7a888c1fd add requestId to separate request/reply flows 2023-01-27 14:37:06 +03:00
Sergey Cherepanov b78cf2c710 handle replies in separate thread 2023-01-27 13:58:28 +03:00
Sergey Cherepanov 92bb1ce576 space milti queue 2023-01-27 13:50:36 +03:00
mcrakhman bf56f338f2 Set heads in tree storage 2023-01-26 12:55:34 +01:00
mcrakhman 82b398ff7f Set object tree id at the start 2023-01-26 10:06:06 +01:00
Sergey Cherepanov 6cb696d508 debug/fixes 2023-01-25 23:35:20 +03:00
mcrakhman dffb418e60 Add tree exporter 2023-01-25 21:20:23 +01:00
mcrakhman 348b94a554 Change debug api for tree 2023-01-25 11:26:37 +01:00
mcrakhman 60b2c99660 Change interfaces for import tree 2023-01-25 10:13:01 +01:00
Sergey Cherepanov ddd20ae5b5 ctx logger + streapool add/removeTags + space subscribe message 2023-01-24 22:10:28 +03:00
mcrakhman ed615379c4 Change dataconverter interface 2023-01-24 17:04:38 +01:00
Sergey Cherepanov 09aee68bd7 debug 2023-01-24 16:23:35 +03:00
mcrakhman 7174e0e081 Add exporter/viewer interfaces and empty methods, refactoring 2023-01-22 22:16:14 +01:00
Sergey Cherepanov ebe05db662 peer and space last usage 2023-01-20 19:32:38 +03:00
Sergey Cherepanov 64e5479ca0 sync fixes + fix tests 2023-01-20 14:55:45 +03:00
Sergey Cherepanov 7fdd4f215e merge 2023-01-19 15:25:34 +03:00
Sergey Cherepanov 79d81662ce commonspace with new streampool 2023-01-19 15:17:04 +03:00
Sergey Cherepanov 330c97ab30 common streampool pkg 2023-01-18 13:46:49 +03:00
Sergey Cherepanov c1bf0e12cc PoolService still keeps the default Pool but with ability to create new pools 2023-01-18 13:44:50 +03:00
mcrakhman a5f01265d6 Fix timestamps 2023-01-18 10:06:01 +01:00
mcrakhman 9b9c278718 Few fixes re history 2023-01-18 09:16:38 +01:00
mcrakhman e7ee5dd864 Optimize interfaces 2023-01-18 08:36:54 +01:00
mcrakhman d466d40cf0 Expose header in history 2023-01-18 08:24:33 +01:00
mcrakhman 0acc14144a Add GetChange method to tree 2023-01-18 08:07:47 +01:00
mcrakhman 41f66edfb9 WIP history tree 2023-01-17 23:07:15 +01:00
mcrakhman 9c41451986 Update synctree to be able to provide listener 2023-01-17 15:16:04 +01:00
mcrakhman fb4e2b1fc9 Update delete loop 2023-01-17 00:04:14 +01:00
mcrakhman ea5856c4b0 Refactor commongetter to hide reserved objects 2023-01-16 20:34:26 +01:00
mcrakhman cf464ef25f Save tls listeners 2023-01-15 23:41:54 +01:00
Sergey Cherepanov bded394761 Merge pull request #4 from anytypeio/net-fixes
Net fixes
2023-01-15 22:53:07 +03:00
Sergey Cherepanov ef1155743e Merge branch 'middleware-compatibility' of github.com:anytypeio/any-sync into net-fixes 2023-01-15 22:50:53 +03:00
Sergey Cherepanov 4ff4c5950d pool.Get instead pool.Dial for pullSpace 2023-01-15 22:45:43 +03:00
Sergey Cherepanov 8e1053a116 addr in dial logs 2023-01-15 22:44:09 +03:00
Sergey Cherepanov 576cef2d53 fix nil ctx 2023-01-15 18:23:46 +03:00
mcrakhman 5e23680b36 Transactional add in object tree 2023-01-15 15:49:09 +01:00
Sergey Cherepanov 4e7c3840ac log connection error 2023-01-15 17:13:17 +03:00
mcrakhman 265b64b6f0 Make settings iterate from root, hide settings from user 2023-01-14 22:24:57 +01:00
mcrakhman 3bd8c92f51 Change objecttree to set mode Rebuild if we update the snapshot after current heads 2023-01-14 20:59:54 +01:00
Sergey Cherepanov a51f26c785 Merge branch 'nodesync' 2023-01-14 16:57:59 +03:00
Mikhail Rakhmanov af753ec392 Merge pull request #3 from anytypeio/nodesync 2023-01-14 14:51:42 +01:00
Sergey Cherepanov 8de24af4b9 write space hash on open 2023-01-13 19:51:29 +03:00
Sergey Cherepanov 790be1b02b save space hash 2023-01-13 19:13:26 +03:00
Sergey Cherepanov a093e1c9f0 StateHash method for space 2023-01-13 17:02:25 +03:00
Sergey Cherepanov 3381a84dec nodeconf Partition + ReplKey + configuration tests 2023-01-13 15:41:08 +03:00
Sergey Cherepanov 42913d3d18 shorter repl key 2023-01-13 15:40:20 +03:00
Mikhail Rakhmanov b84dfc595f Merge pull request #1 from anytypeio/middleware-compatibility 2023-01-13 13:09:39 +01:00
mcrakhman b6be4e8975 Change PutTree 2023-01-13 12:59:13 +01:00
mcrakhman 25a52f41b8 Update synctree to connect with responsible node 2023-01-13 12:55:28 +01:00
Sergey Cherepanov 18bc3314fe Merge branch 'main' into middleware-compatibility 2023-01-13 14:19:49 +03:00
mcrakhman 4c4abc9266 Update any-sync to wait sync for docs 2023-01-13 11:42:44 +01:00
Sergey Cherepanov 8cf6ad560e test account improve 2023-01-13 12:11:20 +03:00
Sergey Cherepanov 059f0ca636 update chash 2023-01-13 10:40:34 +03:00
Sergey Cherepanov 4cb79eeab9 nodeconf: expose CHash 2023-01-13 10:14:50 +03:00
Sergey Cherepanov 9f0c7f4711 Merge branch 'middleware-compatibility' of github.com:anytypeio/any-sync into middleware-compatibility 2023-01-12 13:53:51 +03:00
Sergey Cherepanov cfbae15999 net pool metrics 2023-01-12 13:53:32 +03:00
mcrakhman a7a54bcc8b Add handling for root ids 2023-01-11 19:18:04 +01:00
mcrakhman 1eed7e5050 Change object tree debug dump 2023-01-11 19:04:47 +01:00
Grigory Efimov d21dd91a77 .github/workflows/coverage.yml update actions 2023-01-11 20:32:06 +03:00
Grigory Efimov c792ff1fcb fix set-output 2023-01-11 20:25:15 +03:00
Grigory Efimov ec96ecafcc .github/workflows/coverage.yml add go-cache-paths 2023-01-11 20:20:28 +03:00
Grigory Efimov b875c504a0 .github/workflows/coverage.yml add actions/cach 2023-01-11 20:09:54 +03:00
Grigory Efimov 1bb707f0a2 refact 2023-01-11 19:59:54 +03:00
Grigory Efimov 28dd15e07e fix go version 2023-01-11 19:48:47 +03:00
Grigory Efimov 0fb8ec2b7c fix .github/workflows/coverage.yml 2023-01-11 19:43:52 +03:00
Grigory Efimov ca1ebaaf14 testbytest 2023-01-11 19:39:50 +03:00
Grigory Efimov 7f165dd6f8 testbytest 2023-01-11 19:38:44 +03:00
Grigory Efimov abe4603639 testbytest 2023-01-11 19:36:30 +03:00
Grigory Efimov da61fec0a0 testbytest 2023-01-11 18:52:12 +03:00
Grigory Efimov 39bffdab9c testbytest 2023-01-11 18:51:30 +03:00
Grigory Efimov c12bb0f081 testbytest 2023-01-11 18:37:56 +03:00
Grigory Efimov 96896d3060 testbytest 2023-01-11 18:34:28 +03:00
Grigory Efimov d158b98b35 testbytest 2023-01-11 18:33:17 +03:00
Grigory Efimov 57eb691b53 testbytest 2023-01-11 18:06:13 +03:00
Grigory Efimov fe8f8c43f5 testbytest 2023-01-11 18:04:16 +03:00
Grigory Efimov 39f79e03ce testbytest 2023-01-11 17:49:00 +03:00
Grigory Efimov a9c564777a testbytest 2023-01-11 17:17:19 +03:00
Grigory Efimov 876f94ef4e testbytest 2023-01-11 17:11:52 +03:00
Grigory Efimov 3bc23edfd3 .github/workflows/coverage.yml add test go install 2023-01-11 17:09:12 +03:00
Grigory Efimov 88cc804d01 Makefile: revert tests 2023-01-11 17:08:43 +03:00
Grigory Efimov 993403bf76 fix .github/workflows/coverage.yml 2023-01-11 16:29:21 +03:00
Grigory Efimov 35e58ae20f fix .github/workflows/coverage.yml 2023-01-11 16:25:52 +03:00
Grigory Efimov 27b6f754b5 fix .github/workflows/coverage.yml 2023-01-11 15:53:34 +03:00
Grigory Efimov 4186e30912 fix .github/workflows/coverage.yml 2023-01-11 15:43:55 +03:00
Sergey Cherepanov 9205f414cc Merge branch 'middleware-compatibility' of github.com:anytypeio/any-sync into middleware-compatibility 2023-01-11 15:39:52 +03:00
Sergey Cherepanov d986290db8 fileservice.HasCid 2023-01-11 15:39:32 +03:00
Grigory Efimov dcae5da0ec fix .github/workflows/coverage.yml 2023-01-11 15:38:53 +03:00
Grigory Efimov bcc0aa8fd6 Merge branch 'middleware-compatibility' of github.com:anytypeio/any-sync into middleware-compatibility 2023-01-11 15:34:54 +03:00
Grigory Efimov 8c36953790 fix .github/workflows/coverage.yml 2023-01-11 15:34:49 +03:00
Sergey Cherepanov 18bc40a531 Merge branch 'middleware-compatibility' of github.com:anytypeio/any-sync into middleware-compatibility 2023-01-11 15:30:52 +03:00
Sergey Cherepanov da74f45d46 update chash 2023-01-11 15:30:31 +03:00
Grigory Efimov 527d104ba7 fix GOPRIVATE 2023-01-11 15:22:43 +03:00
Grigory Efimov 7e41284cc8 add export GIT_TERMINAL_PROMPT=1 2023-01-11 15:20:13 +03:00
Grigory Efimov 071d85cd22 fix .github/workflows/coverage.yml 2023-01-11 15:18:40 +03:00
Grigory Efimov d0ac577686 coverage.yml add GOPRIVATE 2023-01-11 15:16:18 +03:00
Grigory Efimov 0dd992b7a6 fix 2023-01-11 15:06:17 +03:00
Grigory Efimov 2f95c66360 coverage.yml test 2023-01-11 15:03:48 +03:00
Grigory Efimov c7254ed8be .github/workflows/coverage.yml add git config 2023-01-11 14:53:55 +03:00
Sergey Cherepanov bb25dd0ea1 files: export DAGService 2023-01-11 14:50:20 +03:00
Sergey Cherepanov c565071dad build deps locally 2023-01-11 14:15:20 +03:00
mcrakhman b96654dd14 Fix acl derivation 2023-01-10 17:54:04 +01:00
mcrakhman 048406edfd Fix incorrect Graph method 2023-01-07 12:41:13 +01:00
Sergey Cherepanov 62c4e80c36 go vet 2023-01-07 13:35:12 +03:00
Sergey Cherepanov b3194da23c fix CI 2023-01-07 13:26:46 +03:00
mcrakhman 207e2dce52 Update libp2p ver 2023-01-07 00:11:27 +01:00
mcrakhman dfd2b0e8b9 Use account service in secureservice instead of config 2023-01-06 20:55:10 +01:00
Sergey Cherepanov 248a112236 fix CI 2023-01-06 21:44:28 +03:00
Sergey Cherepanov dbfc62eef5 crypro: DeriveRSA 2023-01-06 21:39:36 +03:00
Sergey Cherepanov b8b4f937d4 CI coverage 2023-01-06 21:38:48 +03:00
Sergey Cherepanov f79d7f8eb4 Revert "remove key.Key.Equals for more compatibility"
This reverts commit 7f5df570631033b017e2d724f7cd118445834ec9.
2023-01-05 18:24:48 +03:00
Sergey Cherepanov c88b31ca2f remove key.Key.Equals for more compatibility 2023-01-05 17:53:24 +03:00
Sergey Cherepanov 4c5cb61802 remove consensus dep 2023-01-05 15:39:08 +03:00
Sergey Cherepanov 90f0d0bb37 remove consensus dep 2023-01-05 15:39:08 +03:00
Sergey Cherepanov 71cfefe6fa make common module as any-sync repo 2023-01-05 15:34:09 +03:00
Mikhail Rakhmanov 4aa8c9ac09 Merge pull request #28 from anytypeio/middleware-compatibility 2023-01-05 13:18:21 +01:00
Sergey Cherepanov 6bb542ca09 Merge branch 'middleware-compatibility' of github.com:anytypeio/go-anytype-infrastructure-experiments into middleware-compatibility 2023-01-05 15:02:38 +03:00
Sergey Cherepanov f9c65bf8e1 go.mod: update mb/v3 2023-01-05 15:02:08 +03:00
mcrakhman 411a67c31f Change object creation logic 2023-01-05 12:45:50 +01:00
mcrakhman 02e92a0db0 Update space service to return id in case the space storage is already created 2023-01-04 15:07:56 +01:00
mcrakhman 9b59e6029d Rename ObjectTree.Iterate to IterateRoot 2023-01-04 14:39:26 +01:00
mcrakhman ac84febce9 Add easy access to unmarshalled header 2023-01-03 18:55:43 +01:00
Sergey Cherepanov e57ef72be1 Merge pull request #27 from anytypeio/files
files: rpcstore.AddAsync + lazy connect in clientmgr
2023-01-03 17:02:16 +03:00
Sergey Cherepanov 2d605ca694 files: rpcstore.AddAsync + lazy connect in clientmgr 2023-01-03 15:08:07 +03:00
mcrakhman c64f732878 Refactor space storage and add tree root 2023-01-03 00:02:33 +01:00
mcrakhman 40f8fb503a Make tree creation logic more transparent 2023-01-02 23:30:58 +01:00
mcrakhman 987bd0154f Update ocache ObjectLocker 2023-01-02 14:27:24 +01:00
mcrakhman 8680c24329 Add generic mustcomponent as in middleware 2023-01-02 13:16:49 +01:00
mcrakhman 1a2ceb6e2b Remove unnecessary context cleanup 2023-01-02 11:31:43 +01:00
mcrakhman 8d8aff2680 Remove error handling on broadcasting
In theory this can cause a non-decrease of a treesUsed var
2023-01-02 11:26:26 +01:00
mcrakhman 397252d97a Change headsync logic (ensure space closing on nodes) 2023-01-02 10:54:17 +01:00
mcrakhman 58389432d7 Change object creation to do it through cache 2023-01-01 22:05:29 +01:00
Sergey Cherepanov a55306aa27 Merge pull request #26 from anytypeio/files
files:partial upload
2022-12-29 22:36:41 +03:00
mcrakhman e44bcfedaf Remove unused function in confconnector 2022-12-29 18:27:27 +01:00
Mikhail Rakhmanov 33a2b5cc84 Merge pull request #25 from anytypeio/naming-and-refactoring 2022-12-29 15:39:07 +01:00
mcrakhman a855f02f63 Add queue test 2022-12-29 15:16:30 +01:00
mcrakhman 8eafaa266d Merge branch 'bilienko/integration-tests-start' into naming-and-refactoring
# Conflicts:
#	common/commonspace/objectsync/streampool.go
#	util/cmd/debug/commands/scripts.go
2022-12-29 13:51:41 +01:00
mcrakhman b5dc56d72c Update action queue 2022-12-29 13:47:17 +01:00
mcrakhman a103c960a4 Some streampool fixes 2022-12-28 17:52:10 +01:00
mcrakhman 98b8584efc Change solution to use queue for better performance 2022-12-27 22:37:47 +01:00
mcrakhman fa076ee83f Fix waiters in streampool 2022-12-27 19:59:27 +01:00
mcrakhman d4b90e5202 Fix streampool waiters bug 2022-12-27 19:57:01 +01:00
Sergey Cherepanov 84b21c62c5 refactor configs 2022-12-27 16:50:20 +03:00
Sergey Cherepanov caec31dd7f files:partial upload 2022-12-27 13:55:51 +03:00
Dmitry f86337ffc8 Nodes descriptions added 2022-12-27 17:57:17 +08:00
Dmitry 70163c3aaf First integrational test! 2022-12-27 17:53:14 +08:00
mcrakhman 5b98550349 Change port number for file node in deploy 2022-12-26 18:05:33 +01:00
mcrakhman 0a24886ce6 Change ID to Id 2022-12-26 18:01:18 +01:00
mcrakhman 9da4e0e7e9 Change ACL to Acl 2022-12-26 17:52:39 +01:00
mcrakhman d35d5c9437 Add comments and change cnames 2022-12-26 17:36:42 +01:00
Sergey Cherepanov ac018be8b1 Merge pull request #24 from anytypeio/files
file fixes: rpcstore context listen
2022-12-26 19:13:43 +03:00
Sergey Cherepanov 6fb720e0ea files: rpcstore context listen 2022-12-26 18:59:33 +03:00
mcrakhman e736ff313f Add generation for textchanges in test client 2022-12-25 21:20:41 +01:00
mcrakhman f696d774bb Refactor spacesyncproto 2022-12-25 21:12:17 +01:00
mcrakhman 50dcc735c7 Moving around files and refactoring of common 2022-12-25 20:27:37 +01:00
mcrakhman 3c30f72cd5 WIP refactoring syncstatus headsync 2022-12-25 15:08:26 +01:00
Sergey Cherepanov 874d5bbca0 Merge pull request #22 from anytypeio/files
Move fileserver to filenode, filenode in debugapi
2022-12-23 23:26:56 +03:00
Sergey Cherepanov acf21c7d54 move fileserver to filenode, filenode in debugapi 2022-12-23 21:23:44 +03:00
Sergey Cherepanov b7d4c27920 Merge pull request #21 from anytypeio/files
Files
2022-12-23 13:35:50 +03:00
Sergey Cherepanov 555ff804fe files: fix syncer loop, check ctx.Done 2022-12-22 14:50:20 +03:00
Sergey Cherepanov ec900d6e9d go mod fixes 2022-12-22 14:08:15 +03:00
Sergey Cherepanov 0b27fe7642 merge 2022-12-22 14:01:36 +03:00
Sergey Cherepanov ad8055635d gitignore debug.json 2022-12-22 13:46:22 +03:00
Mikhail Rakhmanov f4a27fb1d3 Merge pull request #20 from anytypeio/status-service 2022-12-21 21:34:00 +01:00
mcrakhman b0b75ac8ea Fix sync bugs (connection with different nodes on stream and diff) 2022-12-21 21:24:17 +01:00
mcrakhman 108900e88a Send head notify message on object create 2022-12-21 19:50:46 +01:00
Sergey Cherepanov 439182cdfa files: ref, proxyStore, index 2022-12-21 21:23:57 +03:00
mcrakhman 9b0aba1339 Change some parameter names 2022-12-21 10:48:32 +01:00
mcrakhman b3024740d0 Change naming, add debug mode and remove sh script 2022-12-21 00:23:09 +01:00
mcrakhman cfd2ea8ece More cmd fixes 2022-12-20 23:49:36 +01:00
mcrakhman d0adbf5026 Change command a bit, change debug command parameter 2022-12-20 23:30:32 +01:00
mcrakhman 9d6e0886f4 Add preliminary deploy script 2022-12-20 18:26:47 +01:00
mcrakhman f1d1a26b64 Few more fixes in tests 2022-12-19 18:51:31 +01:00
mcrakhman 15ccf29f0b Statusservice refactoring 2022-12-19 17:54:06 +01:00
mcrakhman ad87d5e545 Update status service 2022-12-19 16:59:43 +01:00
mcrakhman 322a8d144a Add some comments and checks 2022-12-18 13:43:05 +01:00
mcrakhman 4aa9ce1a27 Change sync status update logic 2022-12-18 13:16:37 +01:00
mcrakhman 364ec32a39 Added simple watcher and statusservice upgrades 2022-12-17 21:48:48 +01:00
mcrakhman 6444042a02 Add status service implementation 2022-12-17 17:19:19 +01:00
mcrakhman 5df4f13938 Add preliminary interfaces for syncstatus 2022-12-17 16:55:15 +01:00
Mikhail Rakhmanov 4eba413631 Merge pull request #19 from anytypeio/sync-fixes 2022-12-17 14:50:26 +01:00
mcrakhman 060c14792c Fix tests 2022-12-17 14:00:39 +01:00
mcrakhman a43d25841d Fixing tests wip 2022-12-16 23:39:00 +01:00
mcrakhman da0398c103 Fix objecttree tests 2022-12-16 21:16:30 +01:00
mcrakhman 3b320087f3 Add comments and fix streampool test 2022-12-16 21:02:34 +01:00
mcrakhman 0c85336e82 Add queue to sync client 2022-12-16 16:40:23 +01:00
mcrakhman 367bd1978d Move queue to separate object 2022-12-16 13:09:15 +01:00
mcrakhman 8a6a5a6bc1 More fixes to synctree and objecttree 2022-12-15 21:53:58 +01:00
mcrakhman c064f89988 Add queue to handle quickly updating records 2022-12-15 17:20:11 +01:00
mcrakhman c36fe14272 Removed sending under a lock in sync tree handler 2022-12-14 21:37:36 +01:00
Sergey Cherepanov 8a19ea8dcb file to client 2022-12-14 19:14:47 +03:00
Sergey Cherepanov d0a4b05706 file-node 2022-12-14 16:37:59 +03:00
mcrakhman 38e055ed12 Add connection with timeout, message limits more logs etc 2022-12-14 14:27:43 +01:00
mcrakhman 0924413056 Fix treebuilder errors 2022-12-13 13:57:23 +01:00
mcrakhman 09acec0d11 More raw loader fixes 2022-12-12 21:22:35 +01:00
Sergey Cherepanov fa8ac48c2a merge 2022-12-12 20:53:18 +03:00
Sergey Cherepanov da70f7b78c commonfile 2022-12-12 20:49:43 +03:00
mcrakhman e0c1e6f19d Fix not taking lock 2022-12-12 17:30:21 +01:00
mcrakhman b71360ec01 Change obj tree logic to include their heads on add raw changes 2022-12-12 17:17:09 +01:00
mcrakhman 3807d8e6ef Remove sugared logger 2022-12-09 22:11:22 +01:00
mcrakhman d7cce3c9fb Few fixes to syncservice and streamchecker 2022-12-09 21:53:24 +01:00
mcrakhman 5eaa3bfe44 Fix stream checker and tests 2022-12-09 19:55:57 +01:00
mcrakhman eb078d311a Add stream checker 2022-12-09 17:16:43 +01:00
Mikhail Rakhmanov 462eb1cc7a Merge pull request #18 from anytypeio/node-client-rpc 2022-12-06 23:07:46 +01:00
mcrakhman 8b703efb55 Change sync period 2022-12-06 21:02:47 +01:00
mcrakhman a08c3873b4 Add config gen to Makefile 2022-12-06 21:02:37 +01:00
mcrakhman 2ffd735e25 Add debug client to script and change commands 2022-12-06 18:15:26 +01:00
mcrakhman 761772f09a Add build helpers for local testing 2022-12-06 16:36:17 +01:00
mcrakhman de12b35216 Change tree creation logic 2022-12-06 10:20:05 +01:00
mcrakhman 6f8a21e547 More fixes to settingsdocument 2022-12-05 23:04:56 +01:00
mcrakhman 50e3d70f70 Fix incorrect args in debug 2022-12-05 20:02:26 +01:00
mcrakhman f9a064e9aa Remove duplicated code 2022-12-05 19:11:16 +01:00
mcrakhman a8507d8120 Update synctree ping logic 2022-12-05 18:51:59 +01:00
mcrakhman acf5b95646 Fix notifiable when creating sync tree 2022-12-05 17:21:14 +01:00
mcrakhman cd197dc7d7 Add scripts 2022-12-05 16:35:14 +01:00
mcrakhman 016543db40 Use cobra command line 2022-12-05 12:43:36 +01:00
mcrakhman 6ed6142129 Add two client script and fix common snapshot bug 2022-12-04 15:02:50 +01:00
mcrakhman cdfee1232e Fix syncservice loop 2022-12-03 17:54:10 +01:00
mcrakhman 25191a2443 Fix dfsprev 2022-12-03 15:59:45 +01:00
mcrakhman 339dd2b006 Add tree params and debugging 2022-12-03 15:06:43 +01:00
mcrakhman eb7a6fb9dc WIP add snapshot methods to debug api 2022-12-03 14:43:18 +01:00
mcrakhman 8b86ad2918 Check space dir existence before creating new 2022-12-03 13:23:14 +01:00
mcrakhman 3d624adc09 WIP node api 2022-12-03 13:09:15 +01:00
mcrakhman 4062a76e21 WIP node api 2022-12-03 12:44:02 +01:00
mcrakhman 9ae5710854 Simplify space locked solution 2022-12-03 11:48:18 +01:00
mcrakhman 494b7552ce Stopped space from unloading if trees are still there 2022-12-02 19:45:43 +01:00
mcrakhman aaecd2ae6d Move notifiable logic to synctree instead of syncclient 2022-12-02 14:14:02 +01:00
mcrakhman 1e4a25c15d Add dump heads and new head when adding text 2022-12-02 12:18:38 +01:00
mcrakhman ea9a51ec02 Add load space methods and fix storage errors 2022-12-02 09:52:04 +01:00
mcrakhman 4593637e23 Add scripts 2022-12-01 22:08:01 +01:00
mcrakhman bdae3a76d0 Redirect logs and add more commands 2022-12-01 21:28:36 +01:00
mcrakhman a370efda87 WIP debug client 2022-12-01 16:30:41 +01:00
mcrakhman bab107ebf6 WIP debug client 2022-12-01 11:15:17 +01:00
mcrakhman ea6dd0dc99 Client api wip 2022-11-30 23:42:26 +01:00
mcrakhman 20b9a36604 Add api grpc server to client 2022-11-30 22:50:43 +01:00
mcrakhman b3b50aa8ab Abstract drpc server and add client drpc api 2022-11-30 22:09:18 +01:00
Mikhail Rakhmanov f749e967f1 Merge pull request #17 from anytypeio/object-delete 2022-11-30 18:59:11 +01:00
mcrakhman 75b5193dc4 Add idprovider tests 2022-11-29 20:54:44 +01:00
mcrakhman 6bc8124413 Add deleter tests 2022-11-29 19:45:51 +01:00
mcrakhman 06d68e25ac Add deletionstate tests 2022-11-29 19:37:18 +01:00
mcrakhman 19bc9a0c93 Add settingsdocument tests 2022-11-29 18:53:04 +01:00
mcrakhman 4a3f20b236 Tests and refactoring 2022-11-29 16:54:04 +01:00
mcrakhman 82400407b9 Improve interfaces for synctree, generate mocks 2022-11-28 16:54:31 +01:00
mcrakhman d49384b98b Changes for tests and database fixes 2022-11-28 16:15:25 +01:00
mcrakhman 3c1072db4a Update diffsyncer 2022-11-25 15:24:25 +01:00
mcrakhman 41fde6f1ad Fix diffservice tests 2022-11-25 15:09:44 +01:00
mcrakhman 620731b464 Re-generate mocks and fix sync tree tests 2022-11-25 14:37:00 +01:00
mcrakhman 558cecea76 Fix more bugs 2022-11-25 11:40:36 +01:00
mcrakhman ff81bf1fac Add sync listener nil check 2022-11-25 09:46:01 +01:00
mcrakhman 71717a5dbb Fix settings document 2022-11-24 22:41:34 +01:00
mcrakhman 9155a3cbf2 Fix tests 2022-11-24 22:20:01 +01:00
mcrakhman f98d629a78 Add deletion test 2022-11-24 21:03:43 +01:00
mcrakhman 55e04cd2cd Change push space and some tree deletion logic 2022-11-24 20:24:59 +01:00
mcrakhman f6d8183001 Simplify deletion pipeline and change other stuff 2022-11-24 01:17:56 +01:00
mcrakhman b9f4ddd480 Add remove notifial 2022-11-13 14:16:58 +01:00
mcrakhman 0d9360490d Change deletion logic 2022-11-13 09:35:47 +01:00
mcrakhman 8854bbbe95 Fix some document deletion bugs 2022-11-12 22:55:12 +01:00
mcrakhman b617cdc690 Add sync loop to settings document 2022-11-12 18:17:01 +01:00
mcrakhman 62d26fbafd WIP more deletion checks 2022-11-12 17:49:25 +01:00
mcrakhman 6c38474c40 Diff service notify logic 2022-11-12 16:24:06 +01:00
mcrakhman fed41ba3aa Add non-encrypted changes and settings document logic 2022-11-12 13:36:30 +01:00
Sergey Cherepanov 181d74a139 ipfs store + cache store impl 2022-11-12 15:22:33 +03:00
mcrakhman 72d83ca171 WIP document deletion 2022-11-11 12:37:45 +01:00
mcrakhman 020b1f1d71 Change pullspace and pushspace methods and handlers 2022-11-10 20:10:57 +01:00
mcrakhman 11e57a43f7 Create space with settings 2022-11-10 19:44:33 +01:00
Sergey Cherepanov f9647c0691 merge 2022-11-08 14:46:43 +03:00
Mikhail Rakhmanov 92bdf7d57b Merge pull request #16 from anytypeio/acl-invite 2022-11-07 15:56:05 +01:00
mcrakhman d5ef4f9313 Add client space 2022-11-07 15:53:27 +01:00
mcrakhman 925005a9de Fix tests and change peer id context logic 2022-11-07 13:36:10 +01:00
mcrakhman 354ee3b6c7 Merge remote-tracking branch 'origin/acl-sync' into acl-invite
# Conflicts:
#	common/commonspace/service.go
#	common/pkg/acl/aclrecordproto/aclrecord.pb.go
#	common/pkg/acl/aclrecordproto/protos/aclrecord.proto
#	common/pkg/acl/list/list.go
#	common/pkg/acl/storage/inmemory.go
#	common/pkg/acl/testutils/acllistbuilder/liststoragebuilder.go
#	node/nodespace/rpchandler.go
#	node/nodespace/service.go
2022-11-07 12:36:48 +01:00
Sergey Cherepanov 9f080b4533 acl sync handler [wip] 2022-11-07 11:22:48 +03:00
mcrakhman 16b7181ebc Fix bugs in syncer etc, add peerkey for clients 2022-11-06 22:43:28 +01:00
mcrakhman 7c1d449743 Add load space command 2022-11-06 18:36:25 +01:00
mcrakhman 9bbb1a02e5 Add pull space method to proto and provide get space 2022-11-06 14:25:27 +01:00
Sergey Cherepanov 37035bfacd node acl service 2022-11-02 13:54:57 +03:00
Sergey Cherepanov 434f149e04 merge 2022-10-31 11:33:42 +03:00
Sergey Cherepanov 25f30cacc2 acl service wip 2022-10-31 11:25:58 +03:00
mcrakhman 8f862fd1a1 Fix building list 2022-10-28 13:24:05 +02:00
mcrakhman 5904815908 Add test for user join build and fix bugs 2022-10-28 13:01:20 +02:00
mcrakhman 36b80c25d4 Add raw record to list 2022-10-28 12:04:23 +02:00
mcrakhman 994ffe3d5f Change lisstoragebuilder to use inmemorystorage 2022-10-28 11:26:39 +02:00
mcrakhman 08b8ecba32 Change invite logic 2022-10-27 17:09:07 +02:00
Mikhail Rakhmanov 675885d875 Merge pull request #15 from anytypeio/streampool-test 2022-10-27 16:58:01 +02:00
mcrakhman f0b8e5ab64 Further test refactoring 2022-10-27 16:57:01 +02:00
mcrakhman 9a202207b4 Refactor tests 2022-10-27 16:43:10 +02:00
mcrakhman f4027b433d Fix mocks and formatting 2022-10-26 22:05:55 +02:00
mcrakhman ab4fee8229 Update tests 2022-10-26 22:03:02 +02:00
mcrakhman 1920a190bc Add space storage test 2022-10-26 21:48:59 +02:00
mcrakhman b9db1881ca Add badger list storage and tree storage test 2022-10-26 21:26:36 +02:00
mcrakhman 9c1c1e609d Finish pogreb tests and fix some issues 2022-10-26 21:00:15 +02:00
Sergey Cherepanov 7a2d366bdd node acl service (wip), fixes 2022-10-26 17:02:27 +03:00
mcrakhman 6497e461bd Add list storage 2022-10-26 14:18:44 +02:00
mcrakhman 6a6b750b4e Add more tree tests 2022-10-26 13:27:57 +02:00
mcrakhman 7b5c51a198 Change tree methods and start adding tree tests 2022-10-26 12:55:47 +02:00
mcrakhman 600ae38c0f Fix ocache closing 2022-10-25 12:16:04 +02:00
mcrakhman f5b9275192 Test stream pool 2022-10-24 21:57:19 +02:00
Sergey Cherepanov c33e944ec4 Merge branch 'main' of github.com:anytypeio/go-anytype-infrastructure-experiments into acl-sync 2022-10-24 16:16:47 +03:00
Mikhail Rakhmanov 8263bbfde2 Merge pull request #14 from anytypeio/add-client-functionality 2022-10-24 14:46:48 +02:00
mcrakhman 305cd2d795 Remove lastusage from syncclient 2022-10-24 14:46:24 +02:00
mcrakhman 93bf6d4dc4 Remove fleet artefacts 2022-10-24 14:44:15 +02:00
mcrakhman 3546fabdfa Merge branch 'main' into add-client-functionality
# Conflicts:
#	common/commonspace/syncservice/streampool.go
#	consensus/stream/service.go
2022-10-24 14:40:44 +02:00
mcrakhman f507e4cabd Better logs in space storage 2022-10-24 12:09:59 +02:00
Sergey Cherepanov 9992b1fa7e remove debug 2022-10-24 13:05:51 +03:00
mcrakhman e419e59f1d Change space payload 2022-10-24 11:59:37 +02:00
Sergey Cherepanov a3d5a56077 Merge pull request #13 from anytypeio/acl-sync
Acl sync
2022-10-24 12:21:20 +03:00
mcrakhman 7bd593f0e5 Stream pool and synctree fixes 2022-10-24 10:31:29 +02:00
mcrakhman 63d353a078 Add tests for sync handler 2022-10-23 22:24:13 +02:00
mcrakhman bf7a8b0854 Test sync tree 2022-10-23 18:19:15 +02:00
mcrakhman 09f28cf829 Refactor synctree 2022-10-22 16:19:47 +02:00
mcrakhman 58f0cf2916 Change proto and generalise logic 2022-10-22 16:05:06 +02:00
mcrakhman d6a45cced0 Move concrete logic to concrete packages 2022-10-22 14:23:55 +02:00
mcrakhman 8cc44c4a79 WIP sync refactoring 2022-10-22 13:04:58 +02:00
mcrakhman 5b93c78ebd Add timeout when waiting in streampool 2022-10-21 23:50:14 +02:00
mcrakhman 2085e3ae40 Don't broadcast when no content added 2022-10-21 21:32:37 +02:00
mcrakhman 3886436841 Storage close and diff fixes and logs 2022-10-21 20:34:37 +02:00
mcrakhman 40ce73db68 Ocache fix tests and return deadline back 2022-10-21 11:21:20 +02:00
mcrakhman 27b3213bac Fix ranges deadlock 2022-10-21 11:00:38 +02:00
mcrakhman 829cfb3054 Fix bug with nested read locks in diff 2022-10-20 21:48:31 +02:00
mcrakhman 3a3d21cd01 Raw loader and key fixes 2022-10-20 20:12:00 +02:00
mcrakhman aa47d8ecf8 Fix listener sync tree and diff iter bug 2022-10-20 19:39:45 +02:00
Sergey Cherepanov e8353897eb fix tests + remove consensus testclient 2022-10-20 20:20:03 +03:00
Sergey Cherepanov 58f9e623a4 reconnection for consensus client + fixes 2022-10-20 20:14:27 +03:00
mcrakhman d871c9b061 Fix add text bug 2022-10-20 19:08:43 +02:00
mcrakhman 4da28bcd86 Fix bug with head update not being sent on first receive 2022-10-20 18:56:09 +02:00
mcrakhman f21f05585c Fix bugs with storage 2022-10-20 16:02:16 +02:00
Sergey Cherepanov a606e5f79e consensus stream watch error 2022-10-20 14:04:59 +03:00
Sergey Cherepanov 15bf4c7018 WIP: acl service 2022-10-20 14:04:26 +03:00
mcrakhman efa3acc368 Create waiters map 2022-10-19 22:57:34 +02:00
mcrakhman 64e0ba3b77 Fix ocache and streampool bugs, fix push space 2022-10-19 22:21:44 +02:00
mcrakhman c9ded8956c Fix bugs appearing while creating tree 2022-10-19 20:21:58 +02:00
mcrakhman 6cb6b91bd4 Document creation fixes 2022-10-19 17:59:12 +02:00
mcrakhman 6470454221 Fix client imports 2022-10-19 15:07:29 +02:00
mcrakhman a3814f644c Add client configs to nodegen 2022-10-19 14:09:04 +02:00
mcrakhman a89d6dc468 Add API service 2022-10-19 13:38:20 +02:00
mcrakhman ad8f90d12e Add controller which calls different api methods 2022-10-19 12:48:55 +02:00
mcrakhman 95cb91ffad Add space and tree close logic and fix some related tests 2022-10-19 12:20:24 +02:00
mcrakhman 04fd072ae9 Add client cmd 2022-10-18 15:16:06 +02:00
mcrakhman 6201951e3b Add space and document creation logic for client 2022-10-18 14:17:49 +02:00
mcrakhman 9f1735110c Revert "Push main go remove replace"
This reverts commit 440d63ade0.
2022-10-18 11:17:57 +02:00
mcrakhman 440d63ade0 Push main go remove replace 2022-10-18 11:17:15 +02:00
Sergey Cherepanov 09e3eb3e54 go mod fixes 2022-10-17 18:11:00 +03:00
Sergey Cherepanov 22074a5fbd mod replace 2022-10-17 17:05:42 +03:00
Sergey Cherepanov 5a17320c44 go workspaces 2022-10-17 15:31:09 +03:00
Sergey Cherepanov 7548082355 move pkgs 2022-10-17 14:11:55 +03:00
Mikhail Rakhmanov 36181b0ef8 Merge pull request #12 from anytypeio/add-node-db 2022-10-17 12:55:59 +02:00
mcrakhman d478d973fd Merge branch 'main' into add-node-db
# Conflicts:
#	config/config.go
#	etc/configs/node1.yml
#	go.mod
#	go.sum
#	pkg/ocache/ocache.go
2022-10-17 12:52:12 +02:00
mcrakhman d80986e6d0 Change space service methods and change list storage 2022-10-17 12:40:55 +02:00
Sergey Cherepanov f929bf280f Merge pull request #10 from anytypeio/metrics
Prometheus metrics
2022-10-17 12:19:18 +03:00
mcrakhman 0013f64c61 Add preliminary API and fix space service methods 2022-10-16 17:59:16 +02:00
mcrakhman a2d2c61f1e Further wip badger 2022-10-16 16:41:01 +02:00
mcrakhman 700e04cc14 Add transaction benchmarks for badger 2022-10-16 10:13:17 +02:00
mcrakhman baa275b05b Client badger WIP 2022-10-15 21:43:01 +02:00
mcrakhman e6d6cae35c Change tree keys logic 2022-10-15 17:09:25 +02:00
mcrakhman 3facea9a06 Change pogreb implementation 2022-10-15 16:26:03 +02:00
mcrakhman a1aacbffed Fix keys and remove close waiter 2022-10-14 13:32:40 +02:00
mcrakhman 401402021a Add tests for ocache 2022-10-14 12:37:45 +02:00
Sergey Cherepanov e7602a164a prometheus handle metrics 2022-10-13 17:00:51 +03:00
mcrakhman 980357509d Change ocache to include wait for closing 2022-10-13 13:27:18 +02:00
mcrakhman ad021e5758 Add close waiter and close logic for db 2022-10-12 21:36:16 +02:00
Sergey Cherepanov 5e2a6778f0 change copaction interval 2022-10-12 15:14:19 +03:00
mcrakhman 296d9a86d0 Naming fixes 2022-10-11 23:11:17 +02:00
mcrakhman 85d83ed9ba Add checks for bench 2022-10-11 23:01:41 +02:00
mcrakhman d2b64fb272 Add wip benchmarks 2022-10-11 21:30:06 +02:00
Sergey Cherepanov 8970730ec9 fix 2022-10-11 12:36:05 +03:00
Sergey Cherepanov bc9ffc1a79 merge and fix test 2022-10-11 12:20:49 +03:00
mcrakhman 46f562e91d Change mutex logic 2022-10-11 11:15:46 +02:00
Sergey Cherepanov 3695a29b32 drpc metrics to quantile 2022-10-11 12:13:31 +03:00
mcrakhman 03eb8834c1 Wire up node 2022-10-11 10:25:29 +02:00
mcrakhman 79f5fc6908 Fix incorrect storage key 2022-10-11 09:56:51 +02:00
mcrakhman 526ddf12d5 WIP storage 2022-10-10 23:08:44 +02:00
mcrakhman d3acc71d95 Add some raw prototypes for storage 2022-10-10 20:10:47 +02:00
Mikhail Rakhmanov 3438770f30 Merge pull request #11 from anytypeio/sync-tests 2022-10-10 16:02:58 +02:00
Sergey Cherepanov 46d5d464b6 log config 2022-10-10 13:48:56 +03:00
Sergey Cherepanov 01438d630d fix 2022-10-10 11:38:25 +03:00
mcrakhman c3d8273caa SyncTree tests 2022-10-10 09:10:47 +02:00
mcrakhman 2999f2dcdd Add conf connector and change configuration logic 2022-10-09 21:54:34 +02:00
Sergey Cherepanov 1676d02d80 metric service, ocache and drpc handlers 2022-10-08 20:16:20 +03:00
Sergey Cherepanov 65e7d0a2cd Merge pull request #9 from anytypeio/consensus-node
Consensus node
2022-10-07 17:15:52 +03:00
mcrakhman afc9141a7c Merge remote-tracking branch 'origin/consensus-node' into sync-tests
# Conflicts:
#	Makefile
#	go.mod
#	go.sum
2022-10-07 16:02:43 +02:00
Sergey Cherepanov fd8ba951dc cleanup and comments 2022-10-07 15:06:54 +03:00
Sergey Cherepanov 6374ef8d1b rewrite for single stream 2022-10-07 11:04:48 +03:00
Sergey Cherepanov 9f16e7c272 pool.Dial* methods + consesus test client 2022-10-05 11:41:48 +03:00
Sergey Cherepanov b59cc28dc5 consensus client 2022-10-04 17:55:30 +03:00
Sergey Cherepanov 9930db8dc7 ignore consensus nodes in configuratrion 2022-10-04 16:40:40 +03:00
Sergey Cherepanov 9f902bfbe9 nodesgen consensus configs 2022-10-04 16:36:11 +03:00
Sergey Cherepanov 5729a32959 consensus node 2022-10-04 15:39:29 +03:00
mcrakhman 86616b6936 Add sync handler tests 2022-10-03 23:35:25 +02:00
mcrakhman d7a2a12129 Add head update tests 2022-10-02 22:40:13 +02:00
mcrakhman 274573df28 Further updates to sync logic 2022-10-02 16:11:47 +02:00
mcrakhman 20234269e9 Update sync handler 2022-10-02 11:54:34 +02:00
mcrakhman edc9a14ef5 Few tree sync fixes and additions 2022-10-01 21:04:31 +02:00
Sergey Cherepanov 7e6c72a198 consistent node db component 2022-09-30 19:16:44 +03:00
mcrakhman 48ae45550d Add diffservice tests 2022-09-30 16:55:34 +02:00
mcrakhman aaaab9deca Add periodicsync test 2022-09-30 16:12:41 +02:00
mcrakhman 53d10dc597 Add more syncer tests 2022-09-30 16:02:47 +02:00
mcrakhman 86ef6d2dd6 Move mocks to separate packages 2022-09-30 15:53:33 +02:00
mcrakhman beccba6d95 Require no error in tests 2022-09-30 14:43:11 +02:00
mcrakhman c01441460b Add push space custom matchers 2022-09-30 14:41:36 +02:00
mcrakhman 68ed132e61 Make sync test 2022-09-30 14:21:48 +02:00
mcrakhman b9c9424103 Add mock to write diffservice tests 2022-09-30 13:49:31 +02:00
mcrakhman 82eda9a092 Prepare components for testing 2022-09-30 09:28:13 +02:00
mcrakhman 8e6237e885 Separate diff service into components 2022-09-29 16:51:05 +02:00
mcrakhman e99650615d Fix circular deps etc 2022-09-29 16:22:56 +02:00
Mikhail Rakhmanov 58b4dc760e Merge pull request #8 from anytypeio/space-sync 2022-09-29 13:33:37 +02:00
mcrakhman 131f4a6968 Change cache logic 2022-09-28 20:56:11 +02:00
mcrakhman 533880e9f1 Add better error handling 2022-09-28 19:55:47 +02:00
mcrakhman 9be23b4ff7 Add push space logic and change storage interface 2022-09-28 09:18:04 +02:00
mcrakhman 53af9c6c1e Merge remote-tracking branch 'origin/rpcerr' into space-sync 2022-09-27 21:07:19 +02:00
mcrakhman 51af8c223d Add space/tree creation and derivation and push space protocol changes 2022-09-27 21:06:49 +02:00
Sergey Cherepanov dbc951a9d0 rpc errors 2022-09-27 19:43:39 +03:00
mcrakhman ac3a50a31c Gen new configs 2022-09-27 14:05:21 +02:00
mcrakhman c111257f63 Change account logic and add space objects 2022-09-27 13:31:50 +02:00
mcrakhman 67f0b82ae4 Fix keys 2022-09-26 22:26:42 +02:00
mcrakhman 0b97aaa302 Remove unneeded files 2022-09-26 22:20:08 +02:00
mcrakhman 227e7d47ab Few more fixes, remove node service 2022-09-26 22:15:40 +02:00
mcrakhman 9d73e1d5c1 Remove decoders and fix sync tree 2022-09-26 20:32:59 +02:00
mcrakhman 632d73698f More test fixes 2022-09-26 14:41:50 +02:00
mcrakhman 674e784318 Make acl list etc compile 2022-09-26 13:47:34 +02:00
mcrakhman d77d8dbdd1 Fixes to acls 2022-09-26 12:03:20 +02:00
mcrakhman 58812a568f WIP root change refactoring 2022-09-25 13:50:35 +02:00
mcrakhman e768e83507 Update gitignore to include vendor 2022-09-21 09:26:33 +02:00
mcrakhman dc69e93064 Some renamings and fixes 2022-09-18 11:23:01 +02:00
mcrakhman 832d0d5bfa Add treecache to node 2022-09-17 22:18:11 +02:00
mcrakhman 04184b1582 Remove responsibility of objecttree to notify and move to synctree 2022-09-17 21:39:17 +02:00
mcrakhman 70dabf70dd Move diff logic to separate service, simplify send sync 2022-09-17 20:54:23 +02:00
mcrakhman f420ac144c Update stream configuration and sync service 2022-09-16 11:20:43 +02:00
mcrakhman 1bc9242644 Update sync handler 2022-09-16 10:17:54 +02:00
mcrakhman eb36eed4c0 Add tree validation before saving 2022-09-16 07:47:36 +02:00
mcrakhman 91aacdd955 Add build tree sync request 2022-09-16 00:22:22 +02:00
mcrakhman a48e8f357a Update peer reading logic 2022-09-15 14:11:49 +02:00
mcrakhman 34ef43786c Update tree sync logic 2022-09-15 13:42:51 +02:00
mcrakhman fbdf820734 Add sync tree and remove head send from sync handler 2022-09-15 12:34:28 +02:00
mcrakhman aecb95bc92 Add more logic re sync service 2022-09-15 11:35:16 +02:00
mcrakhman f900cbff0b WIP sync 2022-09-14 18:54:10 +02:00
mcrakhman 206b695e62 Add sync handler 2022-09-13 22:26:51 +02:00
mcrakhman c94a0c94f3 Add .idea to gitignore 2022-09-13 16:37:15 +02:00
mcrakhman 5599277bf2 Generate drpc and some fixes 2022-09-13 15:52:47 +02:00
mcrakhman 7a62b348cd Revert "Use string instead of bytes, to simplify code"
This reverts commit a907cde399.
2022-09-13 12:26:37 +02:00
mcrakhman a907cde399 Use string instead of bytes, to simplify code 2022-09-13 10:44:02 +02:00
mcrakhman 77bbb64feb Fix pkg tests 2022-09-13 09:04:47 +02:00
mcrakhman b5aa0b135f Fix list package and some storage stuff 2022-09-12 21:25:27 +02:00
mcrakhman 9976a84d42 WIP proto refactoring 2022-09-12 21:06:20 +02:00
mcrakhman 018406d25c Merge branch 'main' into space-sync 2022-09-11 21:26:43 +02:00
mcrakhman 639cc302d2 Fix tree append logic, validtate only new changes and check unattached changes which were added at this round 2022-09-11 18:45:47 +02:00
mcrakhman 3cbdf798a2 Merge branch 'drpc-services' into space-sync
# Conflicts:
#	Makefile
#	cmd/node/node.go
#	pkg/acl/aclchanges/aclpb/aclchanges.pb.go
#	pkg/acl/acltree/aclstatebuilder.go
#	pkg/acl/acltree/change.go
#	pkg/acl/example/plaintextdocument/document.go
#	pkg/acl/example/plaintextdocument/plaintextdocstate.go
#	pkg/acl/list/aclstate.go
#	pkg/acl/list/changebuilder.go
#	pkg/acl/testutils/testchanges/proto/test.pb.go
#	pkg/acl/testutils/treestoragebuilder/treestoragebuilder.go
#	pkg/acl/testutils/treestoragebuilder/treestoragebuildergraph_nix.go
#	pkg/acl/tree/treegraph_nix.go
#	service/document/service.go
#	service/space/service.go
#	service/space/space.go
#	service/sync/requesthandler/requesthandler.go
#	service/treecache/service.go
#	syncproto/helpers.go
#	syncproto/sync.pb.go
2022-09-11 15:36:38 +02:00
mcrakhman 74ebcc5616 Few more changes and tree fixes 2022-09-09 09:45:25 +02:00
mcrakhman 0685f401f3 Change tree Rebuild checks 2022-09-09 09:01:50 +02:00
mcrakhman 235c179d60 Refactor ACLState a little bit 2022-09-08 23:20:11 +02:00
Sergey Cherepanov caa725dc20 package moving + commonaspace and nidespace services 2022-09-08 21:56:32 +03:00
Sergey Cherepanov feff4ad890 rename protos-go -> protos 2022-09-08 12:15:54 +03:00
Sergey Cherepanov bee067ad76 space service periodic sync 2022-09-08 12:05:30 +03:00
Mikhail Rakhmanov 2e47b23827 Merge pull request #7 from anytypeio/new-tree-structure 2022-09-08 08:09:00 +02:00
mcrakhman 88bf93a2cc Fix tests 2022-09-08 08:08:33 +02:00
mcrakhman 3f284889ac Make stuff compile 2022-09-08 07:50:52 +02:00
Sergey Cherepanov 698acad3bd remotediff test 2022-09-08 00:24:19 +03:00
mcrakhman 20f462e603 Add load from database tests and fixes 2022-09-07 23:23:59 +02:00
Sergey Cherepanov 11cb1e1f31 drpc space service, new pool, new peer 2022-09-08 00:00:46 +03:00
mcrakhman 5b1228e14d More tests and fixes for reading from tree 2022-09-07 22:30:29 +02:00
mcrakhman de1a5baaca Change sync logic 2022-09-07 22:01:11 +02:00
mcrakhman 3ca8814e46 WIP algorithm 2022-09-07 21:02:42 +02:00
Sergey Cherepanov 7afde45ac6 change proto generator to std gogo: fix tests 2022-09-07 19:49:54 +03:00
Sergey Cherepanov 357e889dad change proto generator to std gogo 2022-09-07 19:43:01 +03:00
mcrakhman 024f316b3a Merge branch 'main' into new-tree-structure
# Conflicts:
#	Makefile
#	cmd/node/node.go
#	syncproto/sync.pb.go
2022-09-07 16:38:18 +02:00
Mikhail Rakhmanov 2e1c2cbc6e Merge pull request #6 from anytypeio/space-diff 2022-09-07 12:38:17 +02:00
Sergey Cherepanov 88832596da WIP: files 2022-09-07 13:01:15 +03:00
mcrakhman 1375854237 Fix addcontent acl list locks 2022-09-07 11:31:16 +02:00
mcrakhman c7fdd81691 More test cases 2022-09-07 01:02:53 +02:00
mcrakhman 276d935cee Add more tests and fix bugs 2022-09-07 00:12:11 +02:00
mcrakhman 9905b2608b Add first test for object tree 2022-09-06 20:58:41 +02:00
mcrakhman 06f80f0646 Change storages and add iterate 2022-09-06 20:23:57 +02:00
mcrakhman cde907e15a Add mocks for tree 2022-09-06 19:25:06 +02:00
mcrakhman 94c95b44ec Make better dependencies for object tree 2022-09-06 18:08:24 +02:00
mcrakhman b8e3ec8b7b Rename to object tree 2022-09-05 13:08:49 +02:00
mcrakhman 9a1dc89a99 Fixing several copy issues and other stuff in doc tree 2022-09-05 12:40:41 +02:00
mcrakhman 90cee00319 Further changes to treebuilder etc 2022-09-05 11:48:50 +02:00
mcrakhman 1ad4eba17e Fix algorithms and add tests 2022-09-04 17:05:00 +02:00
mcrakhman b87c38e232 Add tree reduce 2022-09-04 16:05:38 +02:00
mcrakhman b602c08603 Change tree iterate 2022-09-04 14:34:37 +02:00
Sergey Cherepanov b18514d9fe file bench service 2022-08-31 14:49:35 +03:00
Sergey Cherepanov e2dffad962 configuration to space 2022-08-24 13:53:32 +03:00
Sergey Cherepanov 3957fa3be6 configuration from config 2022-08-24 00:40:14 +03:00
Sergey Cherepanov 535358b057 configuration service 2022-08-23 21:32:04 +03:00
mcrakhman 9f4dbf0002 Fix change validator 2022-08-23 00:21:46 +02:00
mcrakhman 10a5677241 Add ACL exchange logic 2022-08-22 23:53:14 +02:00
mcrakhman f2cedfd08e Add generated read keys 2022-08-21 17:43:22 +02:00
mcrakhman 13236d8ebc Import acl document from the start 2022-08-21 16:44:05 +02:00
mcrakhman d19f0534e4 Add test for user remove 2022-08-21 15:50:06 +02:00
mcrakhman f5d79a5469 Add first ACL list test and other refactoring 2022-08-21 15:23:47 +02:00
mcrakhman 88c58237c1 Few tree fixes 2022-08-20 23:55:18 +02:00
mcrakhman 03d8c187ed Add tree tests and benchmarks 2022-08-20 23:22:01 +02:00
mcrakhman d3ed644b9c Optimize and simplify tree algorithm 2022-08-20 16:57:32 +02:00
mcrakhman 6bfda6743e Update code to work with acl lists and new models 2022-08-20 14:28:06 +02:00
mcrakhman 8cf5acd183 Add some logic in validator 2022-08-19 22:31:13 +02:00
mcrakhman b0432487cc Add ACLList struct 2022-08-19 21:44:38 +02:00
mcrakhman e5fe1b5a92 Change treeheader to header 2022-08-19 20:35:10 +02:00
mcrakhman 1f403291cc Change list builder 2022-08-19 18:17:02 +02:00
mcrakhman edaad85990 Create ACL list and update tree package 2022-08-18 23:03:27 +02:00
mcrakhman f4aeecae84 Add rollback after adding invalid changes 2022-08-16 00:57:38 +02:00
mcrakhman 9142f28d55 Refactoring doc tree 2022-08-15 23:31:00 +02:00
Sergey Cherepanov 23deea0c37 remove example service 2022-08-15 20:06:23 +03:00
Sergey Cherepanov cc2d6194a9 diff container and mod tidy 2022-08-15 20:03:30 +03:00
mcrakhman 340528f5af Fix update document 2022-08-12 10:01:01 +02:00
mcrakhman 66067a3506 Fix bugs related to docTree and aclTree 2022-08-11 19:04:29 +02:00
mcrakhman eed3c518cd Move treeheader to upper level in sync 2022-08-11 16:31:09 +02:00
mcrakhman 88b0e8cd7e Add storage and change API to include both tree types 2022-08-11 15:54:27 +02:00
mcrakhman 028cb99586 Update sync layer to work with different trees 2022-08-11 15:33:39 +02:00
mcrakhman 37eacb5d61 Rewrite document service methods 2022-08-11 11:04:03 +02:00
mcrakhman e7956901de Start tree cache and document rewrite 2022-08-10 22:16:16 +02:00
mcrakhman 0e3d7cc8ca Add doc tree 2022-08-10 18:10:28 +02:00
mcrakhman 103c8a4aef Further work acltree, aclstate etc 2022-08-10 14:48:17 +02:00
mcrakhman b7190b6a74 Add read and write methods for treecache 2022-08-10 12:53:15 +02:00
mcrakhman ddf976e240 Add tree removal for state build 2022-08-10 12:00:22 +02:00
mcrakhman d23cc0a239 Add acltree in new package 2022-08-10 11:52:45 +02:00
mcrakhman ff5a0d6a45 Start refactoring tree package 2022-08-09 21:48:14 +02:00
mcrakhman 6a1e7d9a20 Fix sync logic, move acks after handling finished, made message service async 2022-08-06 10:22:09 +02:00
mcrakhman 7eb965977a Merge branch 'sync-prototype' 2022-08-05 21:06:13 +02:00
mcrakhman 48627c0901 Fix replyId update and add goroutine in handleMessage 2022-08-05 21:05:47 +02:00
mcrakhman fbaf96eb38 Fix sync bugs and add logs 2022-08-05 18:14:35 +02:00
mcrakhman 31517db9bf Add contexts and fix bugs 2022-08-05 17:23:43 +02:00
mcrakhman d0ecf74836 Change dialling logic 2022-08-05 16:33:37 +02:00
mcrakhman 89fb94420a Fix compile errors and add dependencies 2022-08-05 14:19:03 +02:00
mcrakhman 3e81762f27 Add message to syncproto, fix request handler 2022-08-05 13:44:23 +02:00
mcrakhman 06a362225f Merge remote-tracking branch 'origin/net' into sync-prototype
# Conflicts:
#	cmd/node/node.go
#	config/config.go
#	config/grpc.go
#	etc/config.yml
#	service/net/rpc/server/drpcserver.go
#	service/net/secure/service.go
2022-08-05 12:40:02 +02:00
Sergey Cherepanov 62c5d8e3b9 net utils wip 2022-08-05 12:07:34 +03:00
mcrakhman 4724fab97e Add indication in tree graph for non-acl changes 2022-08-03 14:47:31 +02:00
mcrakhman b7e94f2b52 Change config generation logic 2022-08-03 14:20:20 +02:00
mcrakhman ad99bbb7e8 Add logs for receiving a message, descriptive messages for api 2022-08-02 20:50:35 +02:00
mcrakhman 7a1ddf9c83 Fix server starting, add some logs 2022-08-02 20:28:33 +02:00
mcrakhman cc134da1ed Change bootstrap, remove cycles 2022-08-02 20:08:07 +02:00
mcrakhman e6be9ea84e Add http server for debugging 2022-08-02 19:28:32 +02:00
mcrakhman 718df194fe Add component registration on start 2022-08-02 17:36:37 +02:00
mcrakhman 26eb52adf1 Add nodes generation 2022-08-02 17:30:59 +02:00
mcrakhman f5ec49b375 Add document service 2022-07-21 11:54:19 +02:00
mcrakhman 9aaa80dcc1 Update tree cache for document creation 2022-07-21 11:03:39 +02:00
mcrakhman 3463b8d374 WIP sync logic prototype with server 2022-07-19 10:40:31 +02:00
mcrakhman 16b3787258 WIP stream logic 2022-07-19 01:43:21 +02:00
mcrakhman 269f907d1d Add message service to handle streams 2022-07-19 00:47:22 +02:00
mcrakhman 5f2144db0f Use account key instead of generating new 2022-07-18 22:27:27 +02:00
mcrakhman 693c159360 Merge remote-tracking branch 'origin/grpc-experiments' into sync-prototype
# Conflicts:
#	cmd/node/node.go
2022-07-18 14:45:25 +02:00
mcrakhman 7a6c42df62 Add oneof to sync proto 2022-07-18 10:45:05 +02:00
mcrakhman 9f984054b6 Create new tree logic 2022-07-17 13:34:07 +02:00
Sergey Cherepanov 1400e57cf7 merge 2022-07-17 09:54:59 +03:00
Sergey Cherepanov 7f4b18104f drpc server, libp2p tls connections 2022-07-17 09:44:16 +03:00
mcrakhman d9452509b5 WIP new document logic 2022-07-16 14:24:27 +02:00
mcrakhman d120da8000 Move logic to different components 2022-07-16 13:21:46 +02:00
mcrakhman bc5ffe1828 Add getting common snapshot logic 2022-07-16 12:24:49 +02:00
mcrakhman 5211267cd2 Provide for getting new tree 2022-07-16 00:50:08 +02:00
mcrakhman 67e0956042 Fix request handler heads update logic 2022-07-15 18:10:31 +02:00
mcrakhman 0467933ae9 Start using raw changes + wip request handler 2022-07-15 16:27:10 +02:00
Sergey Cherepanov 48af2866d2 remove drpc 2022-07-15 12:35:54 +03:00
mcrakhman d3922d812a WIP handling sync requests 2022-07-15 10:14:39 +02:00
mcrakhman 2893c4b4ad Use pb rawchanges instead of treestorage.RawChange 2022-07-15 09:39:16 +02:00
mcrakhman b8dcac03cc WIP sync proto 2022-07-14 23:52:37 +02:00
mcrakhman 5f8dc32ed6 Simplify code in decoders 2022-07-14 21:29:10 +02:00
mcrakhman 293ada96c8 Add tree cache service 2022-07-14 21:23:40 +02:00
mcrakhman 6c4c4f1e9d Add account reading from file logic 2022-07-14 21:07:34 +02:00
Sergey Cherepanov cbfaf7fd91 drpc experiment 2022-07-14 16:59:01 +03:00
mcrakhman abc6f68062 WIP pubsub and sync logic 2022-07-14 14:28:37 +02:00
Mikhail Rakhmanov 1331123fb4 Merge pull request #3 from anytypeio/improve-acl-naming-and-packages 2022-07-14 13:17:16 +02:00
mcrakhman 5f92bc3d8d Rename pb packages to something more meaningful 2022-07-14 13:16:12 +02:00
mcrakhman c8c6b63cd2 Add context to acl tree 2022-07-14 11:21:52 +02:00
Sergey Cherepanov 8552b676cb grpc server and test client implementation 2022-07-14 00:22:28 +03:00
mcrakhman fe90278b8e Rename keys 2022-07-13 22:57:12 +02:00
mcrakhman 37e6722ffb Remove go-threads 2022-07-13 22:54:47 +02:00
mcrakhman 7b27fca2ad Change decoder logic 2022-07-13 22:36:56 +02:00
mcrakhman f9919bd72c Add error in all methods of tree storage 2022-07-13 22:25:32 +02:00
mcrakhman 3a72587a9e Remove last thread ids 2022-07-13 22:15:07 +02:00
mcrakhman 71d88cdcba Change thread to tree storage and other changes with protos 2022-07-13 22:11:38 +02:00
mcrakhman d439b804fe Update keys 2022-07-13 20:52:56 +02:00
Sergey Cherepanov 84c2ee1e1c Merge pull request #2 from anytypeio/initial-structure
Initial structure
2022-07-13 21:11:10 +03:00
Sergey Cherepanov fecae2228d add pkg/ocache 2022-07-13 21:10:48 +03:00
Sergey Cherepanov 2580b7693c gitignore for bin 2022-07-13 20:20:43 +03:00
Sergey Cherepanov a62e713219 project structure proposal 2022-07-13 20:09:19 +03:00
Sergey Cherepanov 410780d051 fix go.sum 2022-07-13 19:23:16 +03:00
Sergey Cherepanov bb910e20ac inital structure: app, config, node binary 2022-07-13 19:18:43 +03:00
Sergey Cherepanov 31a735ca25 move acl tree files to acl folder 2022-07-13 19:17:50 +03:00
mcrakhman 3d069dbb1c Merge branch 'add-data-package-with-acl' 2022-07-13 13:49:44 +02:00
mcrakhman 5b3f594b2b Remove unneded plaintextdocument file 2022-07-13 09:00:10 +02:00
mcrakhman 3643719161 Improve test 2022-07-12 23:13:51 +02:00
mcrakhman d743317069 Add document tests 2022-07-12 23:07:05 +02:00
mcrakhman f274181270 Add test for thread utility 2022-07-12 21:27:27 +02:00
mcrakhman c90af47f02 Change InviteId logic and add UserAdd to change builder 2022-07-12 20:25:13 +02:00
mcrakhman 7496c79ae5 Add test for rebuild useCase 2022-07-12 17:46:08 +02:00
mcrakhman 11d3d93693 Add first update test 2022-07-12 16:57:02 +02:00
mcrakhman 277d40761b Change updates format in yaml 2022-07-12 16:33:22 +02:00
mcrakhman 27ec72cdb9 Fix header parse and add todos 2022-07-12 13:26:06 +02:00
mcrakhman e17e8e15c7 Simplify paths to yaml tests 2022-07-12 10:55:09 +02:00
mcrakhman 4b6ec6f310 Change first thread entry handling 2022-07-12 10:27:02 +02:00
mcrakhman 04c55cd602 Add synchronization to inmemory store 2022-07-12 09:01:06 +02:00
mcrakhman c242ef49a6 WIP plaintextdocument 2022-07-12 08:55:30 +02:00
mcrakhman 4502ca7bf4 WIP plain text document 2022-07-12 08:45:13 +02:00
mcrakhman 9c72f05fbd Add inmemory thread and build logic 2022-07-12 00:30:33 +02:00
mcrakhman 1fb82a6d7f Update changebuilder to include CID creation 2022-07-11 23:56:13 +02:00
mcrakhman 9242f78b88 Add thread header and make snapshot 2022-07-11 23:40:44 +02:00
mcrakhman 5b9d403b7d Add tree updater to ACL tree 2022-07-11 11:28:58 +02:00
mcrakhman cc802ebc69 WIP ACLTree tests 2022-07-10 17:33:27 +02:00
mcrakhman 0129c97c83 Change ACLTree methods to include change builder 2022-07-10 17:14:15 +02:00
mcrakhman fc01b705a5 Add logic update and create logic 2022-07-10 15:53:50 +02:00
mcrakhman 126d080c63 WIP Change interface rework 2022-07-10 11:44:02 +02:00
mcrakhman d5614eb5ee WIP ACLTree 2022-07-08 15:13:01 +02:00
mcrakhman c07b26d568 WIP ACLTree 2022-07-08 12:31:06 +02:00
mcrakhman 11fcb088e5 WIP ACLTree and moving files to packages 2022-07-08 11:33:32 +02:00
mcrakhman f889aa1b71 Continue adjusting folder structure 2022-07-08 10:52:06 +02:00
mcrakhman ee123efd7d WIP change package structure 2022-07-08 10:19:43 +02:00
mcrakhman 94f53aabe1 WIP create change continue 2022-07-07 11:32:02 +02:00
mcrakhman 9fe502057e WIP change creation logic 2022-07-07 11:11:34 +02:00
mcrakhman 17c556607a Add simple update test 2022-07-07 09:18:55 +02:00
mcrakhman a18938d0b1 Add document build test 2022-07-07 08:53:47 +02:00
mcrakhman d08fb78357 Add updated changes to thread builder 2022-07-07 08:47:11 +02:00
mcrakhman 906d2b6614 Fix some bugs 2022-07-06 23:35:43 +02:00
mcrakhman a09e7ca1cb Add new heads logic 2022-07-06 23:24:11 +02:00
mcrakhman 3ad690d624 WIP document update 2022-07-06 21:51:00 +02:00
mcrakhman db06096169 Add Build method to document 2022-07-05 18:09:45 +02:00
mcrakhman 99d67d531b Change init logic and fix tests 2022-07-05 17:21:45 +02:00
mcrakhman 4e8585bf9a WIP change init logic 2022-07-05 15:58:11 +02:00
mcrakhman 376e5ba119 Unify changes loading in change loader 2022-07-05 15:42:17 +02:00
mcrakhman 3395f35b59 WIP work on document structure 2022-07-05 11:59:01 +02:00
mcrakhman 102b33c3dc Add some comments in tests 2022-07-04 13:42:29 +02:00
mcrakhman aa4222588a Further fixes including tests 2022-07-04 13:28:16 +02:00
mcrakhman 15deb66dff Fix tree builder bugs related to dfs 2022-07-04 11:22:37 +02:00
mcrakhman 4a5da29cfe Fix first ACL test and threadbuilder 2022-07-03 18:15:33 +02:00
mcrakhman 18963546ef Add plain text document state 2022-07-03 17:51:14 +02:00
mcrakhman 3404626dd6 Fix threadbuilder and change examples 2022-07-03 17:35:21 +02:00
mcrakhman e970fa1d4d Make doc state building more abstract 2022-07-03 16:38:48 +02:00
mcrakhman e5b3b7a3c0 Some more fixes for trees and stuff 2022-07-02 22:34:50 +02:00
mcrakhman 64eb6581e3 Further refactoring of ACL logic 2022-07-01 11:39:42 +02:00
mcrakhman dc9f6f3917 Add some deps for threadbuilder 2022-07-01 10:54:47 +02:00
mcrakhman 3c2cd5d936 Rework threadbuilder for new change format 2022-06-30 18:52:20 +02:00
mcrakhman 6e98744aab Add makefile and proto generating 2022-06-29 18:30:44 +02:00
mcrakhman 4beb0d2902 Start adding ACLs 2022-06-28 15:37:09 +02:00
Mikhail Rakhmanov 9c62ec1d2c Initial commit 2022-06-28 13:37:24 +02:00
251 changed files with 9756 additions and 22580 deletions

No files matched your search

+1 -1
View File
@@ -10,5 +10,5 @@ updates:
schedule:
interval: "weekly"
ignore:
- dependency-name: "github.com/anyproto/go-chash"
- dependency-name: "github.com/anytypeio/go-chash"
+1 -1
View File
@@ -7,7 +7,7 @@ jobs:
test:
runs-on: ubuntu-latest
env:
GOPRIVATE: github.com/anyproto
GOPRIVATE: github.com/anytypeio
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2023 Any Association
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.
+3 -4
View File
@@ -1,5 +1,5 @@
.PHONY: proto test deps
export GOPRIVATE=github.com/anyproto
export GOPRIVATE=github.com/anytypeio
export PATH:=deps:$(PATH)
proto:
@@ -8,8 +8,8 @@ proto:
@$(eval P_ACL_RECORDS_PATH_PB := commonspace/object/acl/aclrecordproto)
@$(eval P_TREE_CHANGES_PATH_PB := commonspace/object/tree/treechangeproto)
@$(eval P_CRYPTO_PATH_PB := util/crypto/cryptoproto)
@$(eval P_ACL_RECORDS := M$(P_ACL_RECORDS_PATH_PB)/protos/aclrecord.proto=github.com/anyproto/any-sync/$(P_ACL_RECORDS_PATH_PB))
@$(eval P_TREE_CHANGES := M$(P_TREE_CHANGES_PATH_PB)/protos/treechange.proto=github.com/anyproto/any-sync/$(P_TREE_CHANGES_PATH_PB))
@$(eval P_ACL_RECORDS := M$(P_ACL_RECORDS_PATH_PB)/protos/aclrecord.proto=github.com/anytypeio/any-sync/$(P_ACL_RECORDS_PATH_PB))
@$(eval P_TREE_CHANGES := M$(P_TREE_CHANGES_PATH_PB)/protos/treechange.proto=github.com/anytypeio/any-sync/$(P_TREE_CHANGES_PATH_PB))
protoc --gogofaster_out=:. $(P_ACL_RECORDS_PATH_PB)/protos/*.proto
protoc --gogofaster_out=:. $(P_TREE_CHANGES_PATH_PB)/protos/*.proto
@@ -20,7 +20,6 @@ proto:
protoc --gogofaster_out=$(PKGMAP):. --go-drpc_out=protolib=github.com/gogo/protobuf:. net/streampool/testservice/protos/*.proto
protoc --gogofaster_out=:. net/secureservice/handshake/handshakeproto/protos/*.proto
protoc --gogofaster_out=$(PKGMAP):. --go-drpc_out=protolib=github.com/gogo/protobuf:. coordinator/coordinatorproto/protos/*.proto
protoc --gogofaster_out=:. --go-drpc_out=protolib=github.com/gogo/protobuf:. consensus/consensusproto/protos/*.proto
deps:
go mod download
+3 -3
View File
@@ -1,9 +1,9 @@
//go:generate mockgen -destination mock_accountservice/mock_accountservice.go github.com/anyproto/any-sync/accountservice Service
//go:generate mockgen -destination mock_accountservice/mock_accountservice.go github.com/anytypeio/any-sync/accountservice Service
package accountservice
import (
"github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/commonspace/object/accountdata"
"github.com/anytypeio/any-sync/app"
"github.com/anytypeio/any-sync/commonspace/object/accountdata"
)
const CName = "common.accountservice"
+3 -3
View File
@@ -1,9 +1,9 @@
package mock_accountservice
import (
"github.com/anyproto/any-sync/accountservice"
"github.com/anyproto/any-sync/commonspace/object/accountdata"
"go.uber.org/mock/gomock"
"github.com/anytypeio/any-sync/accountservice"
"github.com/anytypeio/any-sync/commonspace/object/accountdata"
"github.com/golang/mock/gomock"
)
func NewAccountServiceWithAccount(ctrl *gomock.Controller, acc *accountdata.AccountKeys) *MockService {
@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/anyproto/any-sync/accountservice (interfaces: Service)
// Source: github.com/anytypeio/any-sync/accountservice (interfaces: Service)
// Package mock_accountservice is a generated GoMock package.
package mock_accountservice
@@ -7,9 +7,9 @@ package mock_accountservice
import (
reflect "reflect"
app "github.com/anyproto/any-sync/app"
accountdata "github.com/anyproto/any-sync/commonspace/object/accountdata"
gomock "go.uber.org/mock/gomock"
app "github.com/anytypeio/any-sync/app"
accountdata "github.com/anytypeio/any-sync/commonspace/object/accountdata"
gomock "github.com/golang/mock/gomock"
)
// MockService is a mock of Service interface.
+18 -87
View File
@@ -4,11 +4,10 @@ import (
"context"
"errors"
"fmt"
"github.com/anyproto/any-sync/app/logger"
"github.com/anytypeio/any-sync/app/logger"
"go.uber.org/zap"
"os"
"runtime"
"runtime/debug"
"strings"
"sync"
"time"
@@ -16,8 +15,8 @@ import (
var (
// values of this vars will be defined while compilation
AppName, GitCommit, GitBranch, GitState, GitSummary, BuildDate string
name string
GitCommit, GitBranch, GitState, GitSummary, BuildDate string
name string
)
var (
@@ -55,14 +54,11 @@ type ComponentStatable interface {
// App is the central part of the application
// It contains and manages all components
type App struct {
parent *App
components []Component
mu sync.RWMutex
startStat Stat
stopStat Stat
deviceState int
versionName string
anySyncVersion string
components []Component
mu sync.RWMutex
startStat Stat
stopStat Stat
deviceState int
}
// Name returns app name
@@ -70,28 +66,11 @@ func (app *App) Name() string {
return name
}
func (app *App) AppName() string {
return AppName
}
// Version return app version
func (app *App) Version() string {
return GitSummary
}
// SetVersionName sets the custom application version
func (app *App) SetVersionName(v string) {
app.versionName = v
}
// VersionName returns a string with the settled app version or auto-generated version if it didn't set
func (app *App) VersionName() string {
if app.versionName != "" {
return app.versionName
}
return AppName + ":" + GitSummary + "/any-sync:" + app.AnySyncVersion()
}
type Stat struct {
SpentMsPerComp map[string]int64
SpentMsTotal int64
@@ -124,16 +103,6 @@ func VersionDescription() string {
return fmt.Sprintf("build on %s from %s at #%s(%s)", BuildDate, GitBranch, GitCommit, GitState)
}
// ChildApp creates a child container which has access to parent's components
// It doesn't call Start on any of the parent's components
func (app *App) ChildApp() *App {
return &App{
parent: app,
deviceState: app.deviceState,
anySyncVersion: app.AnySyncVersion(),
}
}
// Register adds service to registry
// All components will be started in the order they were registered
func (app *App) Register(s Component) *App {
@@ -153,14 +122,10 @@ func (app *App) Register(s Component) *App {
func (app *App) Component(name string) Component {
app.mu.RLock()
defer app.mu.RUnlock()
current := app
for current != nil {
for _, s := range current.components {
if s.Name() == name {
return s
}
for _, s := range app.components {
if s.Name() == name {
return s
}
current = current.parent
}
return nil
}
@@ -178,14 +143,10 @@ func (app *App) MustComponent(name string) Component {
func MustComponent[i any](app *App) i {
app.mu.RLock()
defer app.mu.RUnlock()
current := app
for current != nil {
for _, s := range current.components {
if v, ok := s.(i); ok {
return v
}
for _, s := range app.components {
if v, ok := s.(i); ok {
return v
}
current = current.parent
}
empty := new(i)
panic(fmt.Errorf("component with interface %T is not found", empty))
@@ -195,13 +156,9 @@ func MustComponent[i any](app *App) i {
func (app *App) ComponentNames() (names []string) {
app.mu.RLock()
defer app.mu.RUnlock()
names = make([]string, 0, len(app.components))
current := app
for current != nil {
for _, c := range current.components {
names = append(names, c.Name())
}
current = current.parent
names = make([]string, len(app.components))
for i, c := range app.components {
names[i] = c.Name()
}
return
}
@@ -262,15 +219,6 @@ func (app *App) Start(ctx context.Context) (err error) {
return
}
// IterateComponents iterates over all registered components. It's safe for concurrent use.
func (app *App) IterateComponents(fn func(Component)) {
app.mu.RLock()
defer app.mu.RUnlock()
for _, s := range app.components {
fn(s)
}
}
func stackAllGoroutines() []byte {
buf := make([]byte, 1024)
for {
@@ -309,7 +257,7 @@ func (app *App) Close(ctx context.Context) error {
case <-time.After(StopWarningAfter):
statLogger(app.stopStat, log).
With(zap.String("in_progress", currentComponentStopping)).
Warn("components close in progress")
Warn("components close in progress")
}
}()
go func() {
@@ -363,20 +311,3 @@ func (app *App) SetDeviceState(state int) {
}
}
}
var onceVersion sync.Once
func (app *App) AnySyncVersion() string {
onceVersion.Do(func() {
info, ok := debug.ReadBuildInfo()
if ok {
for _, mod := range info.Deps {
if mod.Path == "github.com/anyproto/any-sync" {
app.anySyncVersion = mod.Version
break
}
}
}
})
return app.anySyncVersion
}
-34
View File
@@ -34,40 +34,6 @@ func TestAppServiceRegistry(t *testing.T) {
names := app.ComponentNames()
assert.Equal(t, names, []string{"c1", "r1", "s1"})
})
t.Run("Child MustComponent", func(t *testing.T) {
app := app.ChildApp()
app.Register(newTestService(testTypeComponent, "x1", nil, nil))
for _, name := range []string{"c1", "r1", "s1", "x1"} {
assert.NotPanics(t, func() { app.MustComponent(name) }, name)
}
assert.Panics(t, func() { app.MustComponent("not-registered") })
})
t.Run("Child ComponentNames", func(t *testing.T) {
app := app.ChildApp()
app.Register(newTestService(testTypeComponent, "x1", nil, nil))
names := app.ComponentNames()
assert.Equal(t, names, []string{"x1", "c1", "r1", "s1"})
})
t.Run("Child override", func(t *testing.T) {
app := app.ChildApp()
app.Register(newTestService(testTypeRunnable, "s1", nil, nil))
_ = app.MustComponent("s1").(*testRunnable)
})
}
func TestApp_IterateComponents(t *testing.T) {
app := new(App)
app.Register(newTestService(testTypeRunnable, "c1", nil, nil))
app.Register(newTestService(testTypeRunnable, "r1", nil, nil))
app.Register(newTestService(testTypeComponent, "s1", nil, nil))
var got []string
app.IterateComponents(func(s Component) {
got = append(got, s.Name())
})
assert.ElementsMatch(t, []string{"c1", "r1", "s1"}, got)
}
func TestAppStart(t *testing.T) {
+1 -1
View File
@@ -1,7 +1,7 @@
// Package ldiff provides a container of elements with fixed id and changeable content.
// Diff can calculate the difference with another diff container (you can make it remote) with minimum hops and traffic.
//
//go:generate mockgen -destination mock_ldiff/mock_ldiff.go github.com/anyproto/any-sync/app/ldiff Diff,Remote
//go:generate mockgen -destination mock_ldiff/mock_ldiff.go github.com/anytypeio/any-sync/app/ldiff Diff,Remote
package ldiff
import (
+5 -5
View File
@@ -3,9 +3,9 @@ package ldiff
import (
"context"
"fmt"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/mgo.v2/bson"
"math"
"sort"
"testing"
@@ -44,7 +44,7 @@ func TestDiff_Diff(t *testing.T) {
d2 := New(16, 16)
for i := 0; i < 1000; i++ {
id := fmt.Sprint(i)
head := uuid.NewString()
head := bson.NewObjectId().Hex()
d1.Set(Element{
Id: id,
Head: head,
@@ -92,7 +92,7 @@ func TestDiff_Diff(t *testing.T) {
for i := 0; i < 10; i++ {
d2.Set(Element{
Id: fmt.Sprint(i),
Head: uuid.NewString(),
Head: bson.NewObjectId().Hex(),
})
}
@@ -108,7 +108,7 @@ func TestDiff_Diff(t *testing.T) {
for i := 0; i < 10; i++ {
d2.Set(Element{
Id: fmt.Sprint(i),
Head: uuid.NewString(),
Head: bson.NewObjectId().Hex(),
})
}
var cancel func()
@@ -123,7 +123,7 @@ func BenchmarkDiff_Ranges(b *testing.B) {
d := New(16, 16)
for i := 0; i < 10000; i++ {
id := fmt.Sprint(i)
head := uuid.NewString()
head := bson.NewObjectId().Hex()
d.Set(Element{
Id: id,
Head: head,
+3 -3
View File
@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/anyproto/any-sync/app/ldiff (interfaces: Diff,Remote)
// Source: github.com/anytypeio/any-sync/app/ldiff (interfaces: Diff,Remote)
// Package mock_ldiff is a generated GoMock package.
package mock_ldiff
@@ -8,8 +8,8 @@ import (
context "context"
reflect "reflect"
ldiff "github.com/anyproto/any-sync/app/ldiff"
gomock "go.uber.org/mock/gomock"
ldiff "github.com/anytypeio/any-sync/app/ldiff"
gomock "github.com/golang/mock/gomock"
)
// MockDiff is a mock of Diff interface.
+13 -50
View File
@@ -1,13 +1,10 @@
package logger
import (
"fmt"
"strings"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"github.com/anyproto/any-sync/util/slice"
"github.com/anytypeio/any-sync/util/slice"
)
type LogFormat int
@@ -18,19 +15,14 @@ const (
JSONOutput
)
type NamedLevel struct {
Name string `yaml:"name"`
Level string `yaml:"level"`
}
type Config struct {
Production bool `yaml:"production"`
DefaultLevel string `yaml:"defaultLevel"`
Levels []NamedLevel `yaml:"levels"` // first match will be used
AddOutputPaths []string `yaml:"outputPaths"`
DisableStdErr bool `yaml:"disableStdErr"`
Format LogFormat `yaml:"format"`
ZapConfig *zap.Config `yaml:"-"` // optional, if set it will be used instead of other config options
Production bool `yaml:"production"`
DefaultLevel string `yaml:"defaultLevel"`
NamedLevels map[string]string `yaml:"namedLevels"`
AddOutputPaths []string `yaml:"outputPaths"`
DisableStdErr bool `yaml:"disableStdErr"`
Format LogFormat `yaml:"format"`
ZapConfig *zap.Config `yaml:"-"` // optional, if set it will be used instead of other config options
}
func (l Config) ApplyGlobal() {
@@ -76,49 +68,20 @@ func (l Config) ApplyGlobal() {
conf.Level = defaultLevel
}
}
for _, v := range l.Levels {
if lev, err := zap.ParseAtomicLevel(v.Level); err == nil {
var lvl = make(map[string]zap.AtomicLevel)
for k, v := range l.NamedLevels {
if lev, err := zap.ParseAtomicLevel(v); err == nil {
lvl[k] = lev
// we need to have a minimum level of all named loggers for the main logger
if lev.Level() < conf.Level.Level() {
conf.Level.SetLevel(lev.Level())
}
}
}
lg, err := conf.Build()
if err != nil {
Default().Fatal("can't build logger", zap.Error(err))
}
SetDefault(lg)
SetNamedLevels(l.Levels)
}
// LevelsFromStr parses a string of the form "name1=DEBUG;prefix*=WARN;*=ERROR" into a slice of NamedLevel
// it may be useful to parse the log level from the OS env var
func LevelsFromStr(s string) (levels []NamedLevel) {
for _, kv := range strings.Split(s, ";") {
strings.TrimSpace(kv)
parts := strings.Split(kv, "=")
var key, value string
if len(parts) == 1 {
key = "*"
value = parts[0]
_, err := zap.ParseAtomicLevel(value)
if err != nil {
fmt.Printf("Can't parse log level %s: %s\n", parts[0], err.Error())
continue
}
levels = append(levels, NamedLevel{Name: key, Level: value})
} else if len(parts) == 2 {
key = parts[0]
value = parts[1]
}
_, err := zap.ParseAtomicLevel(value)
if err != nil {
fmt.Printf("Can't parse log level %s: %s\n", parts[0], err.Error())
continue
}
levels = append(levels, NamedLevel{Name: key, Level: value})
}
return levels
SetNamedLevels(lvl)
}
+20 -25
View File
@@ -11,17 +11,12 @@ var (
mu sync.Mutex
logger *zap.Logger
loggerConfig zap.Config
namedLevels []namedLevel
namedLevels = make(map[string]zap.AtomicLevel)
namedGlobs = make(map[string]glob.Glob)
namedLoggers = make(map[string]CtxLogger)
namedSugarLoggers = make(map[string]*zap.SugaredLogger)
)
type namedLevel struct {
name string
level zap.AtomicLevel
}
func init() {
loggerConfig = zap.NewDevelopmentConfig()
logger, _ = loggerConfig.Build()
@@ -40,23 +35,18 @@ func SetDefault(l *zap.Logger) {
// it also supports glob patterns for names, like "app*"
// can be racy in case there are existing named loggers
// so consider to call only once at the beginning
func SetNamedLevels(nls []NamedLevel) {
func SetNamedLevels(l map[string]zap.AtomicLevel) {
mu.Lock()
defer mu.Unlock()
namedLevels = namedLevels[:0]
namedLevels = l
var minLevel = logger.Level()
for _, nl := range nls {
l, err := zap.ParseAtomicLevel(nl.Level)
if err != nil {
continue
}
namedLevels = append(namedLevels, namedLevel{name: nl.Name, level: l})
g, err := glob.Compile(nl.Name)
for k, l := range namedLevels {
g, err := glob.Compile(k)
if err == nil {
namedGlobs[nl.Name] = g
namedGlobs[k] = g
}
namedLevels[k] = l
if l.Level() < minLevel {
minLevel = l.Level()
}
@@ -91,18 +81,23 @@ func Default() *zap.Logger {
return logger
}
// getLevel returns the level for the given name
// it return the first matching name or glob pattern whatever comes first
func getLevel(name string) zap.AtomicLevel {
for _, nl := range namedLevels {
if nl.name == name {
return nl.level
level, ok := namedLevels[name]
if !ok {
var found bool
for globName, glob := range namedGlobs {
if glob.Match(name) {
found = true
level, _ = namedLevels[globName]
// no need to check ok, because we know that globName exists
break
}
}
if g, ok := namedGlobs[nl.name]; ok && g.Match(name) {
return nl.level
if !found {
level = loggerConfig.Level
}
}
return zap.NewAtomicLevelAt(logger.Level())
return level
}
func NewNamed(name string, fields ...zap.Field) CtxLogger {
-150
View File
@@ -1,150 +0,0 @@
package logger
import (
"reflect"
"testing"
"go.uber.org/zap"
)
func Test_getLevel1(t *testing.T) {
SetNamedLevels([]NamedLevel{
{Name: "app", Level: "debug"},
{Name: "app*", Level: "info"},
{Name: "app.sub", Level: "warn"},
{Name: "*", Level: "fatal"},
})
tests := []struct {
name string
want zap.AtomicLevel
}{
{
name: "app",
want: zap.NewAtomicLevelAt(zap.DebugLevel),
},
{
name: "app.aaa",
want: zap.NewAtomicLevelAt(zap.InfoLevel),
},
{
name: "app.sub",
want: zap.NewAtomicLevelAt(zap.InfoLevel),
},
{
name: "random",
want: zap.NewAtomicLevelAt(zap.FatalLevel),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := getLevel(tt.name); !reflect.DeepEqual(got, tt.want) {
t.Errorf("getLevel() = %v, want %v", got, tt.want)
}
})
}
}
func Test_getLevel2(t *testing.T) {
SetNamedLevels([]NamedLevel{
{Name: "*", Level: "ERROR"},
{Name: "app", Level: "info"},
{Name: "app.sub", Level: "warn"},
{Name: "*", Level: "fatal"},
})
tests := []struct {
name string
want zap.AtomicLevel
}{
{
name: "app",
want: zap.NewAtomicLevelAt(zap.ErrorLevel),
},
{
name: "app.aaa",
want: zap.NewAtomicLevelAt(zap.ErrorLevel),
},
{
name: "app.sub",
want: zap.NewAtomicLevelAt(zap.ErrorLevel),
},
{
name: "random",
want: zap.NewAtomicLevelAt(zap.ErrorLevel),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := getLevel(tt.name); !reflect.DeepEqual(got, tt.want) {
t.Errorf("getLevel() = %v, want %v", got, tt.want)
}
})
}
}
func Test_getLevel3(t *testing.T) {
SetNamedLevels([]NamedLevel{
{Name: "app", Level: "info"},
{Name: "*.sub", Level: "warn"},
{Name: "*", Level: "fatal"},
})
tests := []struct {
name string
want zap.AtomicLevel
}{
{
name: "app",
want: zap.NewAtomicLevelAt(zap.InfoLevel),
},
{
name: "app.sub",
want: zap.NewAtomicLevelAt(zap.WarnLevel),
},
{
name: "random",
want: zap.NewAtomicLevelAt(zap.FatalLevel),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := getLevel(tt.name); !reflect.DeepEqual(got, tt.want) {
t.Errorf("getLevel() = %v, want %v", got, tt.want)
}
})
}
}
func Test_getLevel4(t *testing.T) {
SetNamedLevels([]NamedLevel{
{Name: "*", Level: "invalid"},
{Name: "app", Level: "info"},
{Name: "b", Level: "invalid"},
})
tests := []struct {
name string
want zap.AtomicLevel
}{
{
name: "app",
want: zap.NewAtomicLevelAt(zap.InfoLevel),
},
{
name: "app.sub",
want: zap.NewAtomicLevelAt(logger.Level()),
},
{
name: "b",
want: zap.NewAtomicLevelAt(logger.Level()),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := getLevel(tt.name); !reflect.DeepEqual(got, tt.want) {
t.Errorf("getLevel() = %v, want %v", got, tt.want)
}
})
}
}
+1 -17
View File
@@ -2,10 +2,9 @@ package ocache
import (
"context"
"go.uber.org/zap"
"sync"
"time"
"go.uber.org/zap"
)
type entryState int
@@ -26,7 +25,6 @@ type entry struct {
value Object
close chan struct{}
mx sync.Mutex
cancel context.CancelFunc
}
func newEntry(id string, value Object, state entryState) *entry {
@@ -51,20 +49,6 @@ func (e *entry) isClosing() bool {
return e.state == entryStateClosed || e.state == entryStateClosing
}
func (e *entry) setCancel(cancel context.CancelFunc) {
e.mx.Lock()
defer e.mx.Unlock()
e.cancel = cancel
}
func (e *entry) cancelLoad() {
e.mx.Lock()
defer e.mx.Unlock()
if e.cancel != nil {
e.cancel()
}
}
func (e *entry) waitLoad(ctx context.Context, id string) (value Object, err error) {
select {
case <-ctx.Done():
+3 -4
View File
@@ -6,9 +6,6 @@ import (
)
func WithPrometheus(reg *prometheus.Registry, namespace, subsystem string) Option {
if reg == nil {
return nil
}
if subsystem == "" {
subsystem = "cache"
}
@@ -16,7 +13,9 @@ func WithPrometheus(reg *prometheus.Registry, namespace, subsystem string) Optio
subSplit := strings.Split(subsystem, ".")
namespace = strings.Join(nameSplit, "_")
subsystem = strings.Join(subSplit, "_")
if reg == nil {
return nil
}
return func(cache *oCache) {
cache.metrics = &metrics{
hit: prometheus.NewCounter(prometheus.CounterOpts{
+2 -7
View File
@@ -3,11 +3,10 @@ package ocache
import (
"context"
"errors"
"github.com/anytypeio/any-sync/app/logger"
"go.uber.org/zap"
"sync"
"time"
"github.com/anyproto/any-sync/app/logger"
"go.uber.org/zap"
)
var (
@@ -158,10 +157,7 @@ func (c *oCache) Pick(ctx context.Context, id string) (value Object, err error)
func (c *oCache) load(ctx context.Context, id string, e *entry) {
defer close(e.load)
ctx, cancel := context.WithCancel(ctx)
e.setCancel(cancel)
value, err := c.loadFunc(ctx, id)
cancel()
c.mu.Lock()
defer c.mu.Unlock()
@@ -319,7 +315,6 @@ func (c *oCache) Close() (err error) {
close(c.closeCh)
var toClose []*entry
for _, e := range c.data {
e.cancelLoad()
toClose = append(toClose, e)
}
c.mu.Unlock()
-19
View File
@@ -386,25 +386,6 @@ func Test_OCache_Remove(t *testing.T) {
})
}
func TestOCacheCancelWhenRemove(t *testing.T) {
c := New(func(ctx context.Context, id string) (value Object, err error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
}
}, WithTTL(time.Millisecond*10))
stopLoad := make(chan struct{})
var err error
go func() {
_, err = c.Get(context.TODO(), "id")
stopLoad <- struct{}{}
}()
time.Sleep(time.Millisecond * 10)
c.Close()
<-stopLoad
require.Equal(t, context.Canceled, err)
}
func TestOCacheFuzzy(t *testing.T) {
t.Run("test many objects gc, get and remove simultaneously, close after", func(t *testing.T) {
tryCloseIds := make(map[string]bool)
+2 -2
View File
@@ -2,8 +2,8 @@ package fileblockstore
import (
"context"
"github.com/anyproto/any-sync/app/logger"
"github.com/anyproto/any-sync/commonfile/fileproto/fileprotoerr"
"github.com/anytypeio/any-sync/app/logger"
"github.com/anytypeio/any-sync/commonfile/fileproto/fileprotoerr"
blocks "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid"
)
+411 -66
View File
@@ -298,6 +298,94 @@ func (m *BlockPushResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_BlockPushResponse proto.InternalMessageInfo
type BlocksDeleteRequest struct {
SpaceId string `protobuf:"bytes,1,opt,name=spaceId,proto3" json:"spaceId,omitempty"`
Cids [][]byte `protobuf:"bytes,2,rep,name=cids,proto3" json:"cids,omitempty"`
}
func (m *BlocksDeleteRequest) Reset() { *m = BlocksDeleteRequest{} }
func (m *BlocksDeleteRequest) String() string { return proto.CompactTextString(m) }
func (*BlocksDeleteRequest) ProtoMessage() {}
func (*BlocksDeleteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_fd665a7e11c833d5, []int{4}
}
func (m *BlocksDeleteRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *BlocksDeleteRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_BlocksDeleteRequest.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *BlocksDeleteRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_BlocksDeleteRequest.Merge(m, src)
}
func (m *BlocksDeleteRequest) XXX_Size() int {
return m.Size()
}
func (m *BlocksDeleteRequest) XXX_DiscardUnknown() {
xxx_messageInfo_BlocksDeleteRequest.DiscardUnknown(m)
}
var xxx_messageInfo_BlocksDeleteRequest proto.InternalMessageInfo
func (m *BlocksDeleteRequest) GetSpaceId() string {
if m != nil {
return m.SpaceId
}
return ""
}
func (m *BlocksDeleteRequest) GetCids() [][]byte {
if m != nil {
return m.Cids
}
return nil
}
type BlocksDeleteResponse struct {
}
func (m *BlocksDeleteResponse) Reset() { *m = BlocksDeleteResponse{} }
func (m *BlocksDeleteResponse) String() string { return proto.CompactTextString(m) }
func (*BlocksDeleteResponse) ProtoMessage() {}
func (*BlocksDeleteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_fd665a7e11c833d5, []int{5}
}
func (m *BlocksDeleteResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *BlocksDeleteResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_BlocksDeleteResponse.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *BlocksDeleteResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_BlocksDeleteResponse.Merge(m, src)
}
func (m *BlocksDeleteResponse) XXX_Size() int {
return m.Size()
}
func (m *BlocksDeleteResponse) XXX_DiscardUnknown() {
xxx_messageInfo_BlocksDeleteResponse.DiscardUnknown(m)
}
var xxx_messageInfo_BlocksDeleteResponse proto.InternalMessageInfo
type BlocksCheckRequest struct {
SpaceId string `protobuf:"bytes,1,opt,name=spaceId,proto3" json:"spaceId,omitempty"`
Cids [][]byte `protobuf:"bytes,2,rep,name=cids,proto3" json:"cids,omitempty"`
@@ -307,7 +395,7 @@ func (m *BlocksCheckRequest) Reset() { *m = BlocksCheckRequest{} }
func (m *BlocksCheckRequest) String() string { return proto.CompactTextString(m) }
func (*BlocksCheckRequest) ProtoMessage() {}
func (*BlocksCheckRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_fd665a7e11c833d5, []int{4}
return fileDescriptor_fd665a7e11c833d5, []int{6}
}
func (m *BlocksCheckRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -358,7 +446,7 @@ func (m *BlocksCheckResponse) Reset() { *m = BlocksCheckResponse{} }
func (m *BlocksCheckResponse) String() string { return proto.CompactTextString(m) }
func (*BlocksCheckResponse) ProtoMessage() {}
func (*BlocksCheckResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_fd665a7e11c833d5, []int{5}
return fileDescriptor_fd665a7e11c833d5, []int{7}
}
func (m *BlocksCheckResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -403,7 +491,7 @@ func (m *BlockAvailability) Reset() { *m = BlockAvailability{} }
func (m *BlockAvailability) String() string { return proto.CompactTextString(m) }
func (*BlockAvailability) ProtoMessage() {}
func (*BlockAvailability) Descriptor() ([]byte, []int) {
return fileDescriptor_fd665a7e11c833d5, []int{6}
return fileDescriptor_fd665a7e11c833d5, []int{8}
}
func (m *BlockAvailability) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -456,7 +544,7 @@ func (m *BlocksBindRequest) Reset() { *m = BlocksBindRequest{} }
func (m *BlocksBindRequest) String() string { return proto.CompactTextString(m) }
func (*BlocksBindRequest) ProtoMessage() {}
func (*BlocksBindRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_fd665a7e11c833d5, []int{7}
return fileDescriptor_fd665a7e11c833d5, []int{9}
}
func (m *BlocksBindRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -513,7 +601,7 @@ func (m *BlocksBindResponse) Reset() { *m = BlocksBindResponse{} }
func (m *BlocksBindResponse) String() string { return proto.CompactTextString(m) }
func (*BlocksBindResponse) ProtoMessage() {}
func (*BlocksBindResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_fd665a7e11c833d5, []int{8}
return fileDescriptor_fd665a7e11c833d5, []int{10}
}
func (m *BlocksBindResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -551,7 +639,7 @@ func (m *FilesDeleteRequest) Reset() { *m = FilesDeleteRequest{} }
func (m *FilesDeleteRequest) String() string { return proto.CompactTextString(m) }
func (*FilesDeleteRequest) ProtoMessage() {}
func (*FilesDeleteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_fd665a7e11c833d5, []int{9}
return fileDescriptor_fd665a7e11c833d5, []int{11}
}
func (m *FilesDeleteRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -601,7 +689,7 @@ func (m *FilesDeleteResponse) Reset() { *m = FilesDeleteResponse{} }
func (m *FilesDeleteResponse) String() string { return proto.CompactTextString(m) }
func (*FilesDeleteResponse) ProtoMessage() {}
func (*FilesDeleteResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_fd665a7e11c833d5, []int{10}
return fileDescriptor_fd665a7e11c833d5, []int{12}
}
func (m *FilesDeleteResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -639,7 +727,7 @@ func (m *FilesInfoRequest) Reset() { *m = FilesInfoRequest{} }
func (m *FilesInfoRequest) String() string { return proto.CompactTextString(m) }
func (*FilesInfoRequest) ProtoMessage() {}
func (*FilesInfoRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_fd665a7e11c833d5, []int{11}
return fileDescriptor_fd665a7e11c833d5, []int{13}
}
func (m *FilesInfoRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -690,7 +778,7 @@ func (m *FilesInfoResponse) Reset() { *m = FilesInfoResponse{} }
func (m *FilesInfoResponse) String() string { return proto.CompactTextString(m) }
func (*FilesInfoResponse) ProtoMessage() {}
func (*FilesInfoResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_fd665a7e11c833d5, []int{12}
return fileDescriptor_fd665a7e11c833d5, []int{14}
}
func (m *FilesInfoResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -736,7 +824,7 @@ func (m *FileInfo) Reset() { *m = FileInfo{} }
func (m *FileInfo) String() string { return proto.CompactTextString(m) }
func (*FileInfo) ProtoMessage() {}
func (*FileInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_fd665a7e11c833d5, []int{13}
return fileDescriptor_fd665a7e11c833d5, []int{15}
}
func (m *FileInfo) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -793,7 +881,7 @@ func (m *CheckRequest) Reset() { *m = CheckRequest{} }
func (m *CheckRequest) String() string { return proto.CompactTextString(m) }
func (*CheckRequest) ProtoMessage() {}
func (*CheckRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_fd665a7e11c833d5, []int{14}
return fileDescriptor_fd665a7e11c833d5, []int{16}
}
func (m *CheckRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -831,7 +919,7 @@ func (m *CheckResponse) Reset() { *m = CheckResponse{} }
func (m *CheckResponse) String() string { return proto.CompactTextString(m) }
func (*CheckResponse) ProtoMessage() {}
func (*CheckResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_fd665a7e11c833d5, []int{15}
return fileDescriptor_fd665a7e11c833d5, []int{17}
}
func (m *CheckResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -882,7 +970,7 @@ func (m *SpaceInfoRequest) Reset() { *m = SpaceInfoRequest{} }
func (m *SpaceInfoRequest) String() string { return proto.CompactTextString(m) }
func (*SpaceInfoRequest) ProtoMessage() {}
func (*SpaceInfoRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_fd665a7e11c833d5, []int{16}
return fileDescriptor_fd665a7e11c833d5, []int{18}
}
func (m *SpaceInfoRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -929,7 +1017,7 @@ func (m *SpaceInfoResponse) Reset() { *m = SpaceInfoResponse{} }
func (m *SpaceInfoResponse) String() string { return proto.CompactTextString(m) }
func (*SpaceInfoResponse) ProtoMessage() {}
func (*SpaceInfoResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_fd665a7e11c833d5, []int{17}
return fileDescriptor_fd665a7e11c833d5, []int{19}
}
func (m *SpaceInfoResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -993,6 +1081,8 @@ func init() {
proto.RegisterType((*BlockGetResponse)(nil), "filesync.BlockGetResponse")
proto.RegisterType((*BlockPushRequest)(nil), "filesync.BlockPushRequest")
proto.RegisterType((*BlockPushResponse)(nil), "filesync.BlockPushResponse")
proto.RegisterType((*BlocksDeleteRequest)(nil), "filesync.BlocksDeleteRequest")
proto.RegisterType((*BlocksDeleteResponse)(nil), "filesync.BlocksDeleteResponse")
proto.RegisterType((*BlocksCheckRequest)(nil), "filesync.BlocksCheckRequest")
proto.RegisterType((*BlocksCheckResponse)(nil), "filesync.BlocksCheckResponse")
proto.RegisterType((*BlockAvailability)(nil), "filesync.BlockAvailability")
@@ -1014,58 +1104,59 @@ func init() {
}
var fileDescriptor_fd665a7e11c833d5 = []byte{
// 805 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0xcd, 0x52, 0xeb, 0x36,
0x14, 0x8e, 0x13, 0x13, 0xe2, 0x13, 0x02, 0x8e, 0xf8, 0x69, 0x1a, 0x52, 0x4f, 0xc6, 0x8b, 0x0e,
0xc3, 0x74, 0x42, 0x87, 0x76, 0xc1, 0xa6, 0x0b, 0x12, 0x12, 0x48, 0xe9, 0xd0, 0xd6, 0x4c, 0x87,
0x69, 0xbb, 0xa9, 0x63, 0x2b, 0xe0, 0x62, 0xac, 0xd4, 0x52, 0x5a, 0xd2, 0x47, 0xe8, 0xaa, 0x7d,
0x99, 0x3e, 0x03, 0x4b, 0x96, 0x77, 0x79, 0x07, 0x5e, 0xe4, 0x8e, 0x64, 0x39, 0x56, 0x7e, 0xee,
0x70, 0x7f, 0x36, 0x8e, 0xf4, 0xe9, 0x9c, 0xf3, 0x1d, 0x7d, 0x92, 0xbe, 0x09, 0x7c, 0xee, 0x91,
0xbb, 0x3b, 0x12, 0x0d, 0x83, 0x10, 0x1f, 0xf0, 0xcf, 0x28, 0x26, 0x8c, 0x1c, 0x88, 0x2f, 0x15,
0x40, 0x4b, 0x8c, 0x51, 0x89, 0x8f, 0xe9, 0x24, 0xf2, 0xec, 0x6f, 0x60, 0xa3, 0x1d, 0x12, 0xef,
0xf6, 0x14, 0x33, 0x07, 0xff, 0x31, 0xc6, 0x94, 0xa1, 0x1a, 0xac, 0xd2, 0x91, 0xeb, 0xe1, 0xbe,
0x5f, 0xd3, 0x9a, 0xda, 0x9e, 0xe1, 0xa4, 0x53, 0x64, 0x42, 0xc1, 0x0b, 0xfc, 0x5a, 0xbe, 0xa9,
0xed, 0xad, 0x39, 0x7c, 0x68, 0x1f, 0x81, 0x99, 0xa5, 0xd3, 0x11, 0x89, 0x28, 0x4e, 0xa3, 0xb4,
0x69, 0x14, 0x42, 0xa0, 0xfb, 0x2e, 0x73, 0x65, 0xa2, 0x18, 0xdb, 0xbf, 0xcb, 0xcc, 0x1f, 0xc6,
0xf4, 0xe6, 0x65, 0xe6, 0x1d, 0x28, 0xf2, 0x96, 0xfb, 0x09, 0xb9, 0xe1, 0xc8, 0x59, 0xca, 0x55,
0x58, 0xe4, 0xd2, 0x15, 0xae, 0x4d, 0xa8, 0x2a, 0x5c, 0x49, 0x9b, 0x76, 0x1b, 0x90, 0x00, 0x69,
0xe7, 0x06, 0x7b, 0xb7, 0x2f, 0xb7, 0x80, 0x40, 0xf7, 0x02, 0x9f, 0xd6, 0xf2, 0xcd, 0x02, 0x2f,
0xcc, 0xc7, 0xf6, 0x00, 0x36, 0x67, 0x6a, 0x48, 0x05, 0xce, 0x01, 0x0d, 0x04, 0x7c, 0xfc, 0xa7,
0x1b, 0x84, 0xee, 0x20, 0x08, 0x03, 0x36, 0xa9, 0x69, 0xcd, 0xc2, 0x5e, 0xf9, 0x70, 0xb7, 0x95,
0x6a, 0xdf, 0x12, 0xa9, 0x6a, 0x88, 0xb3, 0x24, 0xcd, 0xfe, 0x55, 0x36, 0xaf, 0x82, 0x4b, 0x34,
0xfe, 0x1a, 0x8a, 0x94, 0xb9, 0x6c, 0x4c, 0x85, 0x42, 0xeb, 0x87, 0x8d, 0x8c, 0x47, 0xcd, 0xbc,
0x14, 0x31, 0x8e, 0x8c, 0xb5, 0x7f, 0x96, 0xc5, 0x69, 0x3b, 0x88, 0xfc, 0x0f, 0x3f, 0x86, 0x54,
0x9b, 0x82, 0xa2, 0xcd, 0x56, 0xaa, 0x6f, 0x52, 0x5a, 0xaa, 0x7e, 0x06, 0xa8, 0xc7, 0xfb, 0x3a,
0xc1, 0x21, 0x66, 0xf8, 0x65, 0xc6, 0x1a, 0xac, 0x26, 0x1c, 0x89, 0xf0, 0x86, 0x93, 0x4e, 0xed,
0x6d, 0xd8, 0x9c, 0xa9, 0x24, 0x09, 0x7a, 0x60, 0x0a, 0xb8, 0x1f, 0x0d, 0xc9, 0xc7, 0x94, 0xef,
0x42, 0x55, 0xa9, 0x23, 0x0f, 0xf6, 0x4b, 0x30, 0x86, 0x29, 0x28, 0xcf, 0x13, 0x65, 0x3a, 0xf3,
0x78, 0x11, 0x9e, 0x05, 0xd9, 0xbf, 0x41, 0x29, 0x85, 0x15, 0xf5, 0xb4, 0x19, 0xf5, 0x2c, 0x80,
0x31, 0x75, 0xaf, 0x71, 0x7b, 0xc2, 0x70, 0x72, 0x7c, 0xba, 0xa3, 0x20, 0xa8, 0x01, 0x06, 0x57,
0xb4, 0x43, 0xc6, 0x11, 0x13, 0x57, 0xbd, 0xe2, 0x64, 0x80, 0xbd, 0x0e, 0x6b, 0xea, 0x0d, 0xb6,
0xcf, 0xa1, 0x32, 0x7b, 0x1b, 0xeb, 0x50, 0x92, 0xdb, 0xa5, 0xa2, 0x67, 0xc3, 0x99, 0xce, 0x39,
0xb5, 0x1b, 0x86, 0xe4, 0xaf, 0xab, 0x38, 0x60, 0x58, 0x50, 0x97, 0x1c, 0x05, 0xb1, 0xbf, 0x00,
0xf3, 0x52, 0xc4, 0xbe, 0x8b, 0x9a, 0xf6, 0x7f, 0x1a, 0x54, 0x95, 0x70, 0xc9, 0x6f, 0x01, 0x84,
0xc1, 0x5d, 0xc0, 0x92, 0xed, 0x69, 0xc9, 0xf6, 0x32, 0xe4, 0xfd, 0xb7, 0xaf, 0x2b, 0xdb, 0xe7,
0xd9, 0x42, 0xed, 0x64, 0x59, 0x4f, 0xb2, 0x33, 0x64, 0xff, 0x1f, 0x0d, 0x4a, 0xdd, 0x38, 0xee,
0x10, 0x1f, 0x53, 0xb4, 0x0e, 0xf0, 0x53, 0x84, 0xef, 0x47, 0xd8, 0x63, 0xd8, 0x37, 0x73, 0x68,
0x03, 0xca, 0x9d, 0xfe, 0xc9, 0x05, 0x61, 0x3d, 0x32, 0x8e, 0x7c, 0x53, 0x43, 0x15, 0x30, 0x7a,
0x24, 0x1e, 0x04, 0xbe, 0x8f, 0x23, 0x33, 0x8f, 0x76, 0x00, 0x89, 0xfd, 0x7c, 0xc7, 0xbb, 0xed,
0xde, 0x7b, 0x18, 0xfb, 0xd8, 0x37, 0x0b, 0x68, 0x1b, 0xaa, 0x3f, 0x8e, 0x71, 0x3c, 0xb9, 0x0c,
0xfe, 0xc6, 0x53, 0x58, 0xe7, 0xd9, 0x57, 0x31, 0x89, 0xae, 0xcf, 0x5c, 0x7a, 0x63, 0xae, 0x20,
0x13, 0xca, 0xdd, 0x38, 0x26, 0xf1, 0xf7, 0xc3, 0x21, 0xc5, 0xcc, 0x7c, 0xd0, 0xf6, 0xdb, 0x80,
0x16, 0x1f, 0x23, 0x4f, 0xbb, 0x20, 0xac, 0x7b, 0x1f, 0x50, 0x46, 0xcd, 0x1c, 0x02, 0x28, 0xca,
0xb1, 0x86, 0xaa, 0x50, 0x49, 0xc6, 0xfd, 0x48, 0x34, 0x62, 0xe6, 0x0f, 0xff, 0xd7, 0x41, 0xe7,
0x57, 0x0a, 0x1d, 0x43, 0x29, 0xf5, 0x5e, 0xf4, 0xe9, 0x9c, 0xab, 0x64, 0x76, 0x5e, 0xaf, 0x2f,
0x5b, 0x92, 0x47, 0x73, 0x02, 0xc6, 0xd4, 0x18, 0xd1, 0x7c, 0xa0, 0xe2, 0xcc, 0xf5, 0xdd, 0xa5,
0x6b, 0xb2, 0xca, 0xb7, 0x50, 0x56, 0x5c, 0x10, 0x35, 0xe6, 0x62, 0x67, 0x0c, 0xb6, 0xfe, 0xd9,
0x5b, 0x56, 0x65, 0xad, 0x53, 0x80, 0xcc, 0x35, 0xd0, 0x3c, 0xad, 0x6a, 0x53, 0xf5, 0xc6, 0xf2,
0xc5, 0xac, 0x29, 0xc5, 0x1e, 0xd4, 0xa6, 0x16, 0xfd, 0x47, 0x6d, 0x6a, 0x89, 0xa7, 0x70, 0x99,
0xa6, 0x5e, 0xa0, 0xca, 0x34, 0x6f, 0x34, 0xaa, 0x4c, 0x8b, 0xe6, 0x71, 0x04, 0x2b, 0x89, 0x40,
0x3b, 0x59, 0xd4, 0x8c, 0x34, 0x9f, 0x2c, 0xe0, 0x19, 0xff, 0xf4, 0x59, 0xa9, 0xfc, 0xf3, 0x4f,
0x53, 0xe5, 0x5f, 0x78, 0x87, 0xed, 0xd6, 0xc3, 0x93, 0xa5, 0x3d, 0x3e, 0x59, 0xda, 0xeb, 0x27,
0x4b, 0xfb, 0xf7, 0xd9, 0xca, 0x3d, 0x3e, 0x5b, 0xb9, 0x57, 0xcf, 0x56, 0xee, 0x97, 0xad, 0x65,
0x7f, 0x1b, 0x06, 0x45, 0xf1, 0xf3, 0xd5, 0x9b, 0x00, 0x00, 0x00, 0xff, 0xff, 0x45, 0x87, 0x27,
0x98, 0x55, 0x08, 0x00, 0x00,
// 817 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0x4f, 0x53, 0xfb, 0x44,
0x18, 0x6e, 0xda, 0xfc, 0x4a, 0xf3, 0x96, 0x42, 0xba, 0x40, 0xad, 0xa5, 0x66, 0x3a, 0x39, 0x38,
0x0c, 0xe3, 0x14, 0x07, 0x3d, 0x70, 0xf1, 0x40, 0x4b, 0x0b, 0x15, 0x07, 0x35, 0x8c, 0xc3, 0xa8,
0x17, 0xd3, 0x64, 0x0b, 0x91, 0x90, 0xad, 0xd9, 0xad, 0x52, 0x3f, 0x82, 0x27, 0xfd, 0x32, 0x7e,
0x06, 0x8e, 0x1c, 0x3d, 0x3a, 0xf0, 0x45, 0x9c, 0xdd, 0x6c, 0x9a, 0xed, 0x1f, 0x07, 0xc4, 0x4b,
0xd8, 0x3c, 0xfb, 0xbe, 0xef, 0xf3, 0xee, 0xf3, 0x66, 0x1f, 0x0a, 0x1f, 0x7a, 0xe4, 0xee, 0x8e,
0x44, 0xa3, 0x20, 0xc4, 0x07, 0xfc, 0x31, 0x8e, 0x09, 0x23, 0x07, 0xe2, 0x49, 0x05, 0xd0, 0x16,
0x6b, 0x54, 0xe2, 0x6b, 0x3a, 0x8d, 0x3c, 0xfb, 0x33, 0xd8, 0xec, 0x84, 0xc4, 0xbb, 0x3d, 0xc5,
0xcc, 0xc1, 0x3f, 0x4d, 0x30, 0x65, 0xa8, 0x0e, 0x6b, 0x74, 0xec, 0x7a, 0x78, 0xe0, 0xd7, 0xb5,
0x96, 0xb6, 0x67, 0x38, 0xe9, 0x2b, 0x32, 0xa1, 0xe0, 0x05, 0x7e, 0x3d, 0xdf, 0xd2, 0xf6, 0xd6,
0x1d, 0xbe, 0xb4, 0x8f, 0xc0, 0xcc, 0xd2, 0xe9, 0x98, 0x44, 0x14, 0xa7, 0x51, 0xda, 0x2c, 0x0a,
0x21, 0xd0, 0x7d, 0x97, 0xb9, 0x32, 0x51, 0xac, 0xed, 0x1f, 0x65, 0xe6, 0x57, 0x13, 0x7a, 0xf3,
0x32, 0x73, 0x0d, 0x8a, 0xbc, 0xe5, 0x41, 0x42, 0x6e, 0x38, 0xf2, 0x2d, 0xe5, 0x2a, 0x2c, 0x73,
0xe9, 0x0a, 0xd7, 0x16, 0x54, 0x15, 0xae, 0xa4, 0x4d, 0xbb, 0x0b, 0x5b, 0x02, 0xa4, 0x27, 0x38,
0xc4, 0x0c, 0xbf, 0xdc, 0x03, 0x02, 0xdd, 0x0b, 0x7c, 0x5a, 0xcf, 0xb7, 0x0a, 0xbc, 0x32, 0x5f,
0xdb, 0x35, 0xd8, 0x9e, 0x2f, 0x22, 0x8b, 0x77, 0x00, 0x25, 0x78, 0xf7, 0x06, 0x7b, 0xb7, 0x6f,
0xab, 0x3d, 0x4c, 0x1b, 0x94, 0x35, 0xa4, 0xbc, 0xe7, 0x80, 0x86, 0x02, 0x3e, 0xfe, 0xd9, 0x0d,
0x42, 0x77, 0x18, 0x84, 0x01, 0x9b, 0xd6, 0xb5, 0x56, 0x61, 0xaf, 0x7c, 0xb8, 0xdb, 0x4e, 0x07,
0xdb, 0x16, 0xa9, 0x6a, 0x88, 0xb3, 0x22, 0xcd, 0xfe, 0x5e, 0x2a, 0xa3, 0x82, 0x2b, 0x06, 0xf8,
0x29, 0x14, 0x29, 0x73, 0xd9, 0x84, 0x0a, 0xf9, 0x37, 0x0e, 0x9b, 0x19, 0x8f, 0x9a, 0x79, 0x29,
0x62, 0x1c, 0x19, 0x6b, 0x7f, 0x2b, 0x8b, 0xd3, 0x4e, 0x10, 0xf9, 0x6f, 0x9f, 0x71, 0xaa, 0x4d,
0x41, 0xd1, 0x66, 0x3b, 0xd5, 0x37, 0x29, 0x2d, 0x55, 0x3f, 0x03, 0xd4, 0xe7, 0x7d, 0xbd, 0x76,
0xa2, 0x75, 0x58, 0x4b, 0x38, 0x12, 0xe1, 0x0d, 0x27, 0x7d, 0xb5, 0x77, 0x60, 0x6b, 0xae, 0x92,
0x24, 0xe8, 0x83, 0x29, 0xe0, 0x41, 0x34, 0x22, 0xff, 0xa7, 0x7c, 0x0f, 0xaa, 0x4a, 0x1d, 0x39,
0xd8, 0x8f, 0xc1, 0x18, 0xa5, 0xa0, 0x9c, 0x27, 0xca, 0x74, 0xe6, 0xf1, 0x22, 0x3c, 0x0b, 0xb2,
0x7f, 0x80, 0x52, 0x0a, 0x2b, 0xea, 0x69, 0x73, 0xea, 0x59, 0x00, 0x13, 0xea, 0x5e, 0xe3, 0xce,
0x94, 0xe1, 0x64, 0x7c, 0xba, 0xa3, 0x20, 0xa8, 0x09, 0x06, 0x57, 0xb4, 0x4b, 0x26, 0x11, 0x13,
0xf7, 0xa8, 0xe2, 0x64, 0x80, 0xbd, 0x01, 0xeb, 0xea, 0x17, 0x6c, 0x9f, 0x43, 0x65, 0xfe, 0x6b,
0x6c, 0x40, 0x49, 0x1e, 0x97, 0x8a, 0x9e, 0x0d, 0x67, 0xf6, 0xce, 0xa9, 0xdd, 0x30, 0x24, 0xbf,
0x5c, 0xc5, 0x01, 0xc3, 0x82, 0xba, 0xe4, 0x28, 0x88, 0xfd, 0x11, 0x98, 0x97, 0x22, 0xf6, 0x35,
0x6a, 0xda, 0x7f, 0x68, 0x50, 0x55, 0xc2, 0x25, 0xbf, 0x05, 0x10, 0x06, 0x77, 0x01, 0x4b, 0x8e,
0xa7, 0x25, 0xc7, 0xcb, 0x90, 0xff, 0x7e, 0x7c, 0x5d, 0x39, 0x3e, 0xcf, 0x16, 0x6a, 0x27, 0xdb,
0x7a, 0x92, 0x9d, 0x21, 0xfb, 0xbf, 0x69, 0x50, 0xea, 0xc5, 0x71, 0x97, 0xf8, 0x98, 0xa2, 0x0d,
0x80, 0x6f, 0x22, 0x7c, 0x3f, 0xc6, 0x1e, 0xc3, 0xbe, 0x99, 0x43, 0x9b, 0x50, 0xee, 0x0e, 0x4e,
0x2e, 0x08, 0xeb, 0x93, 0x49, 0xe4, 0x9b, 0x1a, 0xaa, 0x80, 0xd1, 0x27, 0xf1, 0x30, 0xf0, 0x7d,
0x1c, 0x99, 0x79, 0x54, 0x03, 0x24, 0xce, 0xf3, 0x05, 0xef, 0xb6, 0x77, 0xef, 0x61, 0xec, 0x63,
0xdf, 0x2c, 0xa0, 0x1d, 0xa8, 0x7e, 0x3d, 0xc1, 0xf1, 0xf4, 0x32, 0xf8, 0x15, 0xcf, 0x60, 0x9d,
0x67, 0x5f, 0xc5, 0x24, 0xba, 0x3e, 0x73, 0xe9, 0x8d, 0xf9, 0x0e, 0x99, 0x50, 0xee, 0xc5, 0x31,
0x89, 0xbf, 0x1c, 0x8d, 0x28, 0x66, 0xe6, 0x83, 0xb6, 0xdf, 0x01, 0xb4, 0x7c, 0x19, 0x79, 0xda,
0x05, 0x61, 0xbd, 0xfb, 0x80, 0x32, 0x6a, 0xe6, 0x10, 0x40, 0x51, 0xae, 0x35, 0x54, 0x85, 0x4a,
0xb2, 0x1e, 0x44, 0xa2, 0x11, 0x33, 0x7f, 0xf8, 0xa7, 0x0e, 0x3a, 0xff, 0xa4, 0xd0, 0x31, 0x94,
0x52, 0x63, 0x47, 0xef, 0x2f, 0xb8, 0x4a, 0xf6, 0xbf, 0xa2, 0xd1, 0x58, 0xb5, 0x25, 0x47, 0x73,
0x02, 0xc6, 0xcc, 0x75, 0xd1, 0x62, 0xa0, 0x62, 0xfb, 0x8d, 0xdd, 0x95, 0x7b, 0xb2, 0xca, 0xe7,
0x50, 0x56, 0x5c, 0x10, 0x35, 0x17, 0x62, 0xe7, 0x0c, 0xb6, 0xf1, 0xc1, 0xbf, 0xec, 0xca, 0x5a,
0xa7, 0x00, 0x99, 0x6b, 0xa0, 0x45, 0x5a, 0xd5, 0xa6, 0x1a, 0xcd, 0xd5, 0x9b, 0x59, 0x53, 0x8a,
0x3d, 0xa8, 0x4d, 0x2d, 0xfb, 0x8f, 0xda, 0xd4, 0x0a, 0x4f, 0xe1, 0x32, 0xcd, 0xbc, 0x40, 0x95,
0x69, 0xd1, 0x68, 0x54, 0x99, 0x96, 0xcd, 0xe3, 0x08, 0xde, 0x25, 0x02, 0xd5, 0xb2, 0xa8, 0x39,
0x69, 0xde, 0x5b, 0xc2, 0x33, 0xfe, 0xd9, 0xb5, 0x52, 0xf9, 0x17, 0xaf, 0xa6, 0xca, 0xbf, 0x74,
0x0f, 0x3b, 0xed, 0x87, 0x27, 0x4b, 0x7b, 0x7c, 0xb2, 0xb4, 0xbf, 0x9f, 0x2c, 0xed, 0xf7, 0x67,
0x2b, 0xf7, 0xf8, 0x6c, 0xe5, 0xfe, 0x7a, 0xb6, 0x72, 0xdf, 0x6d, 0xaf, 0xfa, 0x4d, 0x32, 0x2c,
0x8a, 0x3f, 0x9f, 0xfc, 0x13, 0x00, 0x00, 0xff, 0xff, 0x26, 0x23, 0x7f, 0x83, 0xb2, 0x08, 0x00,
0x00,
}
func (m *BlockGetRequest) Marshal() (dAtA []byte, err error) {
@@ -1216,6 +1307,68 @@ func (m *BlockPushResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
func (m *BlocksDeleteRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *BlocksDeleteRequest) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *BlocksDeleteRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Cids) > 0 {
for iNdEx := len(m.Cids) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.Cids[iNdEx])
copy(dAtA[i:], m.Cids[iNdEx])
i = encodeVarintFile(dAtA, i, uint64(len(m.Cids[iNdEx])))
i--
dAtA[i] = 0x12
}
}
if len(m.SpaceId) > 0 {
i -= len(m.SpaceId)
copy(dAtA[i:], m.SpaceId)
i = encodeVarintFile(dAtA, i, uint64(len(m.SpaceId)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *BlocksDeleteResponse) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *BlocksDeleteResponse) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *BlocksDeleteResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
return len(dAtA) - i, nil
}
func (m *BlocksCheckRequest) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@@ -1791,6 +1944,34 @@ func (m *BlockPushResponse) Size() (n int) {
return n
}
func (m *BlocksDeleteRequest) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.SpaceId)
if l > 0 {
n += 1 + l + sovFile(uint64(l))
}
if len(m.Cids) > 0 {
for _, b := range m.Cids {
l = len(b)
n += 1 + l + sovFile(uint64(l))
}
}
return n
}
func (m *BlocksDeleteResponse) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
return n
}
func (m *BlocksCheckRequest) Size() (n int) {
if m == nil {
return 0
@@ -2487,6 +2668,170 @@ func (m *BlockPushResponse) Unmarshal(dAtA []byte) error {
}
return nil
}
func (m *BlocksDeleteRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowFile
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: BlocksDeleteRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: BlocksDeleteRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field SpaceId", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowFile
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthFile
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthFile
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.SpaceId = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Cids", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowFile
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthFile
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthFile
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Cids = append(m.Cids, make([]byte, postIndex-iNdEx))
copy(m.Cids[len(m.Cids)-1], dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipFile(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthFile
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *BlocksDeleteResponse) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowFile
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: BlocksDeleteResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: BlocksDeleteResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
default:
iNdEx = preIndex
skippy, err := skipFile(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthFile
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *BlocksCheckRequest) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
+1 -1
View File
@@ -1,5 +1,5 @@
// Code generated by protoc-gen-go-drpc. DO NOT EDIT.
// protoc-gen-go-drpc version: v0.0.33
// protoc-gen-go-drpc version: v0.0.32
// source: commonfile/fileproto/protos/file.proto
package fileproto
@@ -2,8 +2,8 @@ package fileprotoerr
import (
"fmt"
"github.com/anyproto/any-sync/commonfile/fileproto"
"github.com/anyproto/any-sync/net/rpc/rpcerr"
"github.com/anytypeio/any-sync/commonfile/fileproto"
"github.com/anytypeio/any-sync/net/rpc/rpcerr"
)
var (
+7
View File
@@ -51,6 +51,13 @@ message BlockPushRequest {
message BlockPushResponse {}
message BlocksDeleteRequest {
string spaceId = 1;
repeated bytes cids = 2;
}
message BlocksDeleteResponse {}
message BlocksCheckRequest {
string spaceId = 1;
+1 -1
View File
@@ -2,7 +2,7 @@ package fileservice
import (
"context"
"github.com/anyproto/any-sync/commonfile/fileblockstore"
"github.com/anytypeio/any-sync/commonfile/fileblockstore"
blocks "github.com/ipfs/go-block-format"
"github.com/ipfs/go-blockservice"
"github.com/ipfs/go-cid"
+3 -3
View File
@@ -3,9 +3,9 @@ package fileservice
import (
"context"
"fmt"
"github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/app/logger"
"github.com/anyproto/any-sync/commonfile/fileblockstore"
"github.com/anytypeio/any-sync/app"
"github.com/anytypeio/any-sync/app/logger"
"github.com/anytypeio/any-sync/commonfile/fileblockstore"
"github.com/ipfs/go-cid"
chunker "github.com/ipfs/go-ipfs-chunker"
ipld "github.com/ipfs/go-ipld-format"
+62
View File
@@ -0,0 +1,62 @@
package commonspace
import (
"context"
"github.com/anytypeio/any-sync/commonspace/object/syncobjectgetter"
"github.com/anytypeio/any-sync/commonspace/object/tree/objecttree"
"github.com/anytypeio/any-sync/commonspace/object/treegetter"
"sync/atomic"
)
type commonGetter struct {
treegetter.TreeGetter
spaceId string
reservedObjects []syncobjectgetter.SyncObject
spaceIsClosed *atomic.Bool
}
func newCommonGetter(spaceId string, getter treegetter.TreeGetter, spaceIsClosed *atomic.Bool) *commonGetter {
return &commonGetter{
TreeGetter: getter,
spaceId: spaceId,
spaceIsClosed: spaceIsClosed,
}
}
func (c *commonGetter) AddObject(object syncobjectgetter.SyncObject) {
c.reservedObjects = append(c.reservedObjects, object)
}
func (c *commonGetter) GetTree(ctx context.Context, spaceId, treeId string) (objecttree.ObjectTree, error) {
if c.spaceIsClosed.Load() {
return nil, ErrSpaceClosed
}
if obj := c.getReservedObject(treeId); obj != nil {
return obj.(objecttree.ObjectTree), nil
}
return c.TreeGetter.GetTree(ctx, spaceId, treeId)
}
func (c *commonGetter) getReservedObject(id string) syncobjectgetter.SyncObject {
for _, obj := range c.reservedObjects {
if obj != nil && obj.Id() == id {
return obj
}
}
return nil
}
func (c *commonGetter) GetObject(ctx context.Context, objectId string) (obj syncobjectgetter.SyncObject, err error) {
if c.spaceIsClosed.Load() {
return nil, ErrSpaceClosed
}
if obj := c.getReservedObject(objectId); obj != nil {
return obj, nil
}
t, err := c.TreeGetter.GetTree(ctx, c.spaceId, objectId)
if err != nil {
return
}
obj = t.(syncobjectgetter.SyncObject)
return
}
+2 -2
View File
@@ -1,8 +1,8 @@
package commonspace
import (
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anyproto/any-sync/commonspace/spacestorage"
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
"github.com/anytypeio/any-sync/commonspace/spacestorage"
)
type commonStorage struct {
+10
View File
@@ -0,0 +1,10 @@
package commonspace
type ConfigGetter interface {
GetSpace() Config
}
type Config struct {
GCTTL int `yaml:"gcTTL"`
SyncPeriod int `yaml:"syncPeriod"`
}
-11
View File
@@ -1,11 +0,0 @@
package config
type ConfigGetter interface {
GetSpace() Config
}
type Config struct {
GCTTL int `yaml:"gcTTL"`
SyncPeriod int `yaml:"syncPeriod"`
KeepTreeDataInMemory bool `yaml:"keepTreeDataInMemory"`
}
@@ -1,10 +1,9 @@
//go:generate mockgen -destination mock_credentialprovider/mock_credentialprovider.go github.com/anyproto/any-sync/commonspace/credentialprovider CredentialProvider
//go:generate mockgen -destination mock_credentialprovider/mock_credentialprovider.go github.com/anytypeio/any-sync/commonspace/credentialprovider CredentialProvider
package credentialprovider
import (
"context"
"github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/commonspace/spacesyncproto"
"github.com/anytypeio/any-sync/commonspace/spacesyncproto"
)
const CName = "common.commonspace.credentialprovider"
@@ -14,21 +13,12 @@ func NewNoOp() CredentialProvider {
}
type CredentialProvider interface {
app.Component
GetCredential(ctx context.Context, spaceHeader *spacesyncproto.RawSpaceHeaderWithId) ([]byte, error)
}
type noOpProvider struct {
}
func (n noOpProvider) Init(a *app.App) (err error) {
return nil
}
func (n noOpProvider) Name() (name string) {
return CName
}
func (n noOpProvider) GetCredential(ctx context.Context, spaceHeader *spacesyncproto.RawSpaceHeaderWithId) ([]byte, error) {
return nil, nil
}
@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/anyproto/any-sync/commonspace/credentialprovider (interfaces: CredentialProvider)
// Source: github.com/anytypeio/any-sync/commonspace/credentialprovider (interfaces: CredentialProvider)
// Package mock_credentialprovider is a generated GoMock package.
package mock_credentialprovider
@@ -8,9 +8,8 @@ import (
context "context"
reflect "reflect"
app "github.com/anyproto/any-sync/app"
spacesyncproto "github.com/anyproto/any-sync/commonspace/spacesyncproto"
gomock "go.uber.org/mock/gomock"
spacesyncproto "github.com/anytypeio/any-sync/commonspace/spacesyncproto"
gomock "github.com/golang/mock/gomock"
)
// MockCredentialProvider is a mock of CredentialProvider interface.
@@ -50,31 +49,3 @@ func (mr *MockCredentialProviderMockRecorder) GetCredential(arg0, arg1 interface
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCredential", reflect.TypeOf((*MockCredentialProvider)(nil).GetCredential), arg0, arg1)
}
// Init mocks base method.
func (m *MockCredentialProvider) Init(arg0 *app.App) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Init", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// Init indicates an expected call of Init.
func (mr *MockCredentialProviderMockRecorder) Init(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockCredentialProvider)(nil).Init), arg0)
}
// Name mocks base method.
func (m *MockCredentialProvider) Name() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Name")
ret0, _ := ret[0].(string)
return ret0
}
// Name indicates an expected call of Name.
func (mr *MockCredentialProviderMockRecorder) Name() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockCredentialProvider)(nil).Name))
}
-278
View File
@@ -1,278 +0,0 @@
package commonspace
import (
"context"
"fmt"
"github.com/anyproto/any-sync/commonspace/object/accountdata"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anyproto/any-sync/commonspace/settings"
"github.com/anyproto/any-sync/commonspace/settings/settingsstate"
"github.com/anyproto/any-sync/commonspace/spacestorage"
"github.com/anyproto/any-sync/commonspace/spacesyncproto"
"github.com/anyproto/any-sync/util/crypto"
"github.com/stretchr/testify/require"
"math/rand"
"testing"
"time"
)
func addIncorrectSnapshot(settingsObject settings.SettingsObject, acc *accountdata.AccountKeys, partialIds map[string]struct{}, newId string) (err error) {
factory := settingsstate.NewChangeFactory()
bytes, err := factory.CreateObjectDeleteChange(newId, &settingsstate.State{DeletedIds: partialIds}, true)
if err != nil {
return
}
ch, err := settingsObject.PrepareChange(objecttree.SignableChangeContent{
Data: bytes,
Key: acc.SignKey,
IsSnapshot: true,
IsEncrypted: false,
Timestamp: time.Now().Unix(),
})
if err != nil {
return
}
res, err := settingsObject.AddRawChanges(context.Background(), objecttree.RawChangesPayload{
NewHeads: []string{ch.Id},
RawChanges: []*treechangeproto.RawTreeChangeWithId{ch},
})
if err != nil {
return
}
if res.Mode != objecttree.Rebuild {
return fmt.Errorf("incorrect mode: %d", res.Mode)
}
return
}
func TestSpaceDeleteIds(t *testing.T) {
fx := newFixture(t)
acc := fx.account.Account()
rk := crypto.NewAES()
ctx := context.Background()
totalObjs := 1500
// creating space
sp, err := fx.spaceService.CreateSpace(ctx, SpaceCreatePayload{
SigningKey: acc.SignKey,
SpaceType: "type",
ReadKey: rk.Bytes(),
ReplicationKey: 10,
MasterKey: acc.PeerKey,
})
require.NoError(t, err)
require.NotNil(t, sp)
// initializing space
spc, err := fx.spaceService.NewSpace(ctx, sp)
require.NoError(t, err)
require.NotNil(t, spc)
// adding space to tree manager
fx.treeManager.space = spc
err = spc.Init(ctx)
require.NoError(t, err)
close(fx.treeManager.waitLoad)
var ids []string
for i := 0; i < totalObjs; i++ {
// creating a tree
bytes := make([]byte, 32)
rand.Read(bytes)
doc, err := spc.TreeBuilder().CreateTree(ctx, objecttree.ObjectTreeCreatePayload{
PrivKey: acc.SignKey,
ChangeType: "some",
SpaceId: spc.Id(),
IsEncrypted: false,
Seed: bytes,
Timestamp: time.Now().Unix(),
})
require.NoError(t, err)
tr, err := spc.TreeBuilder().PutTree(ctx, doc, nil)
require.NoError(t, err)
ids = append(ids, tr.Id())
tr.Close()
}
// deleting trees
for _, id := range ids {
err = spc.DeleteTree(ctx, id)
require.NoError(t, err)
}
time.Sleep(3 * time.Second)
spc.Close()
require.Equal(t, len(ids), len(fx.treeManager.deletedIds))
}
func createTree(t *testing.T, ctx context.Context, spc Space, acc *accountdata.AccountKeys) string {
bytes := make([]byte, 32)
rand.Read(bytes)
doc, err := spc.TreeBuilder().CreateTree(ctx, objecttree.ObjectTreeCreatePayload{
PrivKey: acc.SignKey,
ChangeType: "some",
SpaceId: spc.Id(),
IsEncrypted: false,
Seed: bytes,
Timestamp: time.Now().Unix(),
})
require.NoError(t, err)
tr, err := spc.TreeBuilder().PutTree(ctx, doc, nil)
require.NoError(t, err)
tr.Close()
return tr.Id()
}
func TestSpaceDeleteIdsIncorrectSnapshot(t *testing.T) {
fx := newFixture(t)
acc := fx.account.Account()
rk := crypto.NewAES()
ctx := context.Background()
totalObjs := 1500
partialObjs := 300
// creating space
sp, err := fx.spaceService.CreateSpace(ctx, SpaceCreatePayload{
SigningKey: acc.SignKey,
SpaceType: "type",
ReadKey: rk.Bytes(),
ReplicationKey: 10,
MasterKey: acc.PeerKey,
})
require.NoError(t, err)
require.NotNil(t, sp)
// initializing space
spc, err := fx.spaceService.NewSpace(ctx, sp)
require.NoError(t, err)
require.NotNil(t, spc)
// adding space to tree manager
fx.treeManager.space = spc
err = spc.Init(ctx)
close(fx.treeManager.waitLoad)
require.NoError(t, err)
settingsObject := spc.(*space).app.MustComponent(settings.CName).(settings.Settings).SettingsObject()
var ids []string
for i := 0; i < totalObjs; i++ {
id := createTree(t, ctx, spc, acc)
ids = append(ids, id)
}
// copying storage, so we will have all the trees locally
inmemory := spc.Storage().(*commonStorage).SpaceStorage.(*spacestorage.InMemorySpaceStorage)
storageCopy := inmemory.CopyStorage()
treesCopy := inmemory.AllTrees()
// deleting trees
for _, id := range ids {
err = spc.DeleteTree(ctx, id)
require.NoError(t, err)
}
mapIds := map[string]struct{}{}
for _, id := range ids[:partialObjs] {
mapIds[id] = struct{}{}
}
// adding snapshot that breaks the state
err = addIncorrectSnapshot(settingsObject, acc, mapIds, ids[partialObjs])
require.NoError(t, err)
// copying the contents of the settings tree
treesCopy[settingsObject.Id()] = settingsObject.Storage()
storageCopy.SetTrees(treesCopy)
spc.Close()
time.Sleep(100 * time.Millisecond)
// now we replace the storage, so the trees are back, but the settings object says that they are deleted
fx.storageProvider.(*spacestorage.InMemorySpaceStorageProvider).SetStorage(storageCopy)
spc, err = fx.spaceService.NewSpace(ctx, sp)
require.NoError(t, err)
require.NotNil(t, spc)
fx.treeManager.waitLoad = make(chan struct{})
fx.treeManager.space = spc
fx.treeManager.deletedIds = nil
err = spc.Init(ctx)
require.NoError(t, err)
close(fx.treeManager.waitLoad)
// waiting until everything is deleted
time.Sleep(3 * time.Second)
require.Equal(t, len(ids), len(fx.treeManager.deletedIds))
// checking that new snapshot will contain all the changes
settingsObject = spc.(*space).app.MustComponent(settings.CName).(settings.Settings).SettingsObject()
settings.DoSnapshot = func(treeLen int) bool {
return true
}
id := createTree(t, ctx, spc, acc)
err = spc.DeleteTree(ctx, id)
require.NoError(t, err)
delIds := settingsObject.Root().Model.(*spacesyncproto.SettingsData).Snapshot.DeletedIds
require.Equal(t, totalObjs+1, len(delIds))
}
func TestSpaceDeleteIdsMarkDeleted(t *testing.T) {
fx := newFixture(t)
acc := fx.account.Account()
rk := crypto.NewAES()
ctx := context.Background()
totalObjs := 1500
// creating space
sp, err := fx.spaceService.CreateSpace(ctx, SpaceCreatePayload{
SigningKey: acc.SignKey,
SpaceType: "type",
ReadKey: rk.Bytes(),
ReplicationKey: 10,
MasterKey: acc.PeerKey,
})
require.NoError(t, err)
require.NotNil(t, sp)
// initializing space
spc, err := fx.spaceService.NewSpace(ctx, sp)
require.NoError(t, err)
require.NotNil(t, spc)
// adding space to tree manager
fx.treeManager.space = spc
err = spc.Init(ctx)
require.NoError(t, err)
close(fx.treeManager.waitLoad)
settingsObject := spc.(*space).app.MustComponent(settings.CName).(settings.Settings).SettingsObject()
var ids []string
for i := 0; i < totalObjs; i++ {
id := createTree(t, ctx, spc, acc)
ids = append(ids, id)
}
// copying storage, so we will have the same contents, except for empty trees
inmemory := spc.Storage().(*commonStorage).SpaceStorage.(*spacestorage.InMemorySpaceStorage)
storageCopy := inmemory.CopyStorage()
// deleting trees, this will prepare the document to have all the deletion changes
for _, id := range ids {
err = spc.DeleteTree(ctx, id)
require.NoError(t, err)
}
treesMap := map[string]treestorage.TreeStorage{}
// copying the contents of the settings tree
treesMap[settingsObject.Id()] = settingsObject.Storage()
storageCopy.SetTrees(treesMap)
spc.Close()
time.Sleep(100 * time.Millisecond)
// now we replace the storage, so the trees are back, but the settings object says that they are deleted
fx.storageProvider.(*spacestorage.InMemorySpaceStorageProvider).SetStorage(storageCopy)
spc, err = fx.spaceService.NewSpace(ctx, sp)
require.NoError(t, err)
require.NotNil(t, spc)
fx.treeManager.space = spc
fx.treeManager.waitLoad = make(chan struct{})
fx.treeManager.deletedIds = nil
fx.treeManager.markedIds = nil
err = spc.Init(ctx)
require.NoError(t, err)
close(fx.treeManager.waitLoad)
// waiting until everything is deleted
time.Sleep(3 * time.Second)
require.Equal(t, len(ids), len(fx.treeManager.markedIds))
require.Zero(t, len(fx.treeManager.deletedIds))
}
@@ -1,144 +0,0 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/anyproto/any-sync/commonspace/deletionstate (interfaces: ObjectDeletionState)
// Package mock_deletionstate is a generated GoMock package.
package mock_deletionstate
import (
reflect "reflect"
app "github.com/anyproto/any-sync/app"
deletionstate "github.com/anyproto/any-sync/commonspace/deletionstate"
gomock "go.uber.org/mock/gomock"
)
// MockObjectDeletionState is a mock of ObjectDeletionState interface.
type MockObjectDeletionState struct {
ctrl *gomock.Controller
recorder *MockObjectDeletionStateMockRecorder
}
// MockObjectDeletionStateMockRecorder is the mock recorder for MockObjectDeletionState.
type MockObjectDeletionStateMockRecorder struct {
mock *MockObjectDeletionState
}
// NewMockObjectDeletionState creates a new mock instance.
func NewMockObjectDeletionState(ctrl *gomock.Controller) *MockObjectDeletionState {
mock := &MockObjectDeletionState{ctrl: ctrl}
mock.recorder = &MockObjectDeletionStateMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockObjectDeletionState) EXPECT() *MockObjectDeletionStateMockRecorder {
return m.recorder
}
// Add mocks base method.
func (m *MockObjectDeletionState) Add(arg0 map[string]struct{}) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Add", arg0)
}
// Add indicates an expected call of Add.
func (mr *MockObjectDeletionStateMockRecorder) Add(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockObjectDeletionState)(nil).Add), arg0)
}
// AddObserver mocks base method.
func (m *MockObjectDeletionState) AddObserver(arg0 deletionstate.StateUpdateObserver) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "AddObserver", arg0)
}
// AddObserver indicates an expected call of AddObserver.
func (mr *MockObjectDeletionStateMockRecorder) AddObserver(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddObserver", reflect.TypeOf((*MockObjectDeletionState)(nil).AddObserver), arg0)
}
// Delete mocks base method.
func (m *MockObjectDeletionState) Delete(arg0 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Delete", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// Delete indicates an expected call of Delete.
func (mr *MockObjectDeletionStateMockRecorder) Delete(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockObjectDeletionState)(nil).Delete), arg0)
}
// Exists mocks base method.
func (m *MockObjectDeletionState) Exists(arg0 string) bool {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Exists", arg0)
ret0, _ := ret[0].(bool)
return ret0
}
// Exists indicates an expected call of Exists.
func (mr *MockObjectDeletionStateMockRecorder) Exists(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exists", reflect.TypeOf((*MockObjectDeletionState)(nil).Exists), arg0)
}
// Filter mocks base method.
func (m *MockObjectDeletionState) Filter(arg0 []string) []string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Filter", arg0)
ret0, _ := ret[0].([]string)
return ret0
}
// Filter indicates an expected call of Filter.
func (mr *MockObjectDeletionStateMockRecorder) Filter(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Filter", reflect.TypeOf((*MockObjectDeletionState)(nil).Filter), arg0)
}
// GetQueued mocks base method.
func (m *MockObjectDeletionState) GetQueued() []string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetQueued")
ret0, _ := ret[0].([]string)
return ret0
}
// GetQueued indicates an expected call of GetQueued.
func (mr *MockObjectDeletionStateMockRecorder) GetQueued() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetQueued", reflect.TypeOf((*MockObjectDeletionState)(nil).GetQueued))
}
// Init mocks base method.
func (m *MockObjectDeletionState) Init(arg0 *app.App) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Init", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// Init indicates an expected call of Init.
func (mr *MockObjectDeletionStateMockRecorder) Init(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockObjectDeletionState)(nil).Init), arg0)
}
// Name mocks base method.
func (m *MockObjectDeletionState) Name() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Name")
ret0, _ := ret[0].(string)
return ret0
}
// Name indicates an expected call of Name.
func (mr *MockObjectDeletionStateMockRecorder) Name() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockObjectDeletionState)(nil).Name))
}
+66 -48
View File
@@ -3,17 +3,18 @@ package headsync
import (
"context"
"fmt"
"github.com/anyproto/any-sync/app/ldiff"
"github.com/anyproto/any-sync/app/logger"
"github.com/anyproto/any-sync/commonspace/credentialprovider"
"github.com/anyproto/any-sync/commonspace/deletionstate"
"github.com/anyproto/any-sync/commonspace/object/treemanager"
"github.com/anyproto/any-sync/commonspace/peermanager"
"github.com/anyproto/any-sync/commonspace/spacestorage"
"github.com/anyproto/any-sync/commonspace/spacesyncproto"
"github.com/anyproto/any-sync/commonspace/syncstatus"
"github.com/anyproto/any-sync/net/peer"
"github.com/anyproto/any-sync/net/rpc/rpcerr"
"github.com/anytypeio/any-sync/app/ldiff"
"github.com/anytypeio/any-sync/app/logger"
"github.com/anytypeio/any-sync/commonspace/credentialprovider"
"github.com/anytypeio/any-sync/commonspace/object/tree/synctree"
"github.com/anytypeio/any-sync/commonspace/object/treegetter"
"github.com/anytypeio/any-sync/commonspace/peermanager"
"github.com/anytypeio/any-sync/commonspace/settings/settingsstate"
"github.com/anytypeio/any-sync/commonspace/spacestorage"
"github.com/anytypeio/any-sync/commonspace/spacesyncproto"
"github.com/anytypeio/any-sync/commonspace/syncstatus"
"github.com/anytypeio/any-sync/net/peer"
"github.com/anytypeio/any-sync/net/rpc/rpcerr"
"go.uber.org/zap"
"time"
)
@@ -22,22 +23,29 @@ type DiffSyncer interface {
Sync(ctx context.Context) error
RemoveObjects(ids []string)
UpdateHeads(id string, heads []string)
Init()
Close() error
Init(deletionState settingsstate.ObjectDeletionState)
}
func newDiffSyncer(hs *headSync) DiffSyncer {
func newDiffSyncer(
spaceId string,
diff ldiff.Diff,
peerManager peermanager.PeerManager,
cache treegetter.TreeGetter,
storage spacestorage.SpaceStorage,
clientFactory spacesyncproto.ClientFactory,
syncStatus syncstatus.StatusUpdater,
credentialProvider credentialprovider.CredentialProvider,
log logger.CtxLogger) DiffSyncer {
return &diffSyncer{
diff: hs.diff,
spaceId: hs.spaceId,
treeManager: hs.treeManager,
storage: hs.storage,
peerManager: hs.peerManager,
clientFactory: spacesyncproto.ClientFactoryFunc(spacesyncproto.NewDRPCSpaceSyncClient),
credentialProvider: hs.credentialProvider,
diff: diff,
spaceId: spaceId,
cache: cache,
storage: storage,
peerManager: peerManager,
clientFactory: clientFactory,
credentialProvider: credentialProvider,
log: log,
syncStatus: hs.syncStatus,
deletionState: hs.deletionState,
syncStatus: syncStatus,
}
}
@@ -45,19 +53,18 @@ type diffSyncer struct {
spaceId string
diff ldiff.Diff
peerManager peermanager.PeerManager
treeManager treemanager.TreeManager
cache treegetter.TreeGetter
storage spacestorage.SpaceStorage
clientFactory spacesyncproto.ClientFactory
log logger.CtxLogger
deletionState deletionstate.ObjectDeletionState
deletionState settingsstate.ObjectDeletionState
credentialProvider credentialprovider.CredentialProvider
syncStatus syncstatus.StatusUpdater
treeSyncer treemanager.TreeSyncer
}
func (d *diffSyncer) Init() {
func (d *diffSyncer) Init(deletionState settingsstate.ObjectDeletionState) {
d.deletionState = deletionState
d.deletionState.AddObserver(d.RemoveObjects)
d.treeSyncer = d.treeManager.NewTreeSyncer(d.spaceId, d.treeManager)
}
func (d *diffSyncer) RemoveObjects(ids []string) {
@@ -106,14 +113,8 @@ func (d *diffSyncer) Sync(ctx context.Context) error {
func (d *diffSyncer) syncWithPeer(ctx context.Context, p peer.Peer) (err error) {
ctx = logger.CtxWithFields(ctx, zap.String("peerId", p.Id()))
conn, err := p.AcquireDrpcConn(ctx)
if err != nil {
return
}
defer p.ReleaseDrpcConn(conn)
var (
cl = d.clientFactory.Client(conn)
cl = d.clientFactory.Client(p)
rdiff = NewRemoteDiff(d.spaceId, cl)
stateCounter = d.syncStatus.StateCounter()
)
@@ -123,7 +124,7 @@ func (d *diffSyncer) syncWithPeer(ctx context.Context, p peer.Peer) (err error)
if err != nil && err != spacesyncproto.ErrSpaceMissing {
if err == spacesyncproto.ErrSpaceIsDeleted {
d.log.Debug("got space deleted while syncing")
d.treeSyncer.SyncAll(ctx, p.Id(), []string{d.storage.SpaceSettingsId()}, nil)
d.syncTrees(ctx, p.Id(), []string{d.storage.SpaceSettingsId()})
}
d.syncStatus.SetNodesOnline(p.Id(), false)
return fmt.Errorf("diff error: %v", err)
@@ -136,24 +137,45 @@ func (d *diffSyncer) syncWithPeer(ctx context.Context, p peer.Peer) (err error)
totalLen := len(newIds) + len(changedIds) + len(removedIds)
// not syncing ids which were removed through settings document
missingIds := d.deletionState.Filter(newIds)
existingIds := append(d.deletionState.Filter(removedIds), d.deletionState.Filter(changedIds)...)
filteredIds := d.deletionState.FilterJoin(newIds, changedIds, removedIds)
d.syncStatus.RemoveAllExcept(p.Id(), existingIds, stateCounter)
d.syncStatus.RemoveAllExcept(p.Id(), filteredIds, stateCounter)
d.syncTrees(ctx, p.Id(), filteredIds)
err = d.treeSyncer.SyncAll(ctx, p.Id(), existingIds, missingIds)
if err != nil {
return err
}
d.log.Info("sync done:", zap.Int("newIds", len(newIds)),
zap.Int("changedIds", len(changedIds)),
zap.Int("removedIds", len(removedIds)),
zap.Int("already deleted ids", totalLen-len(existingIds)-len(missingIds)),
zap.Int("already deleted ids", totalLen-len(filteredIds)),
zap.String("peerId", p.Id()),
)
return
}
func (d *diffSyncer) syncTrees(ctx context.Context, peerId string, trees []string) {
for _, tId := range trees {
tree, err := d.cache.GetTree(ctx, d.spaceId, tId)
if err != nil {
d.log.InfoCtx(ctx, "can't load tree", zap.Error(err))
continue
}
syncTree, ok := tree.(synctree.SyncTree)
if !ok {
d.log.InfoCtx(ctx, "not a sync tree", zap.String("objectId", tId))
continue
}
// the idea why we call it directly is that if we try to get it from cache
// it may be already there (i.e. loaded)
// and build func will not be called, thus we won't sync the tree
// therefore we just do it manually
if err = syncTree.SyncWithPeer(ctx, peerId); err != nil {
d.log.WarnCtx(ctx, "synctree.SyncWithPeer error", zap.Error(err), zap.String("treeId", tId))
} else {
d.log.DebugCtx(ctx, "success synctree.SyncWithPeer", zap.String("treeId", tId))
}
}
}
func (d *diffSyncer) sendPushSpaceRequest(ctx context.Context, peerId string, cl spacesyncproto.DRPCSpaceSyncClient) (err error) {
aclStorage, err := d.storage.AclStorage()
if err != nil {
@@ -216,7 +238,3 @@ func (d *diffSyncer) subscribe(ctx context.Context, peerId string) (err error) {
Payload: payload,
})
}
func (d *diffSyncer) Close() error {
return d.treeSyncer.Close()
}
+139 -140
View File
@@ -4,19 +4,28 @@ import (
"bytes"
"context"
"fmt"
"github.com/anytypeio/any-sync/app/ldiff"
"github.com/anytypeio/any-sync/app/ldiff/mock_ldiff"
"github.com/anytypeio/any-sync/app/logger"
"github.com/anytypeio/any-sync/commonspace/credentialprovider/mock_credentialprovider"
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anytypeio/any-sync/commonspace/object/acl/liststorage/mock_liststorage"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
mock_treestorage "github.com/anytypeio/any-sync/commonspace/object/tree/treestorage/mock_treestorage"
"github.com/anytypeio/any-sync/commonspace/object/treegetter/mock_treegetter"
"github.com/anytypeio/any-sync/commonspace/peermanager/mock_peermanager"
"github.com/anytypeio/any-sync/commonspace/settings/settingsstate/mock_settingsstate"
"github.com/anytypeio/any-sync/commonspace/spacestorage/mock_spacestorage"
"github.com/anytypeio/any-sync/commonspace/spacesyncproto"
"github.com/anytypeio/any-sync/commonspace/spacesyncproto/mock_spacesyncproto"
"github.com/anytypeio/any-sync/commonspace/syncstatus"
"github.com/anytypeio/any-sync/net/peer"
"github.com/golang/mock/gomock"
"github.com/libp2p/go-libp2p/core/sec"
"github.com/stretchr/testify/require"
"storj.io/drpc"
"testing"
"time"
"github.com/anyproto/any-sync/app/ldiff"
"github.com/anyproto/any-sync/commonspace/object/acl/liststorage/mock_liststorage"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage/mock_treestorage"
"github.com/anyproto/any-sync/commonspace/spacesyncproto"
"github.com/anyproto/any-sync/consensus/consensusproto"
"github.com/anyproto/any-sync/net/peer"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
"storj.io/drpc"
)
type pushSpaceRequestMatcher struct {
@@ -27,6 +36,56 @@ type pushSpaceRequestMatcher struct {
spaceHeader *spacesyncproto.RawSpaceHeaderWithId
}
func (p pushSpaceRequestMatcher) Matches(x interface{}) bool {
res, ok := x.(*spacesyncproto.SpacePushRequest)
if !ok {
return false
}
return res.Payload.AclPayloadId == p.aclRootId && res.Payload.SpaceHeader == p.spaceHeader && res.Payload.SpaceSettingsPayloadId == p.settingsId && bytes.Equal(p.credential, res.Credential)
}
func (p pushSpaceRequestMatcher) String() string {
return ""
}
type mockPeer struct{}
func (m mockPeer) TryClose(objectTTL time.Duration) (res bool, err error) {
return true, m.Close()
}
func (m mockPeer) Id() string {
return "mockId"
}
func (m mockPeer) LastUsage() time.Time {
return time.Time{}
}
func (m mockPeer) Secure() sec.SecureConn {
return nil
}
func (m mockPeer) UpdateLastUsage() {
}
func (m mockPeer) Close() error {
return nil
}
func (m mockPeer) Closed() <-chan struct{} {
return make(chan struct{})
}
func (m mockPeer) Invoke(ctx context.Context, rpc string, enc drpc.Encoding, in, out drpc.Message) error {
return nil
}
func (m mockPeer) NewStream(ctx context.Context, rpc string, enc drpc.Encoding) (drpc.Stream, error) {
return nil, nil
}
func newPushSpaceRequestMatcher(
spaceId string,
aclRootId string,
@@ -42,135 +101,80 @@ func newPushSpaceRequestMatcher(
}
}
func (p pushSpaceRequestMatcher) Matches(x interface{}) bool {
res, ok := x.(*spacesyncproto.SpacePushRequest)
if !ok {
return false
}
return res.Payload.AclPayloadId == p.aclRootId && res.Payload.SpaceHeader == p.spaceHeader && res.Payload.SpaceSettingsPayloadId == p.settingsId && bytes.Equal(p.credential, res.Credential)
}
func (p pushSpaceRequestMatcher) String() string {
return ""
}
type mockPeer struct {
}
func (m mockPeer) Id() string {
return "peerId"
}
func (m mockPeer) Context() context.Context {
return context.Background()
}
func (m mockPeer) AcquireDrpcConn(ctx context.Context) (drpc.Conn, error) {
return nil, nil
}
func (m mockPeer) ReleaseDrpcConn(conn drpc.Conn) {
return
}
func (m mockPeer) DoDrpc(ctx context.Context, do func(conn drpc.Conn) error) error {
return nil
}
func (m mockPeer) IsClosed() bool {
return false
}
func (m mockPeer) TryClose(objectTTL time.Duration) (res bool, err error) {
return false, err
}
func (m mockPeer) Close() (err error) {
return nil
}
func (fx *headSyncFixture) initDiffSyncer(t *testing.T) {
fx.init(t)
fx.diffSyncer = newDiffSyncer(fx.headSync).(*diffSyncer)
fx.diffSyncer.clientFactory = spacesyncproto.ClientFactoryFunc(func(cc drpc.Conn) spacesyncproto.DRPCSpaceSyncClient {
return fx.clientMock
})
fx.deletionStateMock.EXPECT().AddObserver(gomock.Any())
fx.treeManagerMock.EXPECT().NewTreeSyncer(fx.spaceState.SpaceId, fx.treeManagerMock).Return(fx.treeSyncerMock)
fx.diffSyncer.Init()
}
func TestDiffSyncer(t *testing.T) {
func TestDiffSyncer_Sync(t *testing.T) {
// setup
ctx := context.Background()
ctrl := gomock.NewController(t)
defer ctrl.Finish()
diffMock := mock_ldiff.NewMockDiff(ctrl)
peerManagerMock := mock_peermanager.NewMockPeerManager(ctrl)
cacheMock := mock_treegetter.NewMockTreeGetter(ctrl)
stMock := mock_spacestorage.NewMockSpaceStorage(ctrl)
clientMock := mock_spacesyncproto.NewMockDRPCSpaceSyncClient(ctrl)
factory := spacesyncproto.ClientFactoryFunc(func(cc drpc.Conn) spacesyncproto.DRPCSpaceSyncClient {
return clientMock
})
credentialProvider := mock_credentialprovider.NewMockCredentialProvider(ctrl)
delState := mock_settingsstate.NewMockObjectDeletionState(ctrl)
spaceId := "spaceId"
aclRootId := "aclRootId"
l := logger.NewNamed(spaceId)
diffSyncer := newDiffSyncer(spaceId, diffMock, peerManagerMock, cacheMock, stMock, factory, syncstatus.NewNoOpSyncStatus(), credentialProvider, l)
delState.EXPECT().AddObserver(gomock.Any())
diffSyncer.Init(delState)
t.Run("diff syncer sync", func(t *testing.T) {
fx := newHeadSyncFixture(t)
fx.initDiffSyncer(t)
defer fx.stop()
mPeer := mockPeer{}
fx.peerManagerMock.EXPECT().
peerManagerMock.EXPECT().
GetResponsiblePeers(gomock.Any()).
Return([]peer.Peer{mPeer}, nil)
fx.diffMock.EXPECT().
Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock))).
Return([]peer.Peer{mockPeer{}}, nil)
diffMock.EXPECT().
Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(spaceId, clientMock))).
Return([]string{"new"}, []string{"changed"}, nil, nil)
fx.deletionStateMock.EXPECT().Filter([]string{"new"}).Return([]string{"new"}).Times(1)
fx.deletionStateMock.EXPECT().Filter([]string{"changed"}).Return([]string{"changed"}).Times(1)
fx.deletionStateMock.EXPECT().Filter(nil).Return(nil).Times(1)
fx.treeSyncerMock.EXPECT().SyncAll(gomock.Any(), mPeer.Id(), []string{"changed"}, []string{"new"}).Return(nil)
require.NoError(t, fx.diffSyncer.Sync(ctx))
delState.EXPECT().FilterJoin(gomock.Any()).Return([]string{"new", "changed"})
for _, arg := range []string{"new", "changed"} {
cacheMock.EXPECT().
GetTree(gomock.Any(), spaceId, arg).
Return(nil, nil)
}
require.NoError(t, diffSyncer.Sync(ctx))
})
t.Run("diff syncer sync conf error", func(t *testing.T) {
fx := newHeadSyncFixture(t)
fx.initDiffSyncer(t)
defer fx.stop()
ctx := context.Background()
fx.peerManagerMock.EXPECT().
peerManagerMock.EXPECT().
GetResponsiblePeers(gomock.Any()).
Return(nil, fmt.Errorf("some error"))
require.Error(t, fx.diffSyncer.Sync(ctx))
require.Error(t, diffSyncer.Sync(ctx))
})
t.Run("deletion state remove objects", func(t *testing.T) {
fx := newHeadSyncFixture(t)
fx.initDiffSyncer(t)
defer fx.stop()
deletedId := "id"
fx.deletionStateMock.EXPECT().Exists(deletedId).Return(true)
delState.EXPECT().Exists(deletedId).Return(true)
// this should not result in any mock being called
fx.diffSyncer.UpdateHeads(deletedId, []string{"someHead"})
diffSyncer.UpdateHeads(deletedId, []string{"someHead"})
})
t.Run("update heads updates diff", func(t *testing.T) {
fx := newHeadSyncFixture(t)
fx.initDiffSyncer(t)
defer fx.stop()
newId := "newId"
newHeads := []string{"h1", "h2"}
hash := "hash"
fx.diffMock.EXPECT().Set(ldiff.Element{
diffMock.EXPECT().Set(ldiff.Element{
Id: newId,
Head: concatStrings(newHeads),
})
fx.diffMock.EXPECT().Hash().Return(hash)
fx.deletionStateMock.EXPECT().Exists(newId).Return(false)
fx.storageMock.EXPECT().WriteSpaceHash(hash)
fx.diffSyncer.UpdateHeads(newId, newHeads)
diffMock.EXPECT().Hash().Return(hash)
delState.EXPECT().Exists(newId).Return(false)
stMock.EXPECT().WriteSpaceHash(hash)
diffSyncer.UpdateHeads(newId, newHeads)
})
t.Run("diff syncer sync space missing", func(t *testing.T) {
fx := newHeadSyncFixture(t)
fx.initDiffSyncer(t)
defer fx.stop()
aclStorageMock := mock_liststorage.NewMockListStorage(fx.ctrl)
settingsStorage := mock_treestorage.NewMockTreeStorage(fx.ctrl)
aclStorageMock := mock_liststorage.NewMockListStorage(ctrl)
settingsStorage := mock_treestorage.NewMockTreeStorage(ctrl)
settingsId := "settingsId"
aclRootId := "aclRootId"
aclRoot := &consensusproto.RawRecordWithId{
aclRoot := &aclrecordproto.RawAclRecordWithId{
Id: aclRootId,
}
settingsRoot := &treechangeproto.RawTreeChangeWithId{
@@ -180,61 +184,56 @@ func TestDiffSyncer(t *testing.T) {
spaceSettingsId := "spaceSettingsId"
credential := []byte("credential")
fx.peerManagerMock.EXPECT().
peerManagerMock.EXPECT().
GetResponsiblePeers(gomock.Any()).
Return([]peer.Peer{mockPeer{}}, nil)
fx.diffMock.EXPECT().
Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock))).
diffMock.EXPECT().
Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(spaceId, clientMock))).
Return(nil, nil, nil, spacesyncproto.ErrSpaceMissing)
fx.storageMock.EXPECT().AclStorage().Return(aclStorageMock, nil)
fx.storageMock.EXPECT().SpaceHeader().Return(spaceHeader, nil)
fx.storageMock.EXPECT().SpaceSettingsId().Return(spaceSettingsId)
fx.storageMock.EXPECT().TreeStorage(spaceSettingsId).Return(settingsStorage, nil)
stMock.EXPECT().AclStorage().Return(aclStorageMock, nil)
stMock.EXPECT().SpaceHeader().Return(spaceHeader, nil)
stMock.EXPECT().SpaceSettingsId().Return(spaceSettingsId)
stMock.EXPECT().TreeStorage(spaceSettingsId).Return(settingsStorage, nil)
settingsStorage.EXPECT().Root().Return(settingsRoot, nil)
aclStorageMock.EXPECT().
Root().
Return(aclRoot, nil)
fx.credentialProviderMock.EXPECT().
credentialProvider.EXPECT().
GetCredential(gomock.Any(), spaceHeader).
Return(credential, nil)
fx.clientMock.EXPECT().
SpacePush(gomock.Any(), newPushSpaceRequestMatcher(fx.spaceState.SpaceId, aclRootId, settingsId, credential, spaceHeader)).
clientMock.EXPECT().
SpacePush(gomock.Any(), newPushSpaceRequestMatcher(spaceId, aclRootId, settingsId, credential, spaceHeader)).
Return(nil, nil)
fx.peerManagerMock.EXPECT().SendPeer(gomock.Any(), "peerId", gomock.Any())
peerManagerMock.EXPECT().SendPeer(gomock.Any(), "mockId", gomock.Any())
require.NoError(t, fx.diffSyncer.Sync(ctx))
require.NoError(t, diffSyncer.Sync(ctx))
})
t.Run("diff syncer sync unexpected", func(t *testing.T) {
fx := newHeadSyncFixture(t)
fx.initDiffSyncer(t)
defer fx.stop()
fx.peerManagerMock.EXPECT().
peerManagerMock.EXPECT().
GetResponsiblePeers(gomock.Any()).
Return([]peer.Peer{mockPeer{}}, nil)
fx.diffMock.EXPECT().
Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock))).
diffMock.EXPECT().
Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(spaceId, clientMock))).
Return(nil, nil, nil, spacesyncproto.ErrUnexpected)
require.NoError(t, fx.diffSyncer.Sync(ctx))
require.NoError(t, diffSyncer.Sync(ctx))
})
t.Run("diff syncer sync space is deleted error", func(t *testing.T) {
fx := newHeadSyncFixture(t)
fx.initDiffSyncer(t)
defer fx.stop()
mPeer := mockPeer{}
fx.peerManagerMock.EXPECT().
peerManagerMock.EXPECT().
GetResponsiblePeers(gomock.Any()).
Return([]peer.Peer{mPeer}, nil)
fx.diffMock.EXPECT().
Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(fx.spaceState.SpaceId, fx.clientMock))).
Return([]peer.Peer{mockPeer{}}, nil)
diffMock.EXPECT().
Diff(gomock.Any(), gomock.Eq(NewRemoteDiff(spaceId, clientMock))).
Return(nil, nil, nil, spacesyncproto.ErrSpaceIsDeleted)
fx.storageMock.EXPECT().SpaceSettingsId().Return("settingsId")
fx.treeSyncerMock.EXPECT().SyncAll(gomock.Any(), mPeer.Id(), []string{"settingsId"}, nil).Return(nil)
stMock.EXPECT().SpaceSettingsId().Return("settingsId")
cacheMock.EXPECT().
GetTree(gomock.Any(), spaceId, "settingsId").
Return(nil, nil)
require.NoError(t, fx.diffSyncer.Sync(ctx))
require.NoError(t, diffSyncer.Sync(ctx))
})
}
+105 -103
View File
@@ -1,147 +1,125 @@
//go:generate mockgen -destination mock_headsync/mock_headsync.go github.com/anyproto/any-sync/commonspace/headsync DiffSyncer
//go:generate mockgen -destination mock_headsync/mock_headsync.go github.com/anytypeio/any-sync/commonspace/headsync DiffSyncer
package headsync
import (
"context"
"github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/app/ldiff"
"github.com/anyproto/any-sync/app/logger"
config2 "github.com/anyproto/any-sync/commonspace/config"
"github.com/anyproto/any-sync/commonspace/credentialprovider"
"github.com/anyproto/any-sync/commonspace/deletionstate"
"github.com/anyproto/any-sync/commonspace/object/treemanager"
"github.com/anyproto/any-sync/commonspace/peermanager"
"github.com/anyproto/any-sync/commonspace/spacestate"
"github.com/anyproto/any-sync/commonspace/spacestorage"
"github.com/anyproto/any-sync/commonspace/spacesyncproto"
"github.com/anyproto/any-sync/commonspace/syncstatus"
"github.com/anyproto/any-sync/net/peer"
"github.com/anyproto/any-sync/nodeconf"
"github.com/anyproto/any-sync/util/periodicsync"
"github.com/anyproto/any-sync/util/slice"
"github.com/anytypeio/any-sync/app/ldiff"
"github.com/anytypeio/any-sync/app/logger"
"github.com/anytypeio/any-sync/commonspace/credentialprovider"
"github.com/anytypeio/any-sync/commonspace/object/treegetter"
"github.com/anytypeio/any-sync/commonspace/peermanager"
"github.com/anytypeio/any-sync/commonspace/settings/settingsstate"
"github.com/anytypeio/any-sync/commonspace/spacestorage"
"github.com/anytypeio/any-sync/commonspace/spacesyncproto"
"github.com/anytypeio/any-sync/commonspace/syncstatus"
"github.com/anytypeio/any-sync/net/peer"
"github.com/anytypeio/any-sync/nodeconf"
"github.com/anytypeio/any-sync/util/periodicsync"
"go.uber.org/zap"
"golang.org/x/exp/slices"
"strings"
"sync/atomic"
"time"
)
var log = logger.NewNamed(CName)
const CName = "common.commonspace.headsync"
type TreeHeads struct {
Id string
Heads []string
}
type HeadSync interface {
app.ComponentRunnable
ExternalIds() []string
DebugAllHeads() (res []TreeHeads)
AllIds() []string
Init(objectIds []string, deletionState settingsstate.ObjectDeletionState)
UpdateHeads(id string, heads []string)
HandleRangeRequest(ctx context.Context, req *spacesyncproto.HeadSyncRequest) (resp *spacesyncproto.HeadSyncResponse, err error)
RemoveObjects(ids []string)
AllIds() []string
DebugAllHeads() (res []TreeHeads)
Close() (err error)
}
type headSync struct {
spaceId string
periodicSync periodicsync.PeriodicSync
storage spacestorage.SpaceStorage
diff ldiff.Diff
log logger.CtxLogger
syncer DiffSyncer
configuration nodeconf.Configuration
spaceIsDeleted *atomic.Bool
syncPeriod int
periodicSync periodicsync.PeriodicSync
storage spacestorage.SpaceStorage
diff ldiff.Diff
log logger.CtxLogger
syncer DiffSyncer
configuration nodeconf.NodeConf
peerManager peermanager.PeerManager
treeManager treemanager.TreeManager
credentialProvider credentialprovider.CredentialProvider
syncStatus syncstatus.StatusService
deletionState deletionstate.ObjectDeletionState
syncPeriod int
}
func New() HeadSync {
return &headSync{}
}
func NewHeadSync(
spaceId string,
spaceIsDeleted *atomic.Bool,
syncPeriod int,
configuration nodeconf.Configuration,
storage spacestorage.SpaceStorage,
peerManager peermanager.PeerManager,
cache treegetter.TreeGetter,
syncStatus syncstatus.StatusUpdater,
credentialProvider credentialprovider.CredentialProvider,
log logger.CtxLogger) HeadSync {
var createDiffSyncer = newDiffSyncer
func (h *headSync) Init(a *app.App) (err error) {
shared := a.MustComponent(spacestate.CName).(*spacestate.SpaceState)
cfg := a.MustComponent("config").(config2.ConfigGetter)
h.spaceId = shared.SpaceId
h.spaceIsDeleted = shared.SpaceIsDeleted
h.syncPeriod = cfg.GetSpace().SyncPeriod
h.configuration = a.MustComponent(nodeconf.CName).(nodeconf.NodeConf)
h.log = log.With(zap.String("spaceId", h.spaceId))
h.storage = a.MustComponent(spacestorage.CName).(spacestorage.SpaceStorage)
h.diff = ldiff.New(16, 16)
h.peerManager = a.MustComponent(peermanager.CName).(peermanager.PeerManager)
h.credentialProvider = a.MustComponent(credentialprovider.CName).(credentialprovider.CredentialProvider)
h.syncStatus = a.MustComponent(syncstatus.CName).(syncstatus.StatusService)
h.treeManager = a.MustComponent(treemanager.CName).(treemanager.TreeManager)
h.deletionState = a.MustComponent(deletionstate.CName).(deletionstate.ObjectDeletionState)
h.syncer = createDiffSyncer(h)
diff := ldiff.New(16, 16)
l := log.With(zap.String("spaceId", spaceId))
factory := spacesyncproto.ClientFactoryFunc(spacesyncproto.NewDRPCSpaceSyncClient)
syncer := newDiffSyncer(spaceId, diff, peerManager, cache, storage, factory, syncStatus, credentialProvider, l)
sync := func(ctx context.Context) (err error) {
// for clients cancelling the sync process
if h.spaceIsDeleted.Load() && !h.configuration.IsResponsible(h.spaceId) {
if spaceIsDeleted.Load() && !configuration.IsResponsible(spaceId) {
return spacesyncproto.ErrSpaceIsDeleted
}
return h.syncer.Sync(ctx)
return syncer.Sync(ctx)
}
h.periodicSync = periodicsync.NewPeriodicSync(h.syncPeriod, time.Minute, sync, h.log)
// TODO: move to run?
h.syncer.Init()
return nil
}
periodicSync := periodicsync.NewPeriodicSync(syncPeriod, time.Minute, sync, l)
func (h *headSync) Name() (name string) {
return CName
}
func (h *headSync) Run(ctx context.Context) (err error) {
initialIds, err := h.storage.StoredIds()
if err != nil {
return
return &headSync{
spaceId: spaceId,
storage: storage,
syncer: syncer,
periodicSync: periodicSync,
diff: diff,
log: log,
syncPeriod: syncPeriod,
configuration: configuration,
spaceIsDeleted: spaceIsDeleted,
}
h.fillDiff(initialIds)
h.periodicSync.Run()
return
}
func (h *headSync) HandleRangeRequest(ctx context.Context, req *spacesyncproto.HeadSyncRequest) (resp *spacesyncproto.HeadSyncResponse, err error) {
if h.spaceIsDeleted.Load() {
func (d *headSync) Init(objectIds []string, deletionState settingsstate.ObjectDeletionState) {
d.fillDiff(objectIds)
d.syncer.Init(deletionState)
d.periodicSync.Run()
}
func (d *headSync) HandleRangeRequest(ctx context.Context, req *spacesyncproto.HeadSyncRequest) (resp *spacesyncproto.HeadSyncResponse, err error) {
if d.spaceIsDeleted.Load() {
peerId, err := peer.CtxPeerId(ctx)
if err != nil {
return nil, err
}
// stop receiving all request for sync from clients
if !slices.Contains(h.configuration.NodeIds(h.spaceId), peerId) {
if !slices.Contains(d.configuration.NodeIds(d.spaceId), peerId) {
return nil, spacesyncproto.ErrSpaceIsDeleted
}
}
return HandleRangeRequest(ctx, h.diff, req)
return HandleRangeRequest(ctx, d.diff, req)
}
func (h *headSync) UpdateHeads(id string, heads []string) {
h.syncer.UpdateHeads(id, heads)
func (d *headSync) UpdateHeads(id string, heads []string) {
d.syncer.UpdateHeads(id, heads)
}
func (h *headSync) AllIds() []string {
return h.diff.Ids()
func (d *headSync) AllIds() []string {
return d.diff.Ids()
}
func (h *headSync) ExternalIds() []string {
settingsId := h.storage.SpaceSettingsId()
return slice.DiscardFromSlice(h.AllIds(), func(id string) bool {
return id == settingsId
})
}
func (h *headSync) DebugAllHeads() (res []TreeHeads) {
els := h.diff.Elements()
func (d *headSync) DebugAllHeads() (res []TreeHeads) {
els := d.diff.Elements()
for _, el := range els {
idHead := TreeHeads{
Id: el.Id,
@@ -152,19 +130,19 @@ func (h *headSync) DebugAllHeads() (res []TreeHeads) {
return
}
func (h *headSync) RemoveObjects(ids []string) {
h.syncer.RemoveObjects(ids)
func (d *headSync) RemoveObjects(ids []string) {
d.syncer.RemoveObjects(ids)
}
func (h *headSync) Close(ctx context.Context) (err error) {
h.periodicSync.Close()
return h.syncer.Close()
func (d *headSync) Close() (err error) {
d.periodicSync.Close()
return nil
}
func (h *headSync) fillDiff(objectIds []string) {
func (d *headSync) fillDiff(objectIds []string) {
var els = make([]ldiff.Element, 0, len(objectIds))
for _, id := range objectIds {
st, err := h.storage.TreeStorage(id)
st, err := d.storage.TreeStorage(id)
if err != nil {
continue
}
@@ -177,8 +155,32 @@ func (h *headSync) fillDiff(objectIds []string) {
Head: concatStrings(heads),
})
}
h.diff.Set(els...)
if err := h.storage.WriteSpaceHash(h.diff.Hash()); err != nil {
h.log.Error("can't write space hash", zap.Error(err))
d.diff.Set(els...)
if err := d.storage.WriteSpaceHash(d.diff.Hash()); err != nil {
d.log.Error("can't write space hash", zap.Error(err))
}
}
func concatStrings(strs []string) string {
var (
b strings.Builder
totalLen int
)
for _, s := range strs {
totalLen += len(s)
}
b.Grow(totalLen)
for _, s := range strs {
b.WriteString(s)
}
return b.String()
}
func splitString(str string) (res []string) {
const cidLen = 59
for i := 0; i < len(str); i += cidLen {
res = append(res, str[i:i+cidLen])
}
return
}
+48 -157
View File
@@ -1,179 +1,70 @@
package headsync
import (
"context"
"github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/app/ldiff"
"github.com/anyproto/any-sync/app/ldiff/mock_ldiff"
"github.com/anyproto/any-sync/commonspace/config"
"github.com/anyproto/any-sync/commonspace/credentialprovider"
"github.com/anyproto/any-sync/commonspace/credentialprovider/mock_credentialprovider"
"github.com/anyproto/any-sync/commonspace/deletionstate"
"github.com/anyproto/any-sync/commonspace/deletionstate/mock_deletionstate"
"github.com/anyproto/any-sync/commonspace/headsync/mock_headsync"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage/mock_treestorage"
"github.com/anyproto/any-sync/commonspace/object/treemanager"
"github.com/anyproto/any-sync/commonspace/object/treemanager/mock_treemanager"
"github.com/anyproto/any-sync/commonspace/peermanager"
"github.com/anyproto/any-sync/commonspace/peermanager/mock_peermanager"
"github.com/anyproto/any-sync/commonspace/spacestate"
"github.com/anyproto/any-sync/commonspace/spacestorage"
"github.com/anyproto/any-sync/commonspace/spacestorage/mock_spacestorage"
"github.com/anyproto/any-sync/commonspace/spacesyncproto/mock_spacesyncproto"
"github.com/anyproto/any-sync/commonspace/syncstatus"
"github.com/anyproto/any-sync/nodeconf"
"github.com/anyproto/any-sync/nodeconf/mock_nodeconf"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
"sync/atomic"
"github.com/anytypeio/any-sync/app/ldiff"
"github.com/anytypeio/any-sync/app/ldiff/mock_ldiff"
"github.com/anytypeio/any-sync/app/logger"
"github.com/anytypeio/any-sync/commonspace/headsync/mock_headsync"
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage/mock_treestorage"
"github.com/anytypeio/any-sync/commonspace/settings/settingsstate/mock_settingsstate"
"github.com/anytypeio/any-sync/commonspace/spacestorage/mock_spacestorage"
"github.com/anytypeio/any-sync/util/periodicsync/mock_periodicsync"
"github.com/golang/mock/gomock"
"testing"
)
type mockConfig struct {
}
func (m mockConfig) Init(a *app.App) (err error) {
return nil
}
func (m mockConfig) Name() (name string) {
return "config"
}
func (m mockConfig) GetSpace() config.Config {
return config.Config{}
}
type headSyncFixture struct {
spaceState *spacestate.SpaceState
ctrl *gomock.Controller
app *app.App
configurationMock *mock_nodeconf.MockService
storageMock *mock_spacestorage.MockSpaceStorage
peerManagerMock *mock_peermanager.MockPeerManager
credentialProviderMock *mock_credentialprovider.MockCredentialProvider
syncStatus syncstatus.StatusService
treeManagerMock *mock_treemanager.MockTreeManager
deletionStateMock *mock_deletionstate.MockObjectDeletionState
diffSyncerMock *mock_headsync.MockDiffSyncer
treeSyncerMock *mock_treemanager.MockTreeSyncer
diffMock *mock_ldiff.MockDiff
clientMock *mock_spacesyncproto.MockDRPCSpaceSyncClient
headSync *headSync
diffSyncer *diffSyncer
}
func newHeadSyncFixture(t *testing.T) *headSyncFixture {
spaceState := &spacestate.SpaceState{
SpaceId: "spaceId",
SpaceIsDeleted: &atomic.Bool{},
}
func TestDiffService(t *testing.T) {
ctrl := gomock.NewController(t)
configurationMock := mock_nodeconf.NewMockService(ctrl)
configurationMock.EXPECT().Name().AnyTimes().Return(nodeconf.CName)
defer ctrl.Finish()
spaceId := "spaceId"
l := logger.NewNamed("sync")
pSyncMock := mock_periodicsync.NewMockPeriodicSync(ctrl)
storageMock := mock_spacestorage.NewMockSpaceStorage(ctrl)
storageMock.EXPECT().Name().AnyTimes().Return(spacestorage.CName)
peerManagerMock := mock_peermanager.NewMockPeerManager(ctrl)
peerManagerMock.EXPECT().Name().AnyTimes().Return(peermanager.CName)
credentialProviderMock := mock_credentialprovider.NewMockCredentialProvider(ctrl)
credentialProviderMock.EXPECT().Name().AnyTimes().Return(credentialprovider.CName)
syncStatus := syncstatus.NewNoOpSyncStatus()
treeManagerMock := mock_treemanager.NewMockTreeManager(ctrl)
treeManagerMock.EXPECT().Name().AnyTimes().Return(treemanager.CName)
deletionStateMock := mock_deletionstate.NewMockObjectDeletionState(ctrl)
deletionStateMock.EXPECT().Name().AnyTimes().Return(deletionstate.CName)
diffSyncerMock := mock_headsync.NewMockDiffSyncer(ctrl)
treeSyncerMock := mock_treemanager.NewMockTreeSyncer(ctrl)
treeStorageMock := mock_treestorage.NewMockTreeStorage(ctrl)
diffMock := mock_ldiff.NewMockDiff(ctrl)
clientMock := mock_spacesyncproto.NewMockDRPCSpaceSyncClient(ctrl)
hs := &headSync{}
a := &app.App{}
a.Register(spaceState).
Register(mockConfig{}).
Register(configurationMock).
Register(storageMock).
Register(peerManagerMock).
Register(credentialProviderMock).
Register(syncStatus).
Register(treeManagerMock).
Register(deletionStateMock).
Register(hs)
return &headSyncFixture{
spaceState: spaceState,
ctrl: ctrl,
app: a,
configurationMock: configurationMock,
storageMock: storageMock,
peerManagerMock: peerManagerMock,
credentialProviderMock: credentialProviderMock,
syncStatus: syncStatus,
treeManagerMock: treeManagerMock,
deletionStateMock: deletionStateMock,
headSync: hs,
diffSyncerMock: diffSyncerMock,
treeSyncerMock: treeSyncerMock,
diffMock: diffMock,
clientMock: clientMock,
syncer := mock_headsync.NewMockDiffSyncer(ctrl)
delState := mock_settingsstate.NewMockObjectDeletionState(ctrl)
syncPeriod := 1
initId := "initId"
service := &headSync{
spaceId: spaceId,
storage: storageMock,
periodicSync: pSyncMock,
syncer: syncer,
diff: diffMock,
log: l,
syncPeriod: syncPeriod,
}
}
func (fx *headSyncFixture) init(t *testing.T) {
createDiffSyncer = func(hs *headSync) DiffSyncer {
return fx.diffSyncerMock
}
fx.diffSyncerMock.EXPECT().Init()
err := fx.headSync.Init(fx.app)
require.NoError(t, err)
fx.headSync.diff = fx.diffMock
}
func (fx *headSyncFixture) stop() {
fx.ctrl.Finish()
}
func TestHeadSync(t *testing.T) {
ctx := context.Background()
t.Run("run close", func(t *testing.T) {
fx := newHeadSyncFixture(t)
fx.init(t)
defer fx.stop()
ids := []string{"id1"}
treeMock := mock_treestorage.NewMockTreeStorage(fx.ctrl)
fx.storageMock.EXPECT().StoredIds().Return(ids, nil)
fx.storageMock.EXPECT().TreeStorage(ids[0]).Return(treeMock, nil)
treeMock.EXPECT().Heads().Return([]string{"h1", "h2"}, nil)
fx.diffMock.EXPECT().Set(ldiff.Element{
Id: "id1",
t.Run("init", func(t *testing.T) {
storageMock.EXPECT().TreeStorage(initId).Return(treeStorageMock, nil)
treeStorageMock.EXPECT().Heads().Return([]string{"h1", "h2"}, nil)
syncer.EXPECT().Init(delState)
diffMock.EXPECT().Set(ldiff.Element{
Id: initId,
Head: "h1h2",
})
fx.diffMock.EXPECT().Hash().Return("hash")
fx.storageMock.EXPECT().WriteSpaceHash("hash").Return(nil)
fx.diffSyncerMock.EXPECT().Sync(gomock.Any()).Return(nil)
fx.diffSyncerMock.EXPECT().Close().Return(nil)
err := fx.headSync.Run(ctx)
require.NoError(t, err)
err = fx.headSync.Close(ctx)
require.NoError(t, err)
hash := "123"
diffMock.EXPECT().Hash().Return(hash)
storageMock.EXPECT().WriteSpaceHash(hash)
pSyncMock.EXPECT().Run()
service.Init([]string{initId}, delState)
})
t.Run("update heads", func(t *testing.T) {
fx := newHeadSyncFixture(t)
fx.init(t)
defer fx.stop()
fx.diffSyncerMock.EXPECT().UpdateHeads("id1", []string{"h1"})
fx.headSync.UpdateHeads("id1", []string{"h1"})
syncer.EXPECT().UpdateHeads(initId, []string{"h1", "h2"})
service.UpdateHeads(initId, []string{"h1", "h2"})
})
t.Run("remove objects", func(t *testing.T) {
fx := newHeadSyncFixture(t)
fx.init(t)
defer fx.stop()
syncer.EXPECT().RemoveObjects([]string{"h1", "h2"})
service.RemoveObjects([]string{"h1", "h2"})
})
fx.diffSyncerMock.EXPECT().RemoveObjects([]string{"id1"})
fx.headSync.RemoveObjects([]string{"id1"})
t.Run("close", func(t *testing.T) {
pSyncMock.EXPECT().Close()
service.Close()
})
}
@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/anyproto/any-sync/commonspace/headsync (interfaces: DiffSyncer)
// Source: github.com/anytypeio/any-sync/commonspace/headsync (interfaces: DiffSyncer)
// Package mock_headsync is a generated GoMock package.
package mock_headsync
@@ -8,7 +8,8 @@ import (
context "context"
reflect "reflect"
gomock "go.uber.org/mock/gomock"
settingsstate "github.com/anytypeio/any-sync/commonspace/settings/settingsstate"
gomock "github.com/golang/mock/gomock"
)
// MockDiffSyncer is a mock of DiffSyncer interface.
@@ -34,30 +35,16 @@ func (m *MockDiffSyncer) EXPECT() *MockDiffSyncerMockRecorder {
return m.recorder
}
// Close mocks base method.
func (m *MockDiffSyncer) Close() error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Close")
ret0, _ := ret[0].(error)
return ret0
}
// Close indicates an expected call of Close.
func (mr *MockDiffSyncerMockRecorder) Close() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockDiffSyncer)(nil).Close))
}
// Init mocks base method.
func (m *MockDiffSyncer) Init() {
func (m *MockDiffSyncer) Init(arg0 settingsstate.ObjectDeletionState) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Init")
m.ctrl.Call(m, "Init", arg0)
}
// Init indicates an expected call of Init.
func (mr *MockDiffSyncerMockRecorder) Init() *gomock.Call {
func (mr *MockDiffSyncerMockRecorder) Init(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockDiffSyncer)(nil).Init))
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockDiffSyncer)(nil).Init), arg0)
}
// RemoveObjects mocks base method.
+2 -2
View File
@@ -2,8 +2,8 @@ package headsync
import (
"context"
"github.com/anyproto/any-sync/app/ldiff"
"github.com/anyproto/any-sync/commonspace/spacesyncproto"
"github.com/anytypeio/any-sync/app/ldiff"
"github.com/anytypeio/any-sync/commonspace/spacesyncproto"
)
type Client interface {
+2 -2
View File
@@ -3,8 +3,8 @@ package headsync
import (
"context"
"fmt"
"github.com/anyproto/any-sync/app/ldiff"
"github.com/anyproto/any-sync/commonspace/spacesyncproto"
"github.com/anytypeio/any-sync/app/ldiff"
"github.com/anytypeio/any-sync/commonspace/spacesyncproto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
-27
View File
@@ -1,27 +0,0 @@
package headsync
import "strings"
func concatStrings(strs []string) string {
var (
b strings.Builder
totalLen int
)
for _, s := range strs {
totalLen += len(s)
}
b.Grow(totalLen)
for _, s := range strs {
b.WriteString(s)
}
return b.String()
}
func splitString(str string) (res []string) {
const cidLen = 59
for i := 0; i < len(str); i += cidLen {
res = append(res, str[i:i+cidLen])
}
return
}
@@ -2,7 +2,7 @@ package accountdata
import (
"crypto/rand"
"github.com/anyproto/any-sync/util/crypto"
"github.com/anytypeio/any-sync/util/crypto"
)
type AccountKeys struct {
File diff suppressed because it is too large. Load diff
@@ -2,7 +2,26 @@ syntax = "proto3";
package aclrecord;
option go_package = "commonspace/object/acl/aclrecordproto";
// AclRoot is a root of access control list
message RawAclRecord {
bytes payload = 1;
bytes signature = 2;
bytes acceptorIdentity = 3;
bytes acceptorSignature = 4;
}
message RawAclRecordWithId {
bytes payload = 1;
string id = 2;
}
message AclRecord {
string prevId = 1;
bytes identity = 2;
bytes data = 3;
string readKeyId = 4;
int64 timestamp = 5;
}
message AclRoot {
bytes identity = 1;
bytes masterKey = 2;
@@ -12,95 +31,82 @@ message AclRoot {
bytes identitySignature = 6;
}
// AclAccountInvite contains the public invite key, the private part of which is sent to the user directly
message AclAccountInvite {
bytes inviteKey = 1;
}
// AclAccountRequestJoin contains the reference to the invite record and the data of the person who wants to join, confirmed by the private invite key
message AclAccountRequestJoin {
bytes inviteIdentity = 1;
string inviteRecordId = 2;
bytes inviteIdentitySignature = 3;
bytes metadata = 4;
}
// AclAccountRequestAccept contains the reference to join record and all read keys, encrypted with the identity of the requestor
message AclAccountRequestAccept {
bytes identity = 1;
string requestRecordId = 2;
repeated AclReadKeyWithRecord encryptedReadKeys = 3;
AclUserPermissions permissions = 4;
}
// AclAccountRequestDecline contains the reference to join record
message AclAccountRequestDecline {
string requestRecordId = 1;
}
// AclAccountInviteRevoke revokes the invite record
message AclAccountInviteRevoke {
string inviteRecordId = 1;
}
// AclReadKeys are a read key with record id
message AclReadKeyWithRecord {
string recordId = 1;
bytes encryptedReadKey = 2;
}
// AclEncryptedReadKeys are new key for specific identity
message AclEncryptedReadKey {
bytes identity = 1;
bytes encryptedReadKey = 2;
}
// AclAccountPermissionChange changes permissions of specific account
message AclAccountPermissionChange {
bytes identity = 1;
AclUserPermissions permissions = 2;
}
// AclReadKeyChange changes the key for a space
message AclReadKeyChange {
repeated AclEncryptedReadKey accountKeys = 1;
}
// AclAccountRemove removes an account and changes read key for space
message AclAccountRemove {
repeated bytes identities = 1;
repeated AclEncryptedReadKey accountKeys = 2;
}
// AclAccountRequestRemove adds a request to remove an account
message AclAccountRequestRemove {
}
// AclContentValue contains possible values for Acl
message AclContentValue {
oneof value {
AclAccountInvite invite = 1;
AclAccountInviteRevoke inviteRevoke = 2;
AclAccountRequestJoin requestJoin = 3;
AclAccountRequestAccept requestAccept = 4;
AclAccountPermissionChange permissionChange = 5;
AclAccountRemove accountRemove = 6;
AclReadKeyChange readKeyChange = 7;
AclAccountRequestDecline requestDecline = 8;
AclAccountRequestRemove accountRequestRemove = 9;
AclUserAdd userAdd = 1;
AclUserRemove userRemove = 2;
AclUserPermissionChange userPermissionChange = 3;
AclUserInvite userInvite = 4;
AclUserJoin userJoin = 5;
}
}
// AclData contains different acl content
message AclData {
repeated AclContentValue aclContent = 1;
}
// AclUserPermissions contains different possible user roles
enum AclUserPermissions {
None = 0;
Owner = 1;
Admin = 2;
Writer = 3;
Reader = 4;
message AclState {
repeated string readKeyIds = 1;
repeated AclUserState userStates = 2;
map<string, AclUserInvite> invites = 3;
}
message AclUserState {
bytes identity = 1;
AclUserPermissions permissions = 2;
}
message AclUserAdd {
bytes identity = 1;
repeated bytes encryptedReadKeys = 2;
AclUserPermissions permissions = 3;
}
message AclUserInvite {
bytes acceptPublicKey = 1;
repeated bytes encryptedReadKeys = 2;
AclUserPermissions permissions = 3;
}
message AclUserJoin {
bytes identity = 1;
bytes acceptSignature = 2;
bytes acceptPubKey = 3;
repeated bytes encryptedReadKeys = 4;
}
message AclUserRemove {
bytes identity = 1;
repeated AclReadKeyReplace readKeyReplaces = 2;
}
message AclReadKeyReplace {
bytes identity = 1;
bytes encryptedReadKey = 2;
}
message AclUserPermissionChange {
bytes identity = 1;
AclUserPermissions permissions = 2;
}
enum AclUserPermissions {
Admin = 0;
Writer = 1;
Reader = 2;
}
message AclSyncMessage {
AclSyncContentValue content = 1;
}
// AclSyncContentValue provides different types for acl sync
message AclSyncContentValue {
oneof value {
AclAddRecords addRecords = 1;
}
}
message AclAddRecords {
repeated RawAclRecordWithId records = 1;
}
+22 -394
View File
@@ -1,14 +1,11 @@
package list
import (
"time"
"github.com/anyproto/any-sync/commonspace/object/accountdata"
"github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anyproto/any-sync/consensus/consensusproto"
"github.com/anyproto/any-sync/util/cidutil"
"github.com/anyproto/any-sync/util/crypto"
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anytypeio/any-sync/util/cidutil"
"github.com/anytypeio/any-sync/util/crypto"
"github.com/gogo/protobuf/proto"
"time"
)
type RootContent struct {
@@ -18,387 +15,26 @@ type RootContent struct {
EncryptedReadKey []byte
}
type RequestJoinPayload struct {
InviteRecordId string
InviteKey crypto.PrivKey
Metadata []byte
}
type RequestAcceptPayload struct {
RequestRecordId string
Permissions AclPermissions
}
type PermissionChangePayload struct {
Identity crypto.PubKey
Permissions AclPermissions
}
type AccountRemovePayload struct {
Identities []crypto.PubKey
ReadKey crypto.SymKey
}
type InviteResult struct {
InviteRec *consensusproto.RawRecord
InviteKey crypto.PrivKey
}
type AclRecordBuilder interface {
UnmarshallWithId(rawIdRecord *consensusproto.RawRecordWithId) (rec *AclRecord, err error)
Unmarshall(rawRecord *consensusproto.RawRecord) (rec *AclRecord, err error)
BuildRoot(content RootContent) (rec *consensusproto.RawRecordWithId, err error)
BuildInvite() (res InviteResult, err error)
BuildInviteRevoke(inviteRecordId string) (rawRecord *consensusproto.RawRecord, err error)
BuildRequestJoin(payload RequestJoinPayload) (rawRecord *consensusproto.RawRecord, err error)
BuildRequestAccept(payload RequestAcceptPayload) (rawRecord *consensusproto.RawRecord, err error)
BuildRequestDecline(requestRecordId string) (rawRecord *consensusproto.RawRecord, err error)
BuildRequestRemove() (rawRecord *consensusproto.RawRecord, err error)
BuildPermissionChange(payload PermissionChangePayload) (rawRecord *consensusproto.RawRecord, err error)
BuildReadKeyChange(newKey crypto.SymKey) (rawRecord *consensusproto.RawRecord, err error)
BuildAccountRemove(payload AccountRemovePayload) (rawRecord *consensusproto.RawRecord, err error)
Unmarshall(rawIdRecord *aclrecordproto.RawAclRecordWithId) (rec *AclRecord, err error)
BuildRoot(content RootContent) (rec *aclrecordproto.RawAclRecordWithId, err error)
}
type aclRecordBuilder struct {
id string
keyStorage crypto.KeyStorage
accountKeys *accountdata.AccountKeys
verifier AcceptorVerifier
state *AclState
id string
keyStorage crypto.KeyStorage
}
func NewAclRecordBuilder(id string, keyStorage crypto.KeyStorage, keys *accountdata.AccountKeys, verifier AcceptorVerifier) AclRecordBuilder {
func NewAclRecordBuilder(id string, keyStorage crypto.KeyStorage) AclRecordBuilder {
return &aclRecordBuilder{
id: id,
keyStorage: keyStorage,
accountKeys: keys,
verifier: verifier,
id: id,
keyStorage: keyStorage,
}
}
func (a *aclRecordBuilder) buildRecord(aclContent *aclrecordproto.AclContentValue) (rawRec *consensusproto.RawRecord, err error) {
aclData := &aclrecordproto.AclData{AclContent: []*aclrecordproto.AclContentValue{
aclContent,
}}
marshalledData, err := aclData.Marshal()
if err != nil {
return
}
protoKey, err := a.accountKeys.SignKey.GetPublic().Marshall()
if err != nil {
return
}
rec := &consensusproto.Record{
PrevId: a.state.lastRecordId,
Identity: protoKey,
Data: marshalledData,
Timestamp: time.Now().Unix(),
}
marshalledRec, err := rec.Marshal()
if err != nil {
return
}
signature, err := a.accountKeys.SignKey.Sign(marshalledRec)
if err != nil {
return
}
rawRec = &consensusproto.RawRecord{
Payload: marshalledRec,
Signature: signature,
}
return
}
func (a *aclRecordBuilder) BuildInvite() (res InviteResult, err error) {
if !a.state.Permissions(a.state.pubKey).CanManageAccounts() {
err = ErrInsufficientPermissions
return
}
privKey, pubKey, err := crypto.GenerateRandomEd25519KeyPair()
if err != nil {
return
}
invitePubKey, err := pubKey.Marshall()
if err != nil {
return
}
inviteRec := &aclrecordproto.AclAccountInvite{InviteKey: invitePubKey}
content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_Invite{Invite: inviteRec}}
rawRec, err := a.buildRecord(content)
if err != nil {
return
}
res.InviteKey = privKey
res.InviteRec = rawRec
return
}
func (a *aclRecordBuilder) BuildInviteRevoke(inviteRecordId string) (rawRecord *consensusproto.RawRecord, err error) {
if !a.state.Permissions(a.state.pubKey).CanManageAccounts() {
err = ErrInsufficientPermissions
return
}
_, exists := a.state.inviteKeys[inviteRecordId]
if !exists {
err = ErrNoSuchInvite
return
}
revokeRec := &aclrecordproto.AclAccountInviteRevoke{InviteRecordId: inviteRecordId}
content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_InviteRevoke{InviteRevoke: revokeRec}}
return a.buildRecord(content)
}
func (a *aclRecordBuilder) BuildRequestJoin(payload RequestJoinPayload) (rawRecord *consensusproto.RawRecord, err error) {
key, exists := a.state.inviteKeys[payload.InviteRecordId]
if !exists {
err = ErrNoSuchInvite
return
}
if !payload.InviteKey.GetPublic().Equals(key) {
err = ErrIncorrectInviteKey
}
rawIdentity, err := a.accountKeys.SignKey.GetPublic().Raw()
if err != nil {
return
}
signature, err := payload.InviteKey.Sign(rawIdentity)
if err != nil {
return
}
protoIdentity, err := a.accountKeys.SignKey.GetPublic().Marshall()
if err != nil {
return
}
joinRec := &aclrecordproto.AclAccountRequestJoin{
InviteIdentity: protoIdentity,
InviteRecordId: payload.InviteRecordId,
InviteIdentitySignature: signature,
Metadata: payload.Metadata,
}
content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_RequestJoin{RequestJoin: joinRec}}
return a.buildRecord(content)
}
func (a *aclRecordBuilder) BuildRequestAccept(payload RequestAcceptPayload) (rawRecord *consensusproto.RawRecord, err error) {
if !a.state.Permissions(a.state.pubKey).CanManageAccounts() {
err = ErrInsufficientPermissions
return
}
request, exists := a.state.requestRecords[payload.RequestRecordId]
if !exists {
err = ErrNoSuchRequest
return
}
var encryptedReadKeys []*aclrecordproto.AclReadKeyWithRecord
for keyId, key := range a.state.userReadKeys {
rawKey, err := key.Raw()
if err != nil {
return nil, err
}
enc, err := request.RequestIdentity.Encrypt(rawKey)
if err != nil {
return nil, err
}
encryptedReadKeys = append(encryptedReadKeys, &aclrecordproto.AclReadKeyWithRecord{
RecordId: keyId,
EncryptedReadKey: enc,
})
}
if err != nil {
return
}
requestIdentityProto, err := request.RequestIdentity.Marshall()
if err != nil {
return
}
acceptRec := &aclrecordproto.AclAccountRequestAccept{
Identity: requestIdentityProto,
RequestRecordId: payload.RequestRecordId,
EncryptedReadKeys: encryptedReadKeys,
Permissions: aclrecordproto.AclUserPermissions(payload.Permissions),
}
content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_RequestAccept{RequestAccept: acceptRec}}
return a.buildRecord(content)
}
func (a *aclRecordBuilder) BuildRequestDecline(requestRecordId string) (rawRecord *consensusproto.RawRecord, err error) {
if !a.state.Permissions(a.state.pubKey).CanManageAccounts() {
err = ErrInsufficientPermissions
return
}
_, exists := a.state.requestRecords[requestRecordId]
if !exists {
err = ErrNoSuchRequest
return
}
declineRec := &aclrecordproto.AclAccountRequestDecline{RequestRecordId: requestRecordId}
content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_RequestDecline{RequestDecline: declineRec}}
return a.buildRecord(content)
}
func (a *aclRecordBuilder) BuildPermissionChange(payload PermissionChangePayload) (rawRecord *consensusproto.RawRecord, err error) {
permissions := a.state.Permissions(a.state.pubKey)
if !permissions.CanManageAccounts() || payload.Identity.Equals(a.state.pubKey) {
err = ErrInsufficientPermissions
return
}
if payload.Permissions.IsOwner() {
err = ErrIsOwner
return
}
protoIdentity, err := payload.Identity.Marshall()
if err != nil {
return
}
permissionRec := &aclrecordproto.AclAccountPermissionChange{
Identity: protoIdentity,
Permissions: aclrecordproto.AclUserPermissions(payload.Permissions),
}
content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_PermissionChange{PermissionChange: permissionRec}}
return a.buildRecord(content)
}
func (a *aclRecordBuilder) BuildReadKeyChange(newKey crypto.SymKey) (rawRecord *consensusproto.RawRecord, err error) {
if !a.state.Permissions(a.state.pubKey).CanManageAccounts() {
err = ErrInsufficientPermissions
return
}
rawKey, err := newKey.Raw()
if err != nil {
return
}
if len(rawKey) != crypto.KeyBytes {
err = ErrIncorrectReadKey
return
}
var aclReadKeys []*aclrecordproto.AclEncryptedReadKey
for _, st := range a.state.userStates {
protoIdentity, err := st.PubKey.Marshall()
if err != nil {
return nil, err
}
enc, err := st.PubKey.Encrypt(rawKey)
if err != nil {
return nil, err
}
aclReadKeys = append(aclReadKeys, &aclrecordproto.AclEncryptedReadKey{
Identity: protoIdentity,
EncryptedReadKey: enc,
})
}
readRec := &aclrecordproto.AclReadKeyChange{AccountKeys: aclReadKeys}
content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_ReadKeyChange{ReadKeyChange: readRec}}
return a.buildRecord(content)
}
func (a *aclRecordBuilder) BuildAccountRemove(payload AccountRemovePayload) (rawRecord *consensusproto.RawRecord, err error) {
deletedMap := map[string]struct{}{}
for _, key := range payload.Identities {
permissions := a.state.Permissions(key)
if permissions.IsOwner() {
return nil, ErrInsufficientPermissions
}
if permissions.NoPermissions() {
return nil, ErrNoSuchAccount
}
deletedMap[mapKeyFromPubKey(key)] = struct{}{}
}
if !a.state.Permissions(a.state.pubKey).CanManageAccounts() {
err = ErrInsufficientPermissions
return
}
rawKey, err := payload.ReadKey.Raw()
if err != nil {
return
}
if len(rawKey) != crypto.KeyBytes {
err = ErrIncorrectReadKey
return
}
var aclReadKeys []*aclrecordproto.AclEncryptedReadKey
for _, st := range a.state.userStates {
if _, exists := deletedMap[mapKeyFromPubKey(st.PubKey)]; exists {
continue
}
protoIdentity, err := st.PubKey.Marshall()
if err != nil {
return nil, err
}
enc, err := st.PubKey.Encrypt(rawKey)
if err != nil {
return nil, err
}
aclReadKeys = append(aclReadKeys, &aclrecordproto.AclEncryptedReadKey{
Identity: protoIdentity,
EncryptedReadKey: enc,
})
}
var marshalledIdentities [][]byte
for _, key := range payload.Identities {
protoIdentity, err := key.Marshall()
if err != nil {
return nil, err
}
marshalledIdentities = append(marshalledIdentities, protoIdentity)
}
removeRec := &aclrecordproto.AclAccountRemove{AccountKeys: aclReadKeys, Identities: marshalledIdentities}
content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_AccountRemove{AccountRemove: removeRec}}
return a.buildRecord(content)
}
func (a *aclRecordBuilder) BuildRequestRemove() (rawRecord *consensusproto.RawRecord, err error) {
permissions := a.state.Permissions(a.state.pubKey)
if permissions.NoPermissions() {
err = ErrNoSuchAccount
return
}
if permissions.IsOwner() {
err = ErrIsOwner
return
}
removeRec := &aclrecordproto.AclAccountRequestRemove{}
content := &aclrecordproto.AclContentValue{Value: &aclrecordproto.AclContentValue_AccountRequestRemove{AccountRequestRemove: removeRec}}
return a.buildRecord(content)
}
func (a *aclRecordBuilder) Unmarshall(rawRecord *consensusproto.RawRecord) (rec *AclRecord, err error) {
aclRecord := &consensusproto.Record{}
err = proto.Unmarshal(rawRecord.Payload, aclRecord)
if err != nil {
return
}
pubKey, err := a.keyStorage.PubKeyFromProto(aclRecord.Identity)
if err != nil {
return
}
aclData := &aclrecordproto.AclData{}
err = proto.Unmarshal(aclRecord.Data, aclData)
if err != nil {
return
}
rec = &AclRecord{
PrevId: aclRecord.PrevId,
Timestamp: aclRecord.Timestamp,
Data: aclRecord.Data,
Signature: rawRecord.Signature,
Identity: pubKey,
Model: aclData,
}
res, err := pubKey.Verify(rawRecord.Payload, rawRecord.Signature)
if err != nil {
return
}
if !res {
err = ErrInvalidSignature
return
}
return
}
func (a *aclRecordBuilder) UnmarshallWithId(rawIdRecord *consensusproto.RawRecordWithId) (rec *AclRecord, err error) {
func (a *aclRecordBuilder) Unmarshall(rawIdRecord *aclrecordproto.RawAclRecordWithId) (rec *AclRecord, err error) {
var (
rawRec = &consensusproto.RawRecord{}
rawRec = &aclrecordproto.RawAclRecord{}
pubKey crypto.PubKey
)
err = proto.Unmarshal(rawIdRecord.Payload, rawRec)
@@ -417,17 +53,14 @@ func (a *aclRecordBuilder) UnmarshallWithId(rawIdRecord *consensusproto.RawRecor
}
rec = &AclRecord{
Id: rawIdRecord.Id,
ReadKeyId: rawIdRecord.Id,
Timestamp: aclRoot.Timestamp,
Signature: rawRec.Signature,
Identity: pubKey,
Model: aclRoot,
}
} else {
err = a.verifier.VerifyAcceptor(rawRec)
if err != nil {
return
}
aclRecord := &consensusproto.Record{}
aclRecord := &aclrecordproto.AclRecord{}
err = proto.Unmarshal(rawRec.Payload, aclRecord)
if err != nil {
return
@@ -436,19 +69,14 @@ func (a *aclRecordBuilder) UnmarshallWithId(rawIdRecord *consensusproto.RawRecor
if err != nil {
return
}
aclData := &aclrecordproto.AclData{}
err = proto.Unmarshal(aclRecord.Data, aclData)
if err != nil {
return
}
rec = &AclRecord{
Id: rawIdRecord.Id,
PrevId: aclRecord.PrevId,
ReadKeyId: aclRecord.ReadKeyId,
Timestamp: aclRecord.Timestamp,
Data: aclRecord.Data,
Signature: rawRec.Signature,
Identity: pubKey,
Model: aclData,
}
}
@@ -456,7 +84,7 @@ func (a *aclRecordBuilder) UnmarshallWithId(rawIdRecord *consensusproto.RawRecor
return
}
func (a *aclRecordBuilder) BuildRoot(content RootContent) (rec *consensusproto.RawRecordWithId, err error) {
func (a *aclRecordBuilder) BuildRoot(content RootContent) (rec *aclrecordproto.RawAclRecordWithId, err error) {
rawIdentity, err := content.PrivKey.GetPublic().Raw()
if err != nil {
return
@@ -490,8 +118,8 @@ func (a *aclRecordBuilder) BuildRoot(content RootContent) (rec *consensusproto.R
func verifyRaw(
pubKey crypto.PubKey,
rawRec *consensusproto.RawRecord,
recWithId *consensusproto.RawRecordWithId) (err error) {
rawRec *aclrecordproto.RawAclRecord,
recWithId *aclrecordproto.RawAclRecordWithId) (err error) {
// verifying signature
res, err := pubKey.Verify(rawRec.Payload, rawRec.Signature)
if err != nil {
@@ -509,7 +137,7 @@ func verifyRaw(
return
}
func marshalAclRoot(aclRoot *aclrecordproto.AclRoot, key crypto.PrivKey) (rawWithId *consensusproto.RawRecordWithId, err error) {
func marshalAclRoot(aclRoot *aclrecordproto.AclRoot, key crypto.PrivKey) (rawWithId *aclrecordproto.RawAclRecordWithId, err error) {
marshalledRoot, err := aclRoot.Marshal()
if err != nil {
return
@@ -518,7 +146,7 @@ func marshalAclRoot(aclRoot *aclrecordproto.AclRoot, key crypto.PrivKey) (rawWit
if err != nil {
return
}
raw := &consensusproto.RawRecord{
raw := &aclrecordproto.RawAclRecord{
Payload: marshalledRoot,
Signature: signature,
}
@@ -530,7 +158,7 @@ func marshalAclRoot(aclRoot *aclrecordproto.AclRoot, key crypto.PrivKey) (rawWit
if err != nil {
return
}
rawWithId = &consensusproto.RawRecordWithId{
rawWithId = &aclrecordproto.RawAclRecordWithId{
Payload: marshalledRaw,
Id: aclHeadId,
}
@@ -0,0 +1,9 @@
package list
import (
"testing"
)
func TestAclRecordBuilder_BuildUserJoin(t *testing.T) {
return
}
+115 -253
View File
@@ -2,10 +2,10 @@ package list
import (
"errors"
"github.com/anyproto/any-sync/app/logger"
"github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anyproto/any-sync/util/crypto"
"fmt"
"github.com/anytypeio/any-sync/app/logger"
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anytypeio/any-sync/util/crypto"
"github.com/gogo/protobuf/proto"
"go.uber.org/zap"
)
@@ -13,24 +13,19 @@ import (
var log = logger.NewNamedSugared("common.commonspace.acllist")
var (
ErrNoSuchAccount = errors.New("no such account")
ErrPendingRequest = errors.New("already exists pending request")
ErrUnexpectedContentType = errors.New("unexpected content type")
ErrIncorrectIdentity = errors.New("incorrect identity")
ErrIncorrectInviteKey = errors.New("incorrect invite key")
ErrFailedToDecrypt = errors.New("failed to decrypt key")
ErrNoSuchRecord = errors.New("no such record")
ErrNoSuchRequest = errors.New("no such request")
ErrNoSuchInvite = errors.New("no such invite")
ErrInsufficientPermissions = errors.New("insufficient permissions")
ErrIsOwner = errors.New("can't be made by owner")
ErrIncorrectNumberOfAccounts = errors.New("incorrect number of accounts")
ErrDuplicateAccounts = errors.New("duplicate accounts")
ErrNoReadKey = errors.New("acl state doesn't have a read key")
ErrIncorrectReadKey = errors.New("incorrect read key")
ErrInvalidSignature = errors.New("signature is invalid")
ErrIncorrectRoot = errors.New("incorrect root")
ErrIncorrectRecordSequence = errors.New("incorrect prev id of a record")
ErrNoSuchUser = errors.New("no such user")
ErrFailedToDecrypt = errors.New("failed to decrypt key")
ErrUserRemoved = errors.New("user was removed from the document")
ErrDocumentForbidden = errors.New("your user was forbidden access to the document")
ErrUserAlreadyExists = errors.New("user already exists")
ErrNoSuchRecord = errors.New("no such record")
ErrNoSuchInvite = errors.New("no such invite")
ErrOldInvite = errors.New("invite is too old")
ErrInsufficientPermissions = errors.New("insufficient permissions")
ErrNoReadKey = errors.New("acl state doesn't have a read key")
ErrInvalidSignature = errors.New("signature is invalid")
ErrIncorrectRoot = errors.New("incorrect root")
ErrIncorrectRecordSequence = errors.New("incorrect prev id of a record")
)
type UserPermissionPair struct {
@@ -41,71 +36,37 @@ type UserPermissionPair struct {
type AclState struct {
id string
currentReadKeyId string
// userReadKeys is a map recordId -> read key which tells us about every read key
userReadKeys map[string]crypto.SymKey
// userStates is a map pubKey -> state which defines current user state
userStates map[string]AclUserState
// statesAtRecord is a map recordId -> state which define user state at particular record
// probably this can grow rather large at some point, so we can maybe optimise later to have:
// - map pubKey -> []recordIds (where recordIds is an array where such identity permissions were changed)
statesAtRecord map[string][]AclUserState
// inviteKeys is a map recordId -> invite
inviteKeys map[string]crypto.PubKey
// requestRecords is a map recordId -> RequestRecord
requestRecords map[string]RequestRecord
// pendingRequests is a map pubKey -> recordId
pendingRequests map[string]string
key crypto.PrivKey
pubKey crypto.PubKey
keyStore crypto.KeyStorage
totalReadKeys int
userReadKeys map[string]crypto.SymKey
userStates map[string]AclUserState
statesAtRecord map[string][]AclUserState
key crypto.PrivKey
pubKey crypto.PubKey
keyStore crypto.KeyStorage
totalReadKeys int
lastRecordId string
contentValidator ContentValidator
lastRecordId string
}
func newAclStateWithKeys(
id string,
key crypto.PrivKey) (*AclState, error) {
st := &AclState{
id: id,
key: key,
pubKey: key.GetPublic(),
userReadKeys: make(map[string]crypto.SymKey),
userStates: make(map[string]AclUserState),
statesAtRecord: make(map[string][]AclUserState),
inviteKeys: make(map[string]crypto.PubKey),
requestRecords: make(map[string]RequestRecord),
pendingRequests: make(map[string]string),
keyStore: crypto.NewKeyStorage(),
}
st.contentValidator = &contentValidator{
keyStore: st.keyStore,
aclState: st,
}
return st, nil
return &AclState{
id: id,
key: key,
pubKey: key.GetPublic(),
userReadKeys: make(map[string]crypto.SymKey),
userStates: make(map[string]AclUserState),
statesAtRecord: make(map[string][]AclUserState),
}, nil
}
func newAclState(id string) *AclState {
st := &AclState{
id: id,
userReadKeys: make(map[string]crypto.SymKey),
userStates: make(map[string]AclUserState),
statesAtRecord: make(map[string][]AclUserState),
inviteKeys: make(map[string]crypto.PubKey),
requestRecords: make(map[string]RequestRecord),
pendingRequests: make(map[string]string),
keyStore: crypto.NewKeyStorage(),
return &AclState{
id: id,
userReadKeys: make(map[string]crypto.SymKey),
userStates: make(map[string]AclUserState),
statesAtRecord: make(map[string][]AclUserState),
}
st.contentValidator = &contentValidator{
keyStore: st.keyStore,
aclState: st,
}
return st
}
func (st *AclState) Validator() ContentValidator {
return st.contentValidator
}
func (st *AclState) CurrentReadKeyId() string {
@@ -113,7 +74,7 @@ func (st *AclState) CurrentReadKeyId() string {
}
func (st *AclState) CurrentReadKey() (crypto.SymKey, error) {
key, exists := st.userReadKeys[st.CurrentReadKeyId()]
key, exists := st.userReadKeys[st.currentReadKeyId]
if !exists {
return nil, ErrNoReadKey
}
@@ -136,7 +97,7 @@ func (st *AclState) StateAtRecord(id string, pubKey crypto.PubKey) (AclUserState
return perm, nil
}
}
return AclUserState{}, ErrNoSuchAccount
return AclUserState{}, ErrNoSuchUser
}
func (st *AclState) applyRecord(record *AclRecord) (err error) {
@@ -149,18 +110,17 @@ func (st *AclState) applyRecord(record *AclRecord) (err error) {
err = ErrIncorrectRecordSequence
return
}
// if the record is root record
if record.Id == st.id {
err = st.applyRoot(record)
if err != nil {
return
}
st.statesAtRecord[record.Id] = []AclUserState{
st.userStates[mapKeyFromPubKey(record.Identity)],
{PubKey: record.Identity, Permissions: aclrecordproto.AclUserPermissions_Admin},
}
return
}
// if the model is not cached
if record.Model == nil {
aclData := &aclrecordproto.AclData{}
err = proto.Unmarshal(record.Data, aclData)
@@ -169,16 +129,18 @@ func (st *AclState) applyRecord(record *AclRecord) (err error) {
}
record.Model = aclData
}
// applying records contents
err = st.applyChangeData(record)
if err != nil {
return
}
// getting all states for users at record and saving them
// getting all states for users at record
var states []AclUserState
for _, state := range st.userStates {
states = append(states, state)
}
st.statesAtRecord[record.Id] = states
return
}
@@ -194,9 +156,9 @@ func (st *AclState) applyRoot(record *AclRecord) (err error) {
// adding user to the list
userState := AclUserState{
PubKey: record.Identity,
Permissions: AclPermissions(aclrecordproto.AclUserPermissions_Owner),
Permissions: aclrecordproto.AclUserPermissions_Admin,
}
st.currentReadKeyId = record.Id
st.currentReadKeyId = record.ReadKeyId
st.userStates[mapKeyFromPubKey(record.Identity)] = userState
st.totalReadKeys++
return
@@ -219,191 +181,92 @@ func (st *AclState) saveReadKeyFromRoot(record *AclRecord) (err error) {
return
}
}
st.userReadKeys[record.Id] = readKey
return
}
func (st *AclState) applyChangeData(record *AclRecord) (err error) {
defer func() {
if err != nil {
return
}
if record.ReadKeyId != st.currentReadKeyId {
st.totalReadKeys++
st.currentReadKeyId = record.ReadKeyId
}
}()
model := record.Model.(*aclrecordproto.AclData)
if !st.isUserJoin(model) {
// we check signature when we add this to the List, so no need to do it here
if _, exists := st.userStates[mapKeyFromPubKey(record.Identity)]; !exists {
err = ErrNoSuchUser
return
}
// only Admins can do non-user join changes
if !st.HasPermission(record.Identity, aclrecordproto.AclUserPermissions_Admin) {
// TODO: add string encoding
err = fmt.Errorf("user %s must have admin permissions", record.Identity.Account())
return
}
}
for _, ch := range model.GetAclContent() {
if err = st.applyChangeContent(ch, record.Id, record.Identity); err != nil {
if err = st.applyChangeContent(ch, record.Id); err != nil {
log.Info("error while applying changes: %v; ignore", zap.Error(err))
return err
}
}
return nil
}
func (st *AclState) applyChangeContent(ch *aclrecordproto.AclContentValue, recordId string, authorIdentity crypto.PubKey) error {
func (st *AclState) applyChangeContent(ch *aclrecordproto.AclContentValue, recordId string) error {
switch {
case ch.GetPermissionChange() != nil:
return st.applyPermissionChange(ch.GetPermissionChange(), recordId, authorIdentity)
case ch.GetInvite() != nil:
return st.applyInvite(ch.GetInvite(), recordId, authorIdentity)
case ch.GetInviteRevoke() != nil:
return st.applyInviteRevoke(ch.GetInviteRevoke(), recordId, authorIdentity)
case ch.GetRequestJoin() != nil:
return st.applyRequestJoin(ch.GetRequestJoin(), recordId, authorIdentity)
case ch.GetRequestAccept() != nil:
return st.applyRequestAccept(ch.GetRequestAccept(), recordId, authorIdentity)
case ch.GetRequestDecline() != nil:
return st.applyRequestDecline(ch.GetRequestDecline(), recordId, authorIdentity)
case ch.GetAccountRemove() != nil:
return st.applyAccountRemove(ch.GetAccountRemove(), recordId, authorIdentity)
case ch.GetReadKeyChange() != nil:
return st.applyReadKeyChange(ch.GetReadKeyChange(), recordId, authorIdentity)
case ch.GetAccountRequestRemove() != nil:
return st.applyRequestRemove(ch.GetAccountRequestRemove(), recordId, authorIdentity)
case ch.GetUserPermissionChange() != nil:
return st.applyUserPermissionChange(ch.GetUserPermissionChange(), recordId)
case ch.GetUserAdd() != nil:
return st.applyUserAdd(ch.GetUserAdd(), recordId)
case ch.GetUserRemove() != nil:
return st.applyUserRemove(ch.GetUserRemove(), recordId)
case ch.GetUserInvite() != nil:
return st.applyUserInvite(ch.GetUserInvite(), recordId)
case ch.GetUserJoin() != nil:
return st.applyUserJoin(ch.GetUserJoin(), recordId)
default:
return ErrUnexpectedContentType
return fmt.Errorf("unexpected change type: %v", ch)
}
}
func (st *AclState) applyPermissionChange(ch *aclrecordproto.AclAccountPermissionChange, recordId string, authorIdentity crypto.PubKey) error {
func (st *AclState) applyUserPermissionChange(ch *aclrecordproto.AclUserPermissionChange, recordId string) error {
chIdentity, err := st.keyStore.PubKeyFromProto(ch.Identity)
if err != nil {
return err
}
err = st.contentValidator.ValidatePermissionChange(ch, authorIdentity)
if err != nil {
return err
state, exists := st.userStates[mapKeyFromPubKey(chIdentity)]
if !exists {
return ErrNoSuchUser
}
stringKey := mapKeyFromPubKey(chIdentity)
state, _ := st.userStates[stringKey]
state.Permissions = AclPermissions(ch.Permissions)
st.userStates[stringKey] = state
state.Permissions = ch.Permissions
return nil
}
func (st *AclState) applyInvite(ch *aclrecordproto.AclAccountInvite, recordId string, authorIdentity crypto.PubKey) error {
inviteKey, err := st.keyStore.PubKeyFromProto(ch.InviteKey)
if err != nil {
return err
}
err = st.contentValidator.ValidateInvite(ch, authorIdentity)
if err != nil {
return err
}
st.inviteKeys[recordId] = inviteKey
func (st *AclState) applyUserInvite(ch *aclrecordproto.AclUserInvite, recordId string) error {
// TODO: check old code and bring it back :-)
return nil
}
func (st *AclState) applyInviteRevoke(ch *aclrecordproto.AclAccountInviteRevoke, recordId string, authorIdentity crypto.PubKey) error {
err := st.contentValidator.ValidateInviteRevoke(ch, authorIdentity)
if err != nil {
return err
}
delete(st.inviteKeys, ch.InviteRecordId)
func (st *AclState) applyUserJoin(ch *aclrecordproto.AclUserJoin, recordId string) error {
return nil
}
func (st *AclState) applyRequestJoin(ch *aclrecordproto.AclAccountRequestJoin, recordId string, authorIdentity crypto.PubKey) error {
err := st.contentValidator.ValidateRequestJoin(ch, authorIdentity)
if err != nil {
return err
}
st.pendingRequests[mapKeyFromPubKey(authorIdentity)] = recordId
st.requestRecords[recordId] = RequestRecord{
RequestIdentity: authorIdentity,
RequestMetadata: ch.Metadata,
Type: RequestTypeJoin,
}
func (st *AclState) applyUserAdd(ch *aclrecordproto.AclUserAdd, recordId string) error {
return nil
}
func (st *AclState) applyRequestAccept(ch *aclrecordproto.AclAccountRequestAccept, recordId string, authorIdentity crypto.PubKey) error {
err := st.contentValidator.ValidateRequestAccept(ch, authorIdentity)
if err != nil {
return err
}
acceptIdentity, err := st.keyStore.PubKeyFromProto(ch.Identity)
if err != nil {
return err
}
record, _ := st.requestRecords[ch.RequestRecordId]
st.userStates[mapKeyFromPubKey(acceptIdentity)] = AclUserState{
PubKey: acceptIdentity,
Permissions: AclPermissions(ch.Permissions),
RequestMetadata: record.RequestMetadata,
}
delete(st.pendingRequests, mapKeyFromPubKey(st.requestRecords[ch.RequestRecordId].RequestIdentity))
if !st.pubKey.Equals(acceptIdentity) {
return nil
}
for _, key := range ch.EncryptedReadKeys {
decrypted, err := st.key.Decrypt(key.EncryptedReadKey)
if err != nil {
return err
}
sym, err := crypto.UnmarshallAESKey(decrypted)
if err != nil {
return err
}
st.userReadKeys[key.RecordId] = sym
}
return nil
}
func (st *AclState) applyRequestDecline(ch *aclrecordproto.AclAccountRequestDecline, recordId string, authorIdentity crypto.PubKey) error {
err := st.contentValidator.ValidateRequestDecline(ch, authorIdentity)
if err != nil {
return err
}
delete(st.pendingRequests, mapKeyFromPubKey(st.requestRecords[ch.RequestRecordId].RequestIdentity))
delete(st.requestRecords, ch.RequestRecordId)
return nil
}
func (st *AclState) applyRequestRemove(ch *aclrecordproto.AclAccountRequestRemove, recordId string, authorIdentity crypto.PubKey) error {
err := st.contentValidator.ValidateRequestRemove(ch, authorIdentity)
if err != nil {
return err
}
st.requestRecords[recordId] = RequestRecord{
RequestIdentity: authorIdentity,
Type: RequestTypeRemove,
}
st.pendingRequests[mapKeyFromPubKey(authorIdentity)] = recordId
return nil
}
func (st *AclState) applyAccountRemove(ch *aclrecordproto.AclAccountRemove, recordId string, authorIdentity crypto.PubKey) error {
err := st.contentValidator.ValidateAccountRemove(ch, authorIdentity)
if err != nil {
return err
}
for _, rawIdentity := range ch.Identities {
identity, err := st.keyStore.PubKeyFromProto(rawIdentity)
if err != nil {
return err
}
idKey := mapKeyFromPubKey(identity)
delete(st.userStates, idKey)
delete(st.pendingRequests, idKey)
}
return st.updateReadKey(ch.AccountKeys, recordId)
}
func (st *AclState) applyReadKeyChange(ch *aclrecordproto.AclReadKeyChange, recordId string, authorIdentity crypto.PubKey) error {
err := st.contentValidator.ValidateReadKeyChange(ch, authorIdentity)
if err != nil {
return err
}
return st.updateReadKey(ch.AccountKeys, recordId)
}
func (st *AclState) updateReadKey(keys []*aclrecordproto.AclEncryptedReadKey, recordId string) error {
for _, accKey := range keys {
identity, _ := st.keyStore.PubKeyFromProto(accKey.Identity)
if st.pubKey.Equals(identity) {
res, err := st.decryptReadKey(accKey.EncryptedReadKey)
if err != nil {
return err
}
st.userReadKeys[recordId] = res
}
}
st.currentReadKeyId = recordId
func (st *AclState) applyUserRemove(ch *aclrecordproto.AclUserRemove, recordId string) error {
return nil
}
@@ -412,6 +275,7 @@ func (st *AclState) decryptReadKey(msg []byte) (crypto.SymKey, error) {
if err != nil {
return nil, ErrFailedToDecrypt
}
key, err := crypto.UnmarshallAESKey(decrypted)
if err != nil {
return nil, ErrFailedToDecrypt
@@ -419,31 +283,29 @@ func (st *AclState) decryptReadKey(msg []byte) (crypto.SymKey, error) {
return key, nil
}
func (st *AclState) Permissions(identity crypto.PubKey) AclPermissions {
func (st *AclState) HasPermission(identity crypto.PubKey, permission aclrecordproto.AclUserPermissions) bool {
state, exists := st.userStates[mapKeyFromPubKey(identity)]
if !exists {
return AclPermissions(aclrecordproto.AclUserPermissions_None)
return false
}
return state.Permissions
return state.Permissions == permission
}
func (st *AclState) JoinRecords() (records []RequestRecord) {
for _, recId := range st.pendingRequests {
rec := st.requestRecords[recId]
if rec.Type == RequestTypeJoin {
records = append(records, rec)
}
}
return
func (st *AclState) isUserJoin(data *aclrecordproto.AclData) bool {
// if we have a UserJoin, then it should always be the first one applied
return data.GetAclContent() != nil && data.GetAclContent()[0].GetUserJoin() != nil
}
func (st *AclState) RemoveRecords() (records []RequestRecord) {
for _, recId := range st.pendingRequests {
rec := st.requestRecords[recId]
if rec.Type == RequestTypeRemove {
records = append(records, rec)
}
}
func (st *AclState) isUserAdd(data *aclrecordproto.AclData, identity []byte) bool {
return false
}
func (st *AclState) UserStates() map[string]AclUserState {
return st.userStates
}
func (st *AclState) Invite(acceptPubKey []byte) (invite *aclrecordproto.AclUserInvite, err error) {
return
}
@@ -1,8 +1,8 @@
package list
import (
"github.com/anyproto/any-sync/commonspace/object/accountdata"
"github.com/anyproto/any-sync/util/crypto"
"github.com/anytypeio/any-sync/commonspace/object/accountdata"
"github.com/anytypeio/any-sync/util/crypto"
)
type aclStateBuilder struct {
+31 -89
View File
@@ -1,24 +1,20 @@
//go:generate mockgen -destination mock_list/mock_list.go github.com/anyproto/any-sync/commonspace/object/acl/list AclList
//go:generate mockgen -destination mock_list/mock_list.go github.com/anytypeio/any-sync/commonspace/object/acl/list AclList
package list
import (
"context"
"errors"
"fmt"
"github.com/anytypeio/any-sync/commonspace/object/accountdata"
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anytypeio/any-sync/commonspace/object/acl/liststorage"
"github.com/anytypeio/any-sync/util/crypto"
"sync"
"github.com/anyproto/any-sync/commonspace/object/accountdata"
"github.com/anyproto/any-sync/commonspace/object/acl/liststorage"
"github.com/anyproto/any-sync/consensus/consensusproto"
"github.com/anyproto/any-sync/util/crypto"
)
type IterFunc = func(record *AclRecord) (IsContinue bool)
var (
ErrIncorrectCID = errors.New("incorrect CID")
ErrRecordAlreadyExists = errors.New("record already exists")
)
var ErrIncorrectCID = errors.New("incorrect CID")
type RWLocker interface {
sync.Locker
@@ -26,41 +22,26 @@ type RWLocker interface {
RUnlock()
}
type AcceptorVerifier interface {
VerifyAcceptor(rec *consensusproto.RawRecord) (err error)
}
type NoOpAcceptorVerifier struct {
}
func (n NoOpAcceptorVerifier) VerifyAcceptor(rec *consensusproto.RawRecord) (err error) {
return nil
}
type AclList interface {
RWLocker
Id() string
Root() *consensusproto.RawRecordWithId
Root() *aclrecordproto.RawAclRecordWithId
Records() []*AclRecord
AclState() *AclState
IsAfter(first string, second string) (bool, error)
Head() *AclRecord
Get(id string) (*AclRecord, error)
GetIndex(idx int) (*AclRecord, error)
Iterate(iterFunc IterFunc)
IterateFrom(startId string, iterFunc IterFunc)
KeyStorage() crypto.KeyStorage
RecordBuilder() AclRecordBuilder
ValidateRawRecord(record *consensusproto.RawRecord) (err error)
AddRawRecord(rawRec *consensusproto.RawRecordWithId) (err error)
AddRawRecord(rawRec *aclrecordproto.RawAclRecordWithId) (added bool, err error)
Close() (err error)
}
type aclList struct {
root *consensusproto.RawRecordWithId
root *aclrecordproto.RawAclRecordWithId
records []*AclRecord
indexes map[string]int
id string
@@ -74,45 +55,18 @@ type aclList struct {
sync.RWMutex
}
type internalDeps struct {
storage liststorage.ListStorage
keyStorage crypto.KeyStorage
stateBuilder *aclStateBuilder
recordBuilder AclRecordBuilder
acceptorVerifier AcceptorVerifier
}
func BuildAclListWithIdentity(acc *accountdata.AccountKeys, storage liststorage.ListStorage, verifier AcceptorVerifier) (AclList, error) {
func BuildAclListWithIdentity(acc *accountdata.AccountKeys, storage liststorage.ListStorage) (AclList, error) {
builder := newAclStateBuilderWithIdentity(acc)
keyStorage := crypto.NewKeyStorage()
deps := internalDeps{
storage: storage,
keyStorage: keyStorage,
stateBuilder: newAclStateBuilderWithIdentity(acc),
recordBuilder: NewAclRecordBuilder(storage.Id(), keyStorage, acc, verifier),
acceptorVerifier: verifier,
}
return build(deps)
return build(storage.Id(), keyStorage, builder, NewAclRecordBuilder(storage.Id(), keyStorage), storage)
}
func BuildAclList(storage liststorage.ListStorage, verifier AcceptorVerifier) (AclList, error) {
func BuildAclList(storage liststorage.ListStorage) (AclList, error) {
keyStorage := crypto.NewKeyStorage()
deps := internalDeps{
storage: storage,
keyStorage: keyStorage,
stateBuilder: newAclStateBuilder(),
recordBuilder: NewAclRecordBuilder(storage.Id(), keyStorage, nil, verifier),
acceptorVerifier: verifier,
}
return build(deps)
return build(storage.Id(), keyStorage, newAclStateBuilder(), NewAclRecordBuilder(storage.Id(), crypto.NewKeyStorage()), storage)
}
func build(deps internalDeps) (list AclList, err error) {
var (
storage = deps.storage
id = deps.storage.Id()
recBuilder = deps.recordBuilder
stateBuilder = deps.stateBuilder
)
func build(id string, keyStorage crypto.KeyStorage, stateBuilder *aclStateBuilder, recBuilder AclRecordBuilder, storage liststorage.ListStorage) (list AclList, err error) {
head, err := storage.Head()
if err != nil {
return
@@ -123,7 +77,7 @@ func build(deps internalDeps) (list AclList, err error) {
return
}
record, err := recBuilder.UnmarshallWithId(rawRecordWithId)
record, err := recBuilder.Unmarshall(rawRecordWithId)
if err != nil {
return
}
@@ -135,7 +89,7 @@ func build(deps internalDeps) (list AclList, err error) {
return
}
record, err = recBuilder.UnmarshallWithId(rawRecordWithId)
record, err = recBuilder.Unmarshall(rawRecordWithId)
if err != nil {
return
}
@@ -165,7 +119,6 @@ func build(deps internalDeps) (list AclList, err error) {
return
}
recBuilder.(*aclRecordBuilder).state = state
list = &aclList{
root: rootWithId,
records: records,
@@ -179,27 +132,15 @@ func build(deps internalDeps) (list AclList, err error) {
return
}
func (a *aclList) RecordBuilder() AclRecordBuilder {
return a.recordBuilder
}
func (a *aclList) Records() []*AclRecord {
return a.records
}
func (a *aclList) ValidateRawRecord(rawRec *consensusproto.RawRecord) (err error) {
record, err := a.recordBuilder.Unmarshall(rawRec)
if err != nil {
func (a *aclList) AddRawRecord(rawRec *aclrecordproto.RawAclRecordWithId) (added bool, err error) {
if _, ok := a.indexes[rawRec.Id]; ok {
return
}
return a.aclState.Validator().ValidateAclRecordContents(record)
}
func (a *aclList) AddRawRecord(rawRec *consensusproto.RawRecordWithId) (err error) {
if _, ok := a.indexes[rawRec.Id]; ok {
return ErrRecordAlreadyExists
}
record, err := a.recordBuilder.UnmarshallWithId(rawRec)
record, err := a.recordBuilder.Unmarshall(rawRec)
if err != nil {
return
}
@@ -214,6 +155,15 @@ func (a *aclList) AddRawRecord(rawRec *consensusproto.RawRecordWithId) (err erro
if err = a.storage.SetHead(rawRec.Id); err != nil {
return
}
return true, nil
}
func (a *aclList) IsValidNext(rawRec *aclrecordproto.RawAclRecordWithId) (err error) {
_, err = a.recordBuilder.Unmarshall(rawRec)
if err != nil {
return
}
// TODO: change state and add "check" method for records
return
}
@@ -221,7 +171,7 @@ func (a *aclList) Id() string {
return a.id
}
func (a *aclList) Root() *consensusproto.RawRecordWithId {
func (a *aclList) Root() *aclrecordproto.RawAclRecordWithId {
return a.root
}
@@ -249,19 +199,11 @@ func (a *aclList) Head() *AclRecord {
func (a *aclList) Get(id string) (*AclRecord, error) {
recIdx, ok := a.indexes[id]
if !ok {
return nil, ErrNoSuchRecord
return nil, fmt.Errorf("no such record")
}
return a.records[recIdx], nil
}
func (a *aclList) GetIndex(idx int) (*AclRecord, error) {
// TODO: when we add snapshots we will have to monitor record num in snapshots
if idx < 0 || idx >= len(a.records) {
return nil, ErrNoSuchRecord
}
return a.records[idx], nil
}
func (a *aclList) Iterate(iterFunc IterFunc) {
for _, rec := range a.records {
if !iterFunc(rec) {
+2 -295
View File
@@ -2,114 +2,11 @@ package list
import (
"fmt"
"testing"
"github.com/anyproto/any-sync/commonspace/object/accountdata"
"github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anyproto/any-sync/consensus/consensusproto"
"github.com/anyproto/any-sync/util/cidutil"
"github.com/anyproto/any-sync/util/crypto"
"github.com/anytypeio/any-sync/commonspace/object/accountdata"
"github.com/stretchr/testify/require"
"testing"
)
func wrapRecord(rawRec *consensusproto.RawRecord) *consensusproto.RawRecordWithId {
payload, err := rawRec.Marshal()
if err != nil {
panic(err)
}
id, err := cidutil.NewCidFromBytes(payload)
if err != nil {
panic(err)
}
return &consensusproto.RawRecordWithId{
Payload: payload,
Id: id,
}
}
type aclFixture struct {
ownerKeys *accountdata.AccountKeys
accountKeys *accountdata.AccountKeys
ownerAcl *aclList
accountAcl *aclList
spaceId string
}
func newFixture(t *testing.T) *aclFixture {
ownerKeys, err := accountdata.NewRandom()
require.NoError(t, err)
accountKeys, err := accountdata.NewRandom()
require.NoError(t, err)
spaceId := "spaceId"
ownerAcl, err := NewTestDerivedAcl(spaceId, ownerKeys)
require.NoError(t, err)
accountAcl, err := NewTestAclWithRoot(accountKeys, ownerAcl.Root())
require.NoError(t, err)
return &aclFixture{
ownerKeys: ownerKeys,
accountKeys: accountKeys,
ownerAcl: ownerAcl.(*aclList),
accountAcl: accountAcl.(*aclList),
spaceId: spaceId,
}
}
func (fx *aclFixture) addRec(t *testing.T, rec *consensusproto.RawRecordWithId) {
err := fx.ownerAcl.AddRawRecord(rec)
require.NoError(t, err)
err = fx.accountAcl.AddRawRecord(rec)
require.NoError(t, err)
}
func (fx *aclFixture) inviteAccount(t *testing.T, perms AclPermissions) {
var (
ownerAcl = fx.ownerAcl
ownerState = fx.ownerAcl.aclState
accountAcl = fx.accountAcl
accountState = fx.accountAcl.aclState
)
// building invite
inv, err := ownerAcl.RecordBuilder().BuildInvite()
require.NoError(t, err)
inviteRec := wrapRecord(inv.InviteRec)
fx.addRec(t, inviteRec)
// building request join
requestJoin, err := accountAcl.RecordBuilder().BuildRequestJoin(RequestJoinPayload{
InviteRecordId: inviteRec.Id,
InviteKey: inv.InviteKey,
})
require.NoError(t, err)
requestJoinRec := wrapRecord(requestJoin)
fx.addRec(t, requestJoinRec)
// building request accept
requestAccept, err := ownerAcl.RecordBuilder().BuildRequestAccept(RequestAcceptPayload{
RequestRecordId: requestJoinRec.Id,
Permissions: perms,
})
require.NoError(t, err)
// validate
err = ownerAcl.ValidateRawRecord(requestAccept)
require.NoError(t, err)
requestAcceptRec := wrapRecord(requestAccept)
fx.addRec(t, requestAcceptRec)
// checking acl state
require.True(t, ownerState.Permissions(ownerState.pubKey).IsOwner())
require.True(t, ownerState.Permissions(accountState.pubKey).CanWrite())
require.Equal(t, 0, len(ownerState.pendingRequests))
require.Equal(t, 0, len(accountState.pendingRequests))
require.True(t, accountState.Permissions(ownerState.pubKey).IsOwner())
require.True(t, accountState.Permissions(accountState.pubKey).CanWrite())
_, err = ownerState.StateAtRecord(requestJoinRec.Id, accountState.pubKey)
require.Equal(t, ErrNoSuchAccount, err)
stateAtRec, err := ownerState.StateAtRecord(requestAcceptRec.Id, accountState.pubKey)
require.NoError(t, err)
require.True(t, stateAtRec.Permissions == perms)
}
func TestAclList_BuildRoot(t *testing.T) {
randomKeys, err := accountdata.NewRandom()
require.NoError(t, err)
@@ -117,193 +14,3 @@ func TestAclList_BuildRoot(t *testing.T) {
require.NoError(t, err)
fmt.Println(randomAcl.Id())
}
func TestAclList_InvitePipeline(t *testing.T) {
fx := newFixture(t)
fx.inviteAccount(t, AclPermissions(aclrecordproto.AclUserPermissions_Writer))
}
func TestAclList_InviteRevoke(t *testing.T) {
fx := newFixture(t)
var (
ownerState = fx.ownerAcl.aclState
accountState = fx.accountAcl.aclState
)
// building invite
inv, err := fx.ownerAcl.RecordBuilder().BuildInvite()
require.NoError(t, err)
inviteRec := wrapRecord(inv.InviteRec)
fx.addRec(t, inviteRec)
// building invite revoke
inviteRevoke, err := fx.ownerAcl.RecordBuilder().BuildInviteRevoke(ownerState.lastRecordId)
require.NoError(t, err)
inviteRevokeRec := wrapRecord(inviteRevoke)
fx.addRec(t, inviteRevokeRec)
// checking acl state
require.True(t, ownerState.Permissions(ownerState.pubKey).IsOwner())
require.True(t, ownerState.Permissions(accountState.pubKey).NoPermissions())
require.Empty(t, ownerState.inviteKeys)
require.Empty(t, accountState.inviteKeys)
}
func TestAclList_RequestDecline(t *testing.T) {
fx := newFixture(t)
var (
ownerAcl = fx.ownerAcl
ownerState = fx.ownerAcl.aclState
accountAcl = fx.accountAcl
accountState = fx.accountAcl.aclState
)
// building invite
inv, err := ownerAcl.RecordBuilder().BuildInvite()
require.NoError(t, err)
inviteRec := wrapRecord(inv.InviteRec)
fx.addRec(t, inviteRec)
// building request join
requestJoin, err := accountAcl.RecordBuilder().BuildRequestJoin(RequestJoinPayload{
InviteRecordId: inviteRec.Id,
InviteKey: inv.InviteKey,
})
require.NoError(t, err)
requestJoinRec := wrapRecord(requestJoin)
fx.addRec(t, requestJoinRec)
// building request decline
requestDecline, err := ownerAcl.RecordBuilder().BuildRequestDecline(ownerState.lastRecordId)
require.NoError(t, err)
requestDeclineRec := wrapRecord(requestDecline)
fx.addRec(t, requestDeclineRec)
// checking acl state
require.True(t, ownerState.Permissions(ownerState.pubKey).IsOwner())
require.True(t, ownerState.Permissions(accountState.pubKey).NoPermissions())
require.Empty(t, ownerState.pendingRequests)
require.Empty(t, accountState.pendingRequests)
}
func TestAclList_Remove(t *testing.T) {
fx := newFixture(t)
var (
ownerState = fx.ownerAcl.aclState
accountState = fx.accountAcl.aclState
)
fx.inviteAccount(t, AclPermissions(aclrecordproto.AclUserPermissions_Writer))
newReadKey := crypto.NewAES()
remove, err := fx.ownerAcl.RecordBuilder().BuildAccountRemove(AccountRemovePayload{
Identities: []crypto.PubKey{fx.accountKeys.SignKey.GetPublic()},
ReadKey: newReadKey,
})
require.NoError(t, err)
removeRec := wrapRecord(remove)
fx.addRec(t, removeRec)
// checking acl state
require.True(t, ownerState.Permissions(ownerState.pubKey).IsOwner())
require.True(t, ownerState.Permissions(accountState.pubKey).NoPermissions())
require.True(t, ownerState.userReadKeys[removeRec.Id].Equals(newReadKey))
require.NotNil(t, ownerState.userReadKeys[fx.ownerAcl.Id()])
require.Equal(t, 0, len(ownerState.pendingRequests))
require.Equal(t, 0, len(accountState.pendingRequests))
require.True(t, accountState.Permissions(ownerState.pubKey).IsOwner())
require.True(t, accountState.Permissions(accountState.pubKey).NoPermissions())
require.Nil(t, accountState.userReadKeys[removeRec.Id])
require.NotNil(t, accountState.userReadKeys[fx.ownerAcl.Id()])
}
func TestAclList_ReadKeyChange(t *testing.T) {
fx := newFixture(t)
var (
ownerState = fx.ownerAcl.aclState
accountState = fx.accountAcl.aclState
)
fx.inviteAccount(t, AclPermissions(aclrecordproto.AclUserPermissions_Admin))
newReadKey := crypto.NewAES()
readKeyChange, err := fx.ownerAcl.RecordBuilder().BuildReadKeyChange(newReadKey)
require.NoError(t, err)
readKeyRec := wrapRecord(readKeyChange)
fx.addRec(t, readKeyRec)
// checking acl state
require.True(t, ownerState.Permissions(ownerState.pubKey).IsOwner())
require.True(t, ownerState.Permissions(accountState.pubKey).CanManageAccounts())
require.True(t, ownerState.userReadKeys[readKeyRec.Id].Equals(newReadKey))
require.True(t, accountState.userReadKeys[readKeyRec.Id].Equals(newReadKey))
require.NotNil(t, ownerState.userReadKeys[fx.ownerAcl.Id()])
require.NotNil(t, accountState.userReadKeys[fx.ownerAcl.Id()])
readKey, err := ownerState.CurrentReadKey()
require.NoError(t, err)
require.True(t, newReadKey.Equals(readKey))
require.Equal(t, 0, len(ownerState.pendingRequests))
require.Equal(t, 0, len(accountState.pendingRequests))
}
func TestAclList_PermissionChange(t *testing.T) {
fx := newFixture(t)
var (
ownerState = fx.ownerAcl.aclState
accountState = fx.accountAcl.aclState
)
fx.inviteAccount(t, AclPermissions(aclrecordproto.AclUserPermissions_Admin))
permissionChange, err := fx.ownerAcl.RecordBuilder().BuildPermissionChange(PermissionChangePayload{
Identity: fx.accountKeys.SignKey.GetPublic(),
Permissions: AclPermissions(aclrecordproto.AclUserPermissions_Writer),
})
require.NoError(t, err)
permissionChangeRec := wrapRecord(permissionChange)
fx.addRec(t, permissionChangeRec)
// checking acl state
require.True(t, ownerState.Permissions(ownerState.pubKey).IsOwner())
require.True(t, ownerState.Permissions(accountState.pubKey) == AclPermissions(aclrecordproto.AclUserPermissions_Writer))
require.True(t, accountState.Permissions(ownerState.pubKey).IsOwner())
require.True(t, accountState.Permissions(accountState.pubKey) == AclPermissions(aclrecordproto.AclUserPermissions_Writer))
require.NotNil(t, ownerState.userReadKeys[fx.ownerAcl.Id()])
require.NotNil(t, accountState.userReadKeys[fx.ownerAcl.Id()])
require.Equal(t, 0, len(ownerState.pendingRequests))
require.Equal(t, 0, len(accountState.pendingRequests))
}
func TestAclList_RequestRemove(t *testing.T) {
fx := newFixture(t)
var (
ownerState = fx.ownerAcl.aclState
accountState = fx.accountAcl.aclState
)
fx.inviteAccount(t, AclPermissions(aclrecordproto.AclUserPermissions_Writer))
removeRequest, err := fx.accountAcl.RecordBuilder().BuildRequestRemove()
require.NoError(t, err)
removeRequestRec := wrapRecord(removeRequest)
fx.addRec(t, removeRequestRec)
recs := fx.accountAcl.AclState().RemoveRecords()
require.Len(t, recs, 1)
require.True(t, accountState.pubKey.Equals(recs[0].RequestIdentity))
newReadKey := crypto.NewAES()
remove, err := fx.ownerAcl.RecordBuilder().BuildAccountRemove(AccountRemovePayload{
Identities: []crypto.PubKey{recs[0].RequestIdentity},
ReadKey: newReadKey,
})
require.NoError(t, err)
removeRec := wrapRecord(remove)
fx.addRec(t, removeRec)
// checking acl state
require.True(t, ownerState.Permissions(ownerState.pubKey).IsOwner())
require.True(t, ownerState.Permissions(accountState.pubKey).NoPermissions())
require.True(t, ownerState.userReadKeys[removeRec.Id].Equals(newReadKey))
require.NotNil(t, ownerState.userReadKeys[fx.ownerAcl.Id()])
require.Equal(t, 0, len(ownerState.pendingRequests))
require.Equal(t, 0, len(accountState.pendingRequests))
require.True(t, accountState.Permissions(ownerState.pubKey).IsOwner())
require.True(t, accountState.Permissions(accountState.pubKey).NoPermissions())
require.Nil(t, accountState.userReadKeys[removeRec.Id])
require.NotNil(t, accountState.userReadKeys[fx.ownerAcl.Id()])
}
+7 -17
View File
@@ -1,14 +1,14 @@
package list
import (
"github.com/anyproto/any-sync/commonspace/object/accountdata"
"github.com/anyproto/any-sync/commonspace/object/acl/liststorage"
"github.com/anyproto/any-sync/consensus/consensusproto"
"github.com/anyproto/any-sync/util/crypto"
"github.com/anytypeio/any-sync/commonspace/object/accountdata"
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anytypeio/any-sync/commonspace/object/acl/liststorage"
"github.com/anytypeio/any-sync/util/crypto"
)
func NewTestDerivedAcl(spaceId string, keys *accountdata.AccountKeys) (AclList, error) {
builder := NewAclRecordBuilder("", crypto.NewKeyStorage(), keys, NoOpAcceptorVerifier{})
builder := NewAclRecordBuilder("", crypto.NewKeyStorage())
masterKey, _, err := crypto.GenerateRandomEd25519KeyPair()
if err != nil {
return nil, err
@@ -21,21 +21,11 @@ func NewTestDerivedAcl(spaceId string, keys *accountdata.AccountKeys) (AclList,
if err != nil {
return nil, err
}
st, err := liststorage.NewInMemoryAclListStorage(root.Id, []*consensusproto.RawRecordWithId{
st, err := liststorage.NewInMemoryAclListStorage(root.Id, []*aclrecordproto.RawAclRecordWithId{
root,
})
if err != nil {
return nil, err
}
return BuildAclListWithIdentity(keys, st, NoOpAcceptorVerifier{})
}
func NewTestAclWithRoot(keys *accountdata.AccountKeys, root *consensusproto.RawRecordWithId) (AclList, error) {
st, err := liststorage.NewInMemoryAclListStorage(root.Id, []*consensusproto.RawRecordWithId{
root,
})
if err != nil {
return nil, err
}
return BuildAclListWithIdentity(keys, st, NoOpAcceptorVerifier{})
return BuildAclListWithIdentity(keys, st)
}
@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/anyproto/any-sync/commonspace/object/acl/list (interfaces: AclList)
// Source: github.com/anytypeio/any-sync/commonspace/object/acl/list (interfaces: AclList)
// Package mock_list is a generated GoMock package.
package mock_list
@@ -7,10 +7,10 @@ package mock_list
import (
reflect "reflect"
list "github.com/anyproto/any-sync/commonspace/object/acl/list"
consensusproto "github.com/anyproto/any-sync/consensus/consensusproto"
crypto "github.com/anyproto/any-sync/util/crypto"
gomock "go.uber.org/mock/gomock"
aclrecordproto "github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
list "github.com/anytypeio/any-sync/commonspace/object/acl/list"
crypto "github.com/anytypeio/any-sync/util/crypto"
gomock "github.com/golang/mock/gomock"
)
// MockAclList is a mock of AclList interface.
@@ -51,11 +51,12 @@ func (mr *MockAclListMockRecorder) AclState() *gomock.Call {
}
// AddRawRecord mocks base method.
func (m *MockAclList) AddRawRecord(arg0 *consensusproto.RawRecordWithId) error {
func (m *MockAclList) AddRawRecord(arg0 *aclrecordproto.RawAclRecordWithId) (bool, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AddRawRecord", arg0)
ret0, _ := ret[0].(error)
return ret0
ret0, _ := ret[0].(bool)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// AddRawRecord indicates an expected call of AddRawRecord.
@@ -93,21 +94,6 @@ func (mr *MockAclListMockRecorder) Get(arg0 interface{}) *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockAclList)(nil).Get), arg0)
}
// GetIndex mocks base method.
func (m *MockAclList) GetIndex(arg0 int) (*list.AclRecord, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetIndex", arg0)
ret0, _ := ret[0].(*list.AclRecord)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetIndex indicates an expected call of GetIndex.
func (mr *MockAclListMockRecorder) GetIndex(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetIndex", reflect.TypeOf((*MockAclList)(nil).GetIndex), arg0)
}
// Head mocks base method.
func (m *MockAclList) Head() *list.AclRecord {
m.ctrl.T.Helper()
@@ -225,20 +211,6 @@ func (mr *MockAclListMockRecorder) RUnlock() *gomock.Call {
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RUnlock", reflect.TypeOf((*MockAclList)(nil).RUnlock))
}
// RecordBuilder mocks base method.
func (m *MockAclList) RecordBuilder() list.AclRecordBuilder {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RecordBuilder")
ret0, _ := ret[0].(list.AclRecordBuilder)
return ret0
}
// RecordBuilder indicates an expected call of RecordBuilder.
func (mr *MockAclListMockRecorder) RecordBuilder() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordBuilder", reflect.TypeOf((*MockAclList)(nil).RecordBuilder))
}
// Records mocks base method.
func (m *MockAclList) Records() []*list.AclRecord {
m.ctrl.T.Helper()
@@ -254,10 +226,10 @@ func (mr *MockAclListMockRecorder) Records() *gomock.Call {
}
// Root mocks base method.
func (m *MockAclList) Root() *consensusproto.RawRecordWithId {
func (m *MockAclList) Root() *aclrecordproto.RawAclRecordWithId {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Root")
ret0, _ := ret[0].(*consensusproto.RawRecordWithId)
ret0, _ := ret[0].(*aclrecordproto.RawAclRecordWithId)
return ret0
}
@@ -278,17 +250,3 @@ func (mr *MockAclListMockRecorder) Unlock() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unlock", reflect.TypeOf((*MockAclList)(nil).Unlock))
}
// ValidateRawRecord mocks base method.
func (m *MockAclList) ValidateRawRecord(arg0 *consensusproto.RawRecord) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ValidateRawRecord", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// ValidateRawRecord indicates an expected call of ValidateRawRecord.
func (mr *MockAclListMockRecorder) ValidateRawRecord(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateRawRecord", reflect.TypeOf((*MockAclList)(nil).ValidateRawRecord), arg0)
}
+5 -52
View File
@@ -1,13 +1,14 @@
package list
import (
"github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anyproto/any-sync/util/crypto"
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anytypeio/any-sync/util/crypto"
)
type AclRecord struct {
Id string
PrevId string
ReadKeyId string
Timestamp int64
Data []byte
Identity crypto.PubKey
@@ -15,55 +16,7 @@ type AclRecord struct {
Signature []byte
}
type RequestRecord struct {
RequestIdentity crypto.PubKey
RequestMetadata []byte
Type RequestType
}
type AclUserState struct {
PubKey crypto.PubKey
Permissions AclPermissions
RequestMetadata []byte
}
type RequestType int
const (
RequestTypeRemove RequestType = iota
RequestTypeJoin
)
type AclPermissions aclrecordproto.AclUserPermissions
func (p AclPermissions) NoPermissions() bool {
return aclrecordproto.AclUserPermissions(p) == aclrecordproto.AclUserPermissions_None
}
func (p AclPermissions) IsOwner() bool {
return aclrecordproto.AclUserPermissions(p) == aclrecordproto.AclUserPermissions_Owner
}
func (p AclPermissions) CanWrite() bool {
switch aclrecordproto.AclUserPermissions(p) {
case aclrecordproto.AclUserPermissions_Admin:
return true
case aclrecordproto.AclUserPermissions_Writer:
return true
case aclrecordproto.AclUserPermissions_Owner:
return true
default:
return false
}
}
func (p AclPermissions) CanManageAccounts() bool {
switch aclrecordproto.AclUserPermissions(p) {
case aclrecordproto.AclUserPermissions_Admin:
return true
case aclrecordproto.AclUserPermissions_Owner:
return true
default:
return false
}
PubKey crypto.PubKey
Permissions aclrecordproto.AclUserPermissions
}
-218
View File
@@ -1,218 +0,0 @@
package list
import (
"github.com/anyproto/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anyproto/any-sync/util/crypto"
)
type ContentValidator interface {
ValidateAclRecordContents(ch *AclRecord) (err error)
ValidatePermissionChange(ch *aclrecordproto.AclAccountPermissionChange, authorIdentity crypto.PubKey) (err error)
ValidateInvite(ch *aclrecordproto.AclAccountInvite, authorIdentity crypto.PubKey) (err error)
ValidateInviteRevoke(ch *aclrecordproto.AclAccountInviteRevoke, authorIdentity crypto.PubKey) (err error)
ValidateRequestJoin(ch *aclrecordproto.AclAccountRequestJoin, authorIdentity crypto.PubKey) (err error)
ValidateRequestAccept(ch *aclrecordproto.AclAccountRequestAccept, authorIdentity crypto.PubKey) (err error)
ValidateRequestDecline(ch *aclrecordproto.AclAccountRequestDecline, authorIdentity crypto.PubKey) (err error)
ValidateAccountRemove(ch *aclrecordproto.AclAccountRemove, authorIdentity crypto.PubKey) (err error)
ValidateRequestRemove(ch *aclrecordproto.AclAccountRequestRemove, authorIdentity crypto.PubKey) (err error)
ValidateReadKeyChange(ch *aclrecordproto.AclReadKeyChange, authorIdentity crypto.PubKey) (err error)
}
type contentValidator struct {
keyStore crypto.KeyStorage
aclState *AclState
}
func (c *contentValidator) ValidateAclRecordContents(ch *AclRecord) (err error) {
if ch.PrevId != c.aclState.lastRecordId {
return ErrIncorrectRecordSequence
}
aclData := ch.Model.(*aclrecordproto.AclData)
for _, content := range aclData.AclContent {
err = c.validateAclRecordContent(content, ch.Identity)
if err != nil {
return
}
}
return
}
func (c *contentValidator) validateAclRecordContent(ch *aclrecordproto.AclContentValue, authorIdentity crypto.PubKey) (err error) {
switch {
case ch.GetPermissionChange() != nil:
return c.ValidatePermissionChange(ch.GetPermissionChange(), authorIdentity)
case ch.GetInvite() != nil:
return c.ValidateInvite(ch.GetInvite(), authorIdentity)
case ch.GetInviteRevoke() != nil:
return c.ValidateInviteRevoke(ch.GetInviteRevoke(), authorIdentity)
case ch.GetRequestJoin() != nil:
return c.ValidateRequestJoin(ch.GetRequestJoin(), authorIdentity)
case ch.GetRequestAccept() != nil:
return c.ValidateRequestAccept(ch.GetRequestAccept(), authorIdentity)
case ch.GetRequestDecline() != nil:
return c.ValidateRequestDecline(ch.GetRequestDecline(), authorIdentity)
case ch.GetAccountRemove() != nil:
return c.ValidateAccountRemove(ch.GetAccountRemove(), authorIdentity)
case ch.GetAccountRequestRemove() != nil:
return c.ValidateRequestRemove(ch.GetAccountRequestRemove(), authorIdentity)
case ch.GetReadKeyChange() != nil:
return c.ValidateReadKeyChange(ch.GetReadKeyChange(), authorIdentity)
default:
return ErrUnexpectedContentType
}
}
func (c *contentValidator) ValidatePermissionChange(ch *aclrecordproto.AclAccountPermissionChange, authorIdentity crypto.PubKey) (err error) {
if !c.aclState.Permissions(authorIdentity).CanManageAccounts() {
return ErrInsufficientPermissions
}
chIdentity, err := c.keyStore.PubKeyFromProto(ch.Identity)
if err != nil {
return err
}
_, exists := c.aclState.userStates[mapKeyFromPubKey(chIdentity)]
if !exists {
return ErrNoSuchAccount
}
return
}
func (c *contentValidator) ValidateInvite(ch *aclrecordproto.AclAccountInvite, authorIdentity crypto.PubKey) (err error) {
if !c.aclState.Permissions(authorIdentity).CanManageAccounts() {
return ErrInsufficientPermissions
}
_, err = c.keyStore.PubKeyFromProto(ch.InviteKey)
return
}
func (c *contentValidator) ValidateInviteRevoke(ch *aclrecordproto.AclAccountInviteRevoke, authorIdentity crypto.PubKey) (err error) {
if !c.aclState.Permissions(authorIdentity).CanManageAccounts() {
return ErrInsufficientPermissions
}
_, exists := c.aclState.inviteKeys[ch.InviteRecordId]
if !exists {
return ErrNoSuchInvite
}
return
}
func (c *contentValidator) ValidateRequestJoin(ch *aclrecordproto.AclAccountRequestJoin, authorIdentity crypto.PubKey) (err error) {
inviteKey, exists := c.aclState.inviteKeys[ch.InviteRecordId]
if !exists {
return ErrNoSuchInvite
}
inviteIdentity, err := c.keyStore.PubKeyFromProto(ch.InviteIdentity)
if err != nil {
return
}
if _, exists := c.aclState.pendingRequests[mapKeyFromPubKey(inviteIdentity)]; exists {
return ErrPendingRequest
}
if !authorIdentity.Equals(inviteIdentity) {
return ErrIncorrectIdentity
}
rawInviteIdentity, err := inviteIdentity.Raw()
if err != nil {
return err
}
ok, err := inviteKey.Verify(rawInviteIdentity, ch.InviteIdentitySignature)
if err != nil {
return ErrInvalidSignature
}
if !ok {
return ErrInvalidSignature
}
return
}
func (c *contentValidator) ValidateRequestAccept(ch *aclrecordproto.AclAccountRequestAccept, authorIdentity crypto.PubKey) (err error) {
if !c.aclState.Permissions(authorIdentity).CanManageAccounts() {
return ErrInsufficientPermissions
}
record, exists := c.aclState.requestRecords[ch.RequestRecordId]
if !exists {
return ErrNoSuchRequest
}
acceptIdentity, err := c.keyStore.PubKeyFromProto(ch.Identity)
if err != nil {
return
}
if !acceptIdentity.Equals(record.RequestIdentity) {
return ErrIncorrectIdentity
}
if ch.Permissions == aclrecordproto.AclUserPermissions_Owner {
return ErrInsufficientPermissions
}
return
}
func (c *contentValidator) ValidateRequestDecline(ch *aclrecordproto.AclAccountRequestDecline, authorIdentity crypto.PubKey) (err error) {
if !c.aclState.Permissions(authorIdentity).CanManageAccounts() {
return ErrInsufficientPermissions
}
_, exists := c.aclState.requestRecords[ch.RequestRecordId]
if !exists {
return ErrNoSuchRequest
}
return
}
func (c *contentValidator) ValidateAccountRemove(ch *aclrecordproto.AclAccountRemove, authorIdentity crypto.PubKey) (err error) {
if !c.aclState.Permissions(authorIdentity).CanManageAccounts() {
return ErrInsufficientPermissions
}
seenIdentities := map[string]struct{}{}
for _, rawIdentity := range ch.Identities {
identity, err := c.keyStore.PubKeyFromProto(rawIdentity)
if err != nil {
return err
}
if identity.Equals(authorIdentity) {
return ErrInsufficientPermissions
}
permissions := c.aclState.Permissions(identity)
if permissions.NoPermissions() {
return ErrNoSuchAccount
}
if permissions.IsOwner() {
return ErrInsufficientPermissions
}
idKey := mapKeyFromPubKey(identity)
if _, exists := seenIdentities[idKey]; exists {
return ErrDuplicateAccounts
}
seenIdentities[mapKeyFromPubKey(identity)] = struct{}{}
}
return c.validateAccountReadKeys(ch.AccountKeys, len(c.aclState.userStates)-len(ch.Identities))
}
func (c *contentValidator) ValidateRequestRemove(ch *aclrecordproto.AclAccountRequestRemove, authorIdentity crypto.PubKey) (err error) {
if c.aclState.Permissions(authorIdentity).NoPermissions() {
return ErrInsufficientPermissions
}
if _, exists := c.aclState.pendingRequests[mapKeyFromPubKey(authorIdentity)]; exists {
return ErrPendingRequest
}
return
}
func (c *contentValidator) ValidateReadKeyChange(ch *aclrecordproto.AclReadKeyChange, authorIdentity crypto.PubKey) (err error) {
return c.validateAccountReadKeys(ch.AccountKeys, len(c.aclState.userStates))
}
func (c *contentValidator) validateAccountReadKeys(accountKeys []*aclrecordproto.AclEncryptedReadKey, usersNum int) (err error) {
if len(accountKeys) != usersNum {
return ErrIncorrectNumberOfAccounts
}
for _, encKeys := range accountKeys {
identity, err := c.keyStore.PubKeyFromProto(encKeys.Identity)
if err != nil {
return err
}
_, exists := c.aclState.userStates[mapKeyFromPubKey(identity)]
if !exists {
return ErrNoSuchAccount
}
}
return
}
+8 -10
View File
@@ -3,26 +3,24 @@ package liststorage
import (
"context"
"fmt"
"github.com/anyproto/any-sync/consensus/consensusproto"
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
"sync"
)
type inMemoryAclListStorage struct {
id string
root *consensusproto.RawRecordWithId
root *aclrecordproto.RawAclRecordWithId
head string
records map[string]*consensusproto.RawRecordWithId
records map[string]*aclrecordproto.RawAclRecordWithId
sync.RWMutex
}
func NewInMemoryAclListStorage(
id string,
records []*consensusproto.RawRecordWithId) (ListStorage, error) {
records []*aclrecordproto.RawAclRecordWithId) (ListStorage, error) {
allRecords := make(map[string]*consensusproto.RawRecordWithId)
allRecords := make(map[string]*aclrecordproto.RawAclRecordWithId)
for _, ch := range records {
allRecords[ch.Id] = ch
}
@@ -43,7 +41,7 @@ func (t *inMemoryAclListStorage) Id() string {
return t.id
}
func (t *inMemoryAclListStorage) Root() (*consensusproto.RawRecordWithId, error) {
func (t *inMemoryAclListStorage) Root() (*aclrecordproto.RawAclRecordWithId, error) {
t.RLock()
defer t.RUnlock()
return t.root, nil
@@ -62,7 +60,7 @@ func (t *inMemoryAclListStorage) SetHead(head string) error {
return nil
}
func (t *inMemoryAclListStorage) AddRawRecord(ctx context.Context, record *consensusproto.RawRecordWithId) error {
func (t *inMemoryAclListStorage) AddRawRecord(ctx context.Context, record *aclrecordproto.RawAclRecordWithId) error {
t.Lock()
defer t.Unlock()
// TODO: better to do deep copy
@@ -70,7 +68,7 @@ func (t *inMemoryAclListStorage) AddRawRecord(ctx context.Context, record *conse
return nil
}
func (t *inMemoryAclListStorage) GetRawRecord(ctx context.Context, recordId string) (*consensusproto.RawRecordWithId, error) {
func (t *inMemoryAclListStorage) GetRawRecord(ctx context.Context, recordId string) (*aclrecordproto.RawAclRecordWithId, error) {
t.RLock()
defer t.RUnlock()
if res, exists := t.records[recordId]; exists {
@@ -1,11 +1,10 @@
//go:generate mockgen -destination mock_liststorage/mock_liststorage.go github.com/anyproto/any-sync/commonspace/object/acl/liststorage ListStorage
//go:generate mockgen -destination mock_liststorage/mock_liststorage.go github.com/anytypeio/any-sync/commonspace/object/acl/liststorage ListStorage
package liststorage
import (
"context"
"errors"
"github.com/anyproto/any-sync/consensus/consensusproto"
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
)
var (
@@ -15,15 +14,15 @@ var (
)
type Exporter interface {
ListStorage(root *consensusproto.RawRecordWithId) (ListStorage, error)
ListStorage(root *aclrecordproto.RawAclRecordWithId) (ListStorage, error)
}
type ListStorage interface {
Id() string
Root() (*consensusproto.RawRecordWithId, error)
Root() (*aclrecordproto.RawAclRecordWithId, error)
Head() (string, error)
SetHead(headId string) error
GetRawRecord(ctx context.Context, id string) (*consensusproto.RawRecordWithId, error)
AddRawRecord(ctx context.Context, rec *consensusproto.RawRecordWithId) error
GetRawRecord(ctx context.Context, id string) (*aclrecordproto.RawAclRecordWithId, error)
AddRawRecord(ctx context.Context, rec *aclrecordproto.RawAclRecordWithId) error
}
@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/anyproto/any-sync/commonspace/object/acl/liststorage (interfaces: ListStorage)
// Source: github.com/anytypeio/any-sync/commonspace/object/acl/liststorage (interfaces: ListStorage)
// Package mock_liststorage is a generated GoMock package.
package mock_liststorage
@@ -8,8 +8,8 @@ import (
context "context"
reflect "reflect"
consensusproto "github.com/anyproto/any-sync/consensus/consensusproto"
gomock "go.uber.org/mock/gomock"
aclrecordproto "github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
gomock "github.com/golang/mock/gomock"
)
// MockListStorage is a mock of ListStorage interface.
@@ -36,7 +36,7 @@ func (m *MockListStorage) EXPECT() *MockListStorageMockRecorder {
}
// AddRawRecord mocks base method.
func (m *MockListStorage) AddRawRecord(arg0 context.Context, arg1 *consensusproto.RawRecordWithId) error {
func (m *MockListStorage) AddRawRecord(arg0 context.Context, arg1 *aclrecordproto.RawAclRecordWithId) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AddRawRecord", arg0, arg1)
ret0, _ := ret[0].(error)
@@ -50,10 +50,10 @@ func (mr *MockListStorageMockRecorder) AddRawRecord(arg0, arg1 interface{}) *gom
}
// GetRawRecord mocks base method.
func (m *MockListStorage) GetRawRecord(arg0 context.Context, arg1 string) (*consensusproto.RawRecordWithId, error) {
func (m *MockListStorage) GetRawRecord(arg0 context.Context, arg1 string) (*aclrecordproto.RawAclRecordWithId, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetRawRecord", arg0, arg1)
ret0, _ := ret[0].(*consensusproto.RawRecordWithId)
ret0, _ := ret[0].(*aclrecordproto.RawAclRecordWithId)
ret1, _ := ret[1].(error)
return ret0, ret1
}
@@ -94,10 +94,10 @@ func (mr *MockListStorageMockRecorder) Id() *gomock.Call {
}
// Root mocks base method.
func (m *MockListStorage) Root() (*consensusproto.RawRecordWithId, error) {
func (m *MockListStorage) Root() (*aclrecordproto.RawAclRecordWithId, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Root")
ret0, _ := ret[0].(*consensusproto.RawRecordWithId)
ret0, _ := ret[0].(*aclrecordproto.RawAclRecordWithId)
ret1, _ := ret[1].(error)
return ret0, ret1
}
+10 -33
View File
@@ -1,44 +1,21 @@
package syncacl
import (
"context"
"github.com/anyproto/any-sync/accountservice"
"github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/commonspace/object/acl/list"
"github.com/anyproto/any-sync/commonspace/spacestorage"
"github.com/anyproto/any-sync/commonspace/spacesyncproto"
"github.com/anytypeio/any-sync/commonspace/object/acl/list"
"github.com/anytypeio/any-sync/commonspace/objectsync"
"github.com/anytypeio/any-sync/commonspace/objectsync/synchandler"
)
const CName = "common.acl.syncacl"
func New() *SyncAcl {
return &SyncAcl{}
}
type SyncAcl struct {
list.AclList
synchandler.SyncHandler
messagePool objectsync.MessagePool
}
func (s *SyncAcl) HandleRequest(ctx context.Context, senderId string, request *spacesyncproto.ObjectSyncMessage) (response *spacesyncproto.ObjectSyncMessage, err error) {
return nil, nil
}
func (s *SyncAcl) HandleMessage(ctx context.Context, senderId string, request *spacesyncproto.ObjectSyncMessage) (err error) {
return nil
}
func (s *SyncAcl) Init(a *app.App) (err error) {
storage := a.MustComponent(spacestorage.CName).(spacestorage.SpaceStorage)
aclStorage, err := storage.AclStorage()
if err != nil {
return err
func NewSyncAcl(aclList list.AclList, messagePool objectsync.MessagePool) *SyncAcl {
return &SyncAcl{
AclList: aclList,
SyncHandler: nil,
messagePool: messagePool,
}
acc := a.MustComponent(accountservice.CName).(accountservice.Service)
s.AclList, err = list.BuildAclListWithIdentity(acc.Account(), aclStorage, list.NoOpAcceptorVerifier{})
return err
}
func (s *SyncAcl) Name() (name string) {
return CName
}
@@ -2,9 +2,10 @@ package syncacl
import (
"context"
"github.com/anyproto/any-sync/commonspace/object/acl/list"
"github.com/anyproto/any-sync/commonspace/spacesyncproto"
"fmt"
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anytypeio/any-sync/commonspace/object/acl/list"
"github.com/anytypeio/any-sync/commonspace/spacesyncproto"
)
type syncAclHandler struct {
@@ -12,5 +13,19 @@ type syncAclHandler struct {
}
func (s *syncAclHandler) HandleMessage(ctx context.Context, senderId string, req *spacesyncproto.ObjectSyncMessage) (err error) {
return nil
aclMsg := &aclrecordproto.AclSyncMessage{}
if err = aclMsg.Unmarshal(req.Payload); err != nil {
return
}
content := aclMsg.GetContent()
switch {
case content.GetAddRecords() != nil:
return s.handleAddRecords(ctx, senderId, content.GetAddRecords())
default:
return fmt.Errorf("unexpected aclSync message: %T", content.Value)
}
}
func (s *syncAclHandler) handleAddRecords(ctx context.Context, senderId string, addRecord *aclrecordproto.AclAddRecords) (err error) {
return
}
@@ -2,7 +2,7 @@ package syncobjectgetter
import (
"context"
"github.com/anyproto/any-sync/commonspace/objectsync/synchandler"
"github.com/anytypeio/any-sync/commonspace/objectsync/synchandler"
)
type SyncObject interface {
@@ -1,11 +1,11 @@
package exporter
import (
"github.com/anyproto/any-sync/commonspace/object/acl/liststorage"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anyproto/any-sync/util/crypto"
"github.com/anytypeio/any-sync/commonspace/object/acl/liststorage"
"github.com/anytypeio/any-sync/commonspace/object/tree/objecttree"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
"github.com/anytypeio/any-sync/util/crypto"
)
type DataConverter interface {
@@ -1,10 +1,10 @@
package exporter
import (
"github.com/anyproto/any-sync/commonspace/object/acl/list"
"github.com/anyproto/any-sync/commonspace/object/acl/liststorage"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anytypeio/any-sync/commonspace/object/acl/list"
"github.com/anytypeio/any-sync/commonspace/object/acl/liststorage"
"github.com/anytypeio/any-sync/commonspace/object/tree/objecttree"
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
)
type TreeImportParams struct {
@@ -15,7 +15,7 @@ type TreeImportParams struct {
}
func ImportHistoryTree(params TreeImportParams) (tree objecttree.ReadableObjectTree, err error) {
aclList, err := list.BuildAclList(params.ListStorage, list.NoOpAcceptorVerifier{})
aclList, err := list.BuildAclList(params.ListStorage)
if err != nil {
return
}
+2 -2
View File
@@ -2,8 +2,8 @@ package objecttree
import (
"errors"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/util/crypto"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/util/crypto"
"github.com/gogo/protobuf/proto"
)
@@ -2,10 +2,11 @@ package objecttree
import (
"errors"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/util/cidutil"
"github.com/anyproto/any-sync/util/crypto"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/util/cidutil"
"github.com/anytypeio/any-sync/util/crypto"
"github.com/gogo/protobuf/proto"
"time"
)
var ErrEmptyChange = errors.New("change payload should not be empty")
@@ -19,7 +20,6 @@ type BuilderContent struct {
PrivKey crypto.PrivKey
ReadKey crypto.SymKey
Content []byte
Timestamp int64
}
type InitialContent struct {
@@ -59,16 +59,13 @@ type ChangeBuilder interface {
Marshall(ch *Change) (*treechangeproto.RawTreeChangeWithId, error)
}
type newChangeFunc = func(id string, identity crypto.PubKey, ch *treechangeproto.TreeChange, signature []byte) *Change
type changeBuilder struct {
rootChange *treechangeproto.RawTreeChangeWithId
keys crypto.KeyStorage
newChange newChangeFunc
}
func NewChangeBuilder(keys crypto.KeyStorage, rootChange *treechangeproto.RawTreeChangeWithId) ChangeBuilder {
return &changeBuilder{keys: keys, rootChange: rootChange, newChange: NewChange}
return &changeBuilder{keys: keys, rootChange: rootChange}
}
func (c *changeBuilder) Unmarshall(rawIdChange *treechangeproto.RawTreeChangeWithId, verify bool) (ch *Change, err error) {
@@ -166,7 +163,7 @@ func (c *changeBuilder) Build(payload BuilderContent) (ch *Change, rawIdChange *
AclHeadId: payload.AclHeadId,
SnapshotBaseId: payload.SnapshotBaseId,
ReadKeyId: payload.ReadKeyId,
Timestamp: payload.Timestamp,
Timestamp: time.Now().Unix(),
Identity: identity,
IsSnapshot: payload.IsSnapshot,
}
@@ -200,7 +197,7 @@ func (c *changeBuilder) Build(payload BuilderContent) (ch *Change, rawIdChange *
if err != nil {
return
}
ch = c.newChange(id, payload.PrivKey.GetPublic(), change, signature)
ch = NewChange(id, payload.PrivKey.GetPublic(), change, signature)
rawIdChange = &treechangeproto.RawTreeChangeWithId{
RawChange: marshalledRawChange,
Id: id,
@@ -271,7 +268,7 @@ func (c *changeBuilder) unmarshallRawChange(raw *treechangeproto.RawTreeChange,
if err != nil {
return
}
ch = c.newChange(id, key, unmarshalled, raw.Signature)
ch = NewChange(id, key, unmarshalled, raw.Signature)
return
}
@@ -14,47 +14,34 @@ type historyTree struct {
*objectTree
}
func (h *historyTree) rebuildFromStorage(params HistoryTreeParams) (err error) {
err = h.rebuild(params)
if err != nil {
return
}
h.aclList.RLock()
defer h.aclList.RUnlock()
state := h.aclList.AclState()
return h.readKeysFromAclState(state)
}
func (h *historyTree) rebuild(params HistoryTreeParams) (err error) {
var (
beforeId = params.BeforeId
include = params.IncludeBeforeId
full = params.BuildFullTree
)
h.treeBuilder.Reset()
if full {
h.tree, err = h.treeBuilder.BuildFull()
return
}
if beforeId == h.Id() && !include {
func (h *historyTree) rebuildFromStorage(beforeId string, include bool) (err error) {
ot := h.objectTree
ot.treeBuilder.Reset()
if beforeId == ot.Id() && !include {
return ErrLoadBeforeRoot
}
heads := []string{beforeId}
if beforeId == "" {
heads, err = h.treeStorage.Heads()
heads, err = ot.treeStorage.Heads()
if err != nil {
return
}
} else if !include {
beforeChange, err := h.treeBuilder.loadChange(beforeId)
beforeChange, err := ot.treeBuilder.loadChange(beforeId)
if err != nil {
return err
}
heads = beforeChange.PreviousIds
}
h.tree, err = h.treeBuilder.build(heads, nil, nil)
return
ot.tree, err = ot.treeBuilder.build(heads, nil, nil)
if err != nil {
return
}
ot.aclList.RLock()
defer ot.aclList.RUnlock()
state := ot.aclList.AclState()
return ot.readKeysFromAclState(state)
}
@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/anyproto/any-sync/commonspace/object/tree/objecttree (interfaces: ObjectTree)
// Source: github.com/anytypeio/any-sync/commonspace/object/tree/objecttree (interfaces: ObjectTree)
// Package mock_objecttree is a generated GoMock package.
package mock_objecttree
@@ -9,11 +9,11 @@ import (
reflect "reflect"
time "time"
list "github.com/anyproto/any-sync/commonspace/object/acl/list"
objecttree "github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
treechangeproto "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
treestorage "github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
gomock "go.uber.org/mock/gomock"
list "github.com/anytypeio/any-sync/commonspace/object/acl/list"
objecttree "github.com/anytypeio/any-sync/commonspace/object/tree/objecttree"
treechangeproto "github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
treestorage "github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
gomock "github.com/golang/mock/gomock"
)
// MockObjectTree is a mock of ObjectTree interface.
@@ -1,18 +1,18 @@
//go:generate mockgen -destination mock_objecttree/mock_objecttree.go github.com/anyproto/any-sync/commonspace/object/tree/objecttree ObjectTree
//go:generate mockgen -destination mock_objecttree/mock_objecttree.go github.com/anytypeio/any-sync/commonspace/object/tree/objecttree ObjectTree
package objecttree
import (
"context"
"errors"
"github.com/anytypeio/any-sync/util/crypto"
"sync"
"time"
"github.com/anyproto/any-sync/util/crypto"
"github.com/anyproto/any-sync/commonspace/object/acl/list"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anyproto/any-sync/util/slice"
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anytypeio/any-sync/commonspace/object/acl/list"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
"github.com/anytypeio/any-sync/util/slice"
)
type RWLocker interface {
@@ -116,25 +116,13 @@ type objectTree struct {
}
func (ot *objectTree) rebuildFromStorage(theirHeads []string, newChanges []*Change) (err error) {
oldTree := ot.tree
ot.treeBuilder.Reset()
ot.tree, err = ot.treeBuilder.Build(theirHeads, newChanges)
if err != nil {
return
}
// in case there are new heads
if theirHeads != nil && oldTree != nil {
// checking that old root is still in tree
rootCh, rootExists := ot.tree.attached[oldTree.RootId()]
// checking the case where theirHeads were actually below prevHeads
// so if we did load some extra data in the tree, let's reduce it to old root
if slice.UnsortedEquals(oldTree.headIds, ot.tree.headIds) && rootExists && ot.tree.RootId() != oldTree.RootId() {
ot.tree.makeRootAndRemove(rootCh)
}
}
// during building the tree we may have marked some changes as possible roots,
// but obviously they are not roots, because of the way how we construct the tree
ot.tree.clearPossibleRoots()
@@ -199,7 +187,7 @@ func (ot *objectTree) AddContent(ctx context.Context, content SignableChangeCont
panic(err)
}
err = ot.treeStorage.AddRawChangesSetHeads([]*treechangeproto.RawTreeChangeWithId{rawChange}, []string{objChange.Id})
err = ot.treeStorage.TransactionAdd([]*treechangeproto.RawTreeChangeWithId{rawChange}, []string{objChange.Id})
if err != nil {
return
}
@@ -248,7 +236,9 @@ func (ot *objectTree) prepareBuilderContent(content SignableChangeContent) (cnt
pubKey = content.Key.GetPublic()
readKeyId string
)
if !state.Permissions(pubKey).CanWrite() {
canWrite := state.HasPermission(pubKey, aclrecordproto.AclUserPermissions_Writer) ||
state.HasPermission(pubKey, aclrecordproto.AclUserPermissions_Admin)
if !canWrite {
err = list.ErrInsufficientPermissions
return
}
@@ -261,10 +251,6 @@ func (ot *objectTree) prepareBuilderContent(content SignableChangeContent) (cnt
}
readKey = ot.currentReadKey
}
timestamp := content.Timestamp
if timestamp <= 0 {
timestamp = time.Now().Unix()
}
cnt = BuilderContent{
TreeHeadIds: ot.tree.Heads(),
AclHeadId: ot.aclList.Head().Id,
@@ -274,7 +260,6 @@ func (ot *objectTree) prepareBuilderContent(content SignableChangeContent) (cnt
PrivKey: content.Key,
ReadKey: readKey,
Content: content.Data,
Timestamp: timestamp,
}
return
}
@@ -294,11 +279,7 @@ func (ot *objectTree) AddRawChanges(ctx context.Context, changesPayload RawChang
addResult.Mode = Rebuild
}
err = ot.treeStorage.AddRawChangesSetHeads(addResult.Added, addResult.Heads)
if err != nil {
// rolling back all changes made to inmemory state
ot.rebuildFromStorage(nil, nil)
}
err = ot.treeStorage.TransactionAdd(addResult.Added, addResult.Heads)
return
}
@@ -361,7 +342,7 @@ func (ot *objectTree) addRawChanges(ctx context.Context, changesPayload RawChang
}
// checks if we need to go to database
snapshotNotInTree := func(ch *Change) bool {
isOldSnapshot := func(ch *Change) bool {
if ch.SnapshotId == ot.tree.RootId() {
return false
}
@@ -376,12 +357,26 @@ func (ot *objectTree) addRawChanges(ctx context.Context, changesPayload RawChang
shouldRebuildFromStorage := false
// checking if we have some changes with different snapshot and then rebuilding
for _, ch := range ot.newChangesBuf {
if snapshotNotInTree(ch) {
for idx, ch := range ot.newChangesBuf {
if isOldSnapshot(ch) {
var exists bool
// checking if it exists in the storage, if yes, then at some point it was added to the tree
// thus we don't need to look at this change
exists, err = ot.treeStorage.HasChange(ctx, ch.Id)
if err != nil {
return
}
if exists {
// marking as nil to delete after
ot.newChangesBuf[idx] = nil
continue
}
// we haven't seen the change, and it refers to old snapshot, so we should rebuild
shouldRebuildFromStorage = true
break
}
}
// discarding all previously seen changes
ot.newChangesBuf = slice.DiscardFromSlice(ot.newChangesBuf, func(ch *Change) bool { return ch == nil })
if shouldRebuildFromStorage {
err = ot.rebuildFromStorage(changesPayload.NewHeads, ot.newChangesBuf)
@@ -478,11 +473,6 @@ func (ot *objectTree) createAddResult(oldHeads []string, mode Mode, treeChangesA
var added []*treechangeproto.RawTreeChangeWithId
added, err = getAddedChanges(treeChangesAdded)
if !ot.treeBuilder.keepInMemoryData {
for _, ch := range treeChangesAdded {
ch.Data = nil
}
}
if err != nil {
return
}
@@ -549,8 +539,22 @@ func (ot *objectTree) IterateFrom(id string, convert ChangeConvertFunc, iterate
}
func (ot *objectTree) HasChanges(chs ...string) bool {
hasChange := func(s string) bool {
_, attachedExists := ot.tree.attached[s]
if attachedExists {
return attachedExists
}
has, err := ot.treeStorage.HasChange(context.Background(), s)
if err != nil {
return false
}
return has
}
for _, ch := range chs {
if _, attachedExists := ot.tree.attached[ch]; !attachedExists {
if !hasChange(ch) {
return false
}
}
@@ -617,7 +621,19 @@ func (ot *objectTree) ChangesAfterCommonSnapshot(theirPath, theirHeads []string)
}
}
return ot.rawChangeLoader.Load(commonSnapshot, ot.tree, theirHeads)
if commonSnapshot == ot.tree.RootId() {
return ot.getChangesFromTree(theirHeads)
} else {
return ot.getChangesFromDB(commonSnapshot, theirHeads)
}
}
func (ot *objectTree) getChangesFromTree(theirHeads []string) (rawChanges []*treechangeproto.RawTreeChangeWithId, err error) {
return ot.rawChangeLoader.LoadFromTree(ot.tree, theirHeads)
}
func (ot *objectTree) getChangesFromDB(commonSnapshot string, theirHeads []string) (rawChanges []*treechangeproto.RawTreeChangeWithId, err error) {
return ot.rawChangeLoader.LoadFromStorage(commonSnapshot, ot.tree.headIds, theirHeads)
}
func (ot *objectTree) snapshotPathIsActual() bool {
@@ -2,45 +2,105 @@ package objecttree
import (
"context"
"fmt"
"testing"
"time"
"github.com/anyproto/any-sync/commonspace/object/accountdata"
"github.com/anyproto/any-sync/commonspace/object/acl/list"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/gogo/protobuf/proto"
"crypto/rand"
"github.com/anytypeio/any-sync/commonspace/object/accountdata"
"github.com/anytypeio/any-sync/commonspace/object/acl/list"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
"github.com/anytypeio/any-sync/util/crypto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)
type mockKeyStorage struct {
key crypto.PubKey
}
func newKeyStorage() mockKeyStorage {
_, pk, _ := crypto.GenerateEd25519Key(rand.Reader)
return mockKeyStorage{pk}
}
func (m mockKeyStorage) PubKeyFromProto(protoBytes []byte) (crypto.PubKey, error) {
return m.key, nil
}
type mockChangeCreator struct{}
func (c *mockChangeCreator) createRoot(id, aclId string) *treechangeproto.RawTreeChangeWithId {
aclChange := &treechangeproto.RootChange{
AclHeadId: aclId,
}
res, _ := aclChange.Marshal()
raw := &treechangeproto.RawTreeChange{
Payload: res,
Signature: nil,
}
rawMarshalled, _ := raw.Marshal()
return &treechangeproto.RawTreeChangeWithId{
RawChange: rawMarshalled,
Id: id,
}
}
func (c *mockChangeCreator) createRaw(id, aclId, snapshotId string, isSnapshot bool, prevIds ...string) *treechangeproto.RawTreeChangeWithId {
aclChange := &treechangeproto.TreeChange{
TreeHeadIds: prevIds,
AclHeadId: aclId,
SnapshotBaseId: snapshotId,
ChangesData: nil,
IsSnapshot: isSnapshot,
}
res, _ := aclChange.Marshal()
raw := &treechangeproto.RawTreeChange{
Payload: res,
Signature: nil,
}
rawMarshalled, _ := raw.Marshal()
return &treechangeproto.RawTreeChangeWithId{
RawChange: rawMarshalled,
Id: id,
}
}
func (c *mockChangeCreator) createNewTreeStorage(treeId, aclHeadId string) treestorage.TreeStorage {
root := c.createRoot(treeId, aclHeadId)
treeStorage, _ := treestorage.NewInMemoryTreeStorage(root, []string{root.Id}, []*treechangeproto.RawTreeChangeWithId{root})
return treeStorage
}
type testTreeContext struct {
aclList list.AclList
treeStorage treestorage.TreeStorage
changeCreator *MockChangeCreator
changeBuilder ChangeBuilder
changeCreator *mockChangeCreator
objTree ObjectTree
}
func prepareAclList(t *testing.T) (list.AclList, *accountdata.AccountKeys) {
func prepareAclList(t *testing.T) list.AclList {
randKeys, err := accountdata.NewRandom()
require.NoError(t, err)
aclList, err := list.NewTestDerivedAcl("spaceId", randKeys)
require.NoError(t, err, "building acl list should be without error")
return aclList, randKeys
return aclList
}
func prepareHistoryTreeDeps(aclList list.AclList) (*MockChangeCreator, objectTreeDeps) {
changeCreator := NewMockChangeCreator()
treeStorage := changeCreator.CreateNewTreeStorage("0", aclList.Head().Id)
func prepareTreeDeps(aclList list.AclList) (*mockChangeCreator, objectTreeDeps) {
changeCreator := &mockChangeCreator{}
treeStorage := changeCreator.createNewTreeStorage("0", aclList.Head().Id)
root, _ := treeStorage.Root()
changeBuilder := &nonVerifiableChangeBuilder{
ChangeBuilder: NewChangeBuilder(newMockKeyStorage(), root),
ChangeBuilder: NewChangeBuilder(newKeyStorage(), root),
}
deps := objectTreeDeps{
changeBuilder: changeBuilder,
treeBuilder: newTreeBuilder(true, treeStorage, changeBuilder),
treeBuilder: newTreeBuilder(treeStorage, changeBuilder),
treeStorage: treeStorage,
rawChangeLoader: newRawChangeLoader(treeStorage, changeBuilder),
validator: &noOpTreeValidator{},
@@ -50,26 +110,23 @@ func prepareHistoryTreeDeps(aclList list.AclList) (*MockChangeCreator, objectTre
}
func prepareTreeContext(t *testing.T, aclList list.AclList) testTreeContext {
return prepareContext(t, aclList, BuildTestableTree, nil)
}
func prepareEmptyDataTreeContext(t *testing.T, aclList list.AclList, additionalChanges func(changeCreator *MockChangeCreator) RawChangesPayload) testTreeContext {
return prepareContext(t, aclList, BuildEmptyDataTestableTree, additionalChanges)
}
func prepareContext(
t *testing.T,
aclList list.AclList,
objTreeBuilder BuildObjectTreeFunc,
additionalChanges func(changeCreator *MockChangeCreator) RawChangesPayload) testTreeContext {
changeCreator := NewMockChangeCreator()
treeStorage := changeCreator.CreateNewTreeStorage("0", aclList.Head().Id)
if additionalChanges != nil {
payload := additionalChanges(changeCreator)
err := treeStorage.AddRawChangesSetHeads(payload.RawChanges, payload.NewHeads)
require.NoError(t, err)
changeCreator := &mockChangeCreator{}
treeStorage := changeCreator.createNewTreeStorage("0", aclList.Head().Id)
root, _ := treeStorage.Root()
changeBuilder := &nonVerifiableChangeBuilder{
ChangeBuilder: NewChangeBuilder(newKeyStorage(), root),
}
objTree, err := objTreeBuilder(treeStorage, aclList)
deps := objectTreeDeps{
changeBuilder: changeBuilder,
treeBuilder: newTreeBuilder(treeStorage, changeBuilder),
treeStorage: treeStorage,
rawChangeLoader: newRawChangeLoader(treeStorage, changeBuilder),
validator: &noOpTreeValidator{},
aclList: aclList,
}
// check build
objTree, err := buildObjectTree(deps)
require.NoError(t, err, "building tree should be without error")
// check tree iterate
@@ -79,71 +136,18 @@ func prepareContext(
return true
})
require.NoError(t, err, "iterate should be without error")
if additionalChanges == nil {
assert.Equal(t, []string{"0"}, iterChangesId)
}
assert.Equal(t, []string{"0"}, iterChangesId)
return testTreeContext{
aclList: aclList,
treeStorage: treeStorage,
changeBuilder: changeBuilder,
changeCreator: changeCreator,
objTree: objTree,
}
}
func TestObjectTree(t *testing.T) {
aclList, keys := prepareAclList(t)
ctx := context.Background()
t.Run("add content", func(t *testing.T) {
root, err := CreateObjectTreeRoot(ObjectTreeCreatePayload{
PrivKey: keys.SignKey,
ChangeType: "changeType",
ChangePayload: nil,
SpaceId: "spaceId",
IsEncrypted: true,
}, aclList)
require.NoError(t, err)
store, _ := treestorage.NewInMemoryTreeStorage(root, []string{root.Id}, []*treechangeproto.RawTreeChangeWithId{root})
oTree, err := BuildObjectTree(store, aclList)
require.NoError(t, err)
t.Run("0 timestamp is changed to current", func(t *testing.T) {
start := time.Now()
res, err := oTree.AddContent(ctx, SignableChangeContent{
Data: []byte("some"),
Key: keys.SignKey,
IsSnapshot: false,
IsEncrypted: true,
Timestamp: 0,
})
end := time.Now()
require.NoError(t, err)
require.Len(t, oTree.Heads(), 1)
require.Equal(t, res.Added[0].Id, oTree.Heads()[0])
ch, err := oTree.(*objectTree).changeBuilder.Unmarshall(res.Added[0], true)
require.NoError(t, err)
require.GreaterOrEqual(t, start.Unix(), ch.Timestamp)
require.LessOrEqual(t, end.Unix(), ch.Timestamp)
require.Equal(t, res.Added[0].Id, oTree.(*objectTree).tree.lastIteratedHeadId)
})
t.Run("timestamp is set correctly", func(t *testing.T) {
someTs := time.Now().Add(time.Hour).Unix()
res, err := oTree.AddContent(ctx, SignableChangeContent{
Data: []byte("some"),
Key: keys.SignKey,
IsSnapshot: false,
IsEncrypted: true,
Timestamp: someTs,
})
require.NoError(t, err)
require.Len(t, oTree.Heads(), 1)
require.Equal(t, res.Added[0].Id, oTree.Heads()[0])
ch, err := oTree.(*objectTree).changeBuilder.Unmarshall(res.Added[0], true)
require.NoError(t, err)
require.Equal(t, ch.Timestamp, someTs)
require.Equal(t, res.Added[0].Id, oTree.(*objectTree).tree.lastIteratedHeadId)
})
})
aclList := prepareAclList(t)
t.Run("add simple", func(t *testing.T) {
ctx := prepareTreeContext(t, aclList)
@@ -152,8 +156,8 @@ func TestObjectTree(t *testing.T) {
objTree := ctx.objTree
rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
}
payload := RawChangesPayload{
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
@@ -197,7 +201,7 @@ func TestObjectTree(t *testing.T) {
objTree := ctx.objTree
rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("0", aclList.Head().Id, "", true, ""),
changeCreator.createRaw("0", aclList.Head().Id, "", true, ""),
}
payload := RawChangesPayload{
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
@@ -221,33 +225,7 @@ func TestObjectTree(t *testing.T) {
objTree := ctx.objTree
rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
}
payload := RawChangesPayload{
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
RawChanges: rawChanges,
}
res, err := objTree.AddRawChanges(context.Background(), payload)
require.NoError(t, err, "adding changes should be without error")
// check result
assert.Equal(t, []string{"0"}, res.OldHeads)
assert.Equal(t, []string{"0"}, res.Heads)
assert.Equal(t, 0, len(res.Added))
assert.Equal(t, Nothing, res.Mode)
// check tree heads
assert.Equal(t, []string{"0"}, objTree.Heads())
})
t.Run("add not connected changes", func(t *testing.T) {
ctx := prepareTreeContext(t, aclList)
changeCreator := ctx.changeCreator
objTree := ctx.objTree
// this change could in theory replace current snapshot, we should prevent that
rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("2", aclList.Head().Id, "0", true, "1"),
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
}
payload := RawChangesPayload{
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
@@ -273,10 +251,10 @@ func TestObjectTree(t *testing.T) {
objTree := ctx.objTree
rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"),
changeCreator.CreateRaw("4", aclList.Head().Id, "3", false, "3"),
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
changeCreator.createRaw("4", aclList.Head().Id, "3", false, "3"),
}
payload := RawChangesPayload{
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
@@ -323,9 +301,9 @@ func TestObjectTree(t *testing.T) {
objTree := ctx.objTree
rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"),
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
}
payload := RawChangesPayload{
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
@@ -341,195 +319,18 @@ func TestObjectTree(t *testing.T) {
assert.Equal(t, true, objTree.(*objectTree).snapshotPathIsActual())
})
t.Run("test empty data tree", func(t *testing.T) {
t.Run("empty tree add", func(t *testing.T) {
ctx := prepareEmptyDataTreeContext(t, aclList, nil)
changeCreator := ctx.changeCreator
objTree := ctx.objTree
rawChangesFirst := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRawWithData("1", aclList.Head().Id, "0", false, []byte("1"), "0"),
changeCreator.CreateRawWithData("2", aclList.Head().Id, "0", false, []byte("2"), "1"),
changeCreator.CreateRawWithData("3", aclList.Head().Id, "0", false, []byte("3"), "2"),
}
rawChangesSecond := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRawWithData("4", aclList.Head().Id, "0", false, []byte("4"), "2"),
changeCreator.CreateRawWithData("5", aclList.Head().Id, "0", false, []byte("5"), "1"),
changeCreator.CreateRawWithData("6", aclList.Head().Id, "0", false, []byte("6"), "3", "4", "5"),
}
// making them to be saved in unattached
_, err := objTree.AddRawChanges(context.Background(), RawChangesPayload{
NewHeads: []string{"6"},
RawChanges: rawChangesSecond,
})
require.NoError(t, err, "adding changes should be without error")
// attaching them
res, err := objTree.AddRawChanges(context.Background(), RawChangesPayload{
NewHeads: []string{"3"},
RawChanges: rawChangesFirst,
})
require.NoError(t, err, "adding changes should be without error")
require.Equal(t, "0", objTree.Root().Id)
require.Equal(t, []string{"6"}, objTree.Heads())
require.Equal(t, 6, len(res.Added))
// checking that added changes still have data
for _, ch := range res.Added {
unmarshallRaw := &treechangeproto.RawTreeChange{}
proto.Unmarshal(ch.RawChange, unmarshallRaw)
treeCh := &treechangeproto.TreeChange{}
proto.Unmarshal(unmarshallRaw.Payload, treeCh)
require.Equal(t, ch.Id, string(treeCh.ChangesData))
}
// checking that the tree doesn't have data in memory
err = objTree.IterateRoot(nil, func(change *Change) bool {
if change.Id == "0" {
return true
}
require.Nil(t, change.Data)
return true
})
})
t.Run("empty tree load", func(t *testing.T) {
ctx := prepareEmptyDataTreeContext(t, aclList, func(changeCreator *MockChangeCreator) RawChangesPayload {
rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRawWithData("1", aclList.Head().Id, "0", false, []byte("1"), "0"),
changeCreator.CreateRawWithData("2", aclList.Head().Id, "0", false, []byte("2"), "1"),
changeCreator.CreateRawWithData("3", aclList.Head().Id, "0", false, []byte("3"), "2"),
changeCreator.CreateRawWithData("4", aclList.Head().Id, "0", false, []byte("4"), "2"),
changeCreator.CreateRawWithData("5", aclList.Head().Id, "0", false, []byte("5"), "1"),
changeCreator.CreateRawWithData("6", aclList.Head().Id, "0", false, []byte("6"), "3", "4", "5"),
}
return RawChangesPayload{NewHeads: []string{"6"}, RawChanges: rawChanges}
})
ctx.objTree.IterateRoot(nil, func(change *Change) bool {
if change.Id == "0" {
return true
}
require.Nil(t, change.Data)
return true
})
rawChanges, err := ctx.objTree.ChangesAfterCommonSnapshot([]string{"0"}, []string{"6"})
require.NoError(t, err)
for _, ch := range rawChanges {
unmarshallRaw := &treechangeproto.RawTreeChange{}
proto.Unmarshal(ch.RawChange, unmarshallRaw)
treeCh := &treechangeproto.TreeChange{}
proto.Unmarshal(unmarshallRaw.Payload, treeCh)
require.Equal(t, ch.Id, string(treeCh.ChangesData))
}
})
})
t.Run("rollback when add to storage returns error", func(t *testing.T) {
ctx := prepareTreeContext(t, aclList)
changeCreator := ctx.changeCreator
objTree := ctx.objTree
store := ctx.treeStorage.(*treestorage.InMemoryTreeStorage)
addErr := fmt.Errorf("error saving")
store.SetReturnErrorOnAdd(addErr)
rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("1", aclList.Head().Id, "0", true, "0"),
}
payload := RawChangesPayload{
NewHeads: []string{"1"},
RawChanges: rawChanges,
}
_, err := objTree.AddRawChanges(context.Background(), payload)
require.Error(t, err, addErr)
require.Equal(t, "0", objTree.Root().Id)
})
t.Run("their heads before common snapshot", func(t *testing.T) {
// checking that adding old changes did not affect the tree
ctx := prepareTreeContext(t, aclList)
changeCreator := ctx.changeCreator
objTree := ctx.objTree
rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("1", aclList.Head().Id, "0", true, "0"),
changeCreator.CreateRaw("2", aclList.Head().Id, "1", false, "1"),
changeCreator.CreateRaw("3", aclList.Head().Id, "1", true, "2"),
changeCreator.CreateRaw("4", aclList.Head().Id, "1", false, "2"),
changeCreator.CreateRaw("5", aclList.Head().Id, "1", false, "1"),
changeCreator.CreateRaw("6", aclList.Head().Id, "1", true, "3", "4", "5"),
}
payload := RawChangesPayload{
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
RawChanges: rawChanges,
}
_, err := objTree.AddRawChanges(context.Background(), payload)
require.NoError(t, err, "adding changes should be without error")
require.Equal(t, "6", objTree.Root().Id)
rawChangesPrevious := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("1", aclList.Head().Id, "0", true, "0"),
}
payload = RawChangesPayload{
NewHeads: []string{"1"},
RawChanges: rawChangesPrevious,
}
_, err = objTree.AddRawChanges(context.Background(), payload)
require.NoError(t, err, "adding changes should be without error")
require.Equal(t, "6", objTree.Root().Id)
})
t.Run("stored changes will not break the pipeline if heads were not updated", func(t *testing.T) {
ctx := prepareTreeContext(t, aclList)
changeCreator := ctx.changeCreator
objTree := ctx.objTree
store := ctx.treeStorage.(*treestorage.InMemoryTreeStorage)
rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("1", aclList.Head().Id, "0", true, "0"),
}
payload := RawChangesPayload{
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
RawChanges: rawChanges,
}
_, err := objTree.AddRawChanges(context.Background(), payload)
require.NoError(t, err, "adding changes should be without error")
require.Equal(t, "1", objTree.Root().Id)
// creating changes to save in the storage
// to imitate the condition where all changes are in the storage
// but the head was not updated
storageChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("2", aclList.Head().Id, "1", false, "1"),
changeCreator.CreateRaw("3", aclList.Head().Id, "1", true, "2"),
changeCreator.CreateRaw("4", aclList.Head().Id, "1", false, "2"),
changeCreator.CreateRaw("5", aclList.Head().Id, "1", false, "1"),
changeCreator.CreateRaw("6", aclList.Head().Id, "1", true, "3", "4", "5"),
}
store.AddRawChangesSetHeads(storageChanges, []string{"1"})
// updating with subset of those changes to see that everything will still work
payload = RawChangesPayload{
NewHeads: []string{"6"},
RawChanges: storageChanges,
}
_, err = objTree.AddRawChanges(context.Background(), payload)
require.NoError(t, err, "adding changes should be without error")
require.Equal(t, "6", objTree.Root().Id)
})
t.Run("changes from tree after common snapshot complex", func(t *testing.T) {
ctx := prepareTreeContext(t, aclList)
changeCreator := ctx.changeCreator
objTree := ctx.objTree
rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"),
changeCreator.CreateRaw("4", aclList.Head().Id, "0", false, "2"),
changeCreator.CreateRaw("5", aclList.Head().Id, "0", false, "1"),
changeCreator.CreateRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"),
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
changeCreator.createRaw("4", aclList.Head().Id, "0", false, "2"),
changeCreator.createRaw("5", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"),
}
payload := RawChangesPayload{
@@ -603,13 +404,13 @@ func TestObjectTree(t *testing.T) {
objTree := ctx.objTree
rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"),
changeCreator.CreateRaw("4", aclList.Head().Id, "0", false, "2"),
changeCreator.CreateRaw("5", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
changeCreator.createRaw("4", aclList.Head().Id, "0", false, "2"),
changeCreator.createRaw("5", aclList.Head().Id, "0", false, "1"),
// main difference from tree example
changeCreator.CreateRaw("6", aclList.Head().Id, "0", true, "3", "4", "5"),
changeCreator.createRaw("6", aclList.Head().Id, "0", true, "3", "4", "5"),
}
payload := RawChangesPayload{
@@ -684,9 +485,9 @@ func TestObjectTree(t *testing.T) {
objTree := ctx.objTree
rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"),
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
}
payload := RawChangesPayload{
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
@@ -698,9 +499,9 @@ func TestObjectTree(t *testing.T) {
require.Equal(t, "3", objTree.Root().Id)
rawChanges = []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("4", aclList.Head().Id, "0", false, "2"),
changeCreator.CreateRaw("5", aclList.Head().Id, "0", false, "1"),
changeCreator.CreateRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"),
changeCreator.createRaw("4", aclList.Head().Id, "0", false, "2"),
changeCreator.createRaw("5", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"),
}
payload = RawChangesPayload{
NewHeads: []string{rawChanges[len(rawChanges)-1].Id},
@@ -741,17 +542,17 @@ func TestObjectTree(t *testing.T) {
})
t.Run("test history tree not include", func(t *testing.T) {
changeCreator, deps := prepareHistoryTreeDeps(aclList)
changeCreator, deps := prepareTreeDeps(aclList)
rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"),
changeCreator.CreateRaw("4", aclList.Head().Id, "0", false, "2"),
changeCreator.CreateRaw("5", aclList.Head().Id, "0", false, "1"),
changeCreator.CreateRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"),
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
changeCreator.createRaw("4", aclList.Head().Id, "0", false, "2"),
changeCreator.createRaw("5", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"),
}
deps.treeStorage.AddRawChangesSetHeads(rawChanges, []string{"6"})
deps.treeStorage.TransactionAdd(rawChanges, []string{"6"})
hTree, err := buildHistoryTree(deps, HistoryTreeParams{
BeforeId: "6",
IncludeBeforeId: false,
@@ -771,49 +572,18 @@ func TestObjectTree(t *testing.T) {
assert.Equal(t, "0", hTree.Root().Id)
})
t.Run("test history tree build full", func(t *testing.T) {
changeCreator, deps := prepareHistoryTreeDeps(aclList)
// sequence of snapshots: 5->1->0
rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("1", aclList.Head().Id, "0", true, "0"),
changeCreator.CreateRaw("2", aclList.Head().Id, "1", false, "1"),
changeCreator.CreateRaw("3", aclList.Head().Id, "1", true, "2"),
changeCreator.CreateRaw("4", aclList.Head().Id, "1", false, "2"),
changeCreator.CreateRaw("5", aclList.Head().Id, "1", true, "3", "4"),
changeCreator.CreateRaw("6", aclList.Head().Id, "5", false, "5"),
}
deps.treeStorage.AddRawChangesSetHeads(rawChanges, []string{"6"})
hTree, err := buildHistoryTree(deps, HistoryTreeParams{
BuildFullTree: true,
})
require.NoError(t, err)
// check tree heads
assert.Equal(t, []string{"6"}, hTree.Heads())
// check tree iterate
var iterChangesId []string
err = hTree.IterateFrom(hTree.Root().Id, nil, func(change *Change) bool {
iterChangesId = append(iterChangesId, change.Id)
return true
})
require.NoError(t, err, "iterate should be without error")
assert.Equal(t, []string{"0", "1", "2", "3", "4", "5", "6"}, iterChangesId)
assert.Equal(t, "0", hTree.Root().Id)
})
t.Run("test history tree include", func(t *testing.T) {
changeCreator, deps := prepareHistoryTreeDeps(aclList)
changeCreator, deps := prepareTreeDeps(aclList)
rawChanges := []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"),
changeCreator.CreateRaw("4", aclList.Head().Id, "0", false, "2"),
changeCreator.CreateRaw("5", aclList.Head().Id, "0", false, "1"),
changeCreator.CreateRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"),
changeCreator.createRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.createRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("3", aclList.Head().Id, "0", true, "2"),
changeCreator.createRaw("4", aclList.Head().Id, "0", false, "2"),
changeCreator.createRaw("5", aclList.Head().Id, "0", false, "1"),
changeCreator.createRaw("6", aclList.Head().Id, "0", false, "3", "4", "5"),
}
deps.treeStorage.AddRawChangesSetHeads(rawChanges, []string{"6"})
deps.treeStorage.TransactionAdd(rawChanges, []string{"6"})
hTree, err := buildHistoryTree(deps, HistoryTreeParams{
BeforeId: "6",
IncludeBeforeId: true,
@@ -834,7 +604,7 @@ func TestObjectTree(t *testing.T) {
})
t.Run("test history tree root", func(t *testing.T) {
_, deps := prepareHistoryTreeDeps(aclList)
_, deps := prepareTreeDeps(aclList)
hTree, err := buildHistoryTree(deps, HistoryTreeParams{
BeforeId: "0",
IncludeBeforeId: true,
@@ -853,40 +623,4 @@ func TestObjectTree(t *testing.T) {
assert.Equal(t, []string{"0"}, iterChangesId)
assert.Equal(t, "0", hTree.Root().Id)
})
t.Run("validate correct tree", func(t *testing.T) {
ctx := prepareTreeContext(t, aclList)
changeCreator := ctx.changeCreator
rawChanges := []*treechangeproto.RawTreeChangeWithId{
ctx.objTree.Header(),
changeCreator.CreateRaw("1", aclList.Head().Id, "0", false, "0"),
changeCreator.CreateRaw("2", aclList.Head().Id, "0", false, "1"),
changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"),
}
defaultObjectTreeDeps = nonVerifiableTreeDeps
err := ValidateRawTree(treestorage.TreeStorageCreatePayload{
RootRawChange: ctx.objTree.Header(),
Heads: []string{"3"},
Changes: rawChanges,
}, ctx.aclList)
require.NoError(t, err)
})
t.Run("fail to validate not connected tree", func(t *testing.T) {
ctx := prepareTreeContext(t, aclList)
changeCreator := ctx.changeCreator
rawChanges := []*treechangeproto.RawTreeChangeWithId{
ctx.objTree.Header(),
changeCreator.CreateRaw("3", aclList.Head().Id, "0", true, "2"),
}
defaultObjectTreeDeps = nonVerifiableTreeDeps
err := ValidateRawTree(treestorage.TreeStorageCreatePayload{
RootRawChange: ctx.objTree.Header(),
Heads: []string{"3"},
Changes: rawChanges,
}, ctx.aclList)
require.Equal(t, ErrHasInvalidChanges, err)
})
}
@@ -1,10 +1,12 @@
package objecttree
import (
"github.com/anyproto/any-sync/commonspace/object/acl/list"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anyproto/any-sync/util/crypto"
"github.com/anytypeio/any-sync/commonspace/object/acl/list"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
"github.com/anytypeio/any-sync/util/crypto"
"math/rand"
"time"
)
type ObjectTreeCreatePayload struct {
@@ -13,8 +15,6 @@ type ObjectTreeCreatePayload struct {
ChangePayload []byte
SpaceId string
IsEncrypted bool
Seed []byte
Timestamp int64
}
type HistoryTreeParams struct {
@@ -22,7 +22,6 @@ type HistoryTreeParams struct {
AclList list.AclList
BeforeId string
IncludeBeforeId bool
BuildFullTree bool
}
type objectTreeDeps struct {
@@ -34,16 +33,12 @@ type objectTreeDeps struct {
aclList list.AclList
}
type BuildObjectTreeFunc = func(treeStorage treestorage.TreeStorage, aclList list.AclList) (ObjectTree, error)
var defaultObjectTreeDeps = verifiableTreeDeps
func verifiableTreeDeps(
func defaultObjectTreeDeps(
rootChange *treechangeproto.RawTreeChangeWithId,
treeStorage treestorage.TreeStorage,
aclList list.AclList) objectTreeDeps {
changeBuilder := NewChangeBuilder(crypto.NewKeyStorage(), rootChange)
treeBuilder := newTreeBuilder(true, treeStorage, changeBuilder)
treeBuilder := newTreeBuilder(treeStorage, changeBuilder)
return objectTreeDeps{
changeBuilder: changeBuilder,
treeBuilder: treeBuilder,
@@ -54,28 +49,12 @@ func verifiableTreeDeps(
}
}
func emptyDataTreeDeps(
rootChange *treechangeproto.RawTreeChangeWithId,
treeStorage treestorage.TreeStorage,
aclList list.AclList) objectTreeDeps {
changeBuilder := NewChangeBuilder(crypto.NewKeyStorage(), rootChange)
treeBuilder := newTreeBuilder(false, treeStorage, changeBuilder)
return objectTreeDeps{
changeBuilder: changeBuilder,
treeBuilder: treeBuilder,
treeStorage: treeStorage,
validator: newTreeValidator(),
rawChangeLoader: newStorageLoader(treeStorage, changeBuilder),
aclList: aclList,
}
}
func nonVerifiableTreeDeps(
rootChange *treechangeproto.RawTreeChangeWithId,
treeStorage treestorage.TreeStorage,
aclList list.AclList) objectTreeDeps {
changeBuilder := &nonVerifiableChangeBuilder{NewChangeBuilder(newMockKeyStorage(), rootChange)}
treeBuilder := newTreeBuilder(true, treeStorage, changeBuilder)
changeBuilder := &nonVerifiableChangeBuilder{NewChangeBuilder(nil, rootChange)}
treeBuilder := newTreeBuilder(treeStorage, changeBuilder)
return objectTreeDeps{
changeBuilder: changeBuilder,
treeBuilder: treeBuilder,
@@ -86,47 +65,17 @@ func nonVerifiableTreeDeps(
}
}
func BuildEmptyDataObjectTree(treeStorage treestorage.TreeStorage, aclList list.AclList) (ObjectTree, error) {
rootChange, err := treeStorage.Root()
func CreateObjectTreeRoot(payload ObjectTreeCreatePayload, aclList list.AclList) (root *treechangeproto.RawTreeChangeWithId, err error) {
bytes := make([]byte, 32)
_, err = rand.Read(bytes)
if err != nil {
return nil, err
return
}
deps := emptyDataTreeDeps(rootChange, treeStorage, aclList)
return buildObjectTree(deps)
return createObjectTreeRoot(payload, time.Now().Unix(), bytes, aclList)
}
func BuildTestableTree(treeStorage treestorage.TreeStorage, aclList list.AclList) (ObjectTree, error) {
root, _ := treeStorage.Root()
changeBuilder := &nonVerifiableChangeBuilder{
ChangeBuilder: NewChangeBuilder(newMockKeyStorage(), root),
}
deps := objectTreeDeps{
changeBuilder: changeBuilder,
treeBuilder: newTreeBuilder(true, treeStorage, changeBuilder),
treeStorage: treeStorage,
rawChangeLoader: newRawChangeLoader(treeStorage, changeBuilder),
validator: &noOpTreeValidator{},
aclList: aclList,
}
return buildObjectTree(deps)
}
func BuildEmptyDataTestableTree(treeStorage treestorage.TreeStorage, aclList list.AclList) (ObjectTree, error) {
root, _ := treeStorage.Root()
changeBuilder := &nonVerifiableChangeBuilder{
ChangeBuilder: NewChangeBuilder(newMockKeyStorage(), root),
}
deps := objectTreeDeps{
changeBuilder: changeBuilder,
treeBuilder: newTreeBuilder(false, treeStorage, changeBuilder),
treeStorage: treeStorage,
rawChangeLoader: newStorageLoader(treeStorage, changeBuilder),
validator: &noOpTreeValidator{},
aclList: aclList,
}
return buildObjectTree(deps)
func DeriveObjectTreeRoot(payload ObjectTreeCreatePayload, aclList list.AclList) (root *treechangeproto.RawTreeChangeWithId, err error) {
return createObjectTreeRoot(payload, 0, nil, aclList)
}
func BuildObjectTree(treeStorage treestorage.TreeStorage, aclList list.AclList) (ObjectTree, error) {
@@ -156,7 +105,54 @@ func BuildHistoryTree(params HistoryTreeParams) (HistoryTree, error) {
return buildHistoryTree(deps, params)
}
func CreateObjectTreeRoot(payload ObjectTreeCreatePayload, aclList list.AclList) (root *treechangeproto.RawTreeChangeWithId, err error) {
func CreateDerivedObjectTree(
payload ObjectTreeCreatePayload,
aclList list.AclList,
createStorage treestorage.TreeStorageCreatorFunc) (objTree ObjectTree, err error) {
return createObjectTree(payload, 0, nil, aclList, createStorage)
}
func CreateObjectTree(
payload ObjectTreeCreatePayload,
aclList list.AclList,
createStorage treestorage.TreeStorageCreatorFunc) (objTree ObjectTree, err error) {
bytes := make([]byte, 32)
_, err = rand.Read(bytes)
if err != nil {
return
}
return createObjectTree(payload, time.Now().Unix(), bytes, aclList, createStorage)
}
func createObjectTree(
payload ObjectTreeCreatePayload,
timestamp int64,
seed []byte,
aclList list.AclList,
createStorage treestorage.TreeStorageCreatorFunc) (objTree ObjectTree, err error) {
raw, err := createObjectTreeRoot(payload, timestamp, seed, aclList)
if err != nil {
return
}
// create storage
st, err := createStorage(treestorage.TreeStorageCreatePayload{
RootRawChange: raw,
Changes: []*treechangeproto.RawTreeChangeWithId{raw},
Heads: []string{raw.Id},
})
if err != nil {
return
}
return BuildObjectTree(st, aclList)
}
func createObjectTreeRoot(
payload ObjectTreeCreatePayload,
timestamp int64,
seed []byte,
aclList list.AclList) (root *treechangeproto.RawTreeChangeWithId, err error) {
aclList.RLock()
aclHeadId := aclList.Head().Id
aclList.RUnlock()
@@ -170,8 +166,8 @@ func CreateObjectTreeRoot(payload ObjectTreeCreatePayload, aclList list.AclList)
SpaceId: payload.SpaceId,
ChangeType: payload.ChangeType,
ChangePayload: payload.ChangePayload,
Timestamp: payload.Timestamp,
Seed: payload.Seed,
Timestamp: timestamp,
Seed: seed,
}
_, root, err = NewChangeBuilder(crypto.NewKeyStorage(), nil).BuildRoot(cnt)
@@ -231,7 +227,7 @@ func buildHistoryTree(deps objectTreeDeps, params HistoryTreeParams) (ht History
}
hTree := &historyTree{objectTree: objTree}
err = hTree.rebuildFromStorage(params)
err = hTree.rebuildFromStorage(params.BeforeId, params.IncludeBeforeId)
if err != nil {
return nil, err
}
@@ -1,12 +1,10 @@
package objecttree
import (
"context"
"fmt"
"github.com/anyproto/any-sync/commonspace/object/acl/list"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anyproto/any-sync/util/slice"
"github.com/anytypeio/any-sync/commonspace/object/acl/aclrecordproto"
"github.com/anytypeio/any-sync/commonspace/object/acl/list"
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
)
type ObjectTreeValidator interface {
@@ -52,18 +50,20 @@ func (v *objectTreeValidator) ValidateNewChanges(tree *Tree, aclList list.AclLis
func (v *objectTreeValidator) validateChange(tree *Tree, aclList list.AclList, c *Change) (err error) {
var (
userState list.AclUserState
state = aclList.AclState()
perm list.AclUserState
state = aclList.AclState()
)
// checking if the user could write
userState, err = state.StateAtRecord(c.AclHeadId, c.Identity)
perm, err = state.StateAtRecord(c.AclHeadId, c.Identity)
if err != nil {
return
}
if !userState.Permissions.CanWrite() {
if perm.Permissions != aclrecordproto.AclUserPermissions_Writer && perm.Permissions != aclrecordproto.AclUserPermissions_Admin {
err = list.ErrInsufficientPermissions
return
}
if c.Id == tree.RootId() {
return
}
@@ -88,23 +88,11 @@ func (v *objectTreeValidator) validateChange(tree *Tree, aclList list.AclList, c
}
func ValidateRawTree(payload treestorage.TreeStorageCreatePayload, aclList list.AclList) (err error) {
treeStorage, err := treestorage.NewInMemoryTreeStorage(payload.RootRawChange, []string{payload.RootRawChange.Id}, nil)
treeStorage, err := treestorage.NewInMemoryTreeStorage(payload.RootRawChange, payload.Heads, payload.Changes)
if err != nil {
return
}
tree, err := BuildObjectTree(treeStorage, aclList)
if err != nil {
return
}
res, err := tree.AddRawChanges(context.Background(), RawChangesPayload{
NewHeads: payload.Heads,
RawChanges: payload.Changes,
})
if err != nil {
return
}
if !slice.UnsortedEquals(res.Heads, payload.Heads) {
return ErrHasInvalidChanges
}
_, err = BuildObjectTree(treeStorage, aclList)
return
}
+18 -35
View File
@@ -2,17 +2,15 @@ package objecttree
import (
"context"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
"github.com/anytypeio/any-sync/util/slice"
"time"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anyproto/any-sync/util/slice"
)
type rawChangeLoader struct {
treeStorage treestorage.TreeStorage
changeBuilder ChangeBuilder
alwaysFromStorage bool
treeStorage treestorage.TreeStorage
changeBuilder ChangeBuilder
// buffers
idStack []string
@@ -23,13 +21,6 @@ type rawCacheEntry struct {
change *Change
rawChange *treechangeproto.RawTreeChangeWithId
position int
removed bool
}
func newStorageLoader(treeStorage treestorage.TreeStorage, changeBuilder ChangeBuilder) *rawChangeLoader {
loader := newRawChangeLoader(treeStorage, changeBuilder)
loader.alwaysFromStorage = true
return loader
}
func newRawChangeLoader(treeStorage treestorage.TreeStorage, changeBuilder ChangeBuilder) *rawChangeLoader {
@@ -39,15 +30,7 @@ func newRawChangeLoader(treeStorage treestorage.TreeStorage, changeBuilder Chang
}
}
func (r *rawChangeLoader) Load(commonSnapshot string, t *Tree, breakpoints []string) ([]*treechangeproto.RawTreeChangeWithId, error) {
if commonSnapshot == t.root.Id && !r.alwaysFromStorage {
return r.loadFromTree(t, breakpoints)
} else {
return r.loadFromStorage(commonSnapshot, t.Heads(), breakpoints)
}
}
func (r *rawChangeLoader) loadFromTree(t *Tree, breakpoints []string) ([]*treechangeproto.RawTreeChangeWithId, error) {
func (r *rawChangeLoader) LoadFromTree(t *Tree, breakpoints []string) ([]*treechangeproto.RawTreeChangeWithId, error) {
var stack []*Change
for _, h := range t.headIds {
stack = append(stack, t.attached[h])
@@ -115,7 +98,7 @@ func (r *rawChangeLoader) loadFromTree(t *Tree, breakpoints []string) ([]*treech
return convert(results)
}
func (r *rawChangeLoader) loadFromStorage(commonSnapshot string, heads, breakpoints []string) ([]*treechangeproto.RawTreeChangeWithId, error) {
func (r *rawChangeLoader) LoadFromStorage(commonSnapshot string, heads, breakpoints []string) ([]*treechangeproto.RawTreeChangeWithId, error) {
// resetting cache
r.cache = make(map[string]rawCacheEntry)
defer func() {
@@ -128,6 +111,7 @@ func (r *rawChangeLoader) loadFromStorage(commonSnapshot string, heads, breakpoi
if err != nil {
continue
}
entry.position = -1
r.cache[b] = entry
existingBreakpoints = append(existingBreakpoints, b)
}
@@ -136,7 +120,8 @@ func (r *rawChangeLoader) loadFromStorage(commonSnapshot string, heads, breakpoi
dfs := func(
commonSnapshot string,
heads []string,
shouldVisit func(entry rawCacheEntry, mapExists bool) bool,
startCounter int,
shouldVisit func(counter int, mapExists bool) bool,
visit func(entry rawCacheEntry) rawCacheEntry) bool {
// resetting stack
@@ -150,7 +135,7 @@ func (r *rawChangeLoader) loadFromStorage(commonSnapshot string, heads, breakpoi
r.idStack = r.idStack[:len(r.idStack)-1]
entry, exists := r.cache[id]
if !shouldVisit(entry, exists) {
if !shouldVisit(entry.position, exists) {
continue
}
if id == commonSnapshot {
@@ -159,6 +144,7 @@ func (r *rawChangeLoader) loadFromStorage(commonSnapshot string, heads, breakpoi
}
if !exists {
entry, err = r.loadEntry(id)
entry.position = -1
if err != nil {
continue
}
@@ -173,7 +159,7 @@ func (r *rawChangeLoader) loadFromStorage(commonSnapshot string, heads, breakpoi
break
}
prevEntry, exists := r.cache[prev]
if !shouldVisit(prevEntry, exists) {
if !shouldVisit(prevEntry.position, exists) {
continue
}
r.idStack = append(r.idStack, prev)
@@ -186,8 +172,8 @@ func (r *rawChangeLoader) loadFromStorage(commonSnapshot string, heads, breakpoi
r.idStack = append(r.idStack, heads...)
var buffer []*treechangeproto.RawTreeChangeWithId
rootVisited := dfs(commonSnapshot, heads,
func(_ rawCacheEntry, mapExists bool) bool {
rootVisited := dfs(commonSnapshot, heads, 0,
func(counter int, mapExists bool) bool {
return !mapExists
},
func(entry rawCacheEntry) rawCacheEntry {
@@ -212,13 +198,11 @@ func (r *rawChangeLoader) loadFromStorage(commonSnapshot string, heads, breakpoi
}
// marking all visited as nil
dfs(commonSnapshot, existingBreakpoints,
func(entry rawCacheEntry, mapExists bool) bool {
// only going through already loaded changes
return mapExists && !entry.removed
dfs(commonSnapshot, existingBreakpoints, len(buffer),
func(counter int, mapExists bool) bool {
return !mapExists || counter < len(buffer)
},
func(entry rawCacheEntry) rawCacheEntry {
entry.removed = true
if entry.position != -1 {
buffer[entry.position] = nil
}
@@ -249,7 +233,6 @@ func (r *rawChangeLoader) loadEntry(id string) (entry rawCacheEntry, err error)
entry = rawCacheEntry{
change: change,
rawChange: rawChange,
position: -1,
}
return
}
@@ -1,19 +1,12 @@
package objecttree
import (
"github.com/anyproto/any-sync/util/crypto"
"github.com/anytypeio/any-sync/util/crypto"
)
// SignableChangeContent is a payload to be passed when we are creating change
type SignableChangeContent struct {
// Data is a data provided by the client
Data []byte
// Key is the key which will be used to sign the change
Key crypto.PrivKey
// IsSnapshot tells if the change has snapshot of all previous data
IsSnapshot bool
// IsEncrypted tells if we encrypt the data with the relevant symmetric key
Data []byte
Key crypto.PrivKey
IsSnapshot bool
IsEncrypted bool
// Timestamp is a timestamp of change, if it is <= 0, then we use current timestamp
Timestamp int64
}
@@ -1,121 +0,0 @@
package objecttree
import (
"fmt"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anyproto/any-sync/util/crypto"
libcrypto "github.com/libp2p/go-libp2p/core/crypto"
)
type mockPubKey struct {
}
const mockKeyValue = "mockKey"
func (m mockPubKey) Equals(key crypto.Key) bool {
return true
}
func (m mockPubKey) Raw() ([]byte, error) {
return []byte(mockKeyValue), nil
}
func (m mockPubKey) Encrypt(message []byte) ([]byte, error) {
return message, nil
}
func (m mockPubKey) Verify(data []byte, sig []byte) (bool, error) {
return true, nil
}
func (m mockPubKey) Marshall() ([]byte, error) {
return []byte(mockKeyValue), nil
}
func (m mockPubKey) Storage() []byte {
return []byte(mockKeyValue)
}
func (m mockPubKey) Account() string {
return mockKeyValue
}
func (m mockPubKey) Network() string {
return mockKeyValue
}
func (m mockPubKey) PeerId() string {
return mockKeyValue
}
func (m mockPubKey) LibP2P() (libcrypto.PubKey, error) {
return nil, fmt.Errorf("can't be converted in libp2p")
}
type mockKeyStorage struct {
}
func newMockKeyStorage() mockKeyStorage {
return mockKeyStorage{}
}
func (m mockKeyStorage) PubKeyFromProto(protoBytes []byte) (crypto.PubKey, error) {
return mockPubKey{}, nil
}
type MockChangeCreator struct{}
func NewMockChangeCreator() *MockChangeCreator {
return &MockChangeCreator{}
}
func (c *MockChangeCreator) CreateRoot(id, aclId string) *treechangeproto.RawTreeChangeWithId {
aclChange := &treechangeproto.RootChange{
AclHeadId: aclId,
}
res, _ := aclChange.Marshal()
raw := &treechangeproto.RawTreeChange{
Payload: res,
Signature: nil,
}
rawMarshalled, _ := raw.Marshal()
return &treechangeproto.RawTreeChangeWithId{
RawChange: rawMarshalled,
Id: id,
}
}
func (c *MockChangeCreator) CreateRaw(id, aclId, snapshotId string, isSnapshot bool, prevIds ...string) *treechangeproto.RawTreeChangeWithId {
return c.CreateRawWithData(id, aclId, snapshotId, isSnapshot, nil, prevIds...)
}
func (c *MockChangeCreator) CreateRawWithData(id, aclId, snapshotId string, isSnapshot bool, data []byte, prevIds ...string) *treechangeproto.RawTreeChangeWithId {
aclChange := &treechangeproto.TreeChange{
TreeHeadIds: prevIds,
AclHeadId: aclId,
SnapshotBaseId: snapshotId,
ChangesData: data,
IsSnapshot: isSnapshot,
}
res, _ := aclChange.Marshal()
raw := &treechangeproto.RawTreeChange{
Payload: res,
Signature: nil,
}
rawMarshalled, _ := raw.Marshal()
return &treechangeproto.RawTreeChangeWithId{
RawChange: rawMarshalled,
Id: id,
}
}
func (c *MockChangeCreator) CreateNewTreeStorage(treeId, aclHeadId string) treestorage.TreeStorage {
root := c.CreateRoot(treeId, aclHeadId)
treeStorage, _ := treestorage.NewInMemoryTreeStorage(root, []string{root.Id}, []*treechangeproto.RawTreeChangeWithId{root})
return treeStorage
}
@@ -82,7 +82,6 @@ func (t *Tree) AddMergedHead(c *Change) error {
}
}
t.headIds = []string{c.Id}
t.lastIteratedHeadId = c.Id
return nil
}
@@ -2,12 +2,10 @@ package objecttree
import (
"fmt"
"github.com/stretchr/testify/assert"
"math/rand"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func newChange(id string, snapshotId string, prevIds ...string) *Change {
@@ -28,17 +26,6 @@ func newSnapshot(id, snapshotId string, prevIds ...string) *Change {
}
}
func TestTree_AddMergedHead(t *testing.T) {
tr := new(Tree)
_, _ = tr.Add(
newSnapshot("root", ""),
newChange("one", "root", "root"),
)
require.Equal(t, tr.lastIteratedHeadId, "one")
tr.AddMergedHead(newChange("two", "root", "one"))
require.Equal(t, tr.lastIteratedHeadId, "two")
}
func TestTree_Add(t *testing.T) {
t.Run("add first el", func(t *testing.T) {
tr := new(Tree)
@@ -6,9 +6,9 @@ import (
"fmt"
"time"
"github.com/anyproto/any-sync/app/logger"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anyproto/any-sync/util/slice"
"github.com/anytypeio/any-sync/app/logger"
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
"github.com/anytypeio/any-sync/util/slice"
"go.uber.org/zap"
)
@@ -21,20 +21,18 @@ type treeBuilder struct {
treeStorage treestorage.TreeStorage
builder ChangeBuilder
cache map[string]*Change
tree *Tree
keepInMemoryData bool
cache map[string]*Change
tree *Tree
// buffers
idStack []string
loadBuffer []*Change
}
func newTreeBuilder(keepData bool, storage treestorage.TreeStorage, builder ChangeBuilder) *treeBuilder {
func newTreeBuilder(storage treestorage.TreeStorage, builder ChangeBuilder) *treeBuilder {
return &treeBuilder{
treeStorage: storage,
builder: builder,
keepInMemoryData: keepData,
treeStorage: storage,
builder: builder,
}
}
@@ -51,27 +49,7 @@ func (tb *treeBuilder) Build(theirHeads []string, newChanges []*Change) (*Tree,
return tb.build(heads, theirHeads, newChanges)
}
func (tb *treeBuilder) BuildFull() (*Tree, error) {
defer func() {
tb.cache = make(map[string]*Change)
}()
tb.cache = make(map[string]*Change)
heads, err := tb.treeStorage.Heads()
if err != nil {
return nil, err
}
err = tb.buildTree(heads, tb.treeStorage.Id())
if err != nil {
return nil, err
}
return tb.tree, nil
}
func (tb *treeBuilder) build(heads []string, theirHeads []string, newChanges []*Change) (*Tree, error) {
defer func() {
tb.cache = make(map[string]*Change)
}()
var proposedHeads []string
tb.cache = make(map[string]*Change)
@@ -121,8 +99,8 @@ func (tb *treeBuilder) buildTree(heads []string, breakpoint string) (err error)
if err != nil {
return
}
changes := tb.dfs(heads, breakpoint)
tb.tree.AddFast(ch)
changes := tb.dfs(heads, breakpoint)
tb.tree.AddFast(changes...)
return
}
@@ -181,9 +159,6 @@ func (tb *treeBuilder) loadChange(id string) (ch *Change, err error) {
if err != nil {
return nil, err
}
if !tb.keepInMemoryData {
ch.Data = nil
}
tb.cache[id] = ch
return ch, nil
+1 -1
View File
@@ -2,7 +2,7 @@ package objecttree
import (
"fmt"
"github.com/anyproto/any-sync/util/crypto"
"github.com/anytypeio/any-sync/util/crypto"
"math/rand"
)
@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/anyproto/any-sync/commonspace/object/tree/synctree (interfaces: SyncTree,ReceiveQueue,HeadNotifiable,SyncClient,RequestFactory,TreeSyncProtocol)
// Source: github.com/anytypeio/any-sync/commonspace/object/tree/synctree (interfaces: SyncClient,SyncTree,ReceiveQueue,HeadNotifiable)
// Package mock_synctree is a generated GoMock package.
package mock_synctree
@@ -9,15 +9,124 @@ import (
reflect "reflect"
time "time"
list "github.com/anyproto/any-sync/commonspace/object/acl/list"
objecttree "github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
updatelistener "github.com/anyproto/any-sync/commonspace/object/tree/synctree/updatelistener"
treechangeproto "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
treestorage "github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
spacesyncproto "github.com/anyproto/any-sync/commonspace/spacesyncproto"
gomock "go.uber.org/mock/gomock"
list "github.com/anytypeio/any-sync/commonspace/object/acl/list"
objecttree "github.com/anytypeio/any-sync/commonspace/object/tree/objecttree"
updatelistener "github.com/anytypeio/any-sync/commonspace/object/tree/synctree/updatelistener"
treechangeproto "github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
treestorage "github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
spacesyncproto "github.com/anytypeio/any-sync/commonspace/spacesyncproto"
gomock "github.com/golang/mock/gomock"
)
// MockSyncClient is a mock of SyncClient interface.
type MockSyncClient struct {
ctrl *gomock.Controller
recorder *MockSyncClientMockRecorder
}
// MockSyncClientMockRecorder is the mock recorder for MockSyncClient.
type MockSyncClientMockRecorder struct {
mock *MockSyncClient
}
// NewMockSyncClient creates a new mock instance.
func NewMockSyncClient(ctrl *gomock.Controller) *MockSyncClient {
mock := &MockSyncClient{ctrl: ctrl}
mock.recorder = &MockSyncClientMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockSyncClient) EXPECT() *MockSyncClientMockRecorder {
return m.recorder
}
// Broadcast mocks base method.
func (m *MockSyncClient) Broadcast(arg0 context.Context, arg1 *treechangeproto.TreeSyncMessage) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Broadcast", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// Broadcast indicates an expected call of Broadcast.
func (mr *MockSyncClientMockRecorder) Broadcast(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Broadcast", reflect.TypeOf((*MockSyncClient)(nil).Broadcast), arg0, arg1)
}
// CreateFullSyncRequest mocks base method.
func (m *MockSyncClient) CreateFullSyncRequest(arg0 objecttree.ObjectTree, arg1, arg2 []string) (*treechangeproto.TreeSyncMessage, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateFullSyncRequest", arg0, arg1, arg2)
ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateFullSyncRequest indicates an expected call of CreateFullSyncRequest.
func (mr *MockSyncClientMockRecorder) CreateFullSyncRequest(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncRequest", reflect.TypeOf((*MockSyncClient)(nil).CreateFullSyncRequest), arg0, arg1, arg2)
}
// CreateFullSyncResponse mocks base method.
func (m *MockSyncClient) CreateFullSyncResponse(arg0 objecttree.ObjectTree, arg1, arg2 []string) (*treechangeproto.TreeSyncMessage, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateFullSyncResponse", arg0, arg1, arg2)
ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateFullSyncResponse indicates an expected call of CreateFullSyncResponse.
func (mr *MockSyncClientMockRecorder) CreateFullSyncResponse(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncResponse", reflect.TypeOf((*MockSyncClient)(nil).CreateFullSyncResponse), arg0, arg1, arg2)
}
// CreateHeadUpdate mocks base method.
func (m *MockSyncClient) CreateHeadUpdate(arg0 objecttree.ObjectTree, arg1 []*treechangeproto.RawTreeChangeWithId) *treechangeproto.TreeSyncMessage {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateHeadUpdate", arg0, arg1)
ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage)
return ret0
}
// CreateHeadUpdate indicates an expected call of CreateHeadUpdate.
func (mr *MockSyncClientMockRecorder) CreateHeadUpdate(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateHeadUpdate", reflect.TypeOf((*MockSyncClient)(nil).CreateHeadUpdate), arg0, arg1)
}
// CreateNewTreeRequest mocks base method.
func (m *MockSyncClient) CreateNewTreeRequest() *treechangeproto.TreeSyncMessage {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateNewTreeRequest")
ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage)
return ret0
}
// CreateNewTreeRequest indicates an expected call of CreateNewTreeRequest.
func (mr *MockSyncClientMockRecorder) CreateNewTreeRequest() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNewTreeRequest", reflect.TypeOf((*MockSyncClient)(nil).CreateNewTreeRequest))
}
// SendWithReply mocks base method.
func (m *MockSyncClient) SendWithReply(arg0 context.Context, arg1 string, arg2 *treechangeproto.TreeSyncMessage, arg3 string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SendWithReply", arg0, arg1, arg2, arg3)
ret0, _ := ret[0].(error)
return ret0
}
// SendWithReply indicates an expected call of SendWithReply.
func (mr *MockSyncClientMockRecorder) SendWithReply(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendWithReply", reflect.TypeOf((*MockSyncClient)(nil).SendWithReply), arg0, arg1, arg2, arg3)
}
// MockSyncTree is a mock of SyncTree interface.
type MockSyncTree struct {
ctrl *gomock.Controller
@@ -186,21 +295,6 @@ func (mr *MockSyncTreeMockRecorder) HandleMessage(arg0, arg1, arg2 interface{})
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleMessage", reflect.TypeOf((*MockSyncTree)(nil).HandleMessage), arg0, arg1, arg2)
}
// HandleRequest mocks base method.
func (m *MockSyncTree) HandleRequest(arg0 context.Context, arg1 string, arg2 *spacesyncproto.ObjectSyncMessage) (*spacesyncproto.ObjectSyncMessage, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "HandleRequest", arg0, arg1, arg2)
ret0, _ := ret[0].(*spacesyncproto.ObjectSyncMessage)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// HandleRequest indicates an expected call of HandleRequest.
func (mr *MockSyncTreeMockRecorder) HandleRequest(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleRequest", reflect.TypeOf((*MockSyncTree)(nil).HandleRequest), arg0, arg1, arg2)
}
// HasChanges mocks base method.
func (m *MockSyncTree) HasChanges(arg0 ...string) bool {
m.ctrl.T.Helper()
@@ -605,287 +699,3 @@ func (mr *MockHeadNotifiableMockRecorder) UpdateHeads(arg0, arg1 interface{}) *g
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHeads", reflect.TypeOf((*MockHeadNotifiable)(nil).UpdateHeads), arg0, arg1)
}
// MockSyncClient is a mock of SyncClient interface.
type MockSyncClient struct {
ctrl *gomock.Controller
recorder *MockSyncClientMockRecorder
}
// MockSyncClientMockRecorder is the mock recorder for MockSyncClient.
type MockSyncClientMockRecorder struct {
mock *MockSyncClient
}
// NewMockSyncClient creates a new mock instance.
func NewMockSyncClient(ctrl *gomock.Controller) *MockSyncClient {
mock := &MockSyncClient{ctrl: ctrl}
mock.recorder = &MockSyncClientMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockSyncClient) EXPECT() *MockSyncClientMockRecorder {
return m.recorder
}
// Broadcast mocks base method.
func (m *MockSyncClient) Broadcast(arg0 *treechangeproto.TreeSyncMessage) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Broadcast", arg0)
}
// Broadcast indicates an expected call of Broadcast.
func (mr *MockSyncClientMockRecorder) Broadcast(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Broadcast", reflect.TypeOf((*MockSyncClient)(nil).Broadcast), arg0)
}
// CreateFullSyncRequest mocks base method.
func (m *MockSyncClient) CreateFullSyncRequest(arg0 objecttree.ObjectTree, arg1, arg2 []string) (*treechangeproto.TreeSyncMessage, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateFullSyncRequest", arg0, arg1, arg2)
ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateFullSyncRequest indicates an expected call of CreateFullSyncRequest.
func (mr *MockSyncClientMockRecorder) CreateFullSyncRequest(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncRequest", reflect.TypeOf((*MockSyncClient)(nil).CreateFullSyncRequest), arg0, arg1, arg2)
}
// CreateFullSyncResponse mocks base method.
func (m *MockSyncClient) CreateFullSyncResponse(arg0 objecttree.ObjectTree, arg1, arg2 []string) (*treechangeproto.TreeSyncMessage, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateFullSyncResponse", arg0, arg1, arg2)
ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateFullSyncResponse indicates an expected call of CreateFullSyncResponse.
func (mr *MockSyncClientMockRecorder) CreateFullSyncResponse(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncResponse", reflect.TypeOf((*MockSyncClient)(nil).CreateFullSyncResponse), arg0, arg1, arg2)
}
// CreateHeadUpdate mocks base method.
func (m *MockSyncClient) CreateHeadUpdate(arg0 objecttree.ObjectTree, arg1 []*treechangeproto.RawTreeChangeWithId) *treechangeproto.TreeSyncMessage {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateHeadUpdate", arg0, arg1)
ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage)
return ret0
}
// CreateHeadUpdate indicates an expected call of CreateHeadUpdate.
func (mr *MockSyncClientMockRecorder) CreateHeadUpdate(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateHeadUpdate", reflect.TypeOf((*MockSyncClient)(nil).CreateHeadUpdate), arg0, arg1)
}
// CreateNewTreeRequest mocks base method.
func (m *MockSyncClient) CreateNewTreeRequest() *treechangeproto.TreeSyncMessage {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateNewTreeRequest")
ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage)
return ret0
}
// CreateNewTreeRequest indicates an expected call of CreateNewTreeRequest.
func (mr *MockSyncClientMockRecorder) CreateNewTreeRequest() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNewTreeRequest", reflect.TypeOf((*MockSyncClient)(nil).CreateNewTreeRequest))
}
// QueueRequest mocks base method.
func (m *MockSyncClient) QueueRequest(arg0, arg1 string, arg2 *treechangeproto.TreeSyncMessage) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "QueueRequest", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// QueueRequest indicates an expected call of QueueRequest.
func (mr *MockSyncClientMockRecorder) QueueRequest(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueueRequest", reflect.TypeOf((*MockSyncClient)(nil).QueueRequest), arg0, arg1, arg2)
}
// SendRequest mocks base method.
func (m *MockSyncClient) SendRequest(arg0 context.Context, arg1, arg2 string, arg3 *treechangeproto.TreeSyncMessage) (*spacesyncproto.ObjectSyncMessage, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SendRequest", arg0, arg1, arg2, arg3)
ret0, _ := ret[0].(*spacesyncproto.ObjectSyncMessage)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// SendRequest indicates an expected call of SendRequest.
func (mr *MockSyncClientMockRecorder) SendRequest(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendRequest", reflect.TypeOf((*MockSyncClient)(nil).SendRequest), arg0, arg1, arg2, arg3)
}
// SendUpdate mocks base method.
func (m *MockSyncClient) SendUpdate(arg0, arg1 string, arg2 *treechangeproto.TreeSyncMessage) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SendUpdate", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// SendUpdate indicates an expected call of SendUpdate.
func (mr *MockSyncClientMockRecorder) SendUpdate(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendUpdate", reflect.TypeOf((*MockSyncClient)(nil).SendUpdate), arg0, arg1, arg2)
}
// MockRequestFactory is a mock of RequestFactory interface.
type MockRequestFactory struct {
ctrl *gomock.Controller
recorder *MockRequestFactoryMockRecorder
}
// MockRequestFactoryMockRecorder is the mock recorder for MockRequestFactory.
type MockRequestFactoryMockRecorder struct {
mock *MockRequestFactory
}
// NewMockRequestFactory creates a new mock instance.
func NewMockRequestFactory(ctrl *gomock.Controller) *MockRequestFactory {
mock := &MockRequestFactory{ctrl: ctrl}
mock.recorder = &MockRequestFactoryMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockRequestFactory) EXPECT() *MockRequestFactoryMockRecorder {
return m.recorder
}
// CreateFullSyncRequest mocks base method.
func (m *MockRequestFactory) CreateFullSyncRequest(arg0 objecttree.ObjectTree, arg1, arg2 []string) (*treechangeproto.TreeSyncMessage, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateFullSyncRequest", arg0, arg1, arg2)
ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateFullSyncRequest indicates an expected call of CreateFullSyncRequest.
func (mr *MockRequestFactoryMockRecorder) CreateFullSyncRequest(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncRequest", reflect.TypeOf((*MockRequestFactory)(nil).CreateFullSyncRequest), arg0, arg1, arg2)
}
// CreateFullSyncResponse mocks base method.
func (m *MockRequestFactory) CreateFullSyncResponse(arg0 objecttree.ObjectTree, arg1, arg2 []string) (*treechangeproto.TreeSyncMessage, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateFullSyncResponse", arg0, arg1, arg2)
ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// CreateFullSyncResponse indicates an expected call of CreateFullSyncResponse.
func (mr *MockRequestFactoryMockRecorder) CreateFullSyncResponse(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFullSyncResponse", reflect.TypeOf((*MockRequestFactory)(nil).CreateFullSyncResponse), arg0, arg1, arg2)
}
// CreateHeadUpdate mocks base method.
func (m *MockRequestFactory) CreateHeadUpdate(arg0 objecttree.ObjectTree, arg1 []*treechangeproto.RawTreeChangeWithId) *treechangeproto.TreeSyncMessage {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateHeadUpdate", arg0, arg1)
ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage)
return ret0
}
// CreateHeadUpdate indicates an expected call of CreateHeadUpdate.
func (mr *MockRequestFactoryMockRecorder) CreateHeadUpdate(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateHeadUpdate", reflect.TypeOf((*MockRequestFactory)(nil).CreateHeadUpdate), arg0, arg1)
}
// CreateNewTreeRequest mocks base method.
func (m *MockRequestFactory) CreateNewTreeRequest() *treechangeproto.TreeSyncMessage {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateNewTreeRequest")
ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage)
return ret0
}
// CreateNewTreeRequest indicates an expected call of CreateNewTreeRequest.
func (mr *MockRequestFactoryMockRecorder) CreateNewTreeRequest() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNewTreeRequest", reflect.TypeOf((*MockRequestFactory)(nil).CreateNewTreeRequest))
}
// MockTreeSyncProtocol is a mock of TreeSyncProtocol interface.
type MockTreeSyncProtocol struct {
ctrl *gomock.Controller
recorder *MockTreeSyncProtocolMockRecorder
}
// MockTreeSyncProtocolMockRecorder is the mock recorder for MockTreeSyncProtocol.
type MockTreeSyncProtocolMockRecorder struct {
mock *MockTreeSyncProtocol
}
// NewMockTreeSyncProtocol creates a new mock instance.
func NewMockTreeSyncProtocol(ctrl *gomock.Controller) *MockTreeSyncProtocol {
mock := &MockTreeSyncProtocol{ctrl: ctrl}
mock.recorder = &MockTreeSyncProtocolMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockTreeSyncProtocol) EXPECT() *MockTreeSyncProtocolMockRecorder {
return m.recorder
}
// FullSyncRequest mocks base method.
func (m *MockTreeSyncProtocol) FullSyncRequest(arg0 context.Context, arg1 string, arg2 *treechangeproto.TreeFullSyncRequest) (*treechangeproto.TreeSyncMessage, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FullSyncRequest", arg0, arg1, arg2)
ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// FullSyncRequest indicates an expected call of FullSyncRequest.
func (mr *MockTreeSyncProtocolMockRecorder) FullSyncRequest(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FullSyncRequest", reflect.TypeOf((*MockTreeSyncProtocol)(nil).FullSyncRequest), arg0, arg1, arg2)
}
// FullSyncResponse mocks base method.
func (m *MockTreeSyncProtocol) FullSyncResponse(arg0 context.Context, arg1 string, arg2 *treechangeproto.TreeFullSyncResponse) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "FullSyncResponse", arg0, arg1, arg2)
ret0, _ := ret[0].(error)
return ret0
}
// FullSyncResponse indicates an expected call of FullSyncResponse.
func (mr *MockTreeSyncProtocolMockRecorder) FullSyncResponse(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FullSyncResponse", reflect.TypeOf((*MockTreeSyncProtocol)(nil).FullSyncResponse), arg0, arg1, arg2)
}
// HeadUpdate mocks base method.
func (m *MockTreeSyncProtocol) HeadUpdate(arg0 context.Context, arg1 string, arg2 *treechangeproto.TreeHeadUpdate) (*treechangeproto.TreeSyncMessage, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "HeadUpdate", arg0, arg1, arg2)
ret0, _ := ret[0].(*treechangeproto.TreeSyncMessage)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// HeadUpdate indicates an expected call of HeadUpdate.
func (mr *MockTreeSyncProtocolMockRecorder) HeadUpdate(arg0, arg1, arg2 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HeadUpdate", reflect.TypeOf((*MockTreeSyncProtocol)(nil).HeadUpdate), arg0, arg1, arg2)
}
@@ -1,362 +0,0 @@
package synctree
import (
"context"
"math/rand"
"testing"
"time"
"github.com/anyproto/any-sync/commonspace/object/accountdata"
"github.com/anyproto/any-sync/commonspace/object/acl/list"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anyproto/any-sync/util/slice"
"github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/require"
)
func TestEmptyClientGetsFullHistory(t *testing.T) {
treeId := "treeId"
spaceId := "spaceId"
keys, err := accountdata.NewRandom()
require.NoError(t, err)
aclList, err := list.NewTestDerivedAcl(spaceId, keys)
require.NoError(t, err)
storage := createStorage(treeId, aclList)
changeCreator := objecttree.NewMockChangeCreator()
deps := fixtureDeps{
aclList: aclList,
initStorage: storage.(*treestorage.InMemoryTreeStorage),
connectionMap: map[string][]string{
"peer1": []string{"peer2"},
"peer2": []string{"peer1"},
},
emptyTrees: []string{"peer2"},
treeBuilder: objecttree.BuildTestableTree,
}
fx := newProtocolFixture(t, spaceId, deps)
fx.run(t)
fx.handlers["peer1"].sendRawChanges(context.Background(), objecttree.RawChangesPayload{
NewHeads: nil,
RawChanges: []*treechangeproto.RawTreeChangeWithId{
changeCreator.CreateRaw("1", aclList.Id(), treeId, true, treeId),
},
})
time.Sleep(100 * time.Millisecond)
fx.stop()
firstHeads := fx.handlers["peer1"].tree().Heads()
secondHeads := fx.handlers["peer2"].tree().Heads()
require.True(t, slice.UnsortedEquals(firstHeads, secondHeads))
require.Equal(t, []string{"1"}, firstHeads)
logMsgs := fx.log.batcher.GetAll()
var fullResponseMsg msgDescription
for _, msg := range logMsgs {
descr := msg.description()
if descr.name == "FullSyncResponse" {
fullResponseMsg = descr
}
}
// that means that we got not only the last snapshot, but also the first one
require.Len(t, fullResponseMsg.changes, 2)
}
func TestTreeFuzzyMerge(t *testing.T) {
testTreeFuzzyMerge(t, true)
testTreeFuzzyMerge(t, false)
}
func testTreeFuzzyMerge(t *testing.T, withData bool) {
var (
rnd = rand.New(rand.NewSource(time.Now().Unix()))
levels = 20
perLevel = 20
rounds = 10
)
for i := 0; i < rounds; i++ {
testTreeMerge(t, levels, perLevel, withData, func() bool {
return true
})
testTreeMerge(t, levels, perLevel, withData, func() bool {
return false
})
testTreeMerge(t, levels, perLevel, withData, func() bool {
return rnd.Intn(10) > 8
})
levels += 2
}
}
func testTreeMerge(t *testing.T, levels, perLevel int, hasData bool, isSnapshot func() bool) {
treeId := "treeId"
spaceId := "spaceId"
keys, err := accountdata.NewRandom()
require.NoError(t, err)
aclList, err := list.NewTestDerivedAcl(spaceId, keys)
storage := createStorage(treeId, aclList)
changeCreator := objecttree.NewMockChangeCreator()
builder := objecttree.BuildTestableTree
if hasData {
builder = objecttree.BuildEmptyDataTestableTree
}
params := genParams{
prefix: "peer1",
aclId: aclList.Id(),
startIdx: 0,
levels: levels,
perLevel: perLevel,
snapshotId: treeId,
prevHeads: []string{treeId},
isSnapshot: isSnapshot,
hasData: hasData,
}
// generating initial tree
initialRes := genChanges(changeCreator, params)
err = storage.AddRawChangesSetHeads(initialRes.changes, initialRes.heads)
require.NoError(t, err)
deps := fixtureDeps{
aclList: aclList,
initStorage: storage.(*treestorage.InMemoryTreeStorage),
connectionMap: map[string][]string{
"peer1": []string{"node1"},
"peer2": []string{"node1"},
"node1": []string{"peer1", "peer2"},
},
emptyTrees: []string{"peer2", "node1"},
treeBuilder: builder,
}
fx := newProtocolFixture(t, spaceId, deps)
fx.run(t)
fx.handlers["peer1"].sendRawChanges(context.Background(), objecttree.RawChangesPayload{
NewHeads: initialRes.heads,
RawChanges: initialRes.changes,
})
time.Sleep(50 * time.Millisecond)
firstHeads := fx.handlers["peer1"].tree().Heads()
secondHeads := fx.handlers["peer2"].tree().Heads()
require.True(t, slice.UnsortedEquals(firstHeads, secondHeads))
params = genParams{
prefix: "peer1",
aclId: aclList.Id(),
startIdx: levels,
levels: levels,
perLevel: perLevel,
snapshotId: initialRes.snapshotId,
prevHeads: initialRes.heads,
isSnapshot: isSnapshot,
hasData: hasData,
}
// generating different additions to the tree for different peers
peer1Res := genChanges(changeCreator, params)
params.prefix = "peer2"
peer2Res := genChanges(changeCreator, params)
fx.handlers["peer1"].sendRawChanges(context.Background(), objecttree.RawChangesPayload{
NewHeads: peer1Res.heads,
RawChanges: peer1Res.changes,
})
fx.handlers["peer2"].sendRawChanges(context.Background(), objecttree.RawChangesPayload{
NewHeads: peer2Res.heads,
RawChanges: peer2Res.changes,
})
time.Sleep(50 * time.Millisecond)
fx.stop()
firstTree := fx.handlers["peer1"].tree()
secondTree := fx.handlers["peer2"].tree()
firstHeads = firstTree.Heads()
secondHeads = secondTree.Heads()
require.True(t, slice.UnsortedEquals(firstHeads, secondHeads))
require.True(t, slice.UnsortedEquals(firstHeads, append(peer1Res.heads, peer2Res.heads...)))
firstStorage := firstTree.Storage().(*treestorage.InMemoryTreeStorage)
secondStorage := secondTree.Storage().(*treestorage.InMemoryTreeStorage)
require.True(t, firstStorage.Equal(secondStorage))
if hasData {
for _, ch := range secondStorage.Changes {
if ch.Id == treeId {
continue
}
unmarshallRaw := &treechangeproto.RawTreeChange{}
proto.Unmarshal(ch.RawChange, unmarshallRaw)
treeCh := &treechangeproto.TreeChange{}
proto.Unmarshal(unmarshallRaw.Payload, treeCh)
require.Equal(t, ch.Id, string(treeCh.ChangesData))
}
}
}
func TestTreeStorageHasExtraChanges(t *testing.T) {
var (
rnd = rand.New(rand.NewSource(time.Now().Unix()))
levels = 20
perLevel = 40
)
// simulating cases where one peer has some extra changes saved in storage
// and checking that this will not break the sync
t.Run("tree storage has extra simple", func(t *testing.T) {
testTreeStorageHasExtra(t, levels, perLevel, false, func() bool {
return false
})
testTreeStorageHasExtra(t, levels, perLevel, false, func() bool {
return rnd.Intn(10) > 5
})
testTreeStorageHasExtra(t, levels, perLevel, true, func() bool {
return false
})
testTreeStorageHasExtra(t, levels, perLevel, true, func() bool {
return rnd.Intn(10) > 5
})
})
t.Run("tree storage has extra three parts", func(t *testing.T) {
testTreeStorageHasExtraThreeParts(t, levels, perLevel, false, func() bool {
return false
})
testTreeStorageHasExtraThreeParts(t, levels, perLevel, false, func() bool {
return rnd.Intn(10) > 5
})
testTreeStorageHasExtraThreeParts(t, levels, perLevel, true, func() bool {
return false
})
testTreeStorageHasExtraThreeParts(t, levels, perLevel, true, func() bool {
return rnd.Intn(10) > 5
})
})
}
func testTreeStorageHasExtra(t *testing.T, levels, perLevel int, hasData bool, isSnapshot func() bool) {
treeId := "treeId"
spaceId := "spaceId"
keys, err := accountdata.NewRandom()
require.NoError(t, err)
aclList, err := list.NewTestDerivedAcl(spaceId, keys)
storage := createStorage(treeId, aclList)
changeCreator := objecttree.NewMockChangeCreator()
builder := objecttree.BuildTestableTree
if hasData {
builder = objecttree.BuildEmptyDataTestableTree
}
params := genParams{
prefix: "peer1",
aclId: aclList.Id(),
startIdx: 0,
levels: levels,
perLevel: perLevel,
snapshotId: treeId,
prevHeads: []string{treeId},
isSnapshot: isSnapshot,
hasData: hasData,
}
deps := fixtureDeps{
aclList: aclList,
initStorage: storage.(*treestorage.InMemoryTreeStorage),
connectionMap: map[string][]string{
"peer1": []string{"peer2"},
"peer2": []string{"peer1"},
},
treeBuilder: builder,
}
fx := newProtocolFixture(t, spaceId, deps)
// generating initial tree
initialRes := genChanges(changeCreator, params)
fx.run(t)
// adding some changes to store, but without updating heads
store := fx.handlers["peer1"].tree().Storage().(*treestorage.InMemoryTreeStorage)
oldHeads, _ := store.Heads()
store.AddRawChangesSetHeads(initialRes.changes, oldHeads)
// sending those changes to other peer
fx.handlers["peer2"].sendRawChanges(context.Background(), objecttree.RawChangesPayload{
NewHeads: initialRes.heads,
RawChanges: initialRes.changes,
})
time.Sleep(50 * time.Millisecond)
// here we want that the saved changes in storage should not affect the sync protocol
firstHeads := fx.handlers["peer1"].tree().Heads()
secondHeads := fx.handlers["peer2"].tree().Heads()
require.True(t, slice.UnsortedEquals(firstHeads, secondHeads))
require.True(t, slice.UnsortedEquals(initialRes.heads, firstHeads))
}
func testTreeStorageHasExtraThreeParts(t *testing.T, levels, perLevel int, hasData bool, isSnapshot func() bool) {
treeId := "treeId"
spaceId := "spaceId"
keys, err := accountdata.NewRandom()
require.NoError(t, err)
aclList, err := list.NewTestDerivedAcl(spaceId, keys)
storage := createStorage(treeId, aclList)
changeCreator := objecttree.NewMockChangeCreator()
builder := objecttree.BuildTestableTree
if hasData {
builder = objecttree.BuildEmptyDataTestableTree
}
params := genParams{
prefix: "peer1",
aclId: aclList.Id(),
startIdx: 0,
levels: levels,
perLevel: perLevel,
snapshotId: treeId,
prevHeads: []string{treeId},
isSnapshot: isSnapshot,
hasData: hasData,
}
deps := fixtureDeps{
aclList: aclList,
initStorage: storage.(*treestorage.InMemoryTreeStorage),
connectionMap: map[string][]string{
"peer1": []string{"peer2"},
"peer2": []string{"peer1"},
},
treeBuilder: builder,
}
fx := newProtocolFixture(t, spaceId, deps)
// generating parts
firstPart := genChanges(changeCreator, params)
params.startIdx = levels
params.snapshotId = firstPart.snapshotId
params.prevHeads = firstPart.heads
secondPart := genChanges(changeCreator, params)
params.startIdx = levels * 2
params.snapshotId = secondPart.snapshotId
params.prevHeads = secondPart.heads
thirdPart := genChanges(changeCreator, params)
// adding part1 to first peer and saving part2 and part3 in its storage
res, _ := fx.handlers["peer1"].tree().AddRawChanges(context.Background(), objecttree.RawChangesPayload{
NewHeads: firstPart.heads,
RawChanges: firstPart.changes,
})
require.True(t, slice.UnsortedEquals(res.Heads, firstPart.heads))
store := fx.handlers["peer1"].tree().Storage().(*treestorage.InMemoryTreeStorage)
oldHeads, _ := store.Heads()
store.AddRawChangesSetHeads(secondPart.changes, oldHeads)
store.AddRawChangesSetHeads(thirdPart.changes, oldHeads)
var peer2Initial []*treechangeproto.RawTreeChangeWithId
peer2Initial = append(peer2Initial, firstPart.changes...)
peer2Initial = append(peer2Initial, secondPart.changes...)
// adding part1 and part2 to second peer
res, _ = fx.handlers["peer2"].tree().AddRawChanges(context.Background(), objecttree.RawChangesPayload{
NewHeads: secondPart.heads,
RawChanges: peer2Initial,
})
require.True(t, slice.UnsortedEquals(res.Heads, secondPart.heads))
fx.run(t)
// sending part3 changes to other peer
fx.handlers["peer2"].sendRawChanges(context.Background(), objecttree.RawChangesPayload{
NewHeads: thirdPart.heads,
RawChanges: thirdPart.changes,
})
time.Sleep(50 * time.Millisecond)
firstHeads := fx.handlers["peer1"].tree().Heads()
secondHeads := fx.handlers["peer2"].tree().Heads()
require.True(t, slice.UnsortedEquals(firstHeads, secondHeads))
require.True(t, slice.UnsortedEquals(thirdPart.heads, firstHeads))
}
@@ -2,7 +2,7 @@ package synctree
import (
"errors"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"sync"
)
@@ -2,9 +2,9 @@ package synctree
import (
"fmt"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/util/slice"
"github.com/anytypeio/any-sync/commonspace/object/tree/objecttree"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/util/slice"
)
type RequestFactory interface {
@@ -14,8 +14,10 @@ type RequestFactory interface {
CreateFullSyncResponse(t objecttree.ObjectTree, theirHeads, theirSnapshotPath []string) (*treechangeproto.TreeSyncMessage, error)
}
func NewRequestFactory() RequestFactory {
return &requestFactory{}
var sharedFactory = &requestFactory{}
func GetRequestFactory() RequestFactory {
return sharedFactory
}
type requestFactory struct{}
+25 -41
View File
@@ -1,73 +1,57 @@
//go:generate mockgen -destination mock_synctree/mock_synctree.go github.com/anytypeio/any-sync/commonspace/object/tree/synctree SyncClient,SyncTree,ReceiveQueue,HeadNotifiable
package synctree
import (
"context"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/peermanager"
"github.com/anyproto/any-sync/commonspace/requestmanager"
"github.com/anyproto/any-sync/commonspace/spacesyncproto"
"go.uber.org/zap"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/commonspace/objectsync"
"github.com/anytypeio/any-sync/commonspace/spacesyncproto"
"github.com/anytypeio/any-sync/nodeconf"
)
type SyncClient interface {
RequestFactory
Broadcast(msg *treechangeproto.TreeSyncMessage)
SendUpdate(peerId, objectId string, msg *treechangeproto.TreeSyncMessage) (err error)
QueueRequest(peerId, objectId string, msg *treechangeproto.TreeSyncMessage) (err error)
SendRequest(ctx context.Context, peerId, objectId string, msg *treechangeproto.TreeSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error)
Broadcast(ctx context.Context, msg *treechangeproto.TreeSyncMessage) (err error)
SendWithReply(ctx context.Context, peerId string, msg *treechangeproto.TreeSyncMessage, replyId string) (err error)
}
type syncClient struct {
objectsync.MessagePool
RequestFactory
spaceId string
requestManager requestmanager.RequestManager
peerManager peermanager.PeerManager
spaceId string
configuration nodeconf.Configuration
}
func NewSyncClient(spaceId string, requestManager requestmanager.RequestManager, peerManager peermanager.PeerManager) SyncClient {
func newSyncClient(
spaceId string,
pool objectsync.MessagePool,
factory RequestFactory,
configuration nodeconf.Configuration) SyncClient {
return &syncClient{
RequestFactory: &requestFactory{},
MessagePool: pool,
RequestFactory: factory,
configuration: configuration,
spaceId: spaceId,
requestManager: requestManager,
peerManager: peerManager,
}
}
func (s *syncClient) Broadcast(msg *treechangeproto.TreeSyncMessage) {
objMsg, err := MarshallTreeMessage(msg, s.spaceId, msg.RootChange.Id, "")
if err != nil {
return
}
err = s.peerManager.Broadcast(context.Background(), objMsg)
if err != nil {
log.Debug("broadcast error", zap.Error(err))
}
}
func (s *syncClient) SendUpdate(peerId, objectId string, msg *treechangeproto.TreeSyncMessage) (err error) {
objMsg, err := MarshallTreeMessage(msg, s.spaceId, objectId, "")
func (s *syncClient) Broadcast(ctx context.Context, msg *treechangeproto.TreeSyncMessage) (err error) {
objMsg, err := marshallTreeMessage(msg, s.spaceId, msg.RootChange.Id, "")
if err != nil {
return
}
return s.peerManager.SendPeer(context.Background(), peerId, objMsg)
return s.MessagePool.Broadcast(ctx, objMsg)
}
func (s *syncClient) SendRequest(ctx context.Context, peerId, objectId string, msg *treechangeproto.TreeSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error) {
objMsg, err := MarshallTreeMessage(msg, s.spaceId, objectId, "")
func (s *syncClient) SendWithReply(ctx context.Context, peerId string, msg *treechangeproto.TreeSyncMessage, replyId string) (err error) {
objMsg, err := marshallTreeMessage(msg, s.spaceId, msg.RootChange.Id, replyId)
if err != nil {
return
}
return s.requestManager.SendRequest(ctx, peerId, objMsg)
return s.MessagePool.SendPeer(ctx, peerId, objMsg)
}
func (s *syncClient) QueueRequest(peerId, objectId string, msg *treechangeproto.TreeSyncMessage) (err error) {
objMsg, err := MarshallTreeMessage(msg, s.spaceId, objectId, "")
if err != nil {
return
}
return s.requestManager.QueueRequest(peerId, objMsg)
}
func MarshallTreeMessage(message *treechangeproto.TreeSyncMessage, spaceId, objectId, replyId string) (objMsg *spacesyncproto.ObjectSyncMessage, err error) {
func marshallTreeMessage(message *treechangeproto.TreeSyncMessage, spaceId, objectId, replyId string) (objMsg *spacesyncproto.ObjectSyncMessage, err error) {
payload, err := message.Marshal()
if err != nil {
return
+47 -50
View File
@@ -1,4 +1,3 @@
//go:generate mockgen -destination mock_synctree/mock_synctree.go github.com/anyproto/any-sync/commonspace/object/tree/synctree SyncTree,ReceiveQueue,HeadNotifiable,SyncClient,RequestFactory,TreeSyncProtocol
package synctree
import (
@@ -6,16 +5,17 @@ import (
"errors"
"time"
"github.com/anyproto/any-sync/app/logger"
"github.com/anyproto/any-sync/commonspace/object/acl/list"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
"github.com/anyproto/any-sync/commonspace/object/tree/synctree/updatelistener"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anyproto/any-sync/commonspace/objectsync/synchandler"
"github.com/anyproto/any-sync/commonspace/spacestorage"
"github.com/anyproto/any-sync/commonspace/syncstatus"
"github.com/anyproto/any-sync/net/peer"
"github.com/anyproto/any-sync/nodeconf"
"github.com/anytypeio/any-sync/app/logger"
"github.com/anytypeio/any-sync/commonspace/object/acl/list"
"github.com/anytypeio/any-sync/commonspace/object/tree/objecttree"
"github.com/anytypeio/any-sync/commonspace/object/tree/synctree/updatelistener"
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
"github.com/anytypeio/any-sync/commonspace/objectsync"
"github.com/anytypeio/any-sync/commonspace/objectsync/synchandler"
"github.com/anytypeio/any-sync/commonspace/spacestorage"
"github.com/anytypeio/any-sync/commonspace/syncstatus"
"github.com/anytypeio/any-sync/net/peer"
"github.com/anytypeio/any-sync/nodeconf"
"go.uber.org/zap"
)
@@ -54,35 +54,35 @@ type syncTree struct {
var log = logger.NewNamed("common.commonspace.synctree")
var buildObjectTree = objecttree.BuildObjectTree
var createSyncClient = newSyncClient
type ResponsiblePeersGetter interface {
GetResponsiblePeers(ctx context.Context) (peers []peer.Peer, err error)
}
type BuildDeps struct {
SpaceId string
SyncClient SyncClient
Configuration nodeconf.NodeConf
HeadNotifiable HeadNotifiable
Listener updatelistener.UpdateListener
AclList list.AclList
SpaceStorage spacestorage.SpaceStorage
TreeStorage treestorage.TreeStorage
OnClose func(id string)
SyncStatus syncstatus.StatusUpdater
PeerGetter ResponsiblePeersGetter
BuildObjectTree objecttree.BuildObjectTreeFunc
SpaceId string
ObjectSync objectsync.ObjectSync
Configuration nodeconf.Configuration
HeadNotifiable HeadNotifiable
Listener updatelistener.UpdateListener
AclList list.AclList
SpaceStorage spacestorage.SpaceStorage
TreeStorage treestorage.TreeStorage
OnClose func(id string)
SyncStatus syncstatus.StatusUpdater
PeerGetter ResponsiblePeersGetter
WaitTreeRemoteSync bool
}
func BuildSyncTreeOrGetRemote(ctx context.Context, id string, deps BuildDeps) (t SyncTree, err error) {
var (
remoteGetter = treeRemoteGetter{treeId: id, deps: deps}
isRemote bool
)
deps.TreeStorage, isRemote, err = remoteGetter.getTree(ctx)
remoteGetter := treeRemoteGetter{treeId: id, deps: deps}
deps.TreeStorage, err = remoteGetter.getTree(ctx)
if err != nil {
return
}
return buildSyncTree(ctx, isRemote, deps)
return buildSyncTree(ctx, true, deps)
}
func PutSyncTree(ctx context.Context, payload treestorage.TreeStorageCreatePayload, deps BuildDeps) (t SyncTree, err error) {
@@ -93,12 +93,16 @@ func PutSyncTree(ctx context.Context, payload treestorage.TreeStorageCreatePaylo
return buildSyncTree(ctx, true, deps)
}
func buildSyncTree(ctx context.Context, sendUpdate bool, deps BuildDeps) (t SyncTree, err error) {
objTree, err := deps.BuildObjectTree(deps.TreeStorage, deps.AclList)
func buildSyncTree(ctx context.Context, isFirstBuild bool, deps BuildDeps) (t SyncTree, err error) {
objTree, err := buildObjectTree(deps.TreeStorage, deps.AclList)
if err != nil {
return
}
syncClient := deps.SyncClient
syncClient := createSyncClient(
deps.SpaceId,
deps.ObjectSync.MessagePool(),
sharedFactory,
deps.Configuration)
syncTree := &syncTree{
ObjectTree: objTree,
syncClient: syncClient,
@@ -114,10 +118,12 @@ func buildSyncTree(ctx context.Context, sendUpdate bool, deps BuildDeps) (t Sync
syncTree.afterBuild()
syncTree.Unlock()
if sendUpdate {
if isFirstBuild {
headUpdate := syncTree.syncClient.CreateHeadUpdate(t, nil)
// send to everybody, because everybody should know that the node or client got new tree
syncTree.syncClient.Broadcast(headUpdate)
if e := syncTree.syncClient.Broadcast(ctx, headUpdate); e != nil {
log.ErrorCtx(ctx, "broadcast error", zap.Error(e))
}
}
return
}
@@ -154,7 +160,7 @@ func (s *syncTree) AddContent(ctx context.Context, content objecttree.SignableCh
}
s.syncStatus.HeadsChange(s.Id(), res.Heads)
headUpdate := s.syncClient.CreateHeadUpdate(s, res.Added)
s.syncClient.Broadcast(headUpdate)
err = s.syncClient.Broadcast(ctx, headUpdate)
return
}
@@ -181,7 +187,7 @@ func (s *syncTree) AddRawChanges(ctx context.Context, changesPayload objecttree.
s.notifiable.UpdateHeads(s.Id(), res.Heads)
}
headUpdate := s.syncClient.CreateHeadUpdate(s, res.Added)
s.syncClient.Broadcast(headUpdate)
err = s.syncClient.Broadcast(ctx, headUpdate)
}
return
}
@@ -205,27 +211,18 @@ func (s *syncTree) Delete() (err error) {
}
func (s *syncTree) TryClose(objectTTL time.Duration) (bool, error) {
if !s.TryLock() {
return false, nil
}
log.Debug("closing sync tree", zap.String("id", s.Id()))
return true, s.close()
return true, s.Close()
}
func (s *syncTree) Close() (err error) {
log.Debug("closing sync tree", zap.String("id", s.Id()))
s.Lock()
return s.close()
}
func (s *syncTree) close() (err error) {
defer s.Unlock()
defer func() {
log.Debug("closed sync tree", zap.Error(err), zap.String("id", s.Id()))
}()
s.Lock()
defer s.Unlock()
if s.isClosed {
err = ErrSyncTreeClosed
return
return ErrSyncTreeClosed
}
s.onClose(s.Id())
s.isClosed = true
@@ -246,7 +243,7 @@ func (s *syncTree) SyncWithPeer(ctx context.Context, peerId string) (err error)
s.Lock()
defer s.Unlock()
headUpdate := s.syncClient.CreateHeadUpdate(s, nil)
return s.syncClient.SendUpdate(peerId, headUpdate.RootChange.Id, headUpdate)
return s.syncClient.SendWithReply(ctx, peerId, headUpdate, "")
}
func (s *syncTree) afterBuild() {
@@ -2,17 +2,17 @@ package synctree
import (
"context"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree/mock_objecttree"
"github.com/anyproto/any-sync/commonspace/object/tree/synctree/mock_synctree"
"github.com/anyproto/any-sync/commonspace/object/tree/synctree/updatelistener"
"github.com/anyproto/any-sync/commonspace/object/tree/synctree/updatelistener/mock_updatelistener"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/objectsync"
"github.com/anyproto/any-sync/commonspace/syncstatus"
"github.com/anyproto/any-sync/nodeconf"
"github.com/anytypeio/any-sync/commonspace/object/tree/objecttree"
"github.com/anytypeio/any-sync/commonspace/object/tree/objecttree/mock_objecttree"
"github.com/anytypeio/any-sync/commonspace/object/tree/synctree/mock_synctree"
"github.com/anytypeio/any-sync/commonspace/object/tree/synctree/updatelistener"
"github.com/anytypeio/any-sync/commonspace/object/tree/synctree/updatelistener/mock_updatelistener"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/commonspace/objectsync"
"github.com/anytypeio/any-sync/commonspace/syncstatus"
"github.com/anytypeio/any-sync/nodeconf"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
"testing"
)
@@ -34,8 +34,8 @@ func (s syncTreeMatcher) String() string {
return ""
}
func syncClientFuncCreator(client SyncClient) func(spaceId string, factory RequestFactory, objectSync objectsync.ObjectSync, configuration nodeconf.NodeConf) SyncClient {
return func(spaceId string, factory RequestFactory, objectSync objectsync.ObjectSync, configuration nodeconf.NodeConf) SyncClient {
func syncClientFuncCreator(client SyncClient) func(spaceId string, factory RequestFactory, objectSync objectsync.ObjectSync, configuration nodeconf.Configuration) SyncClient {
return func(spaceId string, factory RequestFactory, objectSync objectsync.ObjectSync, configuration nodeconf.Configuration) SyncClient {
return client
}
}
@@ -73,7 +73,7 @@ func Test_BuildSyncTree(t *testing.T) {
updateListenerMock.EXPECT().Update(tr)
syncClientMock.EXPECT().CreateHeadUpdate(gomock.Eq(tr), gomock.Eq(changes)).Return(headUpdate)
syncClientMock.EXPECT().Broadcast(gomock.Eq(headUpdate))
syncClientMock.EXPECT().Broadcast(gomock.Any(), gomock.Eq(headUpdate)).Return(nil)
res, err := tr.AddRawChanges(ctx, payload)
require.NoError(t, err)
require.Equal(t, expectedRes, res)
@@ -95,7 +95,7 @@ func Test_BuildSyncTree(t *testing.T) {
updateListenerMock.EXPECT().Rebuild(tr)
syncClientMock.EXPECT().CreateHeadUpdate(gomock.Eq(tr), gomock.Eq(changes)).Return(headUpdate)
syncClientMock.EXPECT().Broadcast(gomock.Eq(headUpdate))
syncClientMock.EXPECT().Broadcast(gomock.Any(), gomock.Eq(headUpdate)).Return(nil)
res, err := tr.AddRawChanges(ctx, payload)
require.NoError(t, err)
require.Equal(t, expectedRes, res)
@@ -133,7 +133,7 @@ func Test_BuildSyncTree(t *testing.T) {
Return(expectedRes, nil)
syncClientMock.EXPECT().CreateHeadUpdate(gomock.Eq(tr), gomock.Eq(changes)).Return(headUpdate)
syncClientMock.EXPECT().Broadcast(gomock.Eq(headUpdate))
syncClientMock.EXPECT().Broadcast(gomock.Any(), gomock.Eq(headUpdate)).Return(nil)
res, err := tr.AddContent(ctx, content)
require.NoError(t, err)
require.Equal(t, expectedRes, res)
@@ -2,142 +2,218 @@ package synctree
import (
"context"
"errors"
"sync"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/objectsync/synchandler"
"github.com/anyproto/any-sync/commonspace/spacesyncproto"
"github.com/anyproto/any-sync/commonspace/syncstatus"
"github.com/anyproto/any-sync/util/slice"
"github.com/anytypeio/any-sync/commonspace/object/tree/objecttree"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/commonspace/objectsync/synchandler"
"github.com/anytypeio/any-sync/commonspace/spacesyncproto"
"github.com/anytypeio/any-sync/commonspace/syncstatus"
"github.com/anytypeio/any-sync/util/slice"
"github.com/gogo/protobuf/proto"
)
var (
ErrMessageIsRequest = errors.New("message is request")
ErrMessageIsNotRequest = errors.New("message is not request")
ErrMoreThanOneRequest = errors.New("more than one request for same peer")
"go.uber.org/zap"
"sync"
)
type syncTreeHandler struct {
objTree objecttree.ObjectTree
syncClient SyncClient
syncProtocol TreeSyncProtocol
syncStatus syncstatus.StatusUpdater
spaceId string
handlerLock sync.Mutex
pendingRequests map[string]struct{}
heads []string
objTree objecttree.ObjectTree
syncClient SyncClient
syncStatus syncstatus.StatusUpdater
handlerLock sync.Mutex
spaceId string
queue ReceiveQueue
}
const maxQueueSize = 5
func newSyncTreeHandler(spaceId string, objTree objecttree.ObjectTree, syncClient SyncClient, syncStatus syncstatus.StatusUpdater) synchandler.SyncHandler {
return &syncTreeHandler{
objTree: objTree,
syncProtocol: newTreeSyncProtocol(spaceId, objTree, syncClient),
syncClient: syncClient,
syncStatus: syncStatus,
spaceId: spaceId,
pendingRequests: make(map[string]struct{}),
objTree: objTree,
syncClient: syncClient,
syncStatus: syncStatus,
spaceId: spaceId,
queue: newReceiveQueue(maxQueueSize),
}
}
func (s *syncTreeHandler) HandleRequest(ctx context.Context, senderId string, request *spacesyncproto.ObjectSyncMessage) (response *spacesyncproto.ObjectSyncMessage, err error) {
unmarshalled := &treechangeproto.TreeSyncMessage{}
err = proto.Unmarshal(request.Payload, unmarshalled)
if err != nil {
return
}
fullSyncRequest := unmarshalled.GetContent().GetFullSyncRequest()
if fullSyncRequest == nil {
return nil, ErrMessageIsNotRequest
}
// setting pending requests
s.handlerLock.Lock()
_, exists := s.pendingRequests[senderId]
if exists {
s.handlerLock.Unlock()
return nil, ErrMoreThanOneRequest
}
s.pendingRequests[senderId] = struct{}{}
s.handlerLock.Unlock()
response, err = s.handleRequest(ctx, senderId, fullSyncRequest)
// removing pending requests
s.handlerLock.Lock()
delete(s.pendingRequests, senderId)
s.handlerLock.Unlock()
return
}
func (s *syncTreeHandler) handleRequest(ctx context.Context, senderId string, fullSyncRequest *treechangeproto.TreeFullSyncRequest) (response *spacesyncproto.ObjectSyncMessage, err error) {
s.objTree.Lock()
defer s.objTree.Unlock()
treeResp, err := s.syncProtocol.FullSyncRequest(ctx, senderId, fullSyncRequest)
if err != nil {
return
}
response, err = MarshallTreeMessage(treeResp, s.spaceId, s.objTree.Id(), "")
return
}
func (s *syncTreeHandler) HandleMessage(ctx context.Context, senderId string, msg *spacesyncproto.ObjectSyncMessage) (err error) {
unmarshalled := &treechangeproto.TreeSyncMessage{}
err = proto.Unmarshal(msg.Payload, unmarshalled)
if err != nil {
return
}
heads := treechangeproto.GetHeads(unmarshalled)
s.syncStatus.HeadsReceive(senderId, msg.ObjectId, heads)
s.handlerLock.Lock()
// if the update has same heads then returning not to hang on a lock
if unmarshalled.GetContent().GetHeadUpdate() != nil && slice.UnsortedEquals(heads, s.heads) {
s.handlerLock.Unlock()
s.syncStatus.HeadsReceive(senderId, msg.ObjectId, treechangeproto.GetHeads(unmarshalled))
queueFull := s.queue.AddMessage(senderId, unmarshalled, msg.RequestId)
if queueFull {
return
}
s.handlerLock.Unlock()
return s.handleMessage(ctx, unmarshalled, senderId)
return s.handleMessage(ctx, senderId)
}
func (s *syncTreeHandler) handleMessage(ctx context.Context, msg *treechangeproto.TreeSyncMessage, senderId string) (err error) {
func (s *syncTreeHandler) handleMessage(ctx context.Context, senderId string) (err error) {
s.objTree.Lock()
defer s.objTree.Unlock()
msg, replyId, err := s.queue.GetMessage(senderId)
if err != nil {
return
}
defer s.queue.ClearQueue(senderId)
content := msg.GetContent()
switch {
case content.GetHeadUpdate() != nil:
return s.handleHeadUpdate(ctx, senderId, content.GetHeadUpdate(), replyId)
case content.GetFullSyncRequest() != nil:
return s.handleFullSyncRequest(ctx, senderId, content.GetFullSyncRequest(), replyId)
case content.GetFullSyncResponse() != nil:
return s.handleFullSyncResponse(ctx, senderId, content.GetFullSyncResponse())
}
return
}
func (s *syncTreeHandler) handleHeadUpdate(
ctx context.Context,
senderId string,
update *treechangeproto.TreeHeadUpdate,
replyId string) (err error) {
var (
copyHeads = make([]string, 0, len(s.objTree.Heads()))
treeId = s.objTree.Id()
content = msg.GetContent()
fullRequest *treechangeproto.TreeSyncMessage
isEmptyUpdate = len(update.Changes) == 0
objTree = s.objTree
)
// getting old heads
copyHeads = append(copyHeads, s.objTree.Heads()...)
log := log.With(zap.Strings("heads", objTree.Heads()), zap.String("treeId", objTree.Id()), zap.String("spaceId", s.spaceId))
log.DebugCtx(ctx, "received head update message")
defer func() {
// checking if something changed
if !slice.UnsortedEquals(copyHeads, s.objTree.Heads()) {
s.handlerLock.Lock()
defer s.handlerLock.Unlock()
s.heads = s.heads[:0]
for _, h := range s.objTree.Heads() {
s.heads = append(s.heads, h)
if err != nil {
log.With(zap.Error(err)).Debug("head update finished with error")
} else if fullRequest != nil {
log.DebugCtx(ctx, "sending full sync request")
} else {
if !isEmptyUpdate {
log.DebugCtx(ctx, "head update finished correctly")
}
}
}()
switch {
case content.GetHeadUpdate() != nil:
var syncReq *treechangeproto.TreeSyncMessage
syncReq, err = s.syncProtocol.HeadUpdate(ctx, senderId, content.GetHeadUpdate())
if err != nil || syncReq == nil {
// isEmptyUpdate is sent when the tree is brought up from cache
if isEmptyUpdate {
headEquals := slice.UnsortedEquals(objTree.Heads(), update.Heads)
log.DebugCtx(ctx, "is empty update", zap.String("treeId", objTree.Id()), zap.Bool("headEquals", headEquals))
if headEquals {
return
}
return s.syncClient.QueueRequest(senderId, treeId, syncReq)
case content.GetFullSyncRequest() != nil:
return ErrMessageIsRequest
case content.GetFullSyncResponse() != nil:
return s.syncProtocol.FullSyncResponse(ctx, senderId, content.GetFullSyncResponse())
// we need to sync in any case
fullRequest, err = s.syncClient.CreateFullSyncRequest(objTree, update.Heads, update.SnapshotPath)
if err != nil {
return
}
return s.syncClient.SendWithReply(ctx, senderId, fullRequest, replyId)
}
if s.alreadyHasHeads(objTree, update.Heads) {
return
}
_, err = objTree.AddRawChanges(ctx, objecttree.RawChangesPayload{
NewHeads: update.Heads,
RawChanges: update.Changes,
})
if err != nil {
return
}
if s.alreadyHasHeads(objTree, update.Heads) {
return
}
fullRequest, err = s.syncClient.CreateFullSyncRequest(objTree, update.Heads, update.SnapshotPath)
if err != nil {
return
}
return s.syncClient.SendWithReply(ctx, senderId, fullRequest, replyId)
}
func (s *syncTreeHandler) handleFullSyncRequest(
ctx context.Context,
senderId string,
request *treechangeproto.TreeFullSyncRequest,
replyId string) (err error) {
var (
fullResponse *treechangeproto.TreeSyncMessage
header = s.objTree.Header()
objTree = s.objTree
)
log := log.With(zap.String("senderId", senderId),
zap.Strings("heads", request.Heads),
zap.String("treeId", s.objTree.Id()),
zap.String("replyId", replyId),
zap.String("spaceId", s.spaceId))
log.DebugCtx(ctx, "received full sync request message")
defer func() {
if err != nil {
log.With(zap.Error(err)).DebugCtx(ctx, "full sync request finished with error")
s.syncClient.SendWithReply(ctx, senderId, treechangeproto.WrapError(err, header), replyId)
return
} else if fullResponse != nil {
log.DebugCtx(ctx, "full sync response sent")
}
}()
if len(request.Changes) != 0 && !s.alreadyHasHeads(objTree, request.Heads) {
_, err = objTree.AddRawChanges(ctx, objecttree.RawChangesPayload{
NewHeads: request.Heads,
RawChanges: request.Changes,
})
if err != nil {
return
}
}
fullResponse, err = s.syncClient.CreateFullSyncResponse(objTree, request.Heads, request.SnapshotPath)
if err != nil {
return
}
return s.syncClient.SendWithReply(ctx, senderId, fullResponse, replyId)
}
func (s *syncTreeHandler) handleFullSyncResponse(
ctx context.Context,
senderId string,
response *treechangeproto.TreeFullSyncResponse) (err error) {
var (
objTree = s.objTree
)
log := log.With(zap.Strings("heads", response.Heads), zap.String("treeId", s.objTree.Id()), zap.String("spaceId", s.spaceId))
log.DebugCtx(ctx, "received full sync response message")
defer func() {
if err != nil {
log.With(zap.Error(err)).DebugCtx(ctx, "full sync response failed")
} else {
log.DebugCtx(ctx, "full sync response succeeded")
}
}()
if s.alreadyHasHeads(objTree, response.Heads) {
return
}
_, err = objTree.AddRawChanges(ctx, objecttree.RawChangesPayload{
NewHeads: response.Heads,
RawChanges: response.Changes,
})
return
}
func (s *syncTreeHandler) alreadyHasHeads(t objecttree.ObjectTree, heads []string) bool {
return slice.UnsortedEquals(t.Heads(), heads) || t.HasChanges(heads...)
}
@@ -2,15 +2,19 @@ package synctree
import (
"context"
"fmt"
"sync"
"testing"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree/mock_objecttree"
"github.com/anyproto/any-sync/commonspace/object/tree/synctree/mock_synctree"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/syncstatus"
"github.com/anytypeio/any-sync/app/logger"
"github.com/anytypeio/any-sync/commonspace/object/tree/objecttree"
"github.com/anytypeio/any-sync/commonspace/object/tree/objecttree/mock_objecttree"
"github.com/anytypeio/any-sync/commonspace/object/tree/synctree/mock_synctree"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/commonspace/syncstatus"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
"go.uber.org/zap"
)
type testObjTreeMock struct {
@@ -52,38 +56,29 @@ type syncHandlerFixture struct {
ctrl *gomock.Controller
syncClientMock *mock_synctree.MockSyncClient
objectTreeMock *testObjTreeMock
syncProtocolMock *mock_synctree.MockTreeSyncProtocol
spaceId string
senderId string
treeId string
receiveQueueMock ReceiveQueue
syncHandler *syncTreeHandler
}
func newSyncHandlerFixture(t *testing.T) *syncHandlerFixture {
ctrl := gomock.NewController(t)
objectTreeMock := newTestObjMock(mock_objecttree.NewMockObjectTree(ctrl))
syncClientMock := mock_synctree.NewMockSyncClient(ctrl)
syncProtocolMock := mock_synctree.NewMockTreeSyncProtocol(ctrl)
spaceId := "spaceId"
objectTreeMock := newTestObjMock(mock_objecttree.NewMockObjectTree(ctrl))
receiveQueue := newReceiveQueue(5)
syncHandler := &syncTreeHandler{
objTree: objectTreeMock,
syncClient: syncClientMock,
syncProtocol: syncProtocolMock,
spaceId: spaceId,
syncStatus: syncstatus.NewNoOpSyncStatus(),
pendingRequests: map[string]struct{}{},
objTree: objectTreeMock,
syncClient: syncClientMock,
queue: receiveQueue,
syncStatus: syncstatus.NewNoOpSyncStatus(),
}
return &syncHandlerFixture{
ctrl: ctrl,
objectTreeMock: objectTreeMock,
syncProtocolMock: syncProtocolMock,
syncClientMock: syncClientMock,
objectTreeMock: objectTreeMock,
receiveQueueMock: receiveQueue,
syncHandler: syncHandler,
spaceId: spaceId,
senderId: "senderId",
treeId: "treeId",
}
}
@@ -91,149 +86,333 @@ func (fx *syncHandlerFixture) stop() {
fx.ctrl.Finish()
}
func TestSyncTreeHandler_HandleMessage(t *testing.T) {
func TestSyncHandler_HandleHeadUpdate(t *testing.T) {
ctx := context.Background()
log = logger.CtxLogger{Logger: zap.NewNop()}
t.Run("handle head update message, heads not equal, request returned", func(t *testing.T) {
t.Run("head update non empty all heads added", func(t *testing.T) {
fx := newSyncHandlerFixture(t)
defer fx.stop()
treeId := "treeId"
senderId := "senderId"
chWithId := &treechangeproto.RawTreeChangeWithId{}
headUpdate := &treechangeproto.TreeHeadUpdate{
Heads: []string{"h3"},
Heads: []string{"h1"},
Changes: []*treechangeproto.RawTreeChangeWithId{chWithId},
SnapshotPath: []string{"h1"},
}
treeMsg := treechangeproto.WrapHeadUpdate(headUpdate, chWithId)
objectMsg, _ := MarshallTreeMessage(treeMsg, "spaceId", treeId, "")
objectMsg, _ := marshallTreeMessage(treeMsg, "spaceId", treeId, "")
syncReq := &treechangeproto.TreeSyncMessage{}
fx.syncHandler.heads = []string{"h2"}
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId)
fx.objectTreeMock.EXPECT().Heads().Times(2).Return([]string{"h2"})
fx.objectTreeMock.EXPECT().Heads().Times(2).Return([]string{"h3"})
fx.syncProtocolMock.EXPECT().HeadUpdate(ctx, fx.senderId, gomock.Any()).Return(syncReq, nil)
fx.syncClientMock.EXPECT().QueueRequest(fx.senderId, fx.treeId, syncReq).Return(nil)
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(treeId)
fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2"}).Times(2)
fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(false)
fx.objectTreeMock.EXPECT().
AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{
NewHeads: []string{"h1"},
RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId},
})).
Return(objecttree.AddResult{}, nil)
fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2", "h1"})
fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(true)
err := fx.syncHandler.HandleMessage(ctx, fx.senderId, objectMsg)
require.NoError(t, err)
require.Equal(t, []string{"h3"}, fx.syncHandler.heads)
})
t.Run("handle head update message, heads equal", func(t *testing.T) {
fx := newSyncHandlerFixture(t)
defer fx.stop()
treeId := "treeId"
chWithId := &treechangeproto.RawTreeChangeWithId{}
headUpdate := &treechangeproto.TreeHeadUpdate{
Heads: []string{"h1"},
}
treeMsg := treechangeproto.WrapHeadUpdate(headUpdate, chWithId)
objectMsg, _ := MarshallTreeMessage(treeMsg, "spaceId", treeId, "")
fx.syncHandler.heads = []string{"h1"}
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId)
err := fx.syncHandler.HandleMessage(ctx, fx.senderId, objectMsg)
err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg)
require.NoError(t, err)
})
t.Run("handle head update message, empty sync request returned", func(t *testing.T) {
t.Run("head update non empty heads not added", func(t *testing.T) {
fx := newSyncHandlerFixture(t)
defer fx.stop()
treeId := "treeId"
senderId := "senderId"
chWithId := &treechangeproto.RawTreeChangeWithId{}
headUpdate := &treechangeproto.TreeHeadUpdate{
Heads: []string{"h3"},
Heads: []string{"h1"},
Changes: []*treechangeproto.RawTreeChangeWithId{chWithId},
SnapshotPath: []string{"h1"},
}
treeMsg := treechangeproto.WrapHeadUpdate(headUpdate, chWithId)
objectMsg, _ := MarshallTreeMessage(treeMsg, "spaceId", treeId, "")
objectMsg, _ := marshallTreeMessage(treeMsg, "spaceId", treeId, "")
fullRequest := &treechangeproto.TreeSyncMessage{}
fx.syncHandler.heads = []string{"h2"}
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId)
fx.objectTreeMock.EXPECT().Heads().Times(2).Return([]string{"h2"})
fx.objectTreeMock.EXPECT().Heads().Times(2).Return([]string{"h3"})
fx.syncProtocolMock.EXPECT().HeadUpdate(ctx, fx.senderId, gomock.Any()).Return(nil, nil)
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(treeId)
fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2"}).AnyTimes()
fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(false)
fx.objectTreeMock.EXPECT().
AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{
NewHeads: []string{"h1"},
RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId},
})).
Return(objecttree.AddResult{}, nil)
fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(false)
fx.syncClientMock.EXPECT().
CreateFullSyncRequest(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})).
Return(fullRequest, nil)
fx.syncClientMock.EXPECT().SendWithReply(gomock.Any(), gomock.Eq(senderId), gomock.Eq(fullRequest), gomock.Eq(""))
err := fx.syncHandler.HandleMessage(ctx, fx.senderId, objectMsg)
err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg)
require.NoError(t, err)
require.Equal(t, []string{"h3"}, fx.syncHandler.heads)
})
t.Run("handle full sync request returns error", func(t *testing.T) {
t.Run("head update non empty equal heads", func(t *testing.T) {
fx := newSyncHandlerFixture(t)
defer fx.stop()
treeId := "treeId"
senderId := "senderId"
chWithId := &treechangeproto.RawTreeChangeWithId{}
fullRequest := &treechangeproto.TreeFullSyncRequest{
Heads: []string{"h3"},
headUpdate := &treechangeproto.TreeHeadUpdate{
Heads: []string{"h1"},
Changes: []*treechangeproto.RawTreeChangeWithId{chWithId},
SnapshotPath: []string{"h1"},
}
treeMsg := treechangeproto.WrapFullRequest(fullRequest, chWithId)
objectMsg, _ := MarshallTreeMessage(treeMsg, "spaceId", treeId, "")
treeMsg := treechangeproto.WrapHeadUpdate(headUpdate, chWithId)
objectMsg, _ := marshallTreeMessage(treeMsg, "spaceId", treeId, "")
fx.syncHandler.heads = []string{"h2"}
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId)
fx.objectTreeMock.EXPECT().Heads().Times(3).Return([]string{"h2"})
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(treeId)
fx.objectTreeMock.EXPECT().Heads().Return([]string{"h1"}).AnyTimes()
err := fx.syncHandler.HandleMessage(ctx, fx.senderId, objectMsg)
require.Equal(t, err, ErrMessageIsRequest)
err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg)
require.NoError(t, err)
})
t.Run("handle full sync response", func(t *testing.T) {
t.Run("head update empty", func(t *testing.T) {
fx := newSyncHandlerFixture(t)
defer fx.stop()
treeId := "treeId"
senderId := "senderId"
chWithId := &treechangeproto.RawTreeChangeWithId{}
headUpdate := &treechangeproto.TreeHeadUpdate{
Heads: []string{"h1"},
Changes: nil,
SnapshotPath: []string{"h1"},
}
treeMsg := treechangeproto.WrapHeadUpdate(headUpdate, chWithId)
objectMsg, _ := marshallTreeMessage(treeMsg, "spaceId", treeId, "")
fullRequest := &treechangeproto.TreeSyncMessage{}
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(treeId)
fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2"}).AnyTimes()
fx.syncClientMock.EXPECT().
CreateFullSyncRequest(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})).
Return(fullRequest, nil)
fx.syncClientMock.EXPECT().SendWithReply(gomock.Any(), gomock.Eq(senderId), gomock.Eq(fullRequest), gomock.Eq(""))
err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg)
require.NoError(t, err)
})
t.Run("head update empty equal heads", func(t *testing.T) {
fx := newSyncHandlerFixture(t)
defer fx.stop()
treeId := "treeId"
senderId := "senderId"
chWithId := &treechangeproto.RawTreeChangeWithId{}
headUpdate := &treechangeproto.TreeHeadUpdate{
Heads: []string{"h1"},
Changes: nil,
SnapshotPath: []string{"h1"},
}
treeMsg := treechangeproto.WrapHeadUpdate(headUpdate, chWithId)
objectMsg, _ := marshallTreeMessage(treeMsg, "spaceId", treeId, "")
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(treeId)
fx.objectTreeMock.EXPECT().Heads().Return([]string{"h1"}).AnyTimes()
err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg)
require.NoError(t, err)
})
}
func TestSyncHandler_HandleFullSyncRequest(t *testing.T) {
ctx := context.Background()
log = logger.CtxLogger{Logger: zap.NewNop()}
t.Run("full sync request with change", func(t *testing.T) {
fx := newSyncHandlerFixture(t)
defer fx.stop()
treeId := "treeId"
senderId := "senderId"
chWithId := &treechangeproto.RawTreeChangeWithId{}
fullSyncRequest := &treechangeproto.TreeFullSyncRequest{
Heads: []string{"h1"},
Changes: []*treechangeproto.RawTreeChangeWithId{chWithId},
SnapshotPath: []string{"h1"},
}
treeMsg := treechangeproto.WrapFullRequest(fullSyncRequest, chWithId)
objectMsg, _ := marshallTreeMessage(treeMsg, "spaceId", treeId, "")
fullResponse := &treechangeproto.TreeSyncMessage{}
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(treeId)
fx.objectTreeMock.EXPECT().Header().Return(nil)
fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2"}).AnyTimes()
fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(false)
fx.objectTreeMock.EXPECT().
AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{
NewHeads: []string{"h1"},
RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId},
})).
Return(objecttree.AddResult{}, nil)
fx.syncClientMock.EXPECT().
CreateFullSyncResponse(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})).
Return(fullResponse, nil)
fx.syncClientMock.EXPECT().SendWithReply(gomock.Any(), gomock.Eq(senderId), gomock.Eq(fullResponse), gomock.Eq(""))
err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg)
require.NoError(t, err)
})
t.Run("full sync request with change same heads", func(t *testing.T) {
fx := newSyncHandlerFixture(t)
defer fx.stop()
treeId := "treeId"
senderId := "senderId"
chWithId := &treechangeproto.RawTreeChangeWithId{}
fullSyncRequest := &treechangeproto.TreeFullSyncRequest{
Heads: []string{"h1"},
Changes: []*treechangeproto.RawTreeChangeWithId{chWithId},
SnapshotPath: []string{"h1"},
}
treeMsg := treechangeproto.WrapFullRequest(fullSyncRequest, chWithId)
objectMsg, _ := marshallTreeMessage(treeMsg, "spaceId", treeId, "")
fullResponse := &treechangeproto.TreeSyncMessage{}
fx.objectTreeMock.EXPECT().
Id().AnyTimes().Return(treeId)
fx.objectTreeMock.EXPECT().Header().Return(nil)
fx.objectTreeMock.EXPECT().
Heads().
Return([]string{"h1"}).AnyTimes()
fx.syncClientMock.EXPECT().
CreateFullSyncResponse(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})).
Return(fullResponse, nil)
fx.syncClientMock.EXPECT().SendWithReply(gomock.Any(), gomock.Eq(senderId), gomock.Eq(fullResponse), gomock.Eq(""))
err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg)
require.NoError(t, err)
})
t.Run("full sync request without change but with reply id", func(t *testing.T) {
fx := newSyncHandlerFixture(t)
defer fx.stop()
treeId := "treeId"
senderId := "senderId"
replyId := "replyId"
chWithId := &treechangeproto.RawTreeChangeWithId{}
fullSyncRequest := &treechangeproto.TreeFullSyncRequest{
Heads: []string{"h1"},
SnapshotPath: []string{"h1"},
}
treeMsg := treechangeproto.WrapFullRequest(fullSyncRequest, chWithId)
objectMsg, _ := marshallTreeMessage(treeMsg, "spaceId", treeId, "")
objectMsg.RequestId = replyId
fullResponse := &treechangeproto.TreeSyncMessage{}
fx.objectTreeMock.EXPECT().
Id().AnyTimes().Return(treeId)
fx.objectTreeMock.EXPECT().Header().Return(nil)
fx.syncClientMock.EXPECT().
CreateFullSyncResponse(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})).
Return(fullResponse, nil)
fx.syncClientMock.EXPECT().SendWithReply(gomock.Any(), gomock.Eq(senderId), gomock.Eq(fullResponse), gomock.Eq(replyId))
err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg)
require.NoError(t, err)
})
t.Run("full sync request with add raw changes error", func(t *testing.T) {
fx := newSyncHandlerFixture(t)
defer fx.stop()
treeId := "treeId"
senderId := "senderId"
chWithId := &treechangeproto.RawTreeChangeWithId{}
fullSyncRequest := &treechangeproto.TreeFullSyncRequest{
Heads: []string{"h1"},
Changes: []*treechangeproto.RawTreeChangeWithId{chWithId},
SnapshotPath: []string{"h1"},
}
treeMsg := treechangeproto.WrapFullRequest(fullSyncRequest, chWithId)
objectMsg, _ := marshallTreeMessage(treeMsg, "spaceId", treeId, "")
fx.objectTreeMock.EXPECT().
Id().AnyTimes().Return(treeId)
fx.objectTreeMock.EXPECT().Header().Return(nil)
fx.objectTreeMock.EXPECT().
Heads().
Return([]string{"h2"})
fx.objectTreeMock.EXPECT().
HasChanges(gomock.Eq([]string{"h1"})).
Return(false)
fx.objectTreeMock.EXPECT().
AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{
NewHeads: []string{"h1"},
RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId},
})).
Return(objecttree.AddResult{}, fmt.Errorf(""))
fx.syncClientMock.EXPECT().SendWithReply(gomock.Any(), gomock.Eq(senderId), gomock.Any(), gomock.Eq(""))
err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg)
require.Error(t, err)
})
}
func TestSyncHandler_HandleFullSyncResponse(t *testing.T) {
ctx := context.Background()
log = logger.CtxLogger{Logger: zap.NewNop()}
t.Run("full sync response with change", func(t *testing.T) {
fx := newSyncHandlerFixture(t)
defer fx.stop()
treeId := "treeId"
senderId := "senderId"
replyId := "replyId"
chWithId := &treechangeproto.RawTreeChangeWithId{}
fullSyncResponse := &treechangeproto.TreeFullSyncResponse{
Heads: []string{"h3"},
Heads: []string{"h1"},
Changes: []*treechangeproto.RawTreeChangeWithId{chWithId},
SnapshotPath: []string{"h1"},
}
treeMsg := treechangeproto.WrapFullResponse(fullSyncResponse, chWithId)
objectMsg, _ := MarshallTreeMessage(treeMsg, "spaceId", treeId, "")
objectMsg, _ := marshallTreeMessage(treeMsg, "spaceId", treeId, replyId)
fx.syncHandler.heads = []string{"h2"}
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId)
fx.objectTreeMock.EXPECT().Heads().Times(2).Return([]string{"h2"})
fx.objectTreeMock.EXPECT().Heads().Times(2).Return([]string{"h3"})
fx.syncProtocolMock.EXPECT().FullSyncResponse(ctx, fx.senderId, gomock.Any()).Return(nil)
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(treeId)
fx.objectTreeMock.EXPECT().
Heads().
Return([]string{"h2"}).AnyTimes()
fx.objectTreeMock.EXPECT().
HasChanges(gomock.Eq([]string{"h1"})).
Return(false)
fx.objectTreeMock.EXPECT().
AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{
NewHeads: []string{"h1"},
RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId},
})).
Return(objecttree.AddResult{}, nil)
err := fx.syncHandler.HandleMessage(ctx, fx.senderId, objectMsg)
err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg)
require.NoError(t, err)
})
}
func TestSyncTreeHandler_HandleRequest(t *testing.T) {
ctx := context.Background()
t.Run("handle request", func(t *testing.T) {
t.Run("full sync response with same heads", func(t *testing.T) {
fx := newSyncHandlerFixture(t)
defer fx.stop()
treeId := "treeId"
senderId := "senderId"
replyId := "replyId"
chWithId := &treechangeproto.RawTreeChangeWithId{}
fullRequest := &treechangeproto.TreeFullSyncRequest{}
treeMsg := treechangeproto.WrapFullRequest(fullRequest, chWithId)
objectMsg, _ := MarshallTreeMessage(treeMsg, "spaceId", treeId, "")
syncResp := &treechangeproto.TreeSyncMessage{}
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId)
fx.syncProtocolMock.EXPECT().FullSyncRequest(ctx, fx.senderId, gomock.Any()).Return(syncResp, nil)
res, err := fx.syncHandler.HandleRequest(ctx, fx.senderId, objectMsg)
require.NoError(t, err)
require.NotNil(t, res)
})
t.Run("handle other message", func(t *testing.T) {
fx := newSyncHandlerFixture(t)
defer fx.stop()
treeId := "treeId"
chWithId := &treechangeproto.RawTreeChangeWithId{}
fullResponse := &treechangeproto.TreeFullSyncResponse{}
responseMsg := treechangeproto.WrapFullResponse(fullResponse, chWithId)
headUpdate := &treechangeproto.TreeHeadUpdate{}
headUpdateMsg := treechangeproto.WrapHeadUpdate(headUpdate, chWithId)
for _, msg := range []*treechangeproto.TreeSyncMessage{responseMsg, headUpdateMsg} {
objectMsg, _ := MarshallTreeMessage(msg, "spaceId", treeId, "")
_, err := fx.syncHandler.HandleRequest(ctx, fx.senderId, objectMsg)
require.Equal(t, err, ErrMessageIsNotRequest)
fullSyncResponse := &treechangeproto.TreeFullSyncResponse{
Heads: []string{"h1"},
Changes: []*treechangeproto.RawTreeChangeWithId{chWithId},
SnapshotPath: []string{"h1"},
}
treeMsg := treechangeproto.WrapFullResponse(fullSyncResponse, chWithId)
objectMsg, _ := marshallTreeMessage(treeMsg, "spaceId", treeId, replyId)
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(treeId)
fx.objectTreeMock.EXPECT().
Heads().
Return([]string{"h1"}).AnyTimes()
err := fx.syncHandler.HandleMessage(ctx, senderId, objectMsg)
require.NoError(t, err)
})
}
@@ -2,19 +2,15 @@ package synctree
import (
"context"
"errors"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anyproto/any-sync/commonspace/spacestorage"
"github.com/anyproto/any-sync/net/peer"
"fmt"
"github.com/anytypeio/any-sync/commonspace/object/tree/objecttree"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anytypeio/any-sync/commonspace/object/tree/treestorage"
"github.com/anytypeio/any-sync/commonspace/spacestorage"
"github.com/anytypeio/any-sync/net/peer"
"github.com/gogo/protobuf/proto"
"go.uber.org/zap"
)
var (
ErrNoResponsiblePeers = errors.New("no responsible peers")
"time"
)
type treeRemoteGetter struct {
@@ -39,7 +35,7 @@ func (t treeRemoteGetter) getPeers(ctx context.Context) (peerIds []string, err e
return
}
if len(respPeers) == 0 {
err = ErrNoResponsiblePeers
err = fmt.Errorf("no responsible peers")
return
}
for _, p := range respPeers {
@@ -49,8 +45,13 @@ func (t treeRemoteGetter) getPeers(ctx context.Context) (peerIds []string, err e
}
func (t treeRemoteGetter) treeRequest(ctx context.Context, peerId string) (msg *treechangeproto.TreeSyncMessage, err error) {
newTreeRequest := t.deps.SyncClient.CreateNewTreeRequest()
resp, err := t.deps.SyncClient.SendRequest(ctx, peerId, t.treeId, newTreeRequest)
newTreeRequest := GetRequestFactory().CreateNewTreeRequest()
objMsg, err := marshallTreeMessage(newTreeRequest, t.deps.SpaceId, t.treeId, "")
if err != nil {
return
}
resp, err := t.deps.ObjectSync.MessagePool().SendSync(ctx, peerId, objMsg)
if err != nil {
return
}
@@ -60,20 +61,45 @@ func (t treeRemoteGetter) treeRequest(ctx context.Context, peerId string) (msg *
return
}
func (t treeRemoteGetter) treeRequestLoop(ctx context.Context) (msg *treechangeproto.TreeSyncMessage, err error) {
availablePeers, err := t.getPeers(ctx)
if err != nil {
return
func (t treeRemoteGetter) treeRequestLoop(ctx context.Context, wait bool) (msg *treechangeproto.TreeSyncMessage, err error) {
peerIdx := 0
Loop:
for {
select {
case <-ctx.Done():
return nil, fmt.Errorf("waiting for object %s interrupted, context closed", t.treeId)
default:
break
}
availablePeers, err := t.getPeers(ctx)
if err != nil {
if !wait {
return nil, err
}
select {
// wait for peers to connect
case <-time.After(1 * time.Second):
continue Loop
case <-ctx.Done():
return nil, fmt.Errorf("waiting for object %s interrupted, context closed", t.treeId)
}
}
peerIdx = peerIdx % len(availablePeers)
msg, err = t.treeRequest(ctx, availablePeers[peerIdx])
if err == nil || !wait {
return msg, err
}
peerIdx++
}
// in future we will try to load from different peers
return t.treeRequest(ctx, availablePeers[0])
}
func (t treeRemoteGetter) getTree(ctx context.Context) (treeStorage treestorage.TreeStorage, isRemote bool, err error) {
func (t treeRemoteGetter) getTree(ctx context.Context) (treeStorage treestorage.TreeStorage, err error) {
treeStorage, err = t.deps.SpaceStorage.TreeStorage(t.treeId)
if err == nil {
return
}
if err != nil && err != treestorage.ErrUnknownTreeId {
return
}
@@ -87,16 +113,15 @@ func (t treeRemoteGetter) getTree(ctx context.Context) (treeStorage treestorage.
return
}
isRemote = true
resp, err := t.treeRequestLoop(ctx)
resp, err := t.treeRequestLoop(ctx, t.deps.WaitTreeRemoteSync)
if err != nil {
return
}
fullSyncResp := resp.GetContent().GetFullSyncResponse()
if fullSyncResp == nil {
err = treechangeproto.ErrUnexpected
if resp.GetContent().GetFullSyncResponse() == nil {
err = fmt.Errorf("expected to get full sync response, but got something else")
return
}
fullSyncResp := resp.GetContent().GetFullSyncResponse()
payload := treestorage.TreeStorageCreatePayload{
RootRawChange: resp.RootChange,
@@ -111,6 +136,5 @@ func (t treeRemoteGetter) getTree(ctx context.Context) (treeStorage treestorage.
return
}
// now we are sure that we can save it to the storage
treeStorage, err = t.deps.SpaceStorage.CreateTreeStorage(payload)
return
return t.deps.SpaceStorage.CreateTreeStorage(payload)
}
@@ -1,87 +0,0 @@
package synctree
import (
"context"
"fmt"
"testing"
"github.com/anyproto/any-sync/commonspace/object/tree/synctree/mock_synctree"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/peermanager/mock_peermanager"
"github.com/anyproto/any-sync/commonspace/spacesyncproto"
"github.com/anyproto/any-sync/net/peer"
"github.com/anyproto/any-sync/net/peer/mock_peer"
"github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
)
type treeRemoteGetterFixture struct {
ctrl *gomock.Controller
treeGetter treeRemoteGetter
syncClientMock *mock_synctree.MockSyncClient
peerGetterMock *mock_peermanager.MockPeerManager
}
func newTreeRemoteGetterFixture(t *testing.T) *treeRemoteGetterFixture {
ctrl := gomock.NewController(t)
syncClientMock := mock_synctree.NewMockSyncClient(ctrl)
peerGetterMock := mock_peermanager.NewMockPeerManager(ctrl)
treeGetter := treeRemoteGetter{
deps: BuildDeps{
SyncClient: syncClientMock,
PeerGetter: peerGetterMock,
},
treeId: "treeId",
}
return &treeRemoteGetterFixture{
ctrl: ctrl,
treeGetter: treeGetter,
syncClientMock: syncClientMock,
peerGetterMock: peerGetterMock,
}
}
func (fx *treeRemoteGetterFixture) stop() {
fx.ctrl.Finish()
}
func TestTreeRemoteGetter(t *testing.T) {
ctx := context.Background()
peerId := "peerId"
treeRequest := &treechangeproto.TreeSyncMessage{}
treeResponse := &treechangeproto.TreeSyncMessage{
RootChange: &treechangeproto.RawTreeChangeWithId{Id: "id"},
}
marshalled, _ := proto.Marshal(treeResponse)
objectResponse := &spacesyncproto.ObjectSyncMessage{
Payload: marshalled,
}
t.Run("request works", func(t *testing.T) {
fx := newTreeRemoteGetterFixture(t)
defer fx.stop()
mockPeer := mock_peer.NewMockPeer(fx.ctrl)
mockPeer.EXPECT().Id().AnyTimes().Return(peerId)
fx.peerGetterMock.EXPECT().GetResponsiblePeers(ctx).Return([]peer.Peer{mockPeer}, nil)
fx.syncClientMock.EXPECT().CreateNewTreeRequest().Return(treeRequest)
fx.syncClientMock.EXPECT().SendRequest(ctx, peerId, fx.treeGetter.treeId, treeRequest).Return(objectResponse, nil)
resp, err := fx.treeGetter.treeRequestLoop(ctx)
require.NoError(t, err)
require.Equal(t, "id", resp.RootChange.Id)
})
t.Run("request fails", func(t *testing.T) {
fx := newTreeRemoteGetterFixture(t)
defer fx.stop()
treeRequest := &treechangeproto.TreeSyncMessage{}
mockPeer := mock_peer.NewMockPeer(fx.ctrl)
mockPeer.EXPECT().Id().AnyTimes().Return(peerId)
fx.peerGetterMock.EXPECT().GetResponsiblePeers(ctx).Return([]peer.Peer{mockPeer}, nil)
fx.syncClientMock.EXPECT().CreateNewTreeRequest().Return(treeRequest)
fx.syncClientMock.EXPECT().SendRequest(ctx, peerId, fx.treeGetter.treeId, treeRequest).AnyTimes().Return(nil, fmt.Errorf("some"))
_, err := fx.treeGetter.treeRequestLoop(ctx)
require.Error(t, err)
})
}
@@ -1,153 +0,0 @@
package synctree
import (
"context"
"github.com/anyproto/any-sync/app/logger"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/util/slice"
"go.uber.org/zap"
)
type TreeSyncProtocol interface {
HeadUpdate(ctx context.Context, senderId string, update *treechangeproto.TreeHeadUpdate) (request *treechangeproto.TreeSyncMessage, err error)
FullSyncRequest(ctx context.Context, senderId string, request *treechangeproto.TreeFullSyncRequest) (response *treechangeproto.TreeSyncMessage, err error)
FullSyncResponse(ctx context.Context, senderId string, response *treechangeproto.TreeFullSyncResponse) (err error)
}
type treeSyncProtocol struct {
log logger.CtxLogger
spaceId string
objTree objecttree.ObjectTree
reqFactory RequestFactory
}
func newTreeSyncProtocol(spaceId string, objTree objecttree.ObjectTree, reqFactory RequestFactory) *treeSyncProtocol {
return &treeSyncProtocol{
log: log.With(zap.String("spaceId", spaceId), zap.String("treeId", objTree.Id())),
spaceId: spaceId,
objTree: objTree,
reqFactory: reqFactory,
}
}
func (t *treeSyncProtocol) HeadUpdate(ctx context.Context, senderId string, update *treechangeproto.TreeHeadUpdate) (fullRequest *treechangeproto.TreeSyncMessage, err error) {
var (
isEmptyUpdate = len(update.Changes) == 0
objTree = t.objTree
)
log := t.log.With(
zap.String("senderId", senderId),
zap.Strings("update heads", update.Heads),
zap.Int("len(update changes)", len(update.Changes)))
log.DebugCtx(ctx, "received head update message")
defer func() {
if err != nil {
log.ErrorCtx(ctx, "head update finished with error", zap.Error(err))
} else if fullRequest != nil {
cnt := fullRequest.Content.GetFullSyncRequest()
log = log.With(zap.Strings("request heads", cnt.Heads), zap.Int("len(request changes)", len(cnt.Changes)))
log.DebugCtx(ctx, "returning full sync request")
} else {
if !isEmptyUpdate {
log.DebugCtx(ctx, "head update finished correctly")
}
}
}()
// isEmptyUpdate is sent when the tree is brought up from cache
if isEmptyUpdate {
headEquals := slice.UnsortedEquals(objTree.Heads(), update.Heads)
log.DebugCtx(ctx, "is empty update", zap.String("treeId", objTree.Id()), zap.Bool("headEquals", headEquals))
if headEquals {
return
}
// we need to sync in any case
fullRequest, err = t.reqFactory.CreateFullSyncRequest(objTree, update.Heads, update.SnapshotPath)
return
}
if t.alreadyHasHeads(objTree, update.Heads) {
return
}
_, err = objTree.AddRawChanges(ctx, objecttree.RawChangesPayload{
NewHeads: update.Heads,
RawChanges: update.Changes,
})
if err != nil {
return
}
if t.alreadyHasHeads(objTree, update.Heads) {
return
}
fullRequest, err = t.reqFactory.CreateFullSyncRequest(objTree, update.Heads, update.SnapshotPath)
return
}
func (t *treeSyncProtocol) FullSyncRequest(ctx context.Context, senderId string, request *treechangeproto.TreeFullSyncRequest) (fullResponse *treechangeproto.TreeSyncMessage, err error) {
var (
objTree = t.objTree
)
log := t.log.With(zap.String("senderId", senderId),
zap.Strings("request heads", request.Heads),
zap.Int("len(request changes)", len(request.Changes)))
log.DebugCtx(ctx, "received full sync request message")
defer func() {
if err != nil {
log.ErrorCtx(ctx, "full sync request finished with error", zap.Error(err))
} else if fullResponse != nil {
cnt := fullResponse.Content.GetFullSyncResponse()
log = log.With(zap.Strings("response heads", cnt.Heads), zap.Int("len(response changes)", len(cnt.Changes)))
log.DebugCtx(ctx, "full sync response sent")
}
}()
if len(request.Changes) != 0 && !t.alreadyHasHeads(objTree, request.Heads) {
_, err = objTree.AddRawChanges(ctx, objecttree.RawChangesPayload{
NewHeads: request.Heads,
RawChanges: request.Changes,
})
if err != nil {
return
}
}
fullResponse, err = t.reqFactory.CreateFullSyncResponse(objTree, request.Heads, request.SnapshotPath)
return
}
func (t *treeSyncProtocol) FullSyncResponse(ctx context.Context, senderId string, response *treechangeproto.TreeFullSyncResponse) (err error) {
var (
objTree = t.objTree
)
log := log.With(
zap.Strings("heads", response.Heads),
zap.Int("len(changes)", len(response.Changes)))
log.DebugCtx(ctx, "received full sync response message")
defer func() {
if err != nil {
log.ErrorCtx(ctx, "full sync response failed", zap.Error(err))
} else {
log.DebugCtx(ctx, "full sync response succeeded")
}
}()
if t.alreadyHasHeads(objTree, response.Heads) {
return
}
_, err = objTree.AddRawChanges(ctx, objecttree.RawChangesPayload{
NewHeads: response.Heads,
RawChanges: response.Changes,
})
return
}
func (t *treeSyncProtocol) alreadyHasHeads(ot objecttree.ObjectTree, heads []string) bool {
return slice.UnsortedEquals(ot.Heads(), heads) || ot.HasChanges(heads...)
}
@@ -1,293 +0,0 @@
package synctree
import (
"context"
"fmt"
"github.com/anyproto/any-sync/app/logger"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree/mock_objecttree"
"github.com/anyproto/any-sync/commonspace/object/tree/synctree/mock_synctree"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
"testing"
)
type treeSyncProtocolFixture struct {
log logger.CtxLogger
spaceId string
senderId string
treeId string
objectTreeMock *testObjTreeMock
reqFactory *mock_synctree.MockRequestFactory
ctrl *gomock.Controller
syncProtocol TreeSyncProtocol
}
func newSyncProtocolFixture(t *testing.T) *treeSyncProtocolFixture {
ctrl := gomock.NewController(t)
objTree := &testObjTreeMock{
MockObjectTree: mock_objecttree.NewMockObjectTree(ctrl),
}
spaceId := "spaceId"
reqFactory := mock_synctree.NewMockRequestFactory(ctrl)
objTree.EXPECT().Id().Return("treeId")
syncProtocol := newTreeSyncProtocol(spaceId, objTree, reqFactory)
return &treeSyncProtocolFixture{
log: log,
spaceId: spaceId,
senderId: "senderId",
treeId: "treeId",
objectTreeMock: objTree,
reqFactory: reqFactory,
ctrl: ctrl,
syncProtocol: syncProtocol,
}
}
func (fx *treeSyncProtocolFixture) stop() {
fx.ctrl.Finish()
}
func TestTreeSyncProtocol_HeadUpdate(t *testing.T) {
ctx := context.Background()
fullRequest := &treechangeproto.TreeSyncMessage{
Content: &treechangeproto.TreeSyncContentValue{
Value: &treechangeproto.TreeSyncContentValue_FullSyncRequest{
FullSyncRequest: &treechangeproto.TreeFullSyncRequest{},
},
},
}
t.Run("head update non empty all heads added", func(t *testing.T) {
fx := newSyncProtocolFixture(t)
defer fx.stop()
chWithId := &treechangeproto.RawTreeChangeWithId{}
headUpdate := &treechangeproto.TreeHeadUpdate{
Heads: []string{"h1"},
Changes: []*treechangeproto.RawTreeChangeWithId{chWithId},
SnapshotPath: []string{"h1"},
}
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId)
fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2"}).Times(2)
fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(false)
fx.objectTreeMock.EXPECT().
AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{
NewHeads: []string{"h1"},
RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId},
})).
Return(objecttree.AddResult{}, nil)
fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(true)
res, err := fx.syncProtocol.HeadUpdate(ctx, fx.senderId, headUpdate)
require.NoError(t, err)
require.Nil(t, res)
})
t.Run("head update non empty equal heads", func(t *testing.T) {
fx := newSyncProtocolFixture(t)
defer fx.stop()
chWithId := &treechangeproto.RawTreeChangeWithId{}
headUpdate := &treechangeproto.TreeHeadUpdate{
Heads: []string{"h1"},
Changes: []*treechangeproto.RawTreeChangeWithId{chWithId},
SnapshotPath: []string{"h1"},
}
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId)
fx.objectTreeMock.EXPECT().Heads().Return([]string{"h1"}).AnyTimes()
res, err := fx.syncProtocol.HeadUpdate(ctx, fx.senderId, headUpdate)
require.NoError(t, err)
require.Nil(t, res)
})
t.Run("head update empty", func(t *testing.T) {
fx := newSyncProtocolFixture(t)
defer fx.stop()
headUpdate := &treechangeproto.TreeHeadUpdate{
Heads: []string{"h1"},
Changes: nil,
SnapshotPath: []string{"h1"},
}
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId)
fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2"}).AnyTimes()
fx.reqFactory.EXPECT().
CreateFullSyncRequest(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})).
Return(fullRequest, nil)
res, err := fx.syncProtocol.HeadUpdate(ctx, fx.senderId, headUpdate)
require.NoError(t, err)
require.Equal(t, fullRequest, res)
})
t.Run("head update empty equal heads", func(t *testing.T) {
fx := newSyncProtocolFixture(t)
defer fx.stop()
headUpdate := &treechangeproto.TreeHeadUpdate{
Heads: []string{"h1"},
Changes: nil,
SnapshotPath: []string{"h1"},
}
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId)
fx.objectTreeMock.EXPECT().Heads().Return([]string{"h1"}).AnyTimes()
res, err := fx.syncProtocol.HeadUpdate(ctx, fx.senderId, headUpdate)
require.NoError(t, err)
require.Nil(t, res)
})
}
func TestTreeSyncProtocol_FullSyncRequest(t *testing.T) {
ctx := context.Background()
fullResponse := &treechangeproto.TreeSyncMessage{
Content: &treechangeproto.TreeSyncContentValue{
Value: &treechangeproto.TreeSyncContentValue_FullSyncResponse{
FullSyncResponse: &treechangeproto.TreeFullSyncResponse{},
},
},
}
t.Run("full sync request with change", func(t *testing.T) {
fx := newSyncProtocolFixture(t)
defer fx.stop()
chWithId := &treechangeproto.RawTreeChangeWithId{}
fullSyncRequest := &treechangeproto.TreeFullSyncRequest{
Heads: []string{"h1"},
Changes: []*treechangeproto.RawTreeChangeWithId{chWithId},
SnapshotPath: []string{"h1"},
}
fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2"}).AnyTimes()
fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(false)
fx.objectTreeMock.EXPECT().
AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{
NewHeads: []string{"h1"},
RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId},
})).
Return(objecttree.AddResult{}, nil)
fx.reqFactory.EXPECT().
CreateFullSyncResponse(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})).
Return(fullResponse, nil)
res, err := fx.syncProtocol.FullSyncRequest(ctx, fx.senderId, fullSyncRequest)
require.NoError(t, err)
require.Equal(t, fullResponse, res)
})
t.Run("full sync request with change same heads", func(t *testing.T) {
fx := newSyncProtocolFixture(t)
defer fx.stop()
chWithId := &treechangeproto.RawTreeChangeWithId{}
fullSyncRequest := &treechangeproto.TreeFullSyncRequest{
Heads: []string{"h1"},
Changes: []*treechangeproto.RawTreeChangeWithId{chWithId},
SnapshotPath: []string{"h1"},
}
fx.objectTreeMock.EXPECT().
Heads().
Return([]string{"h1"}).AnyTimes()
fx.reqFactory.EXPECT().
CreateFullSyncResponse(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})).
Return(fullResponse, nil)
res, err := fx.syncProtocol.FullSyncRequest(ctx, fx.senderId, fullSyncRequest)
require.NoError(t, err)
require.Equal(t, fullResponse, res)
})
t.Run("full sync request without changes", func(t *testing.T) {
fx := newSyncProtocolFixture(t)
defer fx.stop()
fullSyncRequest := &treechangeproto.TreeFullSyncRequest{
Heads: []string{"h1"},
SnapshotPath: []string{"h1"},
}
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId)
fx.reqFactory.EXPECT().
CreateFullSyncResponse(gomock.Eq(fx.objectTreeMock), gomock.Eq([]string{"h1"}), gomock.Eq([]string{"h1"})).
Return(fullResponse, nil)
res, err := fx.syncProtocol.FullSyncRequest(ctx, fx.senderId, fullSyncRequest)
require.NoError(t, err)
require.Equal(t, fullResponse, res)
})
t.Run("full sync request with change, raw changes error", func(t *testing.T) {
fx := newSyncProtocolFixture(t)
defer fx.stop()
chWithId := &treechangeproto.RawTreeChangeWithId{}
fullSyncRequest := &treechangeproto.TreeFullSyncRequest{
Heads: []string{"h1"},
Changes: []*treechangeproto.RawTreeChangeWithId{chWithId},
SnapshotPath: []string{"h1"},
}
fx.objectTreeMock.EXPECT().Heads().Return([]string{"h2"}).AnyTimes()
fx.objectTreeMock.EXPECT().HasChanges(gomock.Eq([]string{"h1"})).Return(false)
fx.objectTreeMock.EXPECT().
AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{
NewHeads: []string{"h1"},
RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId},
})).
Return(objecttree.AddResult{}, fmt.Errorf("addRawChanges error"))
_, err := fx.syncProtocol.FullSyncRequest(ctx, fx.senderId, fullSyncRequest)
require.Error(t, err)
})
}
func TestTreeSyncProtocol_FullSyncResponse(t *testing.T) {
ctx := context.Background()
t.Run("full sync response with change", func(t *testing.T) {
fx := newSyncProtocolFixture(t)
defer fx.stop()
chWithId := &treechangeproto.RawTreeChangeWithId{}
fullSyncResponse := &treechangeproto.TreeFullSyncResponse{
Heads: []string{"h1"},
Changes: []*treechangeproto.RawTreeChangeWithId{chWithId},
SnapshotPath: []string{"h1"},
}
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId)
fx.objectTreeMock.EXPECT().
Heads().
Return([]string{"h2"}).AnyTimes()
fx.objectTreeMock.EXPECT().
HasChanges(gomock.Eq([]string{"h1"})).
Return(false)
fx.objectTreeMock.EXPECT().
AddRawChanges(gomock.Any(), gomock.Eq(objecttree.RawChangesPayload{
NewHeads: []string{"h1"},
RawChanges: []*treechangeproto.RawTreeChangeWithId{chWithId},
})).
Return(objecttree.AddResult{}, nil)
err := fx.syncProtocol.FullSyncResponse(ctx, fx.senderId, fullSyncResponse)
require.NoError(t, err)
})
t.Run("full sync response with same heads", func(t *testing.T) {
fx := newSyncProtocolFixture(t)
defer fx.stop()
chWithId := &treechangeproto.RawTreeChangeWithId{}
fullSyncResponse := &treechangeproto.TreeFullSyncResponse{
Heads: []string{"h1"},
Changes: []*treechangeproto.RawTreeChangeWithId{chWithId},
SnapshotPath: []string{"h1"},
}
fx.objectTreeMock.EXPECT().Id().AnyTimes().Return(fx.treeId)
fx.objectTreeMock.EXPECT().
Heads().
Return([]string{"h1"}).AnyTimes()
err := fx.syncProtocol.FullSyncResponse(ctx, fx.senderId, fullSyncResponse)
require.NoError(t, err)
})
}
@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/anyproto/any-sync/commonspace/object/tree/synctree/updatelistener (interfaces: UpdateListener)
// Source: github.com/anytypeio/any-sync/commonspace/object/tree/synctree/updatelistener (interfaces: UpdateListener)
// Package mock_updatelistener is a generated GoMock package.
package mock_updatelistener
@@ -7,8 +7,8 @@ package mock_updatelistener
import (
reflect "reflect"
objecttree "github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
gomock "go.uber.org/mock/gomock"
objecttree "github.com/anytypeio/any-sync/commonspace/object/tree/objecttree"
gomock "github.com/golang/mock/gomock"
)
// MockUpdateListener is a mock of UpdateListener interface.
@@ -1,8 +1,8 @@
//go:generate mockgen -destination mock_updatelistener/mock_updatelistener.go github.com/anyproto/any-sync/commonspace/object/tree/synctree/updatelistener UpdateListener
//go:generate mockgen -destination mock_updatelistener/mock_updatelistener.go github.com/anytypeio/any-sync/commonspace/object/tree/synctree/updatelistener UpdateListener
package updatelistener
import (
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
"github.com/anytypeio/any-sync/commonspace/object/tree/objecttree"
)
type UpdateListener interface {
@@ -1,471 +0,0 @@
package synctree
import (
"context"
"fmt"
"github.com/anyproto/any-sync/app"
"github.com/anyproto/any-sync/commonspace/object/acl/list"
"github.com/anyproto/any-sync/commonspace/object/tree/objecttree"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/commonspace/object/tree/treestorage"
"github.com/anyproto/any-sync/commonspace/objectsync/synchandler"
"github.com/anyproto/any-sync/commonspace/spacesyncproto"
"github.com/anyproto/any-sync/commonspace/syncstatus"
"github.com/anyproto/any-sync/net/peer"
"github.com/cheggaaa/mb/v3"
"github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/require"
"golang.org/x/exp/slices"
"math/rand"
"sync"
"testing"
"time"
)
// protocolMsg is a message used in sync protocol tests
type protocolMsg struct {
msg *spacesyncproto.ObjectSyncMessage
senderId string
receiverId string
userMsg *objecttree.RawChangesPayload
}
// msgDescription is a representation of message used for checking the results of the test
type msgDescription struct {
name string
from string
to string
heads []string
changes []*treechangeproto.RawTreeChangeWithId
}
func (p *protocolMsg) description() (descr msgDescription) {
unmarshalled := &treechangeproto.TreeSyncMessage{}
err := proto.Unmarshal(p.msg.Payload, unmarshalled)
if err != nil {
panic(err)
}
descr = msgDescription{
from: p.senderId,
to: p.receiverId,
}
switch {
case unmarshalled.GetContent().GetHeadUpdate() != nil:
cnt := unmarshalled.GetContent().GetHeadUpdate()
descr.name = "HeadUpdate"
descr.heads = cnt.Heads
descr.changes = unmarshalled.GetContent().GetHeadUpdate().Changes
case unmarshalled.GetContent().GetFullSyncRequest() != nil:
cnt := unmarshalled.GetContent().GetFullSyncRequest()
descr.name = "FullSyncRequest"
descr.heads = cnt.Heads
descr.changes = unmarshalled.GetContent().GetFullSyncRequest().Changes
case unmarshalled.GetContent().GetFullSyncResponse() != nil:
cnt := unmarshalled.GetContent().GetFullSyncResponse()
descr.name = "FullSyncResponse"
descr.heads = cnt.Heads
descr.changes = unmarshalled.GetContent().GetFullSyncResponse().Changes
}
return
}
// messageLog saves all messages that were sent during sync test
type messageLog struct {
batcher *mb.MB[protocolMsg]
}
func newMessageLog() *messageLog {
return &messageLog{batcher: mb.New[protocolMsg](0)}
}
func (m *messageLog) addMessage(msg protocolMsg) {
m.batcher.Add(context.Background(), msg)
}
type requestPeerManager struct {
peerId string
handlers map[string]*testSyncHandler
log *messageLog
}
func newRequestPeerManager(peerId string, log *messageLog) *requestPeerManager {
return &requestPeerManager{
peerId: peerId,
handlers: map[string]*testSyncHandler{},
log: log,
}
}
func (r *requestPeerManager) addHandler(peerId string, handler *testSyncHandler) {
r.handlers[peerId] = handler
}
func (r *requestPeerManager) Run(ctx context.Context) (err error) {
return nil
}
func (r *requestPeerManager) Close(ctx context.Context) (err error) {
return nil
}
func (r *requestPeerManager) SendRequest(ctx context.Context, peerId string, msg *spacesyncproto.ObjectSyncMessage) (reply *spacesyncproto.ObjectSyncMessage, err error) {
panic("should not be called")
}
func (r *requestPeerManager) QueueRequest(peerId string, msg *spacesyncproto.ObjectSyncMessage) (err error) {
pMsg := protocolMsg{
msg: msg,
senderId: r.peerId,
receiverId: peerId,
}
r.log.addMessage(pMsg)
return r.handlers[peerId].send(context.Background(), pMsg)
}
func (r *requestPeerManager) Init(a *app.App) (err error) {
return
}
func (r *requestPeerManager) Name() (name string) {
return
}
func (r *requestPeerManager) SendPeer(ctx context.Context, peerId string, msg *spacesyncproto.ObjectSyncMessage) (err error) {
pMsg := protocolMsg{
msg: msg,
senderId: r.peerId,
receiverId: peerId,
}
r.log.addMessage(pMsg)
return r.handlers[peerId].send(context.Background(), pMsg)
}
func (r *requestPeerManager) Broadcast(ctx context.Context, msg *spacesyncproto.ObjectSyncMessage) (err error) {
for _, handler := range r.handlers {
pMsg := protocolMsg{
msg: msg,
senderId: r.peerId,
receiverId: handler.peerId,
}
r.log.addMessage(pMsg)
handler.send(context.Background(), pMsg)
}
return
}
func (r *requestPeerManager) GetResponsiblePeers(ctx context.Context) (peers []peer.Peer, err error) {
return nil, nil
}
// testSyncHandler is the wrapper around individual tree to test sync protocol
type testSyncHandler struct {
synchandler.SyncHandler
batcher *mb.MB[protocolMsg]
peerId string
aclList list.AclList
log *messageLog
syncClient SyncClient
builder objecttree.BuildObjectTreeFunc
peerManager *requestPeerManager
}
// createSyncHandler creates a sync handler when a tree is already created
func createSyncHandler(peerId, spaceId string, objTree objecttree.ObjectTree, log *messageLog) *testSyncHandler {
peerManager := newRequestPeerManager(peerId, log)
syncClient := NewSyncClient(spaceId, peerManager, peerManager)
netTree := &broadcastTree{
ObjectTree: objTree,
SyncClient: syncClient,
}
handler := newSyncTreeHandler(spaceId, netTree, syncClient, syncstatus.NewNoOpSyncStatus())
return &testSyncHandler{
SyncHandler: handler,
batcher: mb.New[protocolMsg](0),
peerId: peerId,
peerManager: peerManager,
}
}
// createEmptySyncHandler creates a sync handler when the tree will be provided later (this emulates the situation when we have no tree)
func createEmptySyncHandler(peerId, spaceId string, builder objecttree.BuildObjectTreeFunc, aclList list.AclList, log *messageLog) *testSyncHandler {
peerManager := newRequestPeerManager(peerId, log)
syncClient := NewSyncClient(spaceId, peerManager, peerManager)
batcher := mb.New[protocolMsg](0)
return &testSyncHandler{
batcher: batcher,
peerId: peerId,
aclList: aclList,
log: log,
syncClient: syncClient,
builder: builder,
peerManager: peerManager,
}
}
func (h *testSyncHandler) HandleMessage(ctx context.Context, senderId string, request *spacesyncproto.ObjectSyncMessage) (err error) {
if h.SyncHandler != nil {
return h.SyncHandler.HandleMessage(ctx, senderId, request)
}
unmarshalled := &treechangeproto.TreeSyncMessage{}
err = proto.Unmarshal(request.Payload, unmarshalled)
if err != nil {
return
}
if unmarshalled.Content.GetFullSyncResponse() == nil {
newTreeRequest := NewRequestFactory().CreateNewTreeRequest()
return h.syncClient.QueueRequest(senderId, request.ObjectId, newTreeRequest)
}
fullSyncResponse := unmarshalled.Content.GetFullSyncResponse()
treeStorage, _ := treestorage.NewInMemoryTreeStorage(unmarshalled.RootChange, []string{unmarshalled.RootChange.Id}, nil)
tree, err := h.builder(treeStorage, h.aclList)
if err != nil {
return
}
netTree := &broadcastTree{
ObjectTree: tree,
SyncClient: h.syncClient,
}
res, err := netTree.AddRawChanges(context.Background(), objecttree.RawChangesPayload{
NewHeads: fullSyncResponse.Heads,
RawChanges: fullSyncResponse.Changes,
})
if err != nil {
return
}
h.SyncHandler = newSyncTreeHandler(request.SpaceId, netTree, h.syncClient, syncstatus.NewNoOpSyncStatus())
headUpdate := NewRequestFactory().CreateHeadUpdate(netTree, res.Added)
h.syncClient.Broadcast(headUpdate)
return nil
}
func (h *testSyncHandler) manager() *requestPeerManager {
return h.peerManager
}
func (h *testSyncHandler) tree() *broadcastTree {
return h.SyncHandler.(*syncTreeHandler).objTree.(*broadcastTree)
}
func (h *testSyncHandler) send(ctx context.Context, msg protocolMsg) (err error) {
return h.batcher.Add(ctx, msg)
}
func (h *testSyncHandler) sendRawChanges(ctx context.Context, changes objecttree.RawChangesPayload) {
h.batcher.Add(ctx, protocolMsg{userMsg: &changes})
}
func (h *testSyncHandler) run(ctx context.Context, t *testing.T, wg *sync.WaitGroup) {
wg.Add(1)
go func() {
defer wg.Done()
for {
res, err := h.batcher.WaitOne(ctx)
if err != nil {
return
}
if res.userMsg != nil {
h.tree().Lock()
userRes, err := h.tree().AddRawChanges(ctx, *res.userMsg)
require.NoError(t, err)
fmt.Println("user add result", userRes.Heads)
h.tree().Unlock()
continue
}
if res.description().name == "FullSyncRequest" {
resp, err := h.HandleRequest(ctx, res.senderId, res.msg)
if err != nil {
fmt.Println("error handling request", err.Error())
continue
}
h.peerManager.SendPeer(ctx, res.senderId, resp)
} else {
err = h.HandleMessage(ctx, res.senderId, res.msg)
if err != nil {
fmt.Println("error handling message", err.Error())
}
}
}
}()
}
// broadcastTree is the tree that broadcasts changes to everyone when changes are added
// it is a simplified version of SyncTree which is easier to use in the test environment
type broadcastTree struct {
objecttree.ObjectTree
SyncClient
}
func (b *broadcastTree) AddRawChanges(ctx context.Context, changes objecttree.RawChangesPayload) (objecttree.AddResult, error) {
res, err := b.ObjectTree.AddRawChanges(ctx, changes)
if err != nil {
return objecttree.AddResult{}, err
}
upd := b.SyncClient.CreateHeadUpdate(b.ObjectTree, res.Added)
b.SyncClient.Broadcast(upd)
return res, nil
}
func createStorage(treeId string, aclList list.AclList) treestorage.TreeStorage {
changeCreator := objecttree.NewMockChangeCreator()
st := changeCreator.CreateNewTreeStorage(treeId, aclList.Head().Id)
return st
}
func createTestTree(aclList list.AclList, storage treestorage.TreeStorage) (objecttree.ObjectTree, error) {
return objecttree.BuildEmptyDataTestableTree(storage, aclList)
}
type fixtureDeps struct {
aclList list.AclList
initStorage *treestorage.InMemoryTreeStorage
connectionMap map[string][]string
emptyTrees []string
treeBuilder objecttree.BuildObjectTreeFunc
}
// protocolFixture is the test environment for sync protocol tests
type protocolFixture struct {
handlers map[string]*testSyncHandler
log *messageLog
wg *sync.WaitGroup
ctx context.Context
cancel context.CancelFunc
}
func newProtocolFixture(t *testing.T, spaceId string, deps fixtureDeps) *protocolFixture {
var (
handlers = map[string]*testSyncHandler{}
log = newMessageLog()
wg = sync.WaitGroup{}
ctx, cancel = context.WithCancel(context.Background())
)
for peerId := range deps.connectionMap {
var handler *testSyncHandler
if slices.Contains(deps.emptyTrees, peerId) {
handler = createEmptySyncHandler(peerId, spaceId, deps.treeBuilder, deps.aclList, log)
} else {
stCopy := deps.initStorage.Copy()
testTree, err := deps.treeBuilder(stCopy, deps.aclList)
require.NoError(t, err)
handler = createSyncHandler(peerId, spaceId, testTree, log)
}
handlers[peerId] = handler
}
for peerId, connectionMap := range deps.connectionMap {
handler := handlers[peerId]
manager := handler.manager()
for _, connectionId := range connectionMap {
manager.addHandler(connectionId, handlers[connectionId])
}
}
return &protocolFixture{
handlers: handlers,
log: log,
wg: &wg,
ctx: ctx,
cancel: cancel,
}
}
func (p *protocolFixture) run(t *testing.T) {
for _, handler := range p.handlers {
handler.run(p.ctx, t, p.wg)
}
}
func (p *protocolFixture) stop() {
p.cancel()
p.wg.Wait()
}
// genParams is the parameters for genChanges
type genParams struct {
// prefix is the prefix which is added to change id
prefix string
aclId string
startIdx int
levels int
perLevel int
snapshotId string
prevHeads []string
isSnapshot func() bool
hasData bool
}
// genResult is the result of genChanges
type genResult struct {
changes []*treechangeproto.RawTreeChangeWithId
heads []string
snapshotId string
}
// genChanges generates several levels of tree changes where each level is connected only with previous one
func genChanges(creator *objecttree.MockChangeCreator, params genParams) (res genResult) {
src := rand.NewSource(time.Now().Unix())
rnd := rand.New(src)
var (
prevHeads []string
snapshotId = params.snapshotId
)
prevHeads = append(prevHeads, params.prevHeads...)
for i := 0; i < params.levels; i++ {
var (
newHeads []string
usedIds = map[string]struct{}{}
)
newChange := func(isSnapshot bool, idx int, prevIds []string) string {
newId := fmt.Sprintf("%s.%d.%d", params.prefix, params.startIdx+i, idx)
var data []byte
if params.hasData {
data = []byte(newId)
}
newCh := creator.CreateRawWithData(newId, params.aclId, snapshotId, isSnapshot, data, prevIds...)
res.changes = append(res.changes, newCh)
return newId
}
if params.isSnapshot() {
newId := newChange(true, 0, prevHeads)
prevHeads = []string{newId}
snapshotId = newId
continue
}
perLevel := rnd.Intn(params.perLevel)
if perLevel == 0 {
perLevel = 1
}
for j := 0; j < perLevel; j++ {
prevConns := rnd.Intn(len(prevHeads))
if prevConns == 0 {
prevConns = 1
}
rnd.Shuffle(len(prevHeads), func(i, j int) {
prevHeads[i], prevHeads[j] = prevHeads[j], prevHeads[i]
})
// if we didn't connect with all prev ones
if j == perLevel-1 && len(usedIds) != len(prevHeads) {
var unusedIds []string
for _, id := range prevHeads {
if _, exists := usedIds[id]; !exists {
unusedIds = append(unusedIds, id)
}
}
prevHeads = unusedIds
prevConns = len(prevHeads)
}
var prevIds []string
for k := 0; k < prevConns; k++ {
prevIds = append(prevIds, prevHeads[k])
usedIds[prevHeads[k]] = struct{}{}
}
newId := newChange(false, j, prevIds)
newHeads = append(newHeads, newId)
}
prevHeads = newHeads
}
res.heads = prevHeads
res.snapshotId = snapshotId
return
}
@@ -1,14 +0,0 @@
package treechangeproto
import (
"errors"
"github.com/anyproto/any-sync/net/rpc/rpcerr"
)
var (
errGroup = rpcerr.ErrGroup(ErrorCodes_ErrorOffset)
ErrUnexpected = errGroup.Register(errors.New("unexpected error"), uint64(ErrorCodes_Unexpected))
ErrGetTree = errGroup.Register(errors.New("tree not found"), uint64(ErrorCodes_GetTreeError))
ErrFullSync = errGroup.Register(errors.New("full sync request error"), uint64(ErrorCodes_FullSyncRequestError))
)
@@ -56,13 +56,6 @@ message RawTreeChangeWithId {
string id = 2;
}
enum ErrorCodes {
Unexpected = 0;
GetTreeError = 1;
FullSyncRequestError = 2;
ErrorOffset = 400;
}
message TreeSyncMessage {
TreeSyncContentValue content = 1;
RawTreeChangeWithId rootChange = 2;
@@ -102,7 +95,6 @@ message TreeFullSyncResponse {
// TreeErrorResponse is an error sent as a response for a full sync request
message TreeErrorResponse {
string error = 1;
uint64 errCode = 2;
}
// TreeChangeInfo is used internally in Tree implementation for ease of marshalling
@@ -1,7 +1,5 @@
package treechangeproto
import "github.com/anyproto/any-sync/net/rpc/rpcerr"
func WrapHeadUpdate(update *TreeHeadUpdate, rootChange *RawTreeChangeWithId) *TreeSyncMessage {
return &TreeSyncMessage{
Content: &TreeSyncContentValue{
@@ -32,7 +30,7 @@ func WrapFullResponse(response *TreeFullSyncResponse, rootChange *RawTreeChangeW
func WrapError(err error, rootChange *RawTreeChangeWithId) *TreeSyncMessage {
return &TreeSyncMessage{
Content: &TreeSyncContentValue{
Value: &TreeSyncContentValue_ErrorResponse{ErrorResponse: &TreeErrorResponse{ErrCode: rpcerr.Code(err)}},
Value: &TreeSyncContentValue_ErrorResponse{ErrorResponse: &TreeErrorResponse{Error: err.Error()}},
},
RootChange: rootChange,
}
@@ -22,37 +22,6 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
type ErrorCodes int32
const (
ErrorCodes_Unexpected ErrorCodes = 0
ErrorCodes_GetTreeError ErrorCodes = 1
ErrorCodes_FullSyncRequestError ErrorCodes = 2
ErrorCodes_ErrorOffset ErrorCodes = 400
)
var ErrorCodes_name = map[int32]string{
0: "Unexpected",
1: "GetTreeError",
2: "FullSyncRequestError",
400: "ErrorOffset",
}
var ErrorCodes_value = map[string]int32{
"Unexpected": 0,
"GetTreeError": 1,
"FullSyncRequestError": 2,
"ErrorOffset": 400,
}
func (x ErrorCodes) String() string {
return proto.EnumName(ErrorCodes_name, int32(x))
}
func (ErrorCodes) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_5033f0301ef9b772, []int{0}
}
// RootChange is a root of a tree
type RootChange struct {
// AclHeadId is a cid of latest acl record at the time of tree creation
@@ -722,8 +691,7 @@ func (m *TreeFullSyncResponse) GetSnapshotPath() []string {
// TreeErrorResponse is an error sent as a response for a full sync request
type TreeErrorResponse struct {
Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"`
ErrCode uint64 `protobuf:"varint,2,opt,name=errCode,proto3" json:"errCode,omitempty"`
Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"`
}
func (m *TreeErrorResponse) Reset() { *m = TreeErrorResponse{} }
@@ -766,13 +734,6 @@ func (m *TreeErrorResponse) GetError() string {
return ""
}
func (m *TreeErrorResponse) GetErrCode() uint64 {
if m != nil {
return m.ErrCode
}
return 0
}
// TreeChangeInfo is used internally in Tree implementation for ease of marshalling
type TreeChangeInfo struct {
ChangeType string `protobuf:"bytes,1,opt,name=changeType,proto3" json:"changeType,omitempty"`
@@ -827,7 +788,6 @@ func (m *TreeChangeInfo) GetChangePayload() []byte {
}
func init() {
proto.RegisterEnum("treechange.ErrorCodes", ErrorCodes_name, ErrorCodes_value)
proto.RegisterType((*RootChange)(nil), "treechange.RootChange")
proto.RegisterType((*TreeChange)(nil), "treechange.TreeChange")
proto.RegisterType((*RawTreeChange)(nil), "treechange.RawTreeChange")
@@ -846,54 +806,50 @@ func init() {
}
var fileDescriptor_5033f0301ef9b772 = []byte{
// 741 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x55, 0x4f, 0x4f, 0xfb, 0x46,
0x10, 0xf5, 0x3a, 0x81, 0x90, 0x49, 0x08, 0xe9, 0xc2, 0xc1, 0x42, 0xad, 0x6b, 0x59, 0x55, 0x1b,
0xf5, 0x00, 0x12, 0x3d, 0xb5, 0xaa, 0x84, 0x4a, 0x0a, 0x24, 0x42, 0x6d, 0xd1, 0xf2, 0xa7, 0x12,
0xb7, 0xc5, 0x9e, 0x10, 0x57, 0x89, 0xed, 0x7a, 0x37, 0xa5, 0xf9, 0x00, 0xbd, 0xb4, 0x12, 0xe2,
0x2b, 0xf5, 0xf6, 0x3b, 0x72, 0xe4, 0xf8, 0x13, 0x7c, 0x91, 0x9f, 0xbc, 0x1b, 0x27, 0xb6, 0x93,
0x03, 0x37, 0x2e, 0x89, 0xe7, 0x79, 0xe6, 0xed, 0x9b, 0x37, 0xde, 0x5d, 0x38, 0xf4, 0xa2, 0xf1,
0x38, 0x0a, 0x45, 0xcc, 0x3d, 0xdc, 0x8f, 0x6e, 0xff, 0x40, 0x4f, 0xee, 0xcb, 0x04, 0x51, 0xfd,
0x78, 0x43, 0x1e, 0xde, 0x61, 0x9c, 0x44, 0x32, 0xda, 0x57, 0xbf, 0x22, 0x07, 0xef, 0x29, 0x84,
0xc2, 0x02, 0x71, 0x9f, 0x09, 0x00, 0x8b, 0x22, 0xd9, 0x55, 0x21, 0xfd, 0x1c, 0xea, 0xdc, 0x1b,
0xf5, 0x90, 0xfb, 0x7d, 0xdf, 0x22, 0x0e, 0xe9, 0xd4, 0xd9, 0x02, 0xa0, 0x16, 0xd4, 0xd4, 0xaa,
0x7d, 0xdf, 0x32, 0xd5, 0xbb, 0x2c, 0xa4, 0x36, 0x80, 0x26, 0xbc, 0x9c, 0xc6, 0x68, 0x55, 0xd4,
0xcb, 0x1c, 0x92, 0xf2, 0xca, 0x60, 0x8c, 0x42, 0xf2, 0x71, 0x6c, 0x55, 0x1d, 0xd2, 0xa9, 0xb0,
0x05, 0x40, 0x29, 0x54, 0x05, 0xa2, 0x6f, 0xad, 0x39, 0xa4, 0xd3, 0x64, 0xea, 0x99, 0xee, 0xc2,
0x46, 0xe0, 0x63, 0x28, 0x03, 0x39, 0xb5, 0xd6, 0x15, 0x3e, 0x8f, 0xe9, 0x57, 0xb0, 0xa9, 0xb9,
0xcf, 0xf9, 0x74, 0x14, 0x71, 0xdf, 0xaa, 0xa9, 0x84, 0x22, 0xe8, 0x3e, 0x98, 0x00, 0x97, 0x09,
0xe2, 0xac, 0x35, 0x07, 0x1a, 0x69, 0xdf, 0xba, 0x15, 0x61, 0x11, 0xa7, 0xd2, 0xa9, 0xb3, 0x3c,
0x54, 0x6c, 0xde, 0x2c, 0x37, 0xff, 0x35, 0xb4, 0x44, 0xc8, 0x63, 0x31, 0x8c, 0xe4, 0x11, 0x17,
0xa9, 0x07, 0xba, 0xcd, 0x12, 0x9a, 0xae, 0xa3, 0x75, 0x88, 0x9f, 0xb9, 0xe4, 0xaa, 0xd9, 0x26,
0xcb, 0x43, 0xe9, 0x3a, 0x09, 0x72, 0xff, 0x0c, 0xa7, 0x7d, 0xdd, 0x73, 0x9d, 0x2d, 0x80, 0xa2,
0x55, 0xeb, 0x65, 0xab, 0xf2, 0xb6, 0xd4, 0x4a, 0xb6, 0xd8, 0x00, 0x81, 0xb8, 0x98, 0xa9, 0xb1,
0x36, 0x1c, 0xd2, 0xd9, 0x60, 0x39, 0xc4, 0x3d, 0x85, 0x4d, 0xc6, 0xef, 0x73, 0x96, 0x58, 0x50,
0x8b, 0x67, 0x0e, 0x12, 0xc5, 0x95, 0x85, 0xa9, 0x08, 0x11, 0xdc, 0x85, 0x5c, 0x4e, 0x12, 0x54,
0x56, 0x34, 0xd9, 0x02, 0x70, 0xbb, 0xb0, 0x5d, 0x20, 0xfa, 0x3d, 0x90, 0x43, 0xad, 0x3c, 0xe1,
0xf7, 0x1a, 0x9a, 0x11, 0x2e, 0x00, 0xda, 0x02, 0x33, 0xc8, 0x6c, 0x35, 0x03, 0xdf, 0x7d, 0x20,
0xb0, 0x95, 0x52, 0x5c, 0x4c, 0x43, 0xef, 0x17, 0x14, 0x82, 0xdf, 0x21, 0xfd, 0x01, 0x6a, 0x5e,
0x14, 0x4a, 0x0c, 0xa5, 0xaa, 0x6f, 0x1c, 0x38, 0x7b, 0xb9, 0xaf, 0x37, 0xcb, 0xee, 0xea, 0x94,
0x6b, 0x3e, 0x9a, 0x20, 0xcb, 0x0a, 0xe8, 0x21, 0x40, 0x32, 0xff, 0x90, 0xd5, 0x3a, 0x8d, 0x83,
0x2f, 0xf3, 0xe5, 0x2b, 0x24, 0xb3, 0x5c, 0x89, 0xfb, 0xbf, 0x09, 0x3b, 0xab, 0x96, 0xa0, 0x3f,
0x02, 0x0c, 0x91, 0xfb, 0x57, 0xb1, 0xcf, 0x25, 0xce, 0x84, 0xed, 0x96, 0x85, 0xf5, 0xe6, 0x19,
0x3d, 0x83, 0xe5, 0xf2, 0xe9, 0x19, 0x6c, 0x0d, 0x26, 0xa3, 0x51, 0xca, 0xca, 0xf0, 0xcf, 0x09,
0x0a, 0xb9, 0x4a, 0x5c, 0x4a, 0x71, 0x52, 0x4c, 0xeb, 0x19, 0xac, 0x5c, 0x49, 0x7f, 0x85, 0xf6,
0x02, 0x12, 0x71, 0x14, 0x0a, 0xbd, 0xdb, 0x56, 0x38, 0x75, 0x52, 0xca, 0xeb, 0x19, 0x6c, 0xa9,
0x96, 0x1e, 0xc3, 0x26, 0x26, 0x49, 0x94, 0xcc, 0xc9, 0xaa, 0x8a, 0xec, 0x8b, 0x32, 0xd9, 0x71,
0x3e, 0xa9, 0x67, 0xb0, 0x62, 0xd5, 0x51, 0x0d, 0xd6, 0xfe, 0x4a, 0xad, 0x72, 0xff, 0x21, 0xd0,
0x2a, 0xba, 0x41, 0x77, 0x60, 0x2d, 0x75, 0x23, 0xdb, 0x71, 0x3a, 0xa0, 0xdf, 0x43, 0x6d, 0xb6,
0x25, 0x2c, 0xd3, 0xa9, 0xbc, 0x65, 0x54, 0x59, 0x3e, 0x75, 0xa1, 0x99, 0x6d, 0xb9, 0x73, 0x2e,
0x87, 0x56, 0x45, 0xf1, 0x16, 0x30, 0xf7, 0x5f, 0x02, 0xdb, 0x2b, 0x2c, 0x7d, 0x1f, 0x31, 0xff,
0x11, 0xfd, 0x61, 0x95, 0x27, 0xf2, 0x3e, 0x6a, 0xba, 0xf0, 0xd9, 0xd2, 0x44, 0x53, 0x25, 0x6a,
0xa2, 0xb3, 0x33, 0x5f, 0x07, 0xe9, 0xf9, 0x80, 0x49, 0xd2, 0x8d, 0x7c, 0xbd, 0x9f, 0xaa, 0x2c,
0x0b, 0xdd, 0x6b, 0x3d, 0x66, 0xad, 0xa2, 0x1f, 0x0e, 0xa2, 0xd2, 0x0d, 0x40, 0x96, 0x6e, 0x80,
0xa5, 0x33, 0xdb, 0x5c, 0x71, 0x66, 0x7f, 0x7b, 0x03, 0xa0, 0x84, 0xa5, 0x8b, 0x08, 0xda, 0x02,
0xb8, 0x0a, 0xf1, 0xef, 0x18, 0x3d, 0x89, 0x7e, 0xdb, 0xa0, 0x6d, 0x68, 0x9e, 0xa2, 0x9c, 0xab,
0x6f, 0x13, 0x6a, 0xc1, 0x4e, 0x69, 0xc4, 0xfa, 0x8d, 0x49, 0xdb, 0xd0, 0x50, 0x8f, 0xbf, 0x0d,
0x06, 0x02, 0x65, 0xfb, 0xb1, 0x72, 0xf4, 0xd3, 0x87, 0x17, 0x9b, 0x3c, 0xbd, 0xd8, 0xe4, 0xe3,
0x8b, 0x4d, 0x1e, 0x5f, 0x6d, 0xe3, 0xe9, 0xd5, 0x36, 0x9e, 0x5f, 0x6d, 0xe3, 0xe6, 0x9b, 0x37,
0xde, 0xa8, 0xb7, 0xeb, 0xea, 0xef, 0xbb, 0x4f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x1b, 0x35, 0x02,
0x10, 0x83, 0x07, 0x00, 0x00,
// 677 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x55, 0xcf, 0x4f, 0xd4, 0x40,
0x14, 0xee, 0x74, 0x81, 0xb2, 0x8f, 0x05, 0x74, 0xe0, 0xd0, 0x10, 0xad, 0x4d, 0x63, 0x74, 0xbd,
0x40, 0x82, 0x27, 0x8d, 0x09, 0x11, 0x04, 0x77, 0x43, 0x34, 0x64, 0x40, 0x4c, 0xbc, 0x0d, 0xed,
0xc0, 0xd6, 0xec, 0x76, 0x6a, 0x67, 0x56, 0xb2, 0x7f, 0x80, 0x17, 0x4d, 0x88, 0xff, 0x92, 0x37,
0x8f, 0x1c, 0x39, 0x1a, 0xf6, 0x1f, 0x31, 0x9d, 0x69, 0xb7, 0x3f, 0x76, 0x0f, 0xdc, 0xb8, 0x74,
0xf7, 0x7d, 0x7d, 0xef, 0x7b, 0xdf, 0xfb, 0xe6, 0x47, 0x61, 0xc7, 0xe7, 0x83, 0x01, 0x8f, 0x44,
0x4c, 0x7d, 0xb6, 0xc5, 0xcf, 0xbe, 0x32, 0x5f, 0x6e, 0xc9, 0x84, 0x31, 0xf5, 0xf0, 0x7b, 0x34,
0xba, 0x60, 0x71, 0xc2, 0x25, 0xdf, 0x52, 0x4f, 0x51, 0x82, 0x37, 0x15, 0x82, 0xa1, 0x40, 0xbc,
0x1b, 0x04, 0x40, 0x38, 0x97, 0x7b, 0x2a, 0xc4, 0x8f, 0xa0, 0x49, 0xfd, 0x7e, 0x87, 0xd1, 0xa0,
0x1b, 0xd8, 0xc8, 0x45, 0xed, 0x26, 0x29, 0x00, 0x6c, 0x83, 0xa5, 0xba, 0x76, 0x03, 0xdb, 0x54,
0xef, 0xf2, 0x10, 0x3b, 0x00, 0x9a, 0xf0, 0x64, 0x14, 0x33, 0xbb, 0xa1, 0x5e, 0x96, 0x90, 0x94,
0x57, 0x86, 0x03, 0x26, 0x24, 0x1d, 0xc4, 0xf6, 0x9c, 0x8b, 0xda, 0x0d, 0x52, 0x00, 0x18, 0xc3,
0x9c, 0x60, 0x2c, 0xb0, 0xe7, 0x5d, 0xd4, 0x6e, 0x11, 0xf5, 0x1f, 0x6f, 0xc0, 0x62, 0x18, 0xb0,
0x48, 0x86, 0x72, 0x64, 0x2f, 0x28, 0x7c, 0x12, 0xe3, 0xa7, 0xb0, 0xac, 0xb9, 0x8f, 0xe8, 0xa8,
0xcf, 0x69, 0x60, 0x5b, 0x2a, 0xa1, 0x0a, 0x7a, 0x57, 0x26, 0xc0, 0x49, 0xc2, 0x58, 0x36, 0x9a,
0x0b, 0x4b, 0xe9, 0xdc, 0x7a, 0x14, 0x61, 0x23, 0xb7, 0xd1, 0x6e, 0x92, 0x32, 0x54, 0x1d, 0xde,
0xac, 0x0f, 0xff, 0x0c, 0x56, 0x44, 0x44, 0x63, 0xd1, 0xe3, 0x72, 0x97, 0x8a, 0xd4, 0x03, 0x3d,
0x66, 0x0d, 0x4d, 0xfb, 0x68, 0x1d, 0xe2, 0x1d, 0x95, 0x54, 0x0d, 0xdb, 0x22, 0x65, 0x28, 0xed,
0x93, 0x30, 0x1a, 0x1c, 0xb2, 0x51, 0x57, 0xcf, 0xdc, 0x24, 0x05, 0x50, 0xb5, 0x6a, 0xa1, 0x6e,
0x55, 0xd9, 0x16, 0xab, 0x66, 0x8b, 0x03, 0x10, 0x8a, 0xe3, 0x4c, 0x8d, 0xbd, 0xe8, 0xa2, 0xf6,
0x22, 0x29, 0x21, 0xde, 0x7b, 0x58, 0x26, 0xf4, 0xb2, 0x64, 0x89, 0x0d, 0x56, 0x9c, 0x39, 0x88,
0x14, 0x57, 0x1e, 0xa6, 0x22, 0x44, 0x78, 0x11, 0x51, 0x39, 0x4c, 0x98, 0xb2, 0xa2, 0x45, 0x0a,
0xc0, 0xdb, 0x83, 0xb5, 0x0a, 0xd1, 0xe7, 0x50, 0xf6, 0xb4, 0xf2, 0x84, 0x5e, 0x6a, 0x28, 0x23,
0x2c, 0x00, 0xbc, 0x02, 0x66, 0x98, 0xdb, 0x6a, 0x86, 0x81, 0x77, 0x85, 0x60, 0x35, 0xa5, 0x38,
0x1e, 0x45, 0xfe, 0x07, 0x26, 0x04, 0xbd, 0x60, 0xf8, 0x35, 0x58, 0x3e, 0x8f, 0x24, 0x8b, 0xa4,
0xaa, 0x5f, 0xda, 0x76, 0x37, 0x4b, 0xbb, 0x37, 0xcf, 0xde, 0xd3, 0x29, 0xa7, 0xb4, 0x3f, 0x64,
0x24, 0x2f, 0xc0, 0x3b, 0x00, 0xc9, 0x64, 0x23, 0xab, 0x3e, 0x4b, 0xdb, 0x4f, 0xca, 0xe5, 0x33,
0x24, 0x93, 0x52, 0x89, 0xf7, 0xc7, 0x84, 0xf5, 0x59, 0x2d, 0xf0, 0x1b, 0x80, 0x1e, 0xa3, 0xc1,
0xa7, 0x38, 0xa0, 0x92, 0x65, 0xc2, 0x36, 0xea, 0xc2, 0x3a, 0x93, 0x8c, 0x8e, 0x41, 0x4a, 0xf9,
0xf8, 0x10, 0x56, 0xcf, 0x87, 0xfd, 0x7e, 0xca, 0x4a, 0xd8, 0xb7, 0x21, 0x13, 0x72, 0x96, 0xb8,
0x94, 0xe2, 0xa0, 0x9a, 0xd6, 0x31, 0x48, 0xbd, 0x12, 0x7f, 0x84, 0x07, 0x05, 0x24, 0x62, 0x1e,
0x09, 0x7d, 0xda, 0x66, 0x38, 0x75, 0x50, 0xcb, 0xeb, 0x18, 0x64, 0xaa, 0x16, 0xef, 0xc3, 0x32,
0x4b, 0x12, 0x9e, 0x4c, 0xc8, 0xe6, 0x14, 0xd9, 0xe3, 0x3a, 0xd9, 0x7e, 0x39, 0xa9, 0x63, 0x90,
0x6a, 0xd5, 0xae, 0x05, 0xf3, 0xdf, 0x53, 0xab, 0xbc, 0x1f, 0x08, 0x56, 0xaa, 0x6e, 0xe0, 0x75,
0x98, 0x4f, 0xdd, 0xc8, 0x4f, 0x9c, 0x0e, 0xf0, 0x2b, 0xb0, 0xb2, 0x23, 0x61, 0x9b, 0x6e, 0xe3,
0x2e, 0x4b, 0x95, 0xe7, 0x63, 0x0f, 0x5a, 0xf9, 0x91, 0x3b, 0xa2, 0xb2, 0x67, 0x37, 0x14, 0x6f,
0x05, 0xf3, 0x7e, 0x22, 0x58, 0x9b, 0x61, 0xe9, 0xfd, 0x88, 0xf9, 0x85, 0xf4, 0xc6, 0xaa, 0xaf,
0xc8, 0xfd, 0xa8, 0x79, 0x01, 0x0f, 0xa7, 0x56, 0x34, 0x55, 0xa2, 0x56, 0x34, 0xbb, 0xf3, 0x75,
0xe0, 0x9d, 0xea, 0xc5, 0xd4, 0xbd, 0xba, 0xd1, 0x39, 0xaf, 0xdd, 0xf3, 0x68, 0xea, 0x9e, 0x9f,
0xba, 0x99, 0xcd, 0x19, 0x37, 0xf3, 0xee, 0xdb, 0xbf, 0xb7, 0x0e, 0xba, 0xbe, 0x75, 0xd0, 0xbf,
0x5b, 0x07, 0xfd, 0x1e, 0x3b, 0xc6, 0xf5, 0xd8, 0x31, 0x6e, 0xc6, 0x8e, 0xf1, 0xe5, 0xf9, 0x1d,
0xbf, 0x6d, 0x67, 0x0b, 0xea, 0xe7, 0xe5, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xbc, 0xc0, 0xf7,
0x30, 0x0d, 0x07, 0x00, 0x00,
}
func (m *RootChange) Marshal() (dAtA []byte, err error) {
@@ -1470,11 +1426,6 @@ func (m *TreeErrorResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.ErrCode != 0 {
i = encodeVarintTreechange(dAtA, i, uint64(m.ErrCode))
i--
dAtA[i] = 0x10
}
if len(m.Error) > 0 {
i -= len(m.Error)
copy(dAtA[i:], m.Error)
@@ -1812,9 +1763,6 @@ func (m *TreeErrorResponse) Size() (n int) {
if l > 0 {
n += 1 + l + sovTreechange(uint64(l))
}
if m.ErrCode != 0 {
n += 1 + sovTreechange(uint64(m.ErrCode))
}
return n
}
@@ -3444,25 +3392,6 @@ func (m *TreeErrorResponse) Unmarshal(dAtA []byte) error {
}
m.Error = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ErrCode", wireType)
}
m.ErrCode = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTreechange
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ErrCode |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipTreechange(dAtA[iNdEx:])
+19 -55
View File
@@ -3,34 +3,25 @@ package treestorage
import (
"context"
"fmt"
"github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
"github.com/anyproto/any-sync/util/slice"
"github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
"sync"
)
type InMemoryTreeStorage struct {
type inMemoryTreeStorage struct {
id string
root *treechangeproto.RawTreeChangeWithId
heads []string
Changes map[string]*treechangeproto.RawTreeChangeWithId
addErr error
changes map[string]*treechangeproto.RawTreeChangeWithId
sync.RWMutex
}
func (t *InMemoryTreeStorage) SetReturnErrorOnAdd(err error) {
t.addErr = err
}
func (t *InMemoryTreeStorage) AddRawChangesSetHeads(changes []*treechangeproto.RawTreeChangeWithId, heads []string) error {
func (t *inMemoryTreeStorage) TransactionAdd(changes []*treechangeproto.RawTreeChangeWithId, heads []string) error {
t.RLock()
defer t.RUnlock()
if t.addErr != nil {
return t.addErr
}
for _, ch := range changes {
t.Changes[ch.Id] = ch
t.changes[ch.Id] = ch
}
t.heads = append(t.heads[:0], heads...)
return nil
@@ -46,89 +37,62 @@ func NewInMemoryTreeStorage(
}
allChanges[root.Id] = root
return &InMemoryTreeStorage{
return &inMemoryTreeStorage{
id: root.Id,
root: root,
heads: append([]string(nil), heads...),
Changes: allChanges,
heads: heads,
changes: allChanges,
RWMutex: sync.RWMutex{},
}, nil
}
func (t *InMemoryTreeStorage) HasChange(ctx context.Context, id string) (bool, error) {
_, exists := t.Changes[id]
func (t *inMemoryTreeStorage) HasChange(ctx context.Context, id string) (bool, error) {
_, exists := t.changes[id]
return exists, nil
}
func (t *InMemoryTreeStorage) Id() string {
func (t *inMemoryTreeStorage) Id() string {
t.RLock()
defer t.RUnlock()
return t.id
}
func (t *InMemoryTreeStorage) Root() (*treechangeproto.RawTreeChangeWithId, error) {
func (t *inMemoryTreeStorage) Root() (*treechangeproto.RawTreeChangeWithId, error) {
t.RLock()
defer t.RUnlock()
return t.root, nil
}
func (t *InMemoryTreeStorage) Heads() ([]string, error) {
func (t *inMemoryTreeStorage) Heads() ([]string, error) {
t.RLock()
defer t.RUnlock()
return t.heads, nil
}
func (t *InMemoryTreeStorage) SetHeads(heads []string) error {
func (t *inMemoryTreeStorage) SetHeads(heads []string) error {
t.Lock()
defer t.Unlock()
t.heads = append(t.heads[:0], heads...)
return nil
}
func (t *InMemoryTreeStorage) AddRawChange(change *treechangeproto.RawTreeChangeWithId) error {
func (t *inMemoryTreeStorage) AddRawChange(change *treechangeproto.RawTreeChangeWithId) error {
t.Lock()
defer t.Unlock()
// TODO: better to do deep copy
t.Changes[change.Id] = change
t.changes[change.Id] = change
return nil
}
func (t *InMemoryTreeStorage) GetRawChange(ctx context.Context, changeId string) (*treechangeproto.RawTreeChangeWithId, error) {
func (t *inMemoryTreeStorage) GetRawChange(ctx context.Context, changeId string) (*treechangeproto.RawTreeChangeWithId, error) {
t.RLock()
defer t.RUnlock()
if res, exists := t.Changes[changeId]; exists {
if res, exists := t.changes[changeId]; exists {
return res, nil
}
return nil, fmt.Errorf("could not get change with id: %s", changeId)
}
func (t *InMemoryTreeStorage) Delete() error {
func (t *inMemoryTreeStorage) Delete() error {
return nil
}
func (t *InMemoryTreeStorage) Copy() *InMemoryTreeStorage {
var changes []*treechangeproto.RawTreeChangeWithId
for _, ch := range t.Changes {
changes = append(changes, ch)
}
other, _ := NewInMemoryTreeStorage(t.root, t.heads, changes)
return other.(*InMemoryTreeStorage)
}
func (t *InMemoryTreeStorage) Equal(other *InMemoryTreeStorage) bool {
if !slice.UnsortedEquals(t.heads, other.heads) {
return false
}
if len(t.Changes) != len(other.Changes) {
return false
}
for k, v := range t.Changes {
if otherV, exists := other.Changes[k]; exists {
if otherV.Id == v.Id {
continue
}
}
return false
}
return true
}
@@ -1,5 +1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/anyproto/any-sync/commonspace/object/tree/treestorage (interfaces: TreeStorage)
// Source: github.com/anytypeio/any-sync/commonspace/object/tree/treestorage (interfaces: TreeStorage)
// Package mock_treestorage is a generated GoMock package.
package mock_treestorage
@@ -8,8 +8,8 @@ import (
context "context"
reflect "reflect"
treechangeproto "github.com/anyproto/any-sync/commonspace/object/tree/treechangeproto"
gomock "go.uber.org/mock/gomock"
treechangeproto "github.com/anytypeio/any-sync/commonspace/object/tree/treechangeproto"
gomock "github.com/golang/mock/gomock"
)
// MockTreeStorage is a mock of TreeStorage interface.
@@ -49,20 +49,6 @@ func (mr *MockTreeStorageMockRecorder) AddRawChange(arg0 interface{}) *gomock.Ca
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRawChange", reflect.TypeOf((*MockTreeStorage)(nil).AddRawChange), arg0)
}
// AddRawChangesSetHeads mocks base method.
func (m *MockTreeStorage) AddRawChangesSetHeads(arg0 []*treechangeproto.RawTreeChangeWithId, arg1 []string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AddRawChangesSetHeads", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// AddRawChangesSetHeads indicates an expected call of AddRawChangesSetHeads.
func (mr *MockTreeStorageMockRecorder) AddRawChangesSetHeads(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddRawChangesSetHeads", reflect.TypeOf((*MockTreeStorage)(nil).AddRawChangesSetHeads), arg0, arg1)
}
// Delete mocks base method.
func (m *MockTreeStorage) Delete() error {
m.ctrl.T.Helper()
@@ -164,3 +150,17 @@ func (mr *MockTreeStorageMockRecorder) SetHeads(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHeads", reflect.TypeOf((*MockTreeStorage)(nil).SetHeads), arg0)
}
// TransactionAdd mocks base method.
func (m *MockTreeStorage) TransactionAdd(arg0 []*treechangeproto.RawTreeChangeWithId, arg1 []string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "TransactionAdd", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// TransactionAdd indicates an expected call of TransactionAdd.
func (mr *MockTreeStorageMockRecorder) TransactionAdd(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransactionAdd", reflect.TypeOf((*MockTreeStorage)(nil).TransactionAdd), arg0, arg1)
}
Loaded 100 of 251 files, more files were not shown because too many files have changed in this diff. Show more