{"action":"created","comment":{"url":"https://api.github.com/repos/status-im/status.im/pulls/comments/233778719","pull_request_review_id":175263004,"id":233778719,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIzMzc3ODcxOQ==","diff_hunk":"@@ -0,0 +1,292 @@\n+---\n+id: ulc_in_details\n+title: Ultra Light Client in details\n+---\n+\n+# Ultra Light Client in details\n+\n+## Definitions\n+* TD - the total difficulty of the chain until a given block\n+* LES - light Ethereum subprotocol\n+* ULC - ultra light client, an option of LES\n+* CHT - Canonical Hash Trie which maps historical block numbers to their canonical hashes in a Merkle trie. This allows us to discard the block headers themselves in favor of a trie root which encompasses the accumulation of their hashes, and to fetch proofs that a specific block hash is in fact the one we verified earlier [\\[1\\]][1]\n+\n+## Overview\n+ULC is a new option in LES that doesn't break compatibility with the LES protocol, but does significantly reduce the time and resources required to sync with the main Ethereum chain.\n+The main idea is about reducing the amount of messages and doing less client side validation.\n+\n+## What does ULC solves?\n+1) CPU and battery consumption\n+2) Time to start sync\n+3) Time to finish sync\n+\n+## ULC in schemes\n+### Algorithm\n+```flow\n+st=>start: Start LES client\n+with ULC options\n+\n+op=>operation: Connect to N Trusted LES servers\n+Hold these connections\n+\n+op2=>operation: Ask for signed announcements\n+for the block with max TD\n+\n+op3=>operation: Sync CHTs up to known max TD\n+\n+op4=>operation: Ask for N latest\n+signed announcements\n+\n+op5=>operation: Ask usual LES server (can be different each time)\n+for a block header\n+\n+cond=>condition: Are M of N announcements the same?\n+Are all signs valid?\n+\n+st->op->op2->op3->op4->cond->op5->op4\n+cond(yes)->op5\n+cond(no)->op4\n+```\n+\n+### Dataflow\n+```sequence\n+ULC->N_Trusted: handshake\n+Note right of N_Trusted: setup LES params\n+N_Trusted-->ULC: handshake\n+\n+Note left of ULC: CHT sync\n+ULC->N_Trusted: Ask to announce\n+N_Trusted-->ULC: Highest announce\n+Note left of ULC: announce:\\nblock hash, TD, number\n+\n+ULC->N_Trusted: Ask CHTs\n+N_Trusted-->ULC: CHT \"chain\" sync\n+\n+Note left of ULC: Sync headers starting\\nfrom latest block CHT + 1\n+ULC->N_Trusted: Ask to announce\n+N_Trusted-->ULC: announce\n+\n+ULC->Untrusted: handshake\n+Untrusted-->ULC: handshake\n+\n+ULC->Untrusted: ask block header\n+Untrusted-->ULC: header\n+Note left of ULC: header:\\nblock announce, logsBloom,\\nMerkle trees roots:\\nstate, transactions, receipts\n+```\n+\n+### Validation of header \"chain\" for LES and ULC\n+1. sanity check that the provided chain is actually ordered and linked. If we have a header chain of length N, for every $n_i$ and $n_{i-1}$, $i \\epsilon [0; N]$, conditions should hold:\n+    1.1. $n_i.Number = n_{i-1}.Number+1$\n+    1.2. $n_i.ParentHash = n_{i-1}.Hash$\n+2. in Ethereum Yellow Paper section 4.3.4. \"Block Header Validity\"[\\[2\\]][2]\n+    2.1. The length of $n_i.Extra < 32 bytes$\n+    2.2. Checks block timestamp:\n+        2.2.1. It shouldn't be from future more than 15 secs\n+        2.2.2. $n_i.Time > n_{i-1}.Time$\n+    2.3. verify the block's difficulty based in it's timestamp and parent's difficulty: $n_i.Difficulty = expectedDifficulty(n_i)$\n+    2.4. $n_i.gasLimit$ shouldn't overflow `2^63-1`\n+    2.5. $n_i.gasUsed <= n_i.gasLimit$\n+    2.6. checks gas limit:\n+        2.6.1. should be more than MinGasLimit: $n_i.gasLimit >= 5000$\n+        2.6.2. the change of $n_i$ gas should be bounded: $|parent.GasLimit - header.GasLimit| < parent.GasLimit / 1024$\n+    2.7. validate hard forks special fields, eg. every $n_i \\epsilon [DAOForkBlock; DAOForkBlock+10]$ should have special value in `ExtraData` field \n+    2.8. Verify a seal securing the block\n+\n+#### Verify a seal of a block\n+The main improvement of ULC is that a client doesn't need to verify the seal of a block and can skip this step at all.\n+\n+As it said the Ethereum code `light` (actually `fast` and `light`) client has `slow-but-light PoW verification`, but `full` has `fast-but-heavy`. The main difference is about that `full` client generates all the data needed to verify all block in an epoch, but light client calculates many values on-the-fly.\n+\n+The algorithm in details can be found in [Ethereum wiki](https://github.com/ethereum/wiki/wiki/Ethash).\n+\n+The verification has 2 steps: init caches and verify. Let's describe them in details.\n+\n+##### Init step\n+_All numbers below are given for the Epoch 232 (a current epoch at 12 Nov 2018)_\n+_Some parts of this step can be run in parallel._\n+_All numbers and the algorithm steps are from [geth code](https://github.com/ethereum/go-ethereum/blob/master/consensus/ethash/consensus.go#L239)_\n+\n+It runs once per epoch: `epochLength = 30000 blocks ~ each 3.5 days = twice per week`\n+\n+It needs to generate a verification matrix of pseudo-random values (it called `cache`).\n+\n+Calculate seedHash in `epochNumber steps = 232 sha3 operations`\n+Calculate the initial cache in:  `cacheSize/64 steps = 51641792/64 = 806900 sha3 operations`. `CacheSize` can be taken from [table `cacheSizes`](https://github.com/ethereum/go-ethereum/blob/master/consensus/ethash/algorithm.go#L821), for `epoch=232` it equals `51641792`.\n+\n+At the end of the day $O_{cpu}(initStep)= N*O(sha3)$, where N is a current block number.\n+\n+For example, for epoch 232 (a current epoch at 12 Nov 2018) $O_{cpu}(initStep) = O_{cpu}(seedHash)+O_{cpu}(initCahce) = 807133*O(sha3)$\n+\n+This is theoretical lower bound. [Ethash article](https://github.com/ethereum/wiki/wiki/Ethash-Design-Rationale) mentioned that \"a light client should be able to become fully operational and able to verify blocks within 40 seconds in Javascript\".\n+\n+##### Verify step\n+Light mode seal verification doesn't store the entire dataset for block verification, but generates nessusary items on the fly. For a single block it runs `hashimotoLight` algorythm that takes:\n+\n+$generateDatasetItem = (2*sha3 + 512*fnv)$ \n+\n+_More details about FNV32-1 hash function can be found [here](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function)._\n+\n+$hashimotoLight = loopAccesses*mixBytes/hashBytes * (generateDatasetItem + fnvHash)$\n+$hashimotoLight = 64*128/64 *(generateDatasetItem + fnv)$\n+$hashimotoLight = 128*(generateDatasetItem + fnv)$\n+$hashimotoLight = 256 sha3 + 65664 fnv$\n+\n+This is the difference between ULC and LES clients for each block. As far as each CHT is generated once per 32767 blocks the total difference is $[1; 32767] * Cost(hashimotoLight) = [256*O(sha3) + 65664*O(fnv) ; 8.388.352*O(sha3) + 2.151.612.288*O(fnv)]$. The growth is linear.\n+\n+This is theoretical lower bound. As it said in [Ethash article](https://github.com/ethereum/wiki/wiki/Ethash-Design-Rationale) for a single block verification step should take `'0.1 seconds in Python'` in practice it takes `~200ms` in golang Geth code.\n+\n+## LES vs ULC\n+So ULC saves `807133*O(sha3)` at init stage that happens each epoch and [256*O(sha3) + 65664*O(fnv) ; 8.388.352*O(sha3) + 2.151.612.288*O(fnv)] for each block while syncing. As far as difficulty of a block verification grows linear, the total difficulty of syncing N block grows as $N^2$.\n+\n+## ULC in Roles\n+### Trusted LES servers\n+Trusted LES servers are needed only to send announcements (in Geth code it has name `announce(block hash, TD, number)`) to LES(ULC) clients. All announcements should be signed. Trusted servers don't know whether they have been chosen as trusted or not by any given client. Such servers can be started with an `onlyAnnounce` flag, which ensures that the LES server operates under the rule \"only send announcements to my peers, do not process any other requests\".\n+\n+### LES servers (untrusted)\n+LES servers - usual LES servers, a header chain is synchronised with them. Helps to prevent attacks on trusted servers.\n+\n+### ULC client\n+1. has some CHT root at the start; has a CHTs \"chain\", that can be synced from LES servers; CHT chain allows to request any historical information (block, transaction, receipt) from LES server\n+2. trusts to announcements, that receives from N Trusted LES servers. Announcements should be signed by Trusted LES servers. It should be at least M identical announcements to trust.\n+3. Asks for announcements with the biggest TD\n+4. ULC client starts CHT sync before syncing header chain. ULC client requests newer CHTs from LES servers.\n+5. requests headers from untrusted LES server, starting from `the highest block is known to latest CHT + 1` up to latest block number known from trusted announce\n+6. ULC client validates:\n+6.1. announcements checking are there M the same announcements from N received from Trusted LES servers\n+6.2. doesn't validate CHT, if we get incorrect CHT, it'll be clear later after receiving block headers.\n+6.3. validates headers, as usual, LES client except [VerifySeal](https://github.com/ethereum/go-ethereum/blob/master/consensus/ethash/consensus.go#L491). ULC doesn't run `VerifySeal` at all.\n+\n+## ULC client resources\n+### Network\n+* CHT is received from a single LES server that is considered \"best to sync\" at the moment\n+* headers are received from a single LES server\n+* announcements are received from N Trusted LES servers\n+\n+### Network connections\n+* tries to be always connected to N Trusted LES servers, in case of disconnection it reconnects\n+* handles a usual number of connections to LES servers\n+\n+### Storage\n+* at least one CHT, but we can have several consecutive CHTs\n+* headers chain of blocks of the latest Epoch\n+\n+## Security\n+### Main issues\n+1) Mostly are inherited from LES\n+2) Too few LES servers in the Network\n+3) Trusted servers discovery (?)\n+4) DoS on trusted nodes\n+\n+### Sybill attack on ULC (client)\n+Prevented because it is already prevented in a classic LES model and we only download headers with trusted announcements.\n+\n+### Sybill attack on trusted servers\n+Even less possible than in LES model because it's needed to attack,k at least M servers.\n+\n+### DoS on trusted servers\n+Possible. ULC makes it much less possible by hiding what nodes are trusted for each user. A user doesn't send any unusual for LES information to LES servers trusted or untrusted. Any trusted for a user LES server doesn't know that it has been chosen by the user to be trusted LES server.\n+\n+### MITM\n+Prevented because all announcements must be signed by according LES server.\n+\n+### What other security guarantees does ULC give and what is it comparable with?\n+\n+#### Some math\n+There're 2 kinds of security guarantees:\n+1. reducing the probability of failure to perform a correct request due to the failure of remote servers - failure and censorship resistance\n+2. reducing the probability of trust in data coming from malicious nodes\n+\n+The very mechanism of blockchain synchronisation of the ULC is the same as that of the LES. Therefore, comparing the security guarantees of ULC with full, fast and LES does not make sense. It is more important to compare the guarantees of a private RPC server or Infura with ULC.\n+\n+If the probability of failure or hacking Infura or RPC server is taken as P, then with the ULC consensus M/N, the probability of its failure can be considered as Bernoulli process:\n+\n+$$P_{ULC\\_failure}=\\sum_{i=M}^N C_N^i*P^i*(1-P)^{N-i}$$\n+\n+For example, let's calculate the failure probability $P_{ULC\\_failure}$ while syncing or getting an incorrect state, given N=10, M=6 and the failure probability of a single node P:\n+\n+$$P_{ULC\\_failure}=C_{10}^6*P^6*(1-P)^4+C_{10}^7*P^7*(1-P)^3+C_{10}^8*P^8*(1-P)^2+C_{10}^9*P^9*(1-P)+C_{10}^{10}*P^{10}$$\n+\n+$$P_{ULC\\_failure}=210*(1 - P)^4*P^6 + 120*(1 - P)^3*P^7 + 45*(1 - P)^2*P^8 + 10*(1 - P)*P^9 + P^{10}$$\n+\n+Let's take several values of the LES server failure probability and see what the probability of ULC breakage of the client is:\n+| $P_{server\\_failure}$ | 5% | 1% | 0.1% | 0.01% |\n+|---|---|---|---|---|---|---|---|---|---|---|\n+| ~$P_{ULC\\_failure}$ | $10^{-6}\\%$ |$10^{-10}\\%$ | $10^{-16}\\%$ | $10^{-22}\\%$ |\n+\n+![0-1](https://i.imgur.com/D8lh6eG.png)\n+\n+![0-0.001](https://i.imgur.com/0GmOWvy.png)\n+\n+So ULC drastically increases censorship resistance of an Ethereum client. We can develop a far more reliable system using unreliable nodes.\n+\n+A few N/M, given $P_{server\\_failure}=0.01\\%$:\n+| N | M | ~$P_{ULC\\_failure}$ |\n+| ------- | ------- | ------- | \n+| 3 | 2 | $3*10^{-8}\\%$ |\n+| 4 | 2 | $6*10^{-8}\\%$ |\n+| 4 | 3 | $4*10^{-12}\\%$ |\n+| 5 | 2 | $10^{-7}\\%$ |\n+| 5 | 3 | $10^{-11}\\%$ |\n+| 5 | 4 | $5*10^{-16}\\%$ |\n+| 6 | 2 | $1.5*10^{-7}\\%$ |\n+| 6 | 3 | $2*10^{-11}\\%$ |\n+| 6 | 4 | $1.5*10^{-15}\\%$ |\n+| 6 | 5 | $6*10^{-20}\\%$ |\n+\n+Values 2/3, 3/4, 3/5, 4/5 look like reasonable values to use in ULC client.\n+\n+| N | M | ~$P_{ULC\\_failure}$ |\n+| ------- | ------- | ------- | \n+| 3 | 1 | $3*10^{-8}\\%$ |\n+| 4 | 1 | $6*10^{-8}\\%$ |\n+| 5 | 1 | $10^{-7}\\%$ |\n+| 5 | 2 | $10^{-11}\\%$ |\n+| 6 | 1 | $1.5*10^{-7}\\%$ |\n+| 6 | 2 | $2*10^{-11}\\%$ |\n+\n+#### Trusted nodes\n+ULC client needs a set of trusted LES servers to get the current Chain state. It should be said that only ULC client knows it's trusted list, LES servers don't know that has been chosen as trusted by someone ULC client. The only difference is ULC client request and accepts only signed announcements to trust nonce (PoW) without check.\n+\n+We're going to provide predefined in the App trusted LES servers list. E.g. an application wanted to use ULC can define such trusted list for its clients and do load balancing with a simple random choice.\n+\n+Here comes one of the LES problems is that LES server can handle a limited number of clients. At the moment it's $LES\\_limit=25$.\n+\n+So if we want 3(M) out of 4(N) ULC consensus, in avarage we have 1000 users online, so we need minimum $Servers=Max(AvarageUsers*N/LES\\_limit; N)=Max(1000*4/25; 4)=160$\n+\n+That was the reason why in ULC a new option was announced for LES server: `--onlyAnnounce`. It limits LES server to handle only `get announce` requests, but it increases the number of simultaneous users up to ~250 (should be stress tested).\n+\n+With `--onlyAnnounce` the formule looks like:\n+$Servers=Max(AvarageUsers*N/LES\\_only\\_announce\\_limit; N)=Max(1000*4/250; 4)=Max(16; 4)=16$\n+\n+| Users online | N | Server w/o `onlyAnnounce` | With `onlyAnnounce` |\n+| ------- | ------- | ------- | ------- | \n+| 1000    | 4    | 160    | 16 | \n+| 1000    | 5    | 200    | 20 | \n+| 1000    | 6    | 240    | 24 | \n+| 1000    | 7    | 280    | 28 | \n+| 5000    | 4    | 800    | 80 | \n+| 5000    | 5    | 1000    | 100 | \n+| 5000    | 6    | 1200    | 120 | \n+| 5000    | 7    | 1400    | 140 | \n+| 10000    | 4    | 1600    | 160 | \n+| 10000    | 5    | 2000    | 200 | \n+| 10000    | 6    | 2400    | 240 | \n+| 10000    | 7    | 2800    | 280 | \n+\n+It's obvious that scaling due to server expansion inside the service is strictly limited. A prerequisite for the operation of ULC at large scales is an increase in the percentage of LES of servers relative to all Ethereum servers.\n+\n+At the moment there are 15000 nodes. If 30% of them would use the LES server option, more than 300 000 ULC users could be handled simultaneously.\n+\n+## Benchmarks\n+Our beta test showed that ULC sync is ~10x times [faster than LES](https://youtu.be/Z6UUT1TqdTs?t=623)\n+\n+## Plans\n+### Short-Term\n+Status.im is going to start using ULC to achieve greater censorship resistance and enable all possible `web3` features for DApps and developers.\n+\n+### Long-Term\n+* [On ULC incentives](https://hackmd.io/6IhsF6zRSqmtggvNnp1sHQ)\n+* Ethereum services are going to have microtransactions and it will make possible to make a market of LES server quotas using [LES service model](https://github.com/zsfelfoldi/ethereum-docs/blob/master/les/service_model.md#continous-auction)","path":"source/research/ulc_in_details.md","position":289,"original_position":289,"commit_id":"d286330ff8758bfeb839a01d88e022e864d39064","original_commit_id":"d286330ff8758bfeb839a01d88e022e864d39064","user":{"login":"andytudhope","id":13001517,"node_id":"MDQ6VXNlcjEzMDAxNTE3","avatar_url":"https://avatars2.githubusercontent.com/u/13001517?v=4","gravatar_id":"","url":"https://api.github.com/users/andytudhope","html_url":"https://github.com/andytudhope","followers_url":"https://api.github.com/users/andytudhope/followers","following_url":"https://api.github.com/users/andytudhope/following{/other_user}","gists_url":"https://api.github.com/users/andytudhope/gists{/gist_id}","starred_url":"https://api.github.com/users/andytudhope/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/andytudhope/subscriptions","organizations_url":"https://api.github.com/users/andytudhope/orgs","repos_url":"https://api.github.com/users/andytudhope/repos","events_url":"https://api.github.com/users/andytudhope/events{/privacy}","received_events_url":"https://api.github.com/users/andytudhope/received_events","type":"User","site_admin":false},"body":"```suggestion\r\n* Ethereum services are going to have microtransactions and this will make possible to create a market of LES server quotas using the proposed [LES service model](https://github.com/zsfelfoldi/ethereum-docs/blob/master/les/service_model.md#continous-auction)\r\n```","created_at":"2018-11-15T10:00:17Z","updated_at":"2018-11-15T10:00:26Z","html_url":"https://github.com/status-im/status.im/pull/146#discussion_r233778719","pull_request_url":"https://api.github.com/repos/status-im/status.im/pulls/146","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/status-im/status.im/pulls/comments/233778719"},"html":{"href":"https://github.com/status-im/status.im/pull/146#discussion_r233778719"},"pull_request":{"href":"https://api.github.com/repos/status-im/status.im/pulls/146"}}},"pull_request":{"url":"https://api.github.com/repos/status-im/status.im/pulls/146","id":230913747,"node_id":"MDExOlB1bGxSZXF1ZXN0MjMwOTEzNzQ3","html_url":"https://github.com/status-im/status.im/pull/146","diff_url":"https://github.com/status-im/status.im/pull/146.diff","patch_url":"https://github.com/status-im/status.im/pull/146.patch","issue_url":"https://api.github.com/repos/status-im/status.im/issues/146","number":146,"state":"open","locked":false,"title":"ULC in details","user":{"login":"JekaMas","id":3192700,"node_id":"MDQ6VXNlcjMxOTI3MDA=","avatar_url":"https://avatars0.githubusercontent.com/u/3192700?v=4","gravatar_id":"","url":"https://api.github.com/users/JekaMas","html_url":"https://github.com/JekaMas","followers_url":"https://api.github.com/users/JekaMas/followers","following_url":"https://api.github.com/users/JekaMas/following{/other_user}","gists_url":"https://api.github.com/users/JekaMas/gists{/gist_id}","starred_url":"https://api.github.com/users/JekaMas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JekaMas/subscriptions","organizations_url":"https://api.github.com/users/JekaMas/orgs","repos_url":"https://api.github.com/users/JekaMas/repos","events_url":"https://api.github.com/users/JekaMas/events{/privacy}","received_events_url":"https://api.github.com/users/JekaMas/received_events","type":"User","site_admin":false},"body":"","created_at":"2018-11-14T17:09:50Z","updated_at":"2018-11-15T10:00:24Z","closed_at":null,"merged_at":null,"merge_commit_sha":"c7c3a8293adf25c6a8c0676dfb0c67bb16f225c5","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/status-im/status.im/pulls/146/commits","review_comments_url":"https://api.github.com/repos/status-im/status.im/pulls/146/comments","review_comment_url":"https://api.github.com/repos/status-im/status.im/pulls/comments{/number}","comments_url":"https://api.github.com/repos/status-im/status.im/issues/146/comments","statuses_url":"https://api.github.com/repos/status-im/status.im/statuses/d286330ff8758bfeb839a01d88e022e864d39064","head":{"label":"JekaMas:ulc_in_details","ref":"ulc_in_details","sha":"d286330ff8758bfeb839a01d88e022e864d39064","user":{"login":"JekaMas","id":3192700,"node_id":"MDQ6VXNlcjMxOTI3MDA=","avatar_url":"https://avatars0.githubusercontent.com/u/3192700?v=4","gravatar_id":"","url":"https://api.github.com/users/JekaMas","html_url":"https://github.com/JekaMas","followers_url":"https://api.github.com/users/JekaMas/followers","following_url":"https://api.github.com/users/JekaMas/following{/other_user}","gists_url":"https://api.github.com/users/JekaMas/gists{/gist_id}","starred_url":"https://api.github.com/users/JekaMas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JekaMas/subscriptions","organizations_url":"https://api.github.com/users/JekaMas/orgs","repos_url":"https://api.github.com/users/JekaMas/repos","events_url":"https://api.github.com/users/JekaMas/events{/privacy}","received_events_url":"https://api.github.com/users/JekaMas/received_events","type":"User","site_admin":false},"repo":{"id":157582842,"node_id":"MDEwOlJlcG9zaXRvcnkxNTc1ODI4NDI=","name":"status.im","full_name":"JekaMas/status.im","private":false,"owner":{"login":"JekaMas","id":3192700,"node_id":"MDQ6VXNlcjMxOTI3MDA=","avatar_url":"https://avatars0.githubusercontent.com/u/3192700?v=4","gravatar_id":"","url":"https://api.github.com/users/JekaMas","html_url":"https://github.com/JekaMas","followers_url":"https://api.github.com/users/JekaMas/followers","following_url":"https://api.github.com/users/JekaMas/following{/other_user}","gists_url":"https://api.github.com/users/JekaMas/gists{/gist_id}","starred_url":"https://api.github.com/users/JekaMas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JekaMas/subscriptions","organizations_url":"https://api.github.com/users/JekaMas/orgs","repos_url":"https://api.github.com/users/JekaMas/repos","events_url":"https://api.github.com/users/JekaMas/events{/privacy}","received_events_url":"https://api.github.com/users/JekaMas/received_events","type":"User","site_admin":false},"html_url":"https://github.com/JekaMas/status.im","description":"Build a Better Web with Status","fork":true,"url":"https://api.github.com/repos/JekaMas/status.im","forks_url":"https://api.github.com/repos/JekaMas/status.im/forks","keys_url":"https://api.github.com/repos/JekaMas/status.im/keys{/key_id}","collaborators_url":"https://api.github.com/repos/JekaMas/status.im/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/JekaMas/status.im/teams","hooks_url":"https://api.github.com/repos/JekaMas/status.im/hooks","issue_events_url":"https://api.github.com/repos/JekaMas/status.im/issues/events{/number}","events_url":"https://api.github.com/repos/JekaMas/status.im/events","assignees_url":"https://api.github.com/repos/JekaMas/status.im/assignees{/user}","branches_url":"https://api.github.com/repos/JekaMas/status.im/branches{/branch}","tags_url":"https://api.github.com/repos/JekaMas/status.im/tags","blobs_url":"https://api.github.com/repos/JekaMas/status.im/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/JekaMas/status.im/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/JekaMas/status.im/git/refs{/sha}","trees_url":"https://api.github.com/repos/JekaMas/status.im/git/trees{/sha}","statuses_url":"https://api.github.com/repos/JekaMas/status.im/statuses/{sha}","languages_url":"https://api.github.com/repos/JekaMas/status.im/languages","stargazers_url":"https://api.github.com/repos/JekaMas/status.im/stargazers","contributors_url":"https://api.github.com/repos/JekaMas/status.im/contributors","subscribers_url":"https://api.github.com/repos/JekaMas/status.im/subscribers","subscription_url":"https://api.github.com/repos/JekaMas/status.im/subscription","commits_url":"https://api.github.com/repos/JekaMas/status.im/commits{/sha}","git_commits_url":"https://api.github.com/repos/JekaMas/status.im/git/commits{/sha}","comments_url":"https://api.github.com/repos/JekaMas/status.im/comments{/number}","issue_comment_url":"https://api.github.com/repos/JekaMas/status.im/issues/comments{/number}","contents_url":"https://api.github.com/repos/JekaMas/status.im/contents/{+path}","compare_url":"https://api.github.com/repos/JekaMas/status.im/compare/{base}...{head}","merges_url":"https://api.github.com/repos/JekaMas/status.im/merges","archive_url":"https://api.github.com/repos/JekaMas/status.im/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/JekaMas/status.im/downloads","issues_url":"https://api.github.com/repos/JekaMas/status.im/issues{/number}","pulls_url":"https://api.github.com/repos/JekaMas/status.im/pulls{/number}","milestones_url":"https://api.github.com/repos/JekaMas/status.im/milestones{/number}","notifications_url":"https://api.github.com/repos/JekaMas/status.im/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/JekaMas/status.im/labels{/name}","releases_url":"https://api.github.com/repos/JekaMas/status.im/releases{/id}","deployments_url":"https://api.github.com/repos/JekaMas/status.im/deployments","created_at":"2018-11-14T17:04:24Z","updated_at":"2018-11-14T17:04:27Z","pushed_at":"2018-11-15T09:37:10Z","git_url":"git://github.com/JekaMas/status.im.git","ssh_url":"git@github.com:JekaMas/status.im.git","clone_url":"https://github.com/JekaMas/status.im.git","svn_url":"https://github.com/JekaMas/status.im","homepage":"https://status.im/","size":23051,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":0,"open_issues":0,"watchers":0,"default_branch":"develop"}},"base":{"label":"status-im:master","ref":"master","sha":"036df524dde7053f90a16d33abe52a6c4f34e846","user":{"login":"status-im","id":11767950,"node_id":"MDEyOk9yZ2FuaXphdGlvbjExNzY3OTUw","avatar_url":"https://avatars1.githubusercontent.com/u/11767950?v=4","gravatar_id":"","url":"https://api.github.com/users/status-im","html_url":"https://github.com/status-im","followers_url":"https://api.github.com/users/status-im/followers","following_url":"https://api.github.com/users/status-im/following{/other_user}","gists_url":"https://api.github.com/users/status-im/gists{/gist_id}","starred_url":"https://api.github.com/users/status-im/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/status-im/subscriptions","organizations_url":"https://api.github.com/users/status-im/orgs","repos_url":"https://api.github.com/users/status-im/repos","events_url":"https://api.github.com/users/status-im/events{/privacy}","received_events_url":"https://api.github.com/users/status-im/received_events","type":"Organization","site_admin":false},"repo":{"id":89814542,"node_id":"MDEwOlJlcG9zaXRvcnk4OTgxNDU0Mg==","name":"status.im","full_name":"status-im/status.im","private":false,"owner":{"login":"status-im","id":11767950,"node_id":"MDEyOk9yZ2FuaXphdGlvbjExNzY3OTUw","avatar_url":"https://avatars1.githubusercontent.com/u/11767950?v=4","gravatar_id":"","url":"https://api.github.com/users/status-im","html_url":"https://github.com/status-im","followers_url":"https://api.github.com/users/status-im/followers","following_url":"https://api.github.com/users/status-im/following{/other_user}","gists_url":"https://api.github.com/users/status-im/gists{/gist_id}","starred_url":"https://api.github.com/users/status-im/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/status-im/subscriptions","organizations_url":"https://api.github.com/users/status-im/orgs","repos_url":"https://api.github.com/users/status-im/repos","events_url":"https://api.github.com/users/status-im/events{/privacy}","received_events_url":"https://api.github.com/users/status-im/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/status-im/status.im","description":"Build a Better Web with Status","fork":false,"url":"https://api.github.com/repos/status-im/status.im","forks_url":"https://api.github.com/repos/status-im/status.im/forks","keys_url":"https://api.github.com/repos/status-im/status.im/keys{/key_id}","collaborators_url":"https://api.github.com/repos/status-im/status.im/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/status-im/status.im/teams","hooks_url":"https://api.github.com/repos/status-im/status.im/hooks","issue_events_url":"https://api.github.com/repos/status-im/status.im/issues/events{/number}","events_url":"https://api.github.com/repos/status-im/status.im/events","assignees_url":"https://api.github.com/repos/status-im/status.im/assignees{/user}","branches_url":"https://api.github.com/repos/status-im/status.im/branches{/branch}","tags_url":"https://api.github.com/repos/status-im/status.im/tags","blobs_url":"https://api.github.com/repos/status-im/status.im/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/status-im/status.im/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/status-im/status.im/git/refs{/sha}","trees_url":"https://api.github.com/repos/status-im/status.im/git/trees{/sha}","statuses_url":"https://api.github.com/repos/status-im/status.im/statuses/{sha}","languages_url":"https://api.github.com/repos/status-im/status.im/languages","stargazers_url":"https://api.github.com/repos/status-im/status.im/stargazers","contributors_url":"https://api.github.com/repos/status-im/status.im/contributors","subscribers_url":"https://api.github.com/repos/status-im/status.im/subscribers","subscription_url":"https://api.github.com/repos/status-im/status.im/subscription","commits_url":"https://api.github.com/repos/status-im/status.im/commits{/sha}","git_commits_url":"https://api.github.com/repos/status-im/status.im/git/commits{/sha}","comments_url":"https://api.github.com/repos/status-im/status.im/comments{/number}","issue_comment_url":"https://api.github.com/repos/status-im/status.im/issues/comments{/number}","contents_url":"https://api.github.com/repos/status-im/status.im/contents/{+path}","compare_url":"https://api.github.com/repos/status-im/status.im/compare/{base}...{head}","merges_url":"https://api.github.com/repos/status-im/status.im/merges","archive_url":"https://api.github.com/repos/status-im/status.im/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/status-im/status.im/downloads","issues_url":"https://api.github.com/repos/status-im/status.im/issues{/number}","pulls_url":"https://api.github.com/repos/status-im/status.im/pulls{/number}","milestones_url":"https://api.github.com/repos/status-im/status.im/milestones{/number}","notifications_url":"https://api.github.com/repos/status-im/status.im/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/status-im/status.im/labels{/name}","releases_url":"https://api.github.com/repos/status-im/status.im/releases{/id}","deployments_url":"https://api.github.com/repos/status-im/status.im/deployments","created_at":"2017-04-29T21:42:05Z","updated_at":"2018-11-15T08:36:32Z","pushed_at":"2018-11-15T09:37:12Z","git_url":"git://github.com/status-im/status.im.git","ssh_url":"git@github.com:status-im/status.im.git","clone_url":"https://github.com/status-im/status.im.git","svn_url":"https://github.com/status-im/status.im","homepage":"https://status.im/","size":23099,"stargazers_count":20,"watchers_count":20,"language":"JavaScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":34,"mirror_url":null,"archived":false,"open_issues_count":14,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"forks":34,"open_issues":14,"watchers":20,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/status-im/status.im/pulls/146"},"html":{"href":"https://github.com/status-im/status.im/pull/146"},"issue":{"href":"https://api.github.com/repos/status-im/status.im/issues/146"},"comments":{"href":"https://api.github.com/repos/status-im/status.im/issues/146/comments"},"review_comments":{"href":"https://api.github.com/repos/status-im/status.im/pulls/146/comments"},"review_comment":{"href":"https://api.github.com/repos/status-im/status.im/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/status-im/status.im/pulls/146/commits"},"statuses":{"href":"https://api.github.com/repos/status-im/status.im/statuses/d286330ff8758bfeb839a01d88e022e864d39064"}},"author_association":"NONE"}}
{"actor":{"display_login":"andytudhope"}}