{"action":"created","issue":{"url":"https://api.github.com/repos/ethersphere/go-ethereum/issues/881","repository_url":"https://api.github.com/repos/ethersphere/go-ethereum","labels_url":"https://api.github.com/repos/ethersphere/go-ethereum/issues/881/labels{/name}","comments_url":"https://api.github.com/repos/ethersphere/go-ethereum/issues/881/comments","events_url":"https://api.github.com/repos/ethersphere/go-ethereum/issues/881/events","html_url":"https://github.com/ethersphere/go-ethereum/pull/881","id":350086423,"node_id":"MDExOlB1bGxSZXF1ZXN0MjA4MDIxMzEw","number":881,"title":"[PREVIEW] Adaptive frequency MRUs","user":{"login":"jpeletier","id":11574521,"node_id":"MDQ6VXNlcjExNTc0NTIx","avatar_url":"https://avatars0.githubusercontent.com/u/11574521?v=4","gravatar_id":"","url":"https://api.github.com/users/jpeletier","html_url":"https://github.com/jpeletier","followers_url":"https://api.github.com/users/jpeletier/followers","following_url":"https://api.github.com/users/jpeletier/following{/other_user}","gists_url":"https://api.github.com/users/jpeletier/gists{/gist_id}","starred_url":"https://api.github.com/users/jpeletier/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jpeletier/subscriptions","organizations_url":"https://api.github.com/users/jpeletier/orgs","repos_url":"https://api.github.com/users/jpeletier/repos","events_url":"https://api.github.com/users/jpeletier/events{/privacy}","received_events_url":"https://api.github.com/users/jpeletier/received_events","type":"User","site_admin":false},"labels":[{"id":86320164,"node_id":"MDU6TGFiZWw4NjMyMDE2NA==","url":"https://api.github.com/repos/ethersphere/go-ethereum/labels/enhancement","name":"enhancement","color":"84b6eb","default":true},{"id":239695069,"node_id":"MDU6TGFiZWwyMzk2OTUwNjk=","url":"https://api.github.com/repos/ethersphere/go-ethereum/labels/in%20progress","name":"in progress","color":"d2db30","default":false},{"id":962536898,"node_id":"MDU6TGFiZWw5NjI1MzY4OTg=","url":"https://api.github.com/repos/ethersphere/go-ethereum/labels/mutable-resources","name":"mutable-resources","color":"c28fe0","default":false}],"state":"open","locked":false,"assignee":{"login":"jpeletier","id":11574521,"node_id":"MDQ6VXNlcjExNTc0NTIx","avatar_url":"https://avatars0.githubusercontent.com/u/11574521?v=4","gravatar_id":"","url":"https://api.github.com/users/jpeletier","html_url":"https://github.com/jpeletier","followers_url":"https://api.github.com/users/jpeletier/followers","following_url":"https://api.github.com/users/jpeletier/following{/other_user}","gists_url":"https://api.github.com/users/jpeletier/gists{/gist_id}","starred_url":"https://api.github.com/users/jpeletier/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jpeletier/subscriptions","organizations_url":"https://api.github.com/users/jpeletier/orgs","repos_url":"https://api.github.com/users/jpeletier/repos","events_url":"https://api.github.com/users/jpeletier/events{/privacy}","received_events_url":"https://api.github.com/users/jpeletier/received_events","type":"User","site_admin":false},"assignees":[{"login":"jpeletier","id":11574521,"node_id":"MDQ6VXNlcjExNTc0NTIx","avatar_url":"https://avatars0.githubusercontent.com/u/11574521?v=4","gravatar_id":"","url":"https://api.github.com/users/jpeletier","html_url":"https://github.com/jpeletier","followers_url":"https://api.github.com/users/jpeletier/followers","following_url":"https://api.github.com/users/jpeletier/following{/other_user}","gists_url":"https://api.github.com/users/jpeletier/gists{/gist_id}","starred_url":"https://api.github.com/users/jpeletier/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jpeletier/subscriptions","organizations_url":"https://api.github.com/users/jpeletier/orgs","repos_url":"https://api.github.com/users/jpeletier/repos","events_url":"https://api.github.com/users/jpeletier/events{/privacy}","received_events_url":"https://api.github.com/users/jpeletier/received_events","type":"User","site_admin":false}],"milestone":null,"comments":1,"created_at":"2018-08-13T15:46:33Z","updated_at":"2018-08-30T21:13:18Z","closed_at":null,"author_association":"NONE","pull_request":{"url":"https://api.github.com/repos/ethersphere/go-ethereum/pulls/881","html_url":"https://github.com/ethersphere/go-ethereum/pull/881","diff_url":"https://github.com/ethersphere/go-ethereum/pull/881.diff","patch_url":"https://github.com/ethersphere/go-ethereum/pull/881.patch"},"body":"# Abstract\r\n\r\nThe current MRU implementation requires users to agree upon a predefined frequency and start time to start publishing updates about a certain topic. This causes lots of problems if that update frequency is not honored and requires users to know other user's update frequencies / start times in order to look up their updates on common topics\r\n\r\nThis PR removes this limitation via a novel adaptive frequency resource lookup algorithm. This algorithm automatically adjusts to the publisher's actual update frequency and converges quickly whether an update is found or not.\r\n\r\nUsers \"following\" a publisher automatically \"tune\" to the perceived frequency and can guess easily where the next update ought to be, meaning that subsequent lookups to get a newer update run faster or can be prefetched. This also allows to monitor a resource easily.\r\n\r\nThe algorithm is described below.\r\n\r\nAs a result, interactions with Swarm's MRUs are greatly simplified since the user doesn't have to come up with a start time and frequency upfront, but rather start publishing updates about the topic they want.\r\n\r\n# API changes\r\n\r\n## HTTP API\r\n\r\n### To publish an update:\r\n\r\n#### 1.- Get resource metainformation\r\n\r\n* `GET /bzz-resource:/?topic=<TOPIC>&user=<USER>&meta=1`\r\n* `GET /bzz-resource:/<MANIFEST OR ENS NAME>/?meta=1`\r\n\r\nWhere:\r\n* **user**: Ethereum address of the user who publishes the resource\r\n* **topic**: Resource topic, encoded as a hex string.\r\n\r\n> Note:\r\n> * If `topic` is omitted, it is assumed to be zero, `0x000...`\r\n> * if `name=<name>` is provided, a subtopic is composed with that name\r\n> \r\n> A common use is to omit topic and just use `name`, allowing for human-readable topics.\r\n\r\n\r\nYou will receive a JSON like the below:\r\n\r\n```json\r\n{\r\n  \"view\": {\r\n    \"topic\": \"0x6a61766900000000000000000000000000000000000000000000000000000000\",\r\n    \"user\": \"0xdfa2db618eacbfe84e94a71dda2492240993c45b\"\r\n  },\r\n  \"epoch\": {\r\n    \"level\": 16,\r\n    \"time\": 1534237239\r\n  }\r\n}\r\n```\r\n\r\n#### 2.- Post the update\r\n\r\n`POST /bzz-resource:/?topic=<TOPIC>&user=<USER>&level=<LEVEL>&time=<TIME>&signature=<SIGNATURE>`\r\n\r\nbody: binary stream with the update data.\r\n\r\n### To get the last update:\r\n\r\n* `GET /bzz-resource:/?topic=<TOPIC>&user=<USER>`\r\n* `GET /bzz-resource:/<MANIFEST OR ENS NAME>`\r\n\r\n> Note:\r\n> * Again, if `topic` is omitted, it is assumed to be zero, `0x000...`\r\n> * if `name=<name>` is provided, a subtopic is composed with that name\r\n> \r\n> A common use is to omit topic and just use `name`, allowing for human-readable topics.\r\n> Thus, this is also valid: `GET /bzz-resource:/?name=profile-picture&user=<USER>`\r\n\r\n### To get a previous update:\r\n\r\n* `GET /bzz-resource:/?topic=<TOPIC>&user=<USER>&time=<T>`\r\n* `GET /bzz-resource:/<MANIFEST OR ENS NAME>?time=<T>`\r\n\r\n#### Advanced search:\r\nIf you have an idea of when the last update happened, you can also hint the lookup algorithm by adding the following extra parameters:\r\n\r\n* `hint.time`: Time at when you think the last update happened\r\n* `hint.level`: Approximate period you think the updates where happening at, expressed as `log2(T)`, rounded down. For example, a resource updating every 300 seconds, level should be set to `8`. `log2(300) = 8.22`. See the Adaptive Frequency algorithm below for details on this.\r\n\r\nNote that this would only affect first lookups. Your swarm node will keep track of last updates and automatically use the last seen update as a hint. Using these parameters would override that automatic hint.\r\n\r\n### To publish a manifest:\r\n\r\n`POST /bzz-resource:/?topic=<TOPIC>&user=<USER>&manifest=1` with an empty body.\r\n\r\nNote: this functionality could be moved to the client and removed from the node, since this just creates a JSON and publishes it to `bzz-raw`, so the client could actually create this itself and call `client.UploadRaw()`.\r\n\r\n## CLI\r\n\r\n### Creating a resource manifest:\r\n\r\n`swarm resource create` is redefined as a command line to create and publish a MRU manifest only.\r\n\r\n`swarm resource create [command options]`\r\n```\r\ncreates and publishes a new Mutable Resource manifest pointing to a specified user's updates about a particular topic.\r\n          The topic can be specified directly with the --topic flag as an hex string\r\n          If no topic is specified, the default topic (zero) will be used\r\n          The --name flag can be used to specify subtopics with a specific name\r\n          The --user flag allows to have this manifest refer to a user other than yourself. If not specified,\r\n          it will then default to your local account (--bzzaccount)\r\n\r\nOPTIONS:\r\n--name value   User-defined name for the new resource, limited to 32 characters. If combined with topic, the resource will be a subtopic with this name\r\n--topic value  User-defined topic this resource is tracking, hex encoded. Limited to 64 hexadecimal characters\r\n--user value   Indicates the user who updates the resource\r\n```\r\n\r\n### Update a resource\r\n\r\n`swarm resource update [command options] <0x Hex data>`\r\n```\r\ncreates a new update on the specified topic\r\n          The topic can be specified directly with the --topic flag as an hex string\r\n          If no topic is specified, the default topic (zero) will be used\r\n          The --name flag can be used to specify subtopics with a specific name.\r\n          If you have a manifest, you can specify it with --manifest instead of --topic / --name\r\n          to refer to the resource\r\n\r\nOPTIONS:\r\n--manifest value  Refers to the resource through a manifest\r\n--name value      User-defined name for the new resource, limited to 32 characters. If combined with topic, the resource will be a subtopic with this name\r\n--topic value     User-defined topic this resource is tracking, hex encoded. Limited to 64 hexadecimal characters\r\n```\r\n\r\n### Quick and dirty test:\r\n\r\nIn the example, the user wants to publish his/her profile picture so it can be found by anyone who knows his/her Ethereum address.\r\n\r\n```bash\r\n# (OPTIONAL) Publish a manifest:\r\nswarm --bzzaccount \"your public key\" resource create --name \"profile-picture\"\r\n\r\n# the above command will output a manifest hash. We will refer to it as $MH later on\r\n\r\n# Publish the first update:\r\n$ IMAGE=$(swarm up myprofilepicture.jpg) && swarm --bzzaccount \"<YOUR ADDRESS>\" resource update --name \"profile-picture\" \"0x1b20$IMAGE\"\r\n\r\n# To retrieve the latest update:\r\n$ curl 'http://localhost:8500/bzz-resource:/?name=profile-picture&user=<YOUR ADDRESS>'\r\n\r\n# Alternatively, if we created a manifest, we can use it:\r\n$ IMAGE=$(swarm up myprofilepicture.jpg) && swarm --bzzaccount \"your public key\" resource update --manifest \"$MH\" \"0x1b20$IMAGE\"\r\n\r\n# Your last profile picture can be viewed in:\r\n# http://localhost:8500/bzz:/$MH\r\n# (Only if a manifest was published)\r\n```\r\n\r\n# Adaptive frequency lookup algorithm\r\n\r\nAt the core of this PR is a new lookup algorithm with the following properties:\r\n* Does not require the user to commit to an update frequency without affecting lookup time linearly with the time difference since now and when that last update happened.\r\n* The algorithm finishes quickly if no updates found\r\n* Once the last update is found, subsequent lookups take less time.\r\n* If we have a rough idea of where the last update was, we can hint the algorithm to get a faster lookup time.\r\n* It allows time-based lookups.\r\n* The lookup key of the next update can be predicted and monitored.\r\n\r\n## Revamping the resource frequency concept\r\n\r\n> *Note: Starting with this PR, in the documentation and this text, we use the strict definition of frequency as `f = 1 / T`. Thus, higher frequencies mean shorter periods of time.*\r\n\r\nIn this new implementation, period lengths are expressed as powers of 2. The highest frequency (shortest period, update every second) is expressed as 2⁰ = 1 second. The lowest update frequency is currently set to 2²⁵ = 33554432 seconds which equals to roughly one year.\r\n\r\nTherefore, the frequency can be encoded as just the exponent. We call this exponent frequency level, or **level** for short. A higher level means a longer period and thus a smaller frequency.\r\n\r\n## Introducing *Epochs*\r\n\r\nNow that we have determined a set of finite possible frequencies, we can divide time in a grid of *epochs*. One epoch is a concrete time range at a specific frequency level, starting at a specific point in time, called the *epoch base time*. Level 0 epochs have a maximum length of 2⁰ = 1 seconds. Level 3 epochs have a maximum length of 2³ = 8 seconds, etc.\r\n\r\n![image](https://user-images.githubusercontent.com/11574521/44038208-8fbe6d7c-9f16-11e8-81ba-59b181a89088.png)\r\n\r\nTo refer to a specific epoch, or *epoch ID* we need to know the *epoch base time* and the *epoch level*\r\n\r\n![image](https://user-images.githubusercontent.com/11574521/44038257-b2c68cb4-9f16-11e8-8f75-cfbbe0e6c78f.png)\r\n\r\n\r\nWe will use this epoch addressing scheme to derive a chunk address in which to store a particular update.\r\n\r\n### Epoch base time\r\n\r\nTo caclculate the epoch base time of any given instant in time at a particular level, we use the simple formula: \r\n\r\n`baseTime(t, level) = t & ( 0xFFFFFFFFFFFFFFFF << level )`\r\n\r\nIn other words, we are dropping the `level` lowest significant bits of `t`.\r\n\r\n## Seeding algorithm\r\n\r\nThe seeding algorithm describes the process followed by the update publisher to determine in what epoch \"plant\" the content so it can be found (harvested) by users. The algorithm works as follows:\r\n\r\n### First updates\r\n\r\nAny first resource update will have a level of `25`.\r\n\r\n> *Note: We have chosen `25` as the highest level. This is a constant in code and can be changed.*\r\n\r\nThus, if as of writing this it is August 12th 2018 at 16:51 UTC, Unix Time is `1534092715`. Therefore, the epoch base time is `1534092715 & 0xFFFFFFFFFE000000 = 1509949440`\r\n\r\nThe epoch id for a first update now is therefore `(1509949440, 25)`\r\n\r\n![image](https://user-images.githubusercontent.com/11574521/44038321-d4ebd3c6-9f16-11e8-9e5a-4a33bb1a29d9.png)\r\n\r\n### Subsequent updates\r\n\r\nTo determine the epoch in which to store a subsequent update, the publisher needs to know where they stored the previous update. This should be straightforward. However, if the publisher can't or does not want to keep track of this, it can always use the harvesting algorithm (see below) to find their last update.\r\n\r\nThe selected epoch for a subsequent update must be the epoch with the highest possible level that is not already occupied by a previous update.\r\n\r\nLet's say that we want to update our resource 5 minutes later. The Unix Time is now `1534093015`.\r\n\r\nWe calculate `getBaseTime(1534093015, 25) = 1509949440`.\r\nThis results in the same epoch as before `(1534093015, 25)`. Therefore, we decrease the level and calculate again:\r\n`getBaseTime(1534093015, 24) = 1526726656`\r\n\r\nThus, the next update will be located at `(1526726656, 24)`\r\n\r\n![image](https://user-images.githubusercontent.com/11574521/44038371-ed43bcb8-9f16-11e8-8038-ec3105bfa30a.png)\r\n\r\nIf the publisher keeps updating the resource exactly every 5 minutes, the epoch grid will look like this:\r\n\r\n```\r\nupdate #1,  t=1534092715, epoch=(1509949440, 25)\r\nupdate #2,  t=1534093015, epoch=(1526726656, 24)\r\nupdate #3,  t=1534093315, epoch=(1526726656, 23)\r\nupdate #4,  t=1534093615, epoch=(1530920960, 22)\r\nupdate #5,  t=1534093915, epoch=(1533018112, 21)\r\nupdate #6,  t=1534094215, epoch=(1534066688, 20)\r\nupdate #7,  t=1534094515, epoch=(1534066688, 19)\r\nupdate #8,  t=1534094815, epoch=(1534066688, 18)\r\nupdate #9,  t=1534095115, epoch=(1534066688, 17)\r\nupdate #10, t=1534095415, epoch=(1534066688, 16)\r\nupdate #11, t=1534095715, epoch=(1534066688, 15)\r\nupdate #12, t=1534096015, epoch=(1534083072, 14)\r\nupdate #13, t=1534096315, epoch=(1534091264, 13)\r\nupdate #14, t=1534096615, epoch=(1534095360, 12)\r\nupdate #15, t=1534096915, epoch=(1534095360, 11)\r\nupdate #16, t=1534097215, epoch=(1534096384, 10)\r\nupdate #17, t=1534097515, epoch=(1534096896, 9)\r\nupdate #18, t=1534097815, epoch=(1534097408, 11)\r\nupdate #19, t=1534098115, epoch=(1534097408, 10)\r\nupdate #20, t=1534098415, epoch=(1534097920, 9)\r\nupdate #21, t=1534098715, epoch=(1534098176, 8)\r\nupdate #22, t=1534099015, epoch=(1534098432, 10)\r\nupdate #23, t=1534099315, epoch=(1534098944, 9)\r\nupdate #24, t=1534099615, epoch=(1534099200, 8)\r\nupdate #25, t=1534099915, epoch=(1534099456, 15)\r\nupdate #26, t=1534100215, epoch=(1534099456, 14)\r\nupdate #27, t=1534100515, epoch=(1534099456, 13)\r\nupdate #28, t=1534100815, epoch=(1534099456, 12)\r\nupdate #29, t=1534101115, epoch=(1534099456, 11)\r\nupdate #30, t=1534101415, epoch=(1534100480, 10)\r\n\r\n```\r\nIf the publisher keeps updating every 5 minutes (300s), we can expect the updates to stay around level 8-9 (2⁸ = 256 seconds, 2⁹ = 512 seconds). The publisher can however, at any time vary this update frequency or just update randomly. This does not affect the algorithm.\r\n\r\n![image](https://user-images.githubusercontent.com/11574521/44038509-3c431f70-9f17-11e8-83b8-26d0847dd785.png)\r\n\r\nHere is a closer look at the converging levels further down:\r\n\r\n![image](https://user-images.githubusercontent.com/11574521/44038677-97062f1a-9f17-11e8-852e-aca90214660a.png)\r\n\r\n## Harvesting algorithm\r\n\r\nThe harvesting algorithm describes how to find the latest update of a resource. This involves looking up an epoch and walking back in time until we find the last update.\r\n\r\n#### Start Epoch\r\n\r\nTo select the best starting epoch to walk our grid, we have to assume the worst case, which is that the resource was never updated after we last saw it.\r\n\r\nIf we don't know when the resource was last updated, we asume `0` as the \"last time\" it was updated.\r\n\r\nWe can guess a start level as the position of the nonzero bit of `XOR(last base time, now)` counting from the left. The bigger the difference among the two times (last update time and now), the higher the level will be as the update frequency we are estimating is lower.\r\n\r\nIf the resulting level is higher than `25`, we use `25`.\r\n\r\n### Walking the grid - a simple algorithm\r\n\r\nConsider the following grid. In it we have marked in yellow where the updates have happened in the past.\r\n\r\n![image](https://user-images.githubusercontent.com/11574521/44038815-ed7d4e5a-9f17-11e8-89ef-e333daf59289.png)\r\n\r\nAll the above is unknown to the harvester. All we know is the last seen update happened at `(20,2)`, marked in light orange. We call this the \"hint\". The algorithm will consider this hint but will discard it if it proves it really did not contain an update. An invalid hint can pontentially slow down the algorithm but won't stop it from finding the last update.\r\n\r\nNow it is `t=26` and we want to look for the last update. Our guess at a start level is:\r\n\r\n`XOR(20, 26) = 14 = 1110b`, in which the first nonzero bit counting from the left is bit #3. Thus, our first lookup will happen at `(baseTime(26,3), 3) = (24,3)`, shown in dark blue below: \r\n\r\n![image](https://user-images.githubusercontent.com/11574521/44038904-1d2440be-9f18-11e8-905e-084aca6adf89.png)\r\n\r\nIf a lookup at `(24,3)` fails, we consider that there are no updates at lower levels either, since the seeding algorithm would've filled `(24,3)` before going down. This means there are no updates on or after `t=24`. Thus, our search area would be reduced to the area directly below `(20,2)` (green area). We restart the algorithm as if `now` was `23`\r\n\r\nIf however, a lookup at `(24,3)` succeeds, then we know the last update could either be the one at `(24,3)` itself or may be there is a later one in the epochs below (blue area). At this point we consider that the update is in  `24 <= t <= 26`. We restart the algorithm with the hint set to `(24,3)` instead of the original one. If the lookup then fails, then the last update was indeed in `(24,3)`\r\n\r\nThis is how the algorithm would play out if now `t=26` if the last update is in `(22,1)`\r\n\r\n![image](https://user-images.githubusercontent.com/11574521/44038937-32a34700-9f18-11e8-87f0-680e6f9c131e.png)\r\n\r\nLookups are, in this order:\r\n```\r\n#1 (24,3) (fails)\r\n#2 (22,1) (succeeds, but we don't know if this would be the latest update). Go down one level to confirm.\r\n#3 (23,0) (fails)\r\n#4 (22,0) (fails, so we return the last found value at (22,1)\r\n\r\n```\r\n\r\n## Locking on / following a resource\r\n\r\nOnce we have found the last update of a resource, we can easily calculate in what epoch will the next update appear, if the publisher actually makes it.\r\n\r\nIn figure 9 above, if the last update was found at `(22,1)` and now it is `t=26`, the next update must happen exactly at `(24,3)`. This holds true until `t=32`. Beyond that point, the next update can be expected at `(32,4)` until `t=48`\r\n\r\nTherefore, the node following the resource could sample the expected epoch, keeping in sync with the publisher.\r\n\r\n# Final notes:\r\n\r\nPlease let me know your feedback, questions and test issues while I document and clean up the code. I hope you like this feature. I am available on Gitter (@jpeletier). Enjoy!!\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/ethersphere/go-ethereum/issues/comments/417469057","html_url":"https://github.com/ethersphere/go-ethereum/pull/881#issuecomment-417469057","issue_url":"https://api.github.com/repos/ethersphere/go-ethereum/issues/881","id":417469057,"node_id":"MDEyOklzc3VlQ29tbWVudDQxNzQ2OTA1Nw==","user":{"login":"jpeletier","id":11574521,"node_id":"MDQ6VXNlcjExNTc0NTIx","avatar_url":"https://avatars0.githubusercontent.com/u/11574521?v=4","gravatar_id":"","url":"https://api.github.com/users/jpeletier","html_url":"https://github.com/jpeletier","followers_url":"https://api.github.com/users/jpeletier/followers","following_url":"https://api.github.com/users/jpeletier/following{/other_user}","gists_url":"https://api.github.com/users/jpeletier/gists{/gist_id}","starred_url":"https://api.github.com/users/jpeletier/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jpeletier/subscriptions","organizations_url":"https://api.github.com/users/jpeletier/orgs","repos_url":"https://api.github.com/users/jpeletier/repos","events_url":"https://api.github.com/users/jpeletier/events{/privacy}","received_events_url":"https://api.github.com/users/jpeletier/received_events","type":"User","site_admin":false},"created_at":"2018-08-30T21:13:18Z","updated_at":"2018-08-30T21:13:18Z","author_association":"NONE","body":"@nagydani, thank you for your comments.\r\n> Is the order of query string parameters fixed or they can be switched places?\r\n\r\nThe order is not fixed. Parameters work in any order.\r\n\r\n> Is the API extensible by new parameters and how the persence of unknown parameters should be handled? Are they ignored or is it an error?\r\n\r\nUnknown parameters are ignored. No error will be thrown if you put an unknown parameter.\r\n\r\n> Same about responses.\r\n\r\nResponses are either:\r\n* binary content (the resource you asked for)\r\n* a HTTP error\r\n* a JSON. The client ignores fields in the JSON it does not recognize.\r\n\r\n> How about adding protocol version numbers for avoiding compatibility issues? I feel that this is going to change a lot, when people actually start using them.\r\n\r\nYes, we should have API versioning, but I guess not only in `bzz-resource`, but throughout. The easiest right now would be to have it as another query string parameter, e.g., `?version=1`. The cleanest would be to have it as part of the path: `/bzz-resource:/v1/?x=1&y=2`. I think we should have a more holistic API design to have API version scheme be coherent across services."}}
{"actor":{"display_login":"jpeletier"}}