diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index ac381c806..36f809928 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -23,6 +23,7 @@ FEATURES: - [types] allow genesis file to have 0 validators ([#2015](https://github.com/tendermint/tendermint/issues/2015)) IMPROVEMENTS: +- [docs] Lint documentation with `write-good` and `stop-words`. - [scripts] Added json2wal tool, which is supposed to help our users restore corrupted WAL files and compose test WAL files (@bradyjoestar) diff --git a/docs/.textlintrc.json b/docs/.textlintrc.json new file mode 100644 index 000000000..4103f89e8 --- /dev/null +++ b/docs/.textlintrc.json @@ -0,0 +1,9 @@ +{ + "rules": { + "stop-words": { + "severity": "warning", + "defaultWords": false, + "words": "stop-words.txt" + } + } +} diff --git a/docs/app-dev/ecosystem.json b/docs/app-dev/ecosystem.json index 363f18902..728f3a530 100644 --- a/docs/app-dev/ecosystem.json +++ b/docs/app-dev/ecosystem.json @@ -5,24 +5,21 @@ "url": "https://github.com/cosmos/cosmos-sdk", "language": "Go", "author": "Cosmos", - "description": - "A prototypical account based crypto currency state machine supporting plugins" + "description": "A prototypical account based crypto currency state machine supporting plugins" }, { "name": "cb-ledger", "url": "https://github.com/block-finance/cpp-abci", "language": "C++", "author": "Block Finance", - "description": - "Custodian Bank Ledger, integrating central banking with the blockchains of tomorrow" + "description": "Custodian Bank Ledger, integrating central banking with the blockchains of tomorrow" }, { "name": "Clearchain", "url": "https://github.com/tendermint/clearchain", "language": "Go", "author": "FXCLR", - "description": - "Application to manage a distributed ledger for money transfers that support multi-currency accounts" + "description": "Application to manage a distributed ledger for money transfers that support multi-currency accounts" }, { "name": "Ethermint", @@ -43,8 +40,7 @@ "url": "https://github.com/hyperledger/burrow", "language": "Go", "author": "Monax Industries", - "description": - "Ethereum Virtual Machine augmented with native permissioning scheme and global key-value store" + "description": "Ethereum Virtual Machine augmented with native permissioning scheme and global key-value store" }, { "name": "Merkle AVL Tree", @@ -72,8 +68,7 @@ "url": "https://github.com/trusch/passchain", "language": "Go", "author": "trusch", - "description": - "Tool to securely store and share passwords, tokens and other short secrets" + "description": "Tool to securely store and share passwords, tokens and other short secrets" }, { "name": "Passwerk", @@ -87,8 +82,7 @@ "url": "https://github.com/davebryson/py-tendermint", "language": "Python", "author": "Dave Bryson", - "description": - "A Python microframework for building blockchain applications with Tendermint" + "description": "A Python microframework for building blockchain applications with Tendermint" }, { "name": "Stratumn SDK", @@ -102,16 +96,14 @@ "url": "https://github.com/keppel/lotion", "language": "Javascript", "author": "Judd Keppel", - "description": - "A Javascript microframework for building blockchain applications with Tendermint" + "description": "A Javascript microframework for building blockchain applications with Tendermint" }, { "name": "Tendermint Blockchain Chat App", "url": "https://github.com/SaifRehman/tendermint-chat-app/", "language": "Javascript", "author": "Saif Rehman", - "description": - "This is a minimal chat application based on Tendermint using Lotion.js in 30 lines of code!. It also includes web/mobile application built using Ionic 3." + "description": "This is a minimal chat application based on Tendermint using Lotion.js in 30 lines of code!. It also includes web/mobile application built using Ionic 3." }, { "name": "BigchainDB", @@ -184,16 +176,14 @@ "url": "https://github.com/tendermint/tools", "technology": "Docker and Kubernetes", "author": "Tendermint", - "description": - "Deploy a Tendermint test network using Google's kubernetes" + "description": "Deploy a Tendermint test network using Google's kubernetes" }, { "name": "terraforce", "url": "https://github.com/tendermint/tools", "technology": "Terraform", "author": "Tendermint", - "description": - "Terraform + our custom terraforce tool; deploy a production Tendermint network with load balancing over multiple AWS availability zones" + "description": "Terraform + our custom terraforce tool; deploy a production Tendermint network with load balancing over multiple AWS availability zones" }, { "name": "ansible-tendermint", diff --git a/docs/architecture/adr-002-event-subscription.md b/docs/architecture/adr-002-event-subscription.md index cc207c4af..a73d584ab 100644 --- a/docs/architecture/adr-002-event-subscription.md +++ b/docs/architecture/adr-002-event-subscription.md @@ -7,8 +7,7 @@ a subset of transactions** (rather than all of them) using `/subscribe?event=X`. example, I want to subscribe for all transactions associated with a particular account. Same for fetching. The user may want to **fetch transactions based on some filter** (rather than fetching all the blocks). For example, I want to get -all transactions for a particular account in the last two weeks (`tx's block -time >= '2017-06-05'`). +all transactions for a particular account in the last two weeks (`tx's block time >= '2017-06-05'`). Now you can't even subscribe to "all txs" in Tendermint. diff --git a/docs/architecture/adr-004-historical-validators.md b/docs/architecture/adr-004-historical-validators.md index be0de22c1..c98af7026 100644 --- a/docs/architecture/adr-004-historical-validators.md +++ b/docs/architecture/adr-004-historical-validators.md @@ -3,11 +3,11 @@ ## Context Right now, we can query the present validator set, but there is no history. -If you were offline for a long time, there is no way to reconstruct past validators. This is needed for the light client and we agreed needs enhancement of the API. +If you were offline for a long time, there is no way to reconstruct past validators. This is needed for the light client and we agreed needs enhancement of the API. ## Decision -For every block, store a new structure that contains either the latest validator set, +For every block, store a new structure that contains either the latest validator set, or the height of the last block for which the validator set changed. Note this is not the height of the block which returned the validator set change itself, but the next block, ie. the first block it comes into effect for. @@ -19,7 +19,7 @@ are updated frequently - for instance by only saving the diffs, rather than the An alternative approach suggested keeping the validator set, or diffs of it, in a merkle IAVL tree. While it might afford cheaper proofs that a validator set has not changed, it would be more complex, -and likely less efficient. +and likely less efficient. ## Status diff --git a/docs/architecture/adr-005-consensus-params.md b/docs/architecture/adr-005-consensus-params.md index 6656d35b2..ad132c9b9 100644 --- a/docs/architecture/adr-005-consensus-params.md +++ b/docs/architecture/adr-005-consensus-params.md @@ -7,7 +7,7 @@ Since they may be need to be different in different networks, and potentially to networks, we seek to initialize them in a genesis file, and expose them through the ABCI. While we have some specific parameters now, like maximum block and transaction size, we expect to have more in the future, -such as a period over which evidence is valid, or the frequency of checkpoints. +such as a period over which evidence is valid, or the frequency of checkpoints. ## Decision @@ -45,7 +45,7 @@ type BlockGossip struct { The `ConsensusParams` can evolve over time by adding new structs that cover different aspects of the consensus rules. -The `BlockPartSizeBytes` and the `BlockSize.MaxBytes` are enforced to be greater than 0. +The `BlockPartSizeBytes` and the `BlockSize.MaxBytes` are enforced to be greater than 0. The former because we need a part size, the latter so that we always have at least some sanity check over the size of blocks. ### ABCI @@ -53,14 +53,14 @@ The former because we need a part size, the latter so that we always have at lea #### InitChain InitChain currently takes the initial validator set. It should be extended to also take parts of the ConsensusParams. -There is some case to be made for it to take the entire Genesis, except there may be things in the genesis, +There is some case to be made for it to take the entire Genesis, except there may be things in the genesis, like the BlockPartSize, that the app shouldn't really know about. #### EndBlock The EndBlock response includes a `ConsensusParams`, which includes BlockSize and TxSize, but not BlockGossip. Other param struct can be added to `ConsensusParams` in the future. -The `0` value is used to denote no change. +The `0` value is used to denote no change. Any other value will update that parameter in the `State.ConsensusParams`, to be applied for the next block. Tendermint should have hard-coded upper limits as sanity checks. @@ -83,4 +83,3 @@ Proposed. ### Neutral - The TxSize, which checks validity, may be in conflict with the config's `max_block_size_tx`, which determines proposal sizes - diff --git a/docs/architecture/adr-006-trust-metric.md b/docs/architecture/adr-006-trust-metric.md index ec8a0cce7..6fa77a609 100644 --- a/docs/architecture/adr-006-trust-metric.md +++ b/docs/architecture/adr-006-trust-metric.md @@ -8,13 +8,13 @@ The proposed trust metric will allow Tendermint to maintain local trust rankings The Tendermint Core project developers would like to improve Tendermint security and reliability by keeping track of the level of trustworthiness peers have demonstrated within the peer-to-peer network. This way, undesirable outcomes from peers will not immediately result in them being dropped from the network (potentially causing drastic changes to take place). Instead, peers behavior can be monitored with appropriate metrics and be removed from the network once Tendermint Core is certain the peer is a threat. For example, when the PEXReactor makes a request for peers network addresses from a already known peer, and the returned network addresses are unreachable, this untrustworthy behavior should be tracked. Returning a few bad network addresses probably shouldn’t cause a peer to be dropped, while excessive amounts of this behavior does qualify the peer being dropped. -Trust metrics can be circumvented by malicious nodes through the use of strategic oscillation techniques, which adapts the malicious node’s behavior pattern in order to maximize its goals. For instance, if the malicious node learns that the time interval of the Tendermint trust metric is *X* hours, then it could wait *X* hours in-between malicious activities. We could try to combat this issue by increasing the interval length, yet this will make the system less adaptive to recent events. +Trust metrics can be circumvented by malicious nodes through the use of strategic oscillation techniques, which adapts the malicious node’s behavior pattern in order to maximize its goals. For instance, if the malicious node learns that the time interval of the Tendermint trust metric is _X_ hours, then it could wait _X_ hours in-between malicious activities. We could try to combat this issue by increasing the interval length, yet this will make the system less adaptive to recent events. Instead, having shorter intervals, but keeping a history of interval values, will give our metric the flexibility needed in order to keep the network stable, while also making it resilient against a strategic malicious node in the Tendermint peer-to-peer network. Also, the metric can access trust data over a rather long period of time while not greatly increasing its history size by aggregating older history values over a larger number of intervals, and at the same time, maintain great precision for the recent intervals. This approach is referred to as fading memories, and closely resembles the way human beings remember their experiences. The trade-off to using history data is that the interval values should be preserved in-between executions of the node. ### References -S. Mudhakar, L. Xiong, and L. Liu, “TrustGuard: Countering Vulnerabilities in Reputation Management for Decentralized Overlay Networks,” in *Proceedings of the 14th international conference on World Wide Web, pp. 422-431*, May 2005. +S. Mudhakar, L. Xiong, and L. Liu, “TrustGuard: Countering Vulnerabilities in Reputation Management for Decentralized Overlay Networks,” in _Proceedings of the 14th international conference on World Wide Web, pp. 422-431_, May 2005. ## Decision @@ -26,25 +26,23 @@ The three subsections below will cover the process being considered for calculat The proposed trust metric will count good and bad events relevant to the object, and calculate the percent of counters that are good over an interval with a predefined duration. This is the procedure that will continue for the life of the trust metric. When the trust metric is queried for the current **trust value**, a resilient equation will be utilized to perform the calculation. -The equation being proposed resembles a Proportional-Integral-Derivative (PID) controller used in control systems. The proportional component allows us to be sensitive to the value of the most recent interval, while the integral component allows us to incorporate trust values stored in the history data, and the derivative component allows us to give weight to sudden changes in the behavior of a peer. We compute the trust value of a peer in interval i based on its current trust ranking, its trust rating history prior to interval *i* (over the past *maxH* number of intervals) and its trust ranking fluctuation. We will break up the equation into the three components. +The equation being proposed resembles a Proportional-Integral-Derivative (PID) controller used in control systems. The proportional component allows us to be sensitive to the value of the most recent interval, while the integral component allows us to incorporate trust values stored in the history data, and the derivative component allows us to give weight to sudden changes in the behavior of a peer. We compute the trust value of a peer in interval i based on its current trust ranking, its trust rating history prior to interval _i_ (over the past _maxH_ number of intervals) and its trust ranking fluctuation. We will break up the equation into the three components. ```math (1) Proportional Value = a * R[i] ``` -where *R*[*i*] denotes the raw trust value at time interval *i* (where *i* == 0 being current time) and *a* is the weight applied to the contribution of the current reports. The next component of our equation uses a weighted sum over the last *maxH* intervals to calculate the history value for time *i*: - +where _R_[*i*] denotes the raw trust value at time interval _i_ (where _i_ == 0 being current time) and _a_ is the weight applied to the contribution of the current reports. The next component of our equation uses a weighted sum over the last _maxH_ intervals to calculate the history value for time _i_: -`H[i] = ` ![formula1](img/formula1.png "Weighted Sum Formula") +`H[i] =` ![formula1](img/formula1.png "Weighted Sum Formula") - -The weights can be chosen either optimistically or pessimistically. An optimistic weight creates larger weights for newer history data values, while the the pessimistic weight creates larger weights for time intervals with lower scores. The default weights used during the calculation of the history value are optimistic and calculated as *Wk* = 0.8^*k*, for time interval *k*. With the history value available, we can now finish calculating the integral value: +The weights can be chosen either optimistically or pessimistically. An optimistic weight creates larger weights for newer history data values, while the the pessimistic weight creates larger weights for time intervals with lower scores. The default weights used during the calculation of the history value are optimistic and calculated as _Wk_ = 0.8^_k_, for time interval _k_. With the history value available, we can now finish calculating the integral value: ```math (2) Integral Value = b * H[i] ``` -Where *H*[*i*] denotes the history value at time interval *i* and *b* is the weight applied to the contribution of past performance for the object being measured. The derivative component will be calculated as follows: +Where _H_[*i*] denotes the history value at time interval _i_ and _b_ is the weight applied to the contribution of past performance for the object being measured. The derivative component will be calculated as follows: ```math D[i] = R[i] – H[i] @@ -52,25 +50,25 @@ D[i] = R[i] – H[i] (3) Derivative Value = c(D[i]) * D[i] ``` -Where the value of *c* is selected based on the *D*[*i*] value relative to zero. The default selection process makes *c* equal to 0 unless *D*[*i*] is a negative value, in which case c is equal to 1. The result is that the maximum penalty is applied when current behavior is lower than previously experienced behavior. If the current behavior is better than the previously experienced behavior, then the Derivative Value has no impact on the trust value. With the three components brought together, our trust value equation is calculated as follows: +Where the value of _c_ is selected based on the _D_[*i*] value relative to zero. The default selection process makes _c_ equal to 0 unless _D_[*i*] is a negative value, in which case c is equal to 1. The result is that the maximum penalty is applied when current behavior is lower than previously experienced behavior. If the current behavior is better than the previously experienced behavior, then the Derivative Value has no impact on the trust value. With the three components brought together, our trust value equation is calculated as follows: ```math TrustValue[i] = a * R[i] + b * H[i] + c(D[i]) * D[i] ``` -As a performance optimization that will keep the amount of raw interval data being saved to a reasonable size of *m*, while allowing us to represent 2^*m* - 1 history intervals, we can employ the fading memories technique that will trade space and time complexity for the precision of the history data values by summarizing larger quantities of less recent values. While our equation above attempts to access up to *maxH* (which can be 2^*m* - 1), we will map those requests down to *m* values using equation 4 below: +As a performance optimization that will keep the amount of raw interval data being saved to a reasonable size of _m_, while allowing us to represent 2^_m_ - 1 history intervals, we can employ the fading memories technique that will trade space and time complexity for the precision of the history data values by summarizing larger quantities of less recent values. While our equation above attempts to access up to _maxH_ (which can be 2^_m_ - 1), we will map those requests down to _m_ values using equation 4 below: ```math (4) j = index, where index > 0 ``` -Where *j* is one of *(0, 1, 2, … , m – 1)* indices used to access history interval data. Now we can access the raw intervals using the following calculations: +Where _j_ is one of _(0, 1, 2, … , m – 1)_ indices used to access history interval data. Now we can access the raw intervals using the following calculations: ```math R[0] = raw data for current time interval ``` -`R[j] = ` ![formula2](img/formula2.png "Fading Memories Formula") +`R[j] =` ![formula2](img/formula2.png "Fading Memories Formula") ### Trust Metric Store @@ -84,9 +82,7 @@ When the node is shutting down, the trust metric store will save history data fo Each trust metric allows for the recording of positive/negative events, querying the current trust value/score, and the stopping/pausing of tracking over time intervals. This can be seen below: - ```go - // TrustMetric - keeps track of peer reliability type TrustMetric struct { // Private elements. @@ -123,13 +119,11 @@ tm.BadEvents(1) score := tm.TrustScore() tm.Stop() - ``` Some of the trust metric parameters can be configured. The weight values should probably be left alone in more cases, yet the time durations for the tracking window and individual time interval should be considered. ```go - // TrustMetricConfig - Configures the weight functions and time intervals for the metric type TrustMetricConfig struct { // Determines the percentage given to current behavior @@ -165,23 +159,21 @@ config := TrustMetricConfig{ tm := NewMetricWithConfig(config) tm.BadEvents(10) -tm.Pause() +tm.Pause() tm.GoodEvents(1) // becomes active again - ``` -A trust metric store should be created with a DB that has persistent storage so it can save history data across node executions. All trust metrics instantiated by the store will be created with the provided TrustMetricConfig configuration. +A trust metric store should be created with a DB that has persistent storage so it can save history data across node executions. All trust metrics instantiated by the store will be created with the provided TrustMetricConfig configuration. When you attempt to fetch the trust metric for a peer, and an entry does not exist in the trust metric store, a new metric is automatically created and the entry made within the store. In additional to the fetching method, GetPeerTrustMetric, the trust metric store provides a method to call when a peer has disconnected from the node. This is so the metric can be paused (history data will not be saved) for periods of time when the node is not having direct experiences with the peer. ```go - // TrustMetricStore - Manages all trust metrics for peers type TrustMetricStore struct { cmn.BaseService - + // Private elements } @@ -214,7 +206,6 @@ tm := tms.GetPeerTrustMetric(key) tm.BadEvents(1) tms.PeerDisconnected(key) - ``` ## Status diff --git a/docs/architecture/adr-007-trust-metric-usage.md b/docs/architecture/adr-007-trust-metric-usage.md index 4d833a69f..de3a088cb 100644 --- a/docs/architecture/adr-007-trust-metric-usage.md +++ b/docs/architecture/adr-007-trust-metric-usage.md @@ -17,11 +17,13 @@ For example, when the PEXReactor makes a request for peers network addresses fro The trust metric implementation allows a developer to obtain a peer's trust metric from a trust metric store, and track good and bad events relevant to a peer's behavior, and at any time, the peer's metric can be queried for a current trust value. The current trust value is calculated with a formula that utilizes current behavior, previous behavior, and change between the two. Current behavior is calculated as the percentage of good behavior within a time interval. The time interval is short; probably set between 30 seconds and 5 minutes. On the other hand, the historic data can estimate a peer's behavior over days worth of tracking. At the end of a time interval, the current behavior becomes part of the historic data, and a new time interval begins with the good and bad counters reset to zero. These are some important things to keep in mind regarding how the trust metrics handle time intervals and scoring: + - Each new time interval begins with a perfect score - Bad events quickly bring the score down and good events cause the score to slowly rise - When the time interval is over, the percentage of good events becomes historic data. Some useful information about the inner workings of the trust metric: + - When a trust metric is first instantiated, a timer (ticker) periodically fires in order to handle transitions between trust metric time intervals - If a peer is disconnected from a node, the timer should be paused, since the node is no longer connected to that peer - The ability to pause the metric is supported with the store **PeerDisconnected** method and the metric **Pause** method @@ -76,6 +78,7 @@ Peer quality is tracked in the connection and across the reactors by storing the thread safe Data store. Peer behaviour is then defined as one of the following: + - Fatal - something outright malicious that causes us to disconnect the peer and ban it from the address book for some amount of time - Bad - Any kind of timeout, messages that don't unmarshal, fail other validity checks, or messages we didn't ask for or aren't expecting (usually worth one bad event) - Neutral - Unknown channels/message types/version upgrades (no good or bad events recorded) diff --git a/docs/architecture/adr-008-priv-validator.md b/docs/architecture/adr-008-priv-validator.md index 4c1d87bed..94e882af4 100644 --- a/docs/architecture/adr-008-priv-validator.md +++ b/docs/architecture/adr-008-priv-validator.md @@ -11,18 +11,18 @@ implementations: The SocketPV address can be provided via flags at the command line - doing so will cause Tendermint to ignore any "priv_validator.json" file and to listen on the given address for incoming connections from an external priv_validator -process. It will halt any operation until at least one external process +process. It will halt any operation until at least one external process succesfully connected. The external priv_validator process will dial the address to connect to Tendermint, and then Tendermint will send requests on the ensuing connection to -sign votes and proposals. Thus the external process initiates the connection, -but the Tendermint process makes all requests. In a later stage we're going to +sign votes and proposals. Thus the external process initiates the connection, +but the Tendermint process makes all requests. In a later stage we're going to support multiple validators for fault tolerance. To prevent double signing they need to be synced, which is deferred to an external solution (see #1185). In addition, Tendermint will provide implementations that can be run in that -external process. These include: +external process. These include: - FilePV will encrypt the private key, and the user must enter password to decrypt key when process is started. diff --git a/docs/architecture/adr-009-ABCI-design.md b/docs/architecture/adr-009-ABCI-design.md index 8b85679b8..fab28853b 100644 --- a/docs/architecture/adr-009-ABCI-design.md +++ b/docs/architecture/adr-009-ABCI-design.md @@ -8,7 +8,7 @@ ## Context -The ABCI was first introduced in late 2015. It's purpose is to be: +The ABCI was first introduced in late 2015. It's purpose is to be: - a generic interface between state machines and their replication engines - agnostic to the language the state machine is written in @@ -66,8 +66,8 @@ possible. ### Validators To change the validator set, applications can return a list of validator updates -with ResponseEndBlock. In these updates, the public key *must* be included, -because Tendermint requires the public key to verify validator signatures. This +with ResponseEndBlock. In these updates, the public key _must_ be included, +because Tendermint requires the public key to verify validator signatures. This means ABCI developers have to work with PubKeys. That said, it would also be convenient to work with address information, and for it to be simple to do so. @@ -80,7 +80,7 @@ in commits. ### InitChain -Tendermint passes in a list of validators here, and nothing else. It would +Tendermint passes in a list of validators here, and nothing else. It would benefit the application to be able to control the initial validator set. For instance the genesis file could include application-based information about the initial validator set that the application could process to determine the @@ -120,7 +120,6 @@ v1 will: That said, an Amino v2 will be worked on to improve the performance of the format and its useability in cryptographic applications. - ### PubKey Encoding schemes infect software. As a generic middleware, ABCI aims to have @@ -143,7 +142,6 @@ where `type` can be: - "ed225519", with `data = ` - "secp256k1", with `data = <33-byte OpenSSL compressed pubkey>` - As we want to retain flexibility here, and since ideally, PubKey would be an interface type, we do not use `enum` or `oneof`. diff --git a/docs/architecture/adr-010-crypto-changes.md b/docs/architecture/adr-010-crypto-changes.md index cfe618421..0bc07d69c 100644 --- a/docs/architecture/adr-010-crypto-changes.md +++ b/docs/architecture/adr-010-crypto-changes.md @@ -66,13 +66,10 @@ Make the following changes: - More modern and standard cryptographic functions with wider adoption and hardware acceleration - ### Negative - Exact authenticated encryption construction isn't already provided in a well-used library - ### Neutral ## References - diff --git a/docs/architecture/adr-011-monitoring.md b/docs/architecture/adr-011-monitoring.md index ca16a9a1c..8f2d009dd 100644 --- a/docs/architecture/adr-011-monitoring.md +++ b/docs/architecture/adr-011-monitoring.md @@ -15,11 +15,11 @@ https://github.com/tendermint/tendermint/issues/986. A few solutions were considered: 1. [Prometheus](https://prometheus.io) - a) Prometheus API - b) [go-kit metrics package](https://github.com/go-kit/kit/tree/master/metrics) as an interface plus Prometheus - c) [telegraf](https://github.com/influxdata/telegraf) - d) new service, which will listen to events emitted by pubsub and report metrics -5. [OpenCensus](https://opencensus.io/go/index.html) + a) Prometheus API + b) [go-kit metrics package](https://github.com/go-kit/kit/tree/master/metrics) as an interface plus Prometheus + c) [telegraf](https://github.com/influxdata/telegraf) + d) new service, which will listen to events emitted by pubsub and report metrics +2. [OpenCensus](https://opencensus.io/go/index.html) ### 1. Prometheus @@ -70,30 +70,30 @@ will need to write interfaces ourselves. ### List of metrics -| | Name | Type | Description | -| - | --------------------------------------- | ------- | ----------------------------------------------------------------------------- | -| A | consensus_height | Gauge | | -| A | consensus_validators | Gauge | Number of validators who signed | -| A | consensus_validators_power | Gauge | Total voting power of all validators | -| A | consensus_missing_validators | Gauge | Number of validators who did not sign | -| A | consensus_missing_validators_power | Gauge | Total voting power of the missing validators | -| A | consensus_byzantine_validators | Gauge | Number of validators who tried to double sign | -| A | consensus_byzantine_validators_power | Gauge | Total voting power of the byzantine validators | -| A | consensus_block_interval | Timing | Time between this and last block (Block.Header.Time) | -| | consensus_block_time | Timing | Time to create a block (from creating a proposal to commit) | -| | consensus_time_between_blocks | Timing | Time between committing last block and (receiving proposal creating proposal) | -| A | consensus_rounds | Gauge | Number of rounds | -| | consensus_prevotes | Gauge | | -| | consensus_precommits | Gauge | | -| | consensus_prevotes_total_power | Gauge | | -| | consensus_precommits_total_power | Gauge | | -| A | consensus_num_txs | Gauge | | -| A | mempool_size | Gauge | | -| A | consensus_total_txs | Gauge | | -| A | consensus_block_size | Gauge | In bytes | -| A | p2p_peers | Gauge | Number of peers node's connected to | - -`A` - will be implemented in the fist place. +| | Name | Type | Description | +| --- | ------------------------------------ | ------ | ----------------------------------------------------------------------------- | +| A | consensus_height | Gauge | | +| A | consensus_validators | Gauge | Number of validators who signed | +| A | consensus_validators_power | Gauge | Total voting power of all validators | +| A | consensus_missing_validators | Gauge | Number of validators who did not sign | +| A | consensus_missing_validators_power | Gauge | Total voting power of the missing validators | +| A | consensus_byzantine_validators | Gauge | Number of validators who tried to double sign | +| A | consensus_byzantine_validators_power | Gauge | Total voting power of the byzantine validators | +| A | consensus_block_interval | Timing | Time between this and last block (Block.Header.Time) | +| | consensus_block_time | Timing | Time to create a block (from creating a proposal to commit) | +| | consensus_time_between_blocks | Timing | Time between committing last block and (receiving proposal creating proposal) | +| A | consensus_rounds | Gauge | Number of rounds | +| | consensus_prevotes | Gauge | | +| | consensus_precommits | Gauge | | +| | consensus_prevotes_total_power | Gauge | | +| | consensus_precommits_total_power | Gauge | | +| A | consensus_num_txs | Gauge | | +| A | mempool_size | Gauge | | +| A | consensus_total_txs | Gauge | | +| A | consensus_block_size | Gauge | In bytes | +| A | p2p_peers | Gauge | Number of peers node's connected to | + +`A` - will be implemented in the fist place. **Proposed solution** diff --git a/docs/architecture/adr-012-ABCI-propose-tx.md b/docs/architecture/adr-012-ABCI-propose-tx.md index 4ea290f0c..497ccd184 100644 --- a/docs/architecture/adr-012-ABCI-propose-tx.md +++ b/docs/architecture/adr-012-ABCI-propose-tx.md @@ -33,7 +33,7 @@ Due to the requirements of [Minimal Viable Plasma (MVP)](https://ethresear.ch/t/ special treatment. 2. Other "internal" transactions on the child chain, which may be initiated - unilaterally. The most basic example of is a coinbase transaction + unilaterally. The most basic example of is a coinbase transaction implementing validator node incentives, but may also be app-specific. In these cases, it may be favourable for such transactions to be ordered in a specific manner, e.g., coinbase transactions will always be @@ -86,14 +86,14 @@ current proposer is passed to `BeginBlock`. It is much easier to relay these transactions directly to the Root Chain smart contract and/or maintain a "compressed" auxiliary chain comprised of Plasma-friendly blocks that 100% reflect the canonical (Tendermint) -blockchain. Unfortunately, this approach not idiomatic (i.e., utilises the +blockchain. Unfortunately, this approach not idiomatic (i.e., utilises the Tendermint consensus engine in unintended ways). Additionally, it does not allow the application developer to: - Control the _ordering_ of transactions in the proposed block (e.g., index 0, -or 0 to `n` for coinbase transactions) + or 0 to `n` for coinbase transactions) - Control the _number_ of transactions in the block (e.g., when a `deposit` -block is required) + block is required) Since determinism is of utmost importance in blockchain engineering, this approach, while more viable, should also not be considered as fit for production. @@ -163,9 +163,9 @@ Pending - Tendermint ABCI apps will be able to function as minimally viable Plasma chains. - It will thereby become possible to add an extension to `cosmos-sdk` to enable - ABCI apps to support both IBC and Plasma, maximising interop. + ABCI apps to support both IBC and Plasma, maximising interop. - ABCI apps will have great control and flexibility in managing blockchain state, - without having to resort to non-deterministic hacks and/or unsafe workarounds + without having to resort to non-deterministic hacks and/or unsafe workarounds ### Negative diff --git a/docs/architecture/adr-012-peer-transport.md b/docs/architecture/adr-012-peer-transport.md index 01a79c8a9..1cf4fb80b 100644 --- a/docs/architecture/adr-012-peer-transport.md +++ b/docs/architecture/adr-012-peer-transport.md @@ -9,8 +9,9 @@ handling. An artifact is the dependency of the Switch on `[config.P2PConfig`](https://github.com/tendermint/tendermint/blob/05a76fb517f50da27b4bfcdc7b4cf185fc61eff6/config/config.go#L272-L339). Addresses: -* [#2046](https://github.com/tendermint/tendermint/issues/2046) -* [#2047](https://github.com/tendermint/tendermint/issues/2047) + +- [#2046](https://github.com/tendermint/tendermint/issues/2046) +- [#2047](https://github.com/tendermint/tendermint/issues/2047) First iteraton in [#2067](https://github.com/tendermint/tendermint/issues/2067) @@ -29,15 +30,14 @@ transport implementation is responsible to filter establishing peers specific to its domain, for the default multiplexed implementation the following will apply: -* connections from our own node -* handshake fails -* upgrade to secret connection fails -* prevent duplicate ip -* prevent duplicate id -* nodeinfo incompatibility - +- connections from our own node +- handshake fails +- upgrade to secret connection fails +- prevent duplicate ip +- prevent duplicate id +- nodeinfo incompatibility -``` go +```go // PeerTransport proxies incoming and outgoing peer connections. type PeerTransport interface { // Accept returns a newly connected Peer. @@ -75,7 +75,7 @@ func NewMTransport( nodeAddr NetAddress, nodeInfo NodeInfo, nodeKey NodeKey, -) *multiplexTransport +) *multiplexTransport ``` ### Switch @@ -84,7 +84,7 @@ From now the Switch will depend on a fully setup `PeerTransport` to retrieve/reach out to its peers. As the more low-level concerns are pushed to the transport, we can omit passing the `config.P2PConfig` to the Switch. -``` go +```go func NewSwitch(transport PeerTransport, opts ...SwitchOption) *Switch ``` @@ -96,17 +96,17 @@ In Review. ### Positive -* free Switch from transport concerns - simpler implementation -* pluggable transport implementation - simpler test setup -* remove Switch dependency on P2PConfig - easier to test +- free Switch from transport concerns - simpler implementation +- pluggable transport implementation - simpler test setup +- remove Switch dependency on P2PConfig - easier to test ### Negative -* more setup for tests which depend on Switches +- more setup for tests which depend on Switches ### Neutral -* multiplexed will be the default implementation +- multiplexed will be the default implementation [0] These guards could be potentially extended to be pluggable much like middlewares to express different concerns required by differentally configured diff --git a/docs/architecture/adr-013-symmetric-crypto.md b/docs/architecture/adr-013-symmetric-crypto.md index 00442ab0d..69bfc2f29 100644 --- a/docs/architecture/adr-013-symmetric-crypto.md +++ b/docs/architecture/adr-013-symmetric-crypto.md @@ -14,22 +14,23 @@ to easily swap these out. ### How do we encrypt with AEAD's -AEAD's typically require a nonce in addition to the key. +AEAD's typically require a nonce in addition to the key. For the purposes we require symmetric cryptography for, we need encryption to be stateless. -Because of this we use random nonces. +Because of this we use random nonces. (Thus the AEAD must support random nonces) -We currently construct a random nonce, and encrypt the data with it. +We currently construct a random nonce, and encrypt the data with it. The returned value is `nonce || encrypted data`. The limitation of this is that does not provide a way to identify which algorithm was used in encryption. -Consequently decryption with multiple algoritms is sub-optimal. +Consequently decryption with multiple algoritms is sub-optimal. (You have to try them all) ## Decision -We should create the following two methods in a new `crypto/encoding/symmetric` package: +We should create the following two methods in a new `crypto/encoding/symmetric` package: + ```golang func Encrypt(aead cipher.AEAD, plaintext []byte) (ciphertext []byte, err error) func Decrypt(key []byte, ciphertext []byte) (plaintext []byte, err error) @@ -37,18 +38,19 @@ func Register(aead cipher.AEAD, algo_name string, NewAead func(key []byte) (ciph ``` This allows you to specify the algorithm in encryption, but not have to specify -it in decryption. +it in decryption. This is intended for ease of use in downstream applications, in addition to people looking at the file directly. One downside is that for the encrypt function you must have already initialized an AEAD, -but I don't really see this as an issue. +but I don't really see this as an issue. -If there is no error in encryption, Encrypt will return `algo_name || nonce || aead_ciphertext`. +If there is no error in encryption, Encrypt will return `algo_name || nonce || aead_ciphertext`. `algo_name` should be length prefixed, using standard varuint encoding. This will be binary data, but thats not a problem considering the nonce and ciphertext are also binary. -This solution requires a mapping from aead type to name. -We can achieve this via reflection. +This solution requires a mapping from aead type to name. +We can achieve this via reflection. + ```golang func getType(myvar interface{}) string { if t := reflect.TypeOf(myvar); t.Kind() == reflect.Ptr { @@ -58,7 +60,8 @@ func getType(myvar interface{}) string { } } ``` -Then we maintain a map from the name returned from `getType(aead)` to `algo_name`. + +Then we maintain a map from the name returned from `getType(aead)` to `algo_name`. In decryption, we read the `algo_name`, and then instantiate a new AEAD with the key. Then we call the AEAD's decrypt method on the provided nonce/ciphertext. @@ -81,13 +84,16 @@ Proposed. ## Consequences ### Positive -* Allows us to support new AEAD's, in a way that makes decryption easier -* Allows downstream users to add their own AEAD + +- Allows us to support new AEAD's, in a way that makes decryption easier +- Allows downstream users to add their own AEAD ### Negative -* We will have to break all private keys stored on disk. -They can be recovered using seed words, and upgrade scripts are simple. + +- We will have to break all private keys stored on disk. + They can be recovered using seed words, and upgrade scripts are simple. ### Neutral -* Caller has to instantiate the AEAD with the private key. -However it forces them to be aware of what signing algorithm they are using, which is a positive. \ No newline at end of file + +- Caller has to instantiate the AEAD with the private key. + However it forces them to be aware of what signing algorithm they are using, which is a positive. diff --git a/docs/architecture/adr-014-secp-malleability.md b/docs/architecture/adr-014-secp-malleability.md index 3351056c6..ce84c7eb7 100644 --- a/docs/architecture/adr-014-secp-malleability.md +++ b/docs/architecture/adr-014-secp-malleability.md @@ -22,21 +22,21 @@ Removing this second layer of signature malleability concerns could ease downstr ### ECDSA context Secp256k1 is ECDSA over a particular curve. -The signature is of the form `(r, s)`, where `s` is a field element. +The signature is of the form `(r, s)`, where `s` is a field element. (The particular field is the `Z_n`, where the elliptic curve has order `n`) However `(r, -s)` is also another valid solution. Note that anyone can negate a group element, and therefore can get this second signature. ## Decision -We can just distinguish a canonical form for the ECDSA signatures. +We can just distinguish a canonical form for the ECDSA signatures. Then we require that all ECDSA signatures be in the form which we defined as canonical. We reject signatures in non-canonical form. -A canonical form is rather easy to define and check. +A canonical form is rather easy to define and check. It would just be the smaller of the two values for `s`, defined lexicographically. This is a simple check, instead of checking if `s < n`, instead check `s <= (n - 1)/2`. -An example of another cryptosystem using this +An example of another cryptosystem using this is the parity definition here https://github.com/zkcrypto/pairing/pull/30#issuecomment-372910663. This is the same solution Ethereum has chosen for solving secp malleability. @@ -52,10 +52,12 @@ Proposed. ## Consequences ### Positive -* Lets us maintain the ability to expect a tx hash to appear in the blockchain. + +- Lets us maintain the ability to expect a tx hash to appear in the blockchain. ### Negative -* More work in all future implementations (Though this is a very simple check) -* Requires us to maintain another fork + +- More work in all future implementations (Though this is a very simple check) +- Requires us to maintain another fork ### Neutral diff --git a/docs/architecture/adr-015-crypto-encoding.md b/docs/architecture/adr-015-crypto-encoding.md index 67cce95f9..f6818d5d1 100644 --- a/docs/architecture/adr-015-crypto-encoding.md +++ b/docs/architecture/adr-015-crypto-encoding.md @@ -1,8 +1,8 @@ -# ADR 015: Crypto encoding +# ADR 015: Crypto encoding ## Context -We must standardize our method for encoding public keys and signatures on chain. +We must standardize our method for encoding public keys and signatures on chain. Currently we amino encode the public keys and signatures. The reason we are using amino here is primarily due to ease of support in parsing for other languages. @@ -54,9 +54,11 @@ When placed in state, signatures will still be amino encoded, but it will be the primitive type `[]byte` getting encoded. #### Ed25519 + Use the canonical representation for signatures. #### Secp256k1 + There isn't a clear canonical representation here. Signatures have two elements `r,s`. These bytes are encoded as `r || s`, where `r` and `s` are both exactly @@ -71,10 +73,13 @@ Needs decision on Enum types. ## Consequences ### Positive -* More space efficient signatures + +- More space efficient signatures ### Negative -* We have an amino dependency for cryptography. + +- We have an amino dependency for cryptography. ### Neutral -* No change to public keys \ No newline at end of file + +- No change to public keys diff --git a/docs/architecture/adr-016-protocol-versions.md b/docs/architecture/adr-016-protocol-versions.md index 636cb181c..4dba4e848 100644 --- a/docs/architecture/adr-016-protocol-versions.md +++ b/docs/architecture/adr-016-protocol-versions.md @@ -8,14 +8,14 @@ ## Changelog - 03-08-2018: Updates from discussion with Jae: - - ProtocolVersion contains Block/AppVersion, not Current/Next - - signal upgrades to Tendermint using EndBlock fields - - dont restrict peer compatibilty by version to simplify syncing old nodes + - ProtocolVersion contains Block/AppVersion, not Current/Next + - signal upgrades to Tendermint using EndBlock fields + - dont restrict peer compatibilty by version to simplify syncing old nodes - 28-07-2018: Updates from review - - split into two ADRs - one for protocol, one for chains - - include signalling for upgrades in header + - split into two ADRs - one for protocol, one for chains + - include signalling for upgrades in header - 16-07-2018: Initial draft - was originally joint ADR for protocol and chain -versions + versions ## Context @@ -59,18 +59,16 @@ to connect to peers with older version. ### BlockVersion - All tendermint hashed data-structures (headers, votes, txs, responses, etc.). - - Note the semantic meaning of a transaction may change according to the AppVersion, - but the way txs are merklized into the header is part of the BlockVersion + - Note the semantic meaning of a transaction may change according to the AppVersion, but the way txs are merklized into the header is part of the BlockVersion - It should be the least frequent/likely to change. - - Tendermint should be stabilizing - it's just Atomic Broadcast. - - We can start considering for Tendermint v2.0 in a year + - Tendermint should be stabilizing - it's just Atomic Broadcast. + - We can start considering for Tendermint v2.0 in a year - It's easy to determine the version of a block from its serialized form ### P2PVersion - All p2p and reactor messaging (messages, detectable behaviour) -- Will change gradually as reactors evolve to improve performance and support new features - - eg proposed new message types BatchTx in the mempool and HasBlockPart in the consensus +- Will change gradually as reactors evolve to improve performance and support new features - eg proposed new message types BatchTx in the mempool and HasBlockPart in the consensus - It's easy to determine the version of a peer from its first serialized message/s - New versions must be compatible with at least one old version to allow gradual upgrades @@ -79,10 +77,10 @@ to connect to peers with older version. - The ABCI state machine (txs, begin/endblock behaviour, commit hashing) - Behaviour and message types will change abruptly in the course of the life of a chain - Need to minimize complexity of the code for supporting different AppVersions at different heights -- Ideally, each version of the software supports only a *single* AppVersion at one time - - this means we checkout different versions of the software at different heights instead of littering the code - with conditionals - - minimize the number of data migrations required across AppVersion (ie. most AppVersion should be able to read the same state from disk as previous AppVersion). +- Ideally, each version of the software supports only a _single_ AppVersion at one time + - this means we checkout different versions of the software at different heights instead of littering the code + with conditionals + - minimize the number of data migrations required across AppVersion (ie. most AppVersion should be able to read the same state from disk as previous AppVersion). ## Ideal @@ -125,7 +123,6 @@ serve as a complete description of the consensus-critical protocol. Using the `NextVersion` field, proposer's can signal their readiness to upgrade to a new Block and/or App version. - ### NodeInfo NodeInfo should include a Version struct as its first field like: @@ -150,7 +147,6 @@ it's SemVer version - this is for convenience only. Eg. The other versions and ChainID will determine peer compatibility (described below). - ### ABCI Since the ABCI is responsible for keeping Tendermint and the App in sync, we @@ -280,7 +276,6 @@ checking out and installing new software versions and restarting the process. It would subscribe to the relevant upgrade event (needs to be implemented) and call `/unsafe_stop` at the correct height (of course only after getting approval from its user!) - ## Consequences ### Positive diff --git a/docs/architecture/adr-017-chain-versions.md b/docs/architecture/adr-017-chain-versions.md index 4be7e6e5e..7113dbaee 100644 --- a/docs/architecture/adr-017-chain-versions.md +++ b/docs/architecture/adr-017-chain-versions.md @@ -7,9 +7,9 @@ ## Changelog - 28-07-2018: Updates from review - - split into two ADRs - one for protocol, one for chains + - split into two ADRs - one for protocol, one for chains - 16-07-2018: Initial draft - was originally joint ADR for protocol and chain -versions + versions ## Context @@ -41,20 +41,19 @@ Peers only connect to other peers with the same NetworkName. We need to support existing networks upgrading and forking, wherein they may do any of: - - revert back to some height, continue with the same versions but new blocks - - arbitrarily mutate state at some height, continue with the same versions (eg. Dao Fork) - - change the AppVersion at some height + - revert back to some height, continue with the same versions but new blocks + - arbitrarily mutate state at some height, continue with the same versions (eg. Dao Fork) + - change the AppVersion at some height Note because of Tendermint's voting power threshold rules, a chain can only be extended under the "original" rules and under the new rules if 1/3 or more is double signing, which is expressly prohibited, and is supposed to result in their punishment on both chains. Since they can censor the punishment, the chain is expected to be hardforked to remove the validators. Thus, if both branches are to continue after a fork, they will each require a new identifier, and the old chain identifier will be retired (ie. only useful for syncing history, not for new blocks).. - TODO: explain how to handle slashing when chain id changed! +TODO: explain how to handle slashing when chain id changed! We need a consistent way to describe forks. - ## Proposal ### ChainDescription @@ -92,9 +91,9 @@ ChainDescription = /x// ``` Where - - ChainID is the ChainID from the previous ChainDescription (ie. its hash) - - `x` denotes that a change occured - - `Height` is the height the change occured - - ForkDescription has the same form as ChainDescription but for the fork - - this allows forks to specify new versions for tendermint or the app, as well as arbitrary changes to the state or validator set +- ChainID is the ChainID from the previous ChainDescription (ie. its hash) +- `x` denotes that a change occured +- `Height` is the height the change occured +- ForkDescription has the same form as ChainDescription but for the fork +- this allows forks to specify new versions for tendermint or the app, as well as arbitrary changes to the state or validator set diff --git a/docs/architecture/adr-019-multisigs.md b/docs/architecture/adr-019-multisigs.md index 98bb5ec0c..3d1c5ba87 100644 --- a/docs/architecture/adr-019-multisigs.md +++ b/docs/architecture/adr-019-multisigs.md @@ -1,6 +1,6 @@ # ADR 019: Encoding standard for Multisignatures -## Changelog +## Changelog 06-08-2018: Minor updates @@ -10,9 +10,9 @@ ## Context -Multisignatures, or technically _Accountable Subgroup Multisignatures_ (ASM), -are signature schemes which enable any subgroup of a set of signers to sign any message, -and reveal to the verifier exactly who the signers were. +Multisignatures, or technically _Accountable Subgroup Multisignatures_ (ASM), +are signature schemes which enable any subgroup of a set of signers to sign any message, +and reveal to the verifier exactly who the signers were. This allows for complex conditionals of when to validate a signature. Suppose the set of signers is of size _n_. @@ -22,7 +22,7 @@ this becomes what is commonly reffered to as a _k of n multisig_ in Bitcoin. This ADR specifies the encoding standard for general accountable subgroup multisignatures, k of n accountable subgroup multisignatures, and its weighted variant. -In the future, we can also allow for more complex conditionals on the accountable subgroup. +In the future, we can also allow for more complex conditionals on the accountable subgroup. ## Proposed Solution @@ -42,6 +42,7 @@ type ThresholdMultiSignaturePubKey struct { // K of N threshold multisig Pubkeys []crypto.Pubkey `json:"pubkeys"` } ``` + We will derive N from the length of pubkeys. (For spatial efficiency in encoding) `Verify` will expect an `[]byte` encoded version of the Multisignature. @@ -56,7 +57,7 @@ the kth public key on the message. Address will be `Hash(amino_encoded_pubkey)` The reason this doesn't use `log_8(n)` bytes per signer is because that heavily optimizes for the case where a very small number of signers are required. -e.g. for `n` of size `24`, that would only be more space efficient for `k < 3`. +e.g. for `n` of size `24`, that would only be more space efficient for `k < 3`. This seems less likely, and that it should not be the case optimized for. #### Weighted threshold signature @@ -70,17 +71,19 @@ type WeightedThresholdMultiSignaturePubKey struct { Pubkeys []crypto.Pubkey `json:"pubkeys"` } ``` + Weights and Pubkeys must be of the same length. -Everything else proceeds identically to the K of N multisig, +Everything else proceeds identically to the K of N multisig, except the multisig fails if the sum of the weights is less than the threshold. #### Multisignature The inter-mediate phase of the signatures (as it accrues more signatures) will be the following struct: + ```golang type Multisignature struct { BitArray CryptoBitArray // Documented later - Sigs [][]byte + Sigs [][]byte ``` It is important to recall that each private key will output a signature on the provided message itself. @@ -88,24 +91,29 @@ So no signing algorithm ever outputs the multisignature. The UI will take a signature, cast into a multisignature, and then keep adding new signatures into it, and when done marshal into `[]byte`. This will require the following helper methods: + ```golang func SigToMultisig(sig []byte, n int) func GetIndex(pk crypto.Pubkey, []crypto.Pubkey) func AddSignature(sig Signature, index int, multiSig *Multisignature) ``` + The multisignature will be converted to an `[]byte` using amino.MarshalBinaryBare. \* #### Bit Array -We would be using a new implementation of a bitarray. The struct it would be encoded/decoded from is + +We would be using a new implementation of a bitarray. The struct it would be encoded/decoded from is + ```golang type CryptoBitArray struct { - ExtraBitsStored byte `json:"extra_bits"` // The number of extra bits in elems. + ExtraBitsStored byte `json:"extra_bits"` // The number of extra bits in elems. Elems []byte `json:"elems"` } ``` + The reason for not using the BitArray currently implemented in `libs/common/bit_array.go` is that it is less space efficient, due to a space / time trade-off. -Evidence for this is outlined in [this issue](https://github.com/tendermint/tendermint/issues/2077). +Evidence for this is outlined in [this issue](https://github.com/tendermint/tendermint/issues/2077). In the multisig, we will not be performing arithmetic operations, so there is no performance increase with the current implementation, @@ -122,7 +130,7 @@ Again the implementation of this space saving feature is straight forward. ### Encoding the structs -We will use straight forward amino encoding. This is chosen for ease of compatibility in other languages. +We will use straight forward amino encoding. This is chosen for ease of compatibility in other languages. ### Future points of discussion @@ -133,18 +141,20 @@ Aggregation of pubkeys / sigs in Schnorr sigs / BLS sigs is not backwards compat ## Status -Proposed. +Proposed. ## Consequences ### Positive -* Supports multisignatures, in a way that won't require any special cases in our downstream verification code. -* Easy to serialize / deserialize -* Unbounded number of signers + +- Supports multisignatures, in a way that won't require any special cases in our downstream verification code. +- Easy to serialize / deserialize +- Unbounded number of signers ### Negative -* Larger codebase, however this should reside in a subfolder of tendermint/crypto, as it provides no new interfaces. (Ref #https://github.com/tendermint/go-crypto/issues/136) -* Space inefficient due to utilization of amino encoding -* Suggested implementation requires a new struct for every ASM. + +- Larger codebase, however this should reside in a subfolder of tendermint/crypto, as it provides no new interfaces. (Ref #https://github.com/tendermint/go-crypto/issues/136) +- Space inefficient due to utilization of amino encoding +- Suggested implementation requires a new struct for every ASM. ### Neutral diff --git a/docs/architecture/adr-template.md b/docs/architecture/adr-template.md index 2303490ad..d47c7f558 100644 --- a/docs/architecture/adr-template.md +++ b/docs/architecture/adr-template.md @@ -6,7 +6,6 @@ ## Status - ## Consequences ### Positive diff --git a/docs/package.json b/docs/package.json index c76bb37c4..d45ba539b 100644 --- a/docs/package.json +++ b/docs/package.json @@ -3,7 +3,9 @@ "prettier": "^1.13.7", "remark-cli": "^5.0.0", "remark-lint-no-dead-urls": "^0.3.0", - "textlint": "^10.2.1" + "remark-lint-write-good": "^1.0.3", + "textlint": "^10.2.1", + "textlint-rule-stop-words": "^1.0.3" }, "name": "tendermint", "description": "Tendermint Core Documentation", @@ -31,7 +33,8 @@ "homepage": "https://tendermint.com/docs/", "remarkConfig": { "plugins": [ - "remark-lint-no-dead-urls" + "remark-lint-no-dead-urls", + "remark-lint-write-good" ] } } diff --git a/docs/spec/README.md b/docs/spec/README.md index ab689d9d6..82ed9717f 100644 --- a/docs/spec/README.md +++ b/docs/spec/README.md @@ -57,7 +57,7 @@ is malicious or faulty. A commit in Tendermint is a set of signed messages from more than 2/3 of the total weight of the current Validator set. Validators take turns proposing blocks and voting on them. Once enough votes are received, the block is considered -committed. These votes are included in the *next* block as proof that the previous block +committed. These votes are included in the _next_ block as proof that the previous block was committed - they cannot be included in the current block, as that block has already been created. @@ -71,8 +71,8 @@ of the latest state of the blockchain. To achieve this, it embeds cryptographic commitments to certain information in the block "header". This information includes the contents of the block (eg. the transactions), the validator set committing the block, as well as the various results returned by the application. -Note, however, that block execution only occurs *after* a block is committed. -Thus, application results can only be included in the *next* block. +Note, however, that block execution only occurs _after_ a block is committed. +Thus, application results can only be included in the _next_ block. Also note that information like the transaction results and the validator set are never directly included in the block - only their cryptographic digests (Merkle roots) are. diff --git a/docs/spec/blockchain/blockchain.md b/docs/spec/blockchain/blockchain.md index eab3acdf9..e1e0c3fe8 100644 --- a/docs/spec/blockchain/blockchain.md +++ b/docs/spec/blockchain/blockchain.md @@ -104,8 +104,8 @@ type Vote struct { ``` There are two types of votes: -a *prevote* has `vote.Type == 1` and -a *precommit* has `vote.Type == 2`. +a _prevote_ has `vote.Type == 1` and +a _precommit_ has `vote.Type == 2`. ## Signature @@ -162,10 +162,10 @@ We refer to certain globally available objects: `prevBlock` is the `block` at the previous height, and `state` keeps track of the validator set, the consensus parameters and other results from the application. At the point when `block` is the block under consideration, -the current version of the `state` corresponds to the state -after executing transactions from the `prevBlock`. +the current version of the `state` corresponds to the state +after executing transactions from the `prevBlock`. Elements of an object are accessed as expected, -ie. `block.Header`. +ie. `block.Header`. See [here](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/state.md) for the definition of `state`. ### Header @@ -288,6 +288,7 @@ This can be used to validate the `LastCommit` included in the next block. ```go block.NextValidatorsHash == SimpleMerkleRoot(state.NextValidators) ``` + Simple Merkle root of the next validator set that will be the validator set that commits the next block. Modifications to the validator set are defined by the application. @@ -427,11 +428,8 @@ Execute(s State, app ABCIApp, block Block) State { AppHash: AppHash, LastValidators: state.Validators, Validators: state.NextValidators, - NextValidators: UpdateValidators(state.NextValidators, ValidatorChanges), + NextValidators: UpdateValidators(state.NextValidators, ValidatorChanges), ConsensusParams: UpdateConsensusParams(state.ConsensusParams, ConsensusParamChanges), } } - ``` - - diff --git a/docs/spec/blockchain/encoding.md b/docs/spec/blockchain/encoding.md index 49c88475b..a0acad393 100644 --- a/docs/spec/blockchain/encoding.md +++ b/docs/spec/blockchain/encoding.md @@ -48,33 +48,33 @@ spec](https://github.com/tendermint/go-amino#computing-the-prefix-and-disambigua In what follows, we provide the type names and prefix bytes directly. Notice that when encoding byte-arrays, the length of the byte-array is appended -to the PrefixBytes. Thus the encoding of a byte array becomes ` - `. In other words, to encode any type listed below you do not need to be +to the PrefixBytes. Thus the encoding of a byte array becomes ` `. In other words, to encode any type listed below you do not need to be familiar with amino encoding. You can simply use below table and concatenate Prefix || Length (of raw bytes) || raw bytes ( while || stands for byte concatenation here). -| Type | Name | Prefix | Length | Notes | -| ---- | ---- | ------ | ----- | ------ | -| PubKeyEd25519 | tendermint/PubKeyEd25519 | 0x1624DE64 | 0x20 | | -| PubKeySecp256k1 | tendermint/PubKeySecp256k1 | 0xEB5AE987 | 0x21 | | -| PrivKeyEd25519 | tendermint/PrivKeyEd25519 | 0xA3288910 | 0x40 | | -| PrivKeySecp256k1 | tendermint/PrivKeySecp256k1 | 0xE1B0F79B | 0x20 | | -| SignatureEd25519 | tendermint/SignatureEd25519 | 0x2031EA53 | 0x40 | | +| Type | Name | Prefix | Length | Notes | +| ------------------ | ----------------------------- | ---------- | -------- | ----- | +| PubKeyEd25519 | tendermint/PubKeyEd25519 | 0x1624DE64 | 0x20 | | +| PubKeySecp256k1 | tendermint/PubKeySecp256k1 | 0xEB5AE987 | 0x21 | | +| PrivKeyEd25519 | tendermint/PrivKeyEd25519 | 0xA3288910 | 0x40 | | +| PrivKeySecp256k1 | tendermint/PrivKeySecp256k1 | 0xE1B0F79B | 0x20 | | +| SignatureEd25519 | tendermint/SignatureEd25519 | 0x2031EA53 | 0x40 | | | SignatureSecp256k1 | tendermint/SignatureSecp256k1 | 0x7FC4A495 | variable | + | ### Examples 1. For example, the 33-byte (or 0x21-byte in hex) Secp256k1 pubkey -`020BD40F225A57ED383B440CF073BC5539D0341F5767D2BF2D78406D00475A2EE9` -would be encoded as -`EB5AE98221020BD40F225A57ED383B440CF073BC5539D0341F5767D2BF2D78406D00475A2EE9` + `020BD40F225A57ED383B440CF073BC5539D0341F5767D2BF2D78406D00475A2EE9` + would be encoded as + `EB5AE98221020BD40F225A57ED383B440CF073BC5539D0341F5767D2BF2D78406D00475A2EE9` 2. For example, the variable size Secp256k1 signature (in this particular example 70 or 0x46 bytes) -`304402201CD4B8C764D2FD8AF23ECFE6666CA8A53886D47754D951295D2D311E1FEA33BF02201E0F906BB1CF2C30EAACFFB032A7129358AFF96B9F79B06ACFFB18AC90C2ADD7` -would be encoded as -`16E1FEEA46304402201CD4B8C764D2FD8AF23ECFE6666CA8A53886D47754D951295D2D311E1FEA33BF02201E0F906BB1CF2C30EAACFFB032A7129358AFF96B9F79B06ACFFB18AC90C2ADD7` + `304402201CD4B8C764D2FD8AF23ECFE6666CA8A53886D47754D951295D2D311E1FEA33BF02201E0F906BB1CF2C30EAACFFB032A7129358AFF96B9F79B06ACFFB18AC90C2ADD7` + would be encoded as + `16E1FEEA46304402201CD4B8C764D2FD8AF23ECFE6666CA8A53886D47754D951295D2D311E1FEA33BF02201E0F906BB1CF2C30EAACFFB032A7129358AFF96B9F79B06ACFFB18AC90C2ADD7` ### Addresses @@ -152,28 +152,27 @@ func MakeParts(obj interface{}, partSize int) []Part For an overview of Merkle trees, see [wikipedia](https://en.wikipedia.org/wiki/Merkle_tree) - A Simple Tree is a simple compact binary tree for a static list of items. Simple Merkle trees are used in numerous places in Tendermint to compute a cryptographic digest of a data structure. In a Simple Tree, the transactions and validation signatures of a block are hashed using this simple merkle tree logic. If the number of items is not a power of two, the tree will not be full and some leaf nodes will be at different levels. Simple Tree tries to keep both sides of the tree the same size, but the left side may be one -greater, for example: +greater, for example: ``` - Simple Tree with 6 items Simple Tree with 7 items - - * * - / \ / \ - / \ / \ - / \ / \ - / \ / \ - * * * * - / \ / \ / \ / \ - / \ / \ / \ / \ - / \ / \ / \ / \ + Simple Tree with 6 items Simple Tree with 7 items + + * * + / \ / \ + / \ / \ + / \ / \ + / \ / \ + * * * * + / \ / \ / \ / \ + / \ / \ / \ / \ + / \ / \ / \ / \ * h2 * h5 * * * h6 - / \ / \ / \ / \ / \ + / \ / \ / \ / \ / \ h0 h1 h3 h4 h0 h1 h2 h3 h4 h5 ``` @@ -224,7 +223,6 @@ For `[]struct` arguments, we compute a `[][]byte` by hashing the individual `str Proof that a leaf is in a Merkle tree consists of a simple structure: - ``` type SimpleProof struct { Aunts [][]byte @@ -265,8 +263,8 @@ func computeHashFromAunts(index, total int, leafHash []byte, innerHashes [][]byt The Simple Tree is used to merkelize a list of items, so to merkelize a (short) dictionary of key-value pairs, encode the dictionary as an -ordered list of ``KVPair`` structs. The block hash is such a hash -derived from all the fields of the block ``Header``. The state hash is +ordered list of `KVPair` structs. The block hash is such a hash +derived from all the fields of the block `Header`. The state hash is similarly derived. ### IAVL+ Tree @@ -300,7 +298,6 @@ For instance, an ED25519 PubKey would look like: Where the `"value"` is the base64 encoding of the raw pubkey bytes, and the `"type"` is the full disfix bytes for Ed25519 pubkeys. - ### Signed Messages Signed messages (eg. votes, proposals) in the consensus are encoded using Amino-JSON, rather than in the standard binary format. diff --git a/docs/spec/blockchain/state.md b/docs/spec/blockchain/state.md index df86cd450..726015eac 100644 --- a/docs/spec/blockchain/state.md +++ b/docs/spec/blockchain/state.md @@ -75,7 +75,6 @@ func TotalVotingPower(vals []Validators) int64{ } ``` - ### ConsensusParams TODO diff --git a/docs/spec/consensus/bft-time.md b/docs/spec/consensus/bft-time.md index a005e9040..c53c80429 100644 --- a/docs/spec/consensus/bft-time.md +++ b/docs/spec/consensus/bft-time.md @@ -1,56 +1,53 @@ -# BFT time in Tendermint +# BFT time in Tendermint -Tendermint provides a deterministic, Byzantine fault-tolerant, source of time. -Time in Tendermint is defined with the Time field of the block header. +Tendermint provides a deterministic, Byzantine fault-tolerant, source of time. +Time in Tendermint is defined with the Time field of the block header. It satisfies the following properties: -- Time Monotonicity: Time is monotonically increasing, i.e., given -a header H1 for height h1 and a header H2 for height `h2 = h1 + 1`, `H1.Time < H2.Time`. -- Time Validity: Given a set of Commit votes that forms the `block.LastCommit` field, a range of -valid values for the Time field of the block header is defined only by -Precommit messages (from the LastCommit field) sent by correct processes, i.e., -a faulty process cannot arbitrarily increase the Time value. +- Time Monotonicity: Time is monotonically increasing, i.e., given + a header H1 for height h1 and a header H2 for height `h2 = h1 + 1`, `H1.Time < H2.Time`. +- Time Validity: Given a set of Commit votes that forms the `block.LastCommit` field, a range of + valid values for the Time field of the block header is defined only by + Precommit messages (from the LastCommit field) sent by correct processes, i.e., + a faulty process cannot arbitrarily increase the Time value. -In the context of Tendermint, time is of type int64 and denotes UNIX time in milliseconds, i.e., -corresponds to the number of milliseconds since January 1, 1970. Before defining rules that need to be enforced by the +In the context of Tendermint, time is of type int64 and denotes UNIX time in milliseconds, i.e., +corresponds to the number of milliseconds since January 1, 1970. Before defining rules that need to be enforced by the Tendermint consensus protocol, so the properties above holds, we introduce the following definition: - median of a set of `Vote` messages is equal to the median of `Vote.Time` fields of the corresponding `Vote` messages, -where the value of `Vote.Time` is counted number of times proportional to the process voting power. As in Tendermint -the voting power is not uniform (one process one vote), a vote message is actually an aggregator of the same votes whose -number is equal to the voting power of the process that has casted the corresponding votes message. + where the value of `Vote.Time` is counted number of times proportional to the process voting power. As in Tendermint + the voting power is not uniform (one process one vote), a vote message is actually an aggregator of the same votes whose + number is equal to the voting power of the process that has casted the corresponding votes message. Let's consider the following example: - - we have four processes p1, p2, p3 and p4, with the following voting power distribution (p1, 23), (p2, 27), (p3, 10) -and (p4, 10). The total voting power is 70 (`N = 3f+1`, where `N` is the total voting power, and `f` is the maximum voting -power of the faulty processes), so we assume that the faulty processes have at most 23 of voting power. -Furthermore, we have the following vote messages in some LastCommit field (we ignore all fields except Time field): - - (p1, 100), (p2, 98), (p3, 1000), (p4, 500). We assume that p3 and p4 are faulty processes. Let's assume that the - `block.LastCommit` message contains votes of processes p2, p3 and p4. Median is then chosen the following way: - the value 98 is counted 27 times, the value 1000 is counted 10 times and the value 500 is counted also 10 times. - So the median value will be the value 98. No matter what set of messages with at least `2f+1` voting power we - choose, the median value will always be between the values sent by correct processes. - -We ensure Time Monotonicity and Time Validity properties by the following rules: - -- let rs denotes `RoundState` (consensus internal state) of some process. Then -`rs.ProposalBlock.Header.Time == median(rs.LastCommit) && -rs.Proposal.Timestamp == rs.ProposalBlock.Header.Time`. - -- Furthermore, when creating the `vote` message, the following rules for determining `vote.Time` field should hold: - - - if `rs.Proposal` is defined then - `vote.Time = max(rs.Proposal.Timestamp + 1, time.Now())`, where `time.Now()` - denotes local Unix time in milliseconds. - - - if `rs.Proposal` is not defined and `rs.Votes` contains +2/3 of the corresponding vote messages (votes for the - current height and round, and with the corresponding type (`Prevote` or `Precommit`)), then - - `vote.Time = max(median(getVotes(rs.Votes, vote.Height, vote.Round, vote.Type)), time.Now())`, - - where `getVotes` function returns the votes for particular `Height`, `Round` and `Type`. - The second rule is relevant for the case when a process jumps to a higher round upon receiving +2/3 votes for a higher - round, but the corresponding `Proposal` message for the higher round hasn't been received yet. +- we have four processes p1, p2, p3 and p4, with the following voting power distribution (p1, 23), (p2, 27), (p3, 10) + and (p4, 10). The total voting power is 70 (`N = 3f+1`, where `N` is the total voting power, and `f` is the maximum voting + power of the faulty processes), so we assume that the faulty processes have at most 23 of voting power. + Furthermore, we have the following vote messages in some LastCommit field (we ignore all fields except Time field): - (p1, 100), (p2, 98), (p3, 1000), (p4, 500). We assume that p3 and p4 are faulty processes. Let's assume that the + `block.LastCommit` message contains votes of processes p2, p3 and p4. Median is then chosen the following way: + the value 98 is counted 27 times, the value 1000 is counted 10 times and the value 500 is counted also 10 times. + So the median value will be the value 98. No matter what set of messages with at least `2f+1` voting power we + choose, the median value will always be between the values sent by correct processes. +We ensure Time Monotonicity and Time Validity properties by the following rules: + +- let rs denotes `RoundState` (consensus internal state) of some process. Then + `rs.ProposalBlock.Header.Time == median(rs.LastCommit) && rs.Proposal.Timestamp == rs.ProposalBlock.Header.Time`. + +- Furthermore, when creating the `vote` message, the following rules for determining `vote.Time` field should hold: + + - if `rs.Proposal` is defined then + `vote.Time = max(rs.Proposal.Timestamp + 1, time.Now())`, where `time.Now()` + denotes local Unix time in milliseconds. + + - if `rs.Proposal` is not defined and `rs.Votes` contains +2/3 of the corresponding vote messages (votes for the + current height and round, and with the corresponding type (`Prevote` or `Precommit`)), then + + `vote.Time = max(median(getVotes(rs.Votes, vote.Height, vote.Round, vote.Type)), time.Now())`, + + where `getVotes` function returns the votes for particular `Height`, `Round` and `Type`. + The second rule is relevant for the case when a process jumps to a higher round upon receiving +2/3 votes for a higher + round, but the corresponding `Proposal` message for the higher round hasn't been received yet. diff --git a/docs/spec/consensus/consensus.md b/docs/spec/consensus/consensus.md index d6804779c..842c73a68 100644 --- a/docs/spec/consensus/consensus.md +++ b/docs/spec/consensus/consensus.md @@ -2,31 +2,31 @@ ## Terms -- The network is composed of optionally connected *nodes*. Nodes - directly connected to a particular node are called *peers*. -- The consensus process in deciding the next block (at some *height* - `H`) is composed of one or many *rounds*. -- `NewHeight`, `Propose`, `Prevote`, `Precommit`, and `Commit` - represent state machine states of a round. (aka `RoundStep` or - just "step"). -- A node is said to be *at* a given height, round, and step, or at - `(H,R,S)`, or at `(H,R)` in short to omit the step. -- To *prevote* or *precommit* something means to broadcast a [prevote - vote](https://godoc.org/github.com/tendermint/tendermint/types#Vote) - or [first precommit - vote](https://godoc.org/github.com/tendermint/tendermint/types#FirstPrecommit) - for something. -- A vote *at* `(H,R)` is a vote signed with the bytes for `H` and `R` - included in its [sign-bytes](block-structure.html#vote-sign-bytes). -- *+2/3* is short for "more than 2/3" -- *1/3+* is short for "1/3 or more" -- A set of +2/3 of prevotes for a particular block or `` at - `(H,R)` is called a *proof-of-lock-change* or *PoLC* for short. +- The network is composed of optionally connected _nodes_. Nodes + directly connected to a particular node are called _peers_. +- The consensus process in deciding the next block (at some _height_ + `H`) is composed of one or many _rounds_. +- `NewHeight`, `Propose`, `Prevote`, `Precommit`, and `Commit` + represent state machine states of a round. (aka `RoundStep` or + just "step"). +- A node is said to be _at_ a given height, round, and step, or at + `(H,R,S)`, or at `(H,R)` in short to omit the step. +- To _prevote_ or _precommit_ something means to broadcast a [prevote + vote](https://godoc.org/github.com/tendermint/tendermint/types#Vote) + or [first precommit + vote](https://godoc.org/github.com/tendermint/tendermint/types#FirstPrecommit) + for something. +- A vote _at_ `(H,R)` is a vote signed with the bytes for `H` and `R` + included in its [sign-bytes](block-structure.html#vote-sign-bytes). +- _+2/3_ is short for "more than 2/3" +- _1/3+_ is short for "1/3 or more" +- A set of +2/3 of prevotes for a particular block or `` at + `(H,R)` is called a _proof-of-lock-change_ or _PoLC_ for short. ## State Machine Overview At each height of the blockchain a round-based protocol is run to -determine the next block. Each round is composed of three *steps* +determine the next block. Each round is composed of three _steps_ (`Propose`, `Prevote`, and `Precommit`), along with two special steps `Commit` and `NewHeight`. @@ -36,22 +36,22 @@ In the optimal scenario, the order of steps is: NewHeight -> (Propose -> Prevote -> Precommit)+ -> Commit -> NewHeight ->... ``` -The sequence `(Propose -> Prevote -> Precommit)` is called a *round*. +The sequence `(Propose -> Prevote -> Precommit)` is called a _round_. There may be more than one round required to commit a block at a given height. Examples for why more rounds may be required include: -- The designated proposer was not online. -- The block proposed by the designated proposer was not valid. -- The block proposed by the designated proposer did not propagate - in time. -- The block proposed was valid, but +2/3 of prevotes for the proposed - block were not received in time for enough validator nodes by the - time they reached the `Precommit` step. Even though +2/3 of prevotes - are necessary to progress to the next step, at least one validator - may have voted `` or maliciously voted for something else. -- The block proposed was valid, and +2/3 of prevotes were received for - enough nodes, but +2/3 of precommits for the proposed block were not - received for enough validator nodes. +- The designated proposer was not online. +- The block proposed by the designated proposer was not valid. +- The block proposed by the designated proposer did not propagate + in time. +- The block proposed was valid, but +2/3 of prevotes for the proposed + block were not received in time for enough validator nodes by the + time they reached the `Precommit` step. Even though +2/3 of prevotes + are necessary to progress to the next step, at least one validator + may have voted `` or maliciously voted for something else. +- The block proposed was valid, and +2/3 of prevotes were received for + enough nodes, but +2/3 of precommits for the proposed block were not + received for enough validator nodes. Some of these problems are resolved by moving onto the next round & proposer. Others are resolved by increasing certain round timeout @@ -80,14 +80,13 @@ parameters over each successive round. +--------------------------------------------------------------------+ ``` -Background Gossip -================= +# Background Gossip A node may not have a corresponding validator private key, but it nevertheless plays an active role in the consensus process by relaying relevant meta-data, proposals, blocks, and votes to its peers. A node that has the private keys of an active validator and is engaged in -signing votes is called a *validator-node*. All nodes (not just +signing votes is called a _validator-node_. All nodes (not just validator-nodes) have an associated state (the current height, round, and step) and work to make progress. @@ -97,21 +96,21 @@ epidemic gossip protocol is implemented among some of these channels to bring peers up to speed on the most recent state of consensus. For example, -- Nodes gossip `PartSet` parts of the current round's proposer's - proposed block. A LibSwift inspired algorithm is used to quickly - broadcast blocks across the gossip network. -- Nodes gossip prevote/precommit votes. A node `NODE_A` that is ahead - of `NODE_B` can send `NODE_B` prevotes or precommits for `NODE_B`'s - current (or future) round to enable it to progress forward. -- Nodes gossip prevotes for the proposed PoLC (proof-of-lock-change) - round if one is proposed. -- Nodes gossip to nodes lagging in blockchain height with block - [commits](https://godoc.org/github.com/tendermint/tendermint/types#Commit) - for older blocks. -- Nodes opportunistically gossip `HasVote` messages to hint peers what - votes it already has. -- Nodes broadcast their current state to all neighboring peers. (but - is not gossiped further) +- Nodes gossip `PartSet` parts of the current round's proposer's + proposed block. A LibSwift inspired algorithm is used to quickly + broadcast blocks across the gossip network. +- Nodes gossip prevote/precommit votes. A node `NODE_A` that is ahead + of `NODE_B` can send `NODE_B` prevotes or precommits for `NODE_B`'s + current (or future) round to enable it to progress forward. +- Nodes gossip prevotes for the proposed PoLC (proof-of-lock-change) + round if one is proposed. +- Nodes gossip to nodes lagging in blockchain height with block + [commits](https://godoc.org/github.com/tendermint/tendermint/types#Commit) + for older blocks. +- Nodes opportunistically gossip `HasVote` messages to hint peers what + votes it already has. +- Nodes broadcast their current state to all neighboring peers. (but + is not gossiped further) There's more, but let's not get ahead of ourselves here. @@ -144,14 +143,14 @@ and all prevotes at `PoLC-Round`. --> goto `Prevote(H,R)` - After Upon entering `Prevote`, each validator broadcasts its prevote vote. -- First, if the validator is locked on a block since `LastLockRound` - but now has a PoLC for something else at round `PoLC-Round` where - `LastLockRound < PoLC-Round < R`, then it unlocks. -- If the validator is still locked on a block, it prevotes that. -- Else, if the proposed block from `Propose(H,R)` is good, it - prevotes that. -- Else, if the proposal is invalid or wasn't received on time, it - prevotes ``. +- First, if the validator is locked on a block since `LastLockRound` + but now has a PoLC for something else at round `PoLC-Round` where + `LastLockRound < PoLC-Round < R`, then it unlocks. +- If the validator is still locked on a block, it prevotes that. +- Else, if the proposed block from `Propose(H,R)` is good, it + prevotes that. +- Else, if the proposal is invalid or wasn't received on time, it + prevotes ``. The `Prevote` step ends: - After +2/3 prevotes for a particular block or ``. -->; goto `Precommit(H,R)` - After `timeoutPrevote` after @@ -161,11 +160,12 @@ receiving any +2/3 prevotes. --> goto `Precommit(H,R)` - After ### Precommit Step (height:H,round:R) Upon entering `Precommit`, each validator broadcasts its precommit vote. + - If the validator has a PoLC at `(H,R)` for a particular block `B`, it -(re)locks (or changes lock to) and precommits `B` and sets -`LastLockRound = R`. - Else, if the validator has a PoLC at `(H,R)` for -``, it unlocks and precommits ``. - Else, it keeps the lock -unchanged and precommits ``. + (re)locks (or changes lock to) and precommits `B` and sets + `LastLockRound = R`. - Else, if the validator has a PoLC at `(H,R)` for + ``, it unlocks and precommits ``. - Else, it keeps the lock + unchanged and precommits ``. A precommit for `` means "I didn’t see a PoLC for this round, but I did get +2/3 prevotes and waited a bit". @@ -177,24 +177,24 @@ conditions](#common-exit-conditions) ### Common exit conditions -- After +2/3 precommits for a particular block. --> goto - `Commit(H)` -- After any +2/3 prevotes received at `(H,R+x)`. --> goto - `Prevote(H,R+x)` -- After any +2/3 precommits received at `(H,R+x)`. --> goto - `Precommit(H,R+x)` +- After +2/3 precommits for a particular block. --> goto + `Commit(H)` +- After any +2/3 prevotes received at `(H,R+x)`. --> goto + `Prevote(H,R+x)` +- After any +2/3 precommits received at `(H,R+x)`. --> goto + `Precommit(H,R+x)` ### Commit Step (height:H) -- Set `CommitTime = now()` -- Wait until block is received. --> goto `NewHeight(H+1)` +- Set `CommitTime = now()` +- Wait until block is received. --> goto `NewHeight(H+1)` ### NewHeight Step (height:H) -- Move `Precommits` to `LastCommit` and increment height. -- Set `StartTime = CommitTime+timeoutCommit` -- Wait until `StartTime` to receive straggler commits. --> goto - `Propose(H,0)` +- Move `Precommits` to `LastCommit` and increment height. +- Set `StartTime = CommitTime+timeoutCommit` +- Wait until `StartTime` to receive straggler commits. --> goto + `Propose(H,0)` ## Proofs @@ -236,20 +236,20 @@ Further, define the JSet at height `H` of a set of validators `VSet` to be the union of the JSets for each validator in `VSet`. For a given commit by honest validators at round `R` for block `B` we can construct a JSet to justify the commit for `B` at `R`. We say that a JSet -*justifies* a commit at `(H,R)` if all the committers (validators in the +_justifies_ a commit at `(H,R)` if all the committers (validators in the commit-set) are each justified in the JSet with no duplicitous vote signatures (by the committers). -- **Lemma**: When a fork is detected by the existence of two - conflicting [commits](./validators.html#commiting-a-block), the - union of the JSets for both commits (if they can be compiled) must - include double-signing by at least 1/3+ of the validator set. - **Proof**: The commit cannot be at the same round, because that - would immediately imply double-signing by 1/3+. Take the union of - the JSets of both commits. If there is no double-signing by at least - 1/3+ of the validator set in the union, then no honest validator - could have precommitted any different block after the first commit. - Yet, +2/3 did. Reductio ad absurdum. +- **Lemma**: When a fork is detected by the existence of two + conflicting [commits](./validators.html#commiting-a-block), the + union of the JSets for both commits (if they can be compiled) must + include double-signing by at least 1/3+ of the validator set. + **Proof**: The commit cannot be at the same round, because that + would immediately imply double-signing by 1/3+. Take the union of + the JSets of both commits. If there is no double-signing by at least + 1/3+ of the validator set in the union, then no honest validator + could have precommitted any different block after the first commit. + Yet, +2/3 did. Reductio ad absurdum. As a corollary, when there is a fork, an external process can determine the blame by requiring each validator to justify all of its round votes. diff --git a/docs/spec/consensus/light-client.md b/docs/spec/consensus/light-client.md index 0ed9d36d4..1b608627c 100644 --- a/docs/spec/consensus/light-client.md +++ b/docs/spec/consensus/light-client.md @@ -1,14 +1,14 @@ # Light client -A light client is a process that connects to the Tendermint Full Node(s) and then tries to verify the Merkle proofs -about the blockchain application. In this document we describe mechanisms that ensures that the Tendermint light client -has the same level of security as Full Node processes (without being itself a Full Node). +A light client is a process that connects to the Tendermint Full Node(s) and then tries to verify the Merkle proofs +about the blockchain application. In this document we describe mechanisms that ensures that the Tendermint light client +has the same level of security as Full Node processes (without being itself a Full Node). -To be able to validate a Merkle proof, a light client needs to validate the blockchain header that contains the root app hash. -Validating a blockchain header in Tendermint consists in verifying that the header is committed (signed) by >2/3 of the -voting power of the corresponding validator set. As the validator set is a dynamic set (it is changing), one of the -core functionality of the light client is updating the current validator set, that is then used to verify the -blockchain header, and further the corresponding Merkle proofs. +To be able to validate a Merkle proof, a light client needs to validate the blockchain header that contains the root app hash. +Validating a blockchain header in Tendermint consists in verifying that the header is committed (signed) by >2/3 of the +voting power of the corresponding validator set. As the validator set is a dynamic set (it is changing), one of the +core functionality of the light client is updating the current validator set, that is then used to verify the +blockchain header, and further the corresponding Merkle proofs. For the purpose of this light client specification, we assume that the Tendermint Full Node exposes the following functions over Tendermint RPC: @@ -19,51 +19,50 @@ Validators(height int64) (ResultValidators, error) // returns validator set for LastHeader(valSetNumber int64) (SignedHeader, error) // returns last header signed by the validator set with the given validator set number type SignedHeader struct { - Header Header + Header Header Commit Commit - ValSetNumber int64 + ValSetNumber int64 } type ResultValidators struct { - BlockHeight int64 - Validators []Validator - // time the current validator set is initialised, i.e, time of the last validator change before header BlockHeight - ValSetTime int64 + BlockHeight int64 + Validators []Validator + // time the current validator set is initialised, i.e, time of the last validator change before header BlockHeight + ValSetTime int64 } ``` -We assume that Tendermint keeps track of the validator set changes and that each time a validator set is changed it is -being assigned the next sequence number. We can call this number the validator set sequence number. Tendermint also remembers +We assume that Tendermint keeps track of the validator set changes and that each time a validator set is changed it is +being assigned the next sequence number. We can call this number the validator set sequence number. Tendermint also remembers the Time from the header when the next validator set is initialised (starts to be in power), and we refer to this time as validator set init time. Furthermore, we assume that each validator set change is signed (committed) by the current validator set. More precisely, -given a block `H` that contains transactions that are modifying the current validator set, the Merkle root hash of the next -validator set (modified based on transactions from block H) will be in block `H+1` (and signed by the current validator -set), and then starting from the block `H+2`, it will be signed by the next validator set. - -Note that the real Tendermint RPC API is slightly different (for example, response messages contain more data and function -names are slightly different); we shortened (and modified) it for the purpose of this document to make the spec more -clear and simple. Furthermore, note that in case of the third function, the returned header has `ValSetNumber` equals to -`valSetNumber+1`. +given a block `H` that contains transactions that are modifying the current validator set, the Merkle root hash of the next +validator set (modified based on transactions from block H) will be in block `H+1` (and signed by the current validator +set), and then starting from the block `H+2`, it will be signed by the next validator set. +Note that the real Tendermint RPC API is slightly different (for example, response messages contain more data and function +names are slightly different); we shortened (and modified) it for the purpose of this document to make the spec more +clear and simple. Furthermore, note that in case of the third function, the returned header has `ValSetNumber` equals to +`valSetNumber+1`. Locally, light client manages the following state: ```golang -valSet []Validator // current validator set (last known and verified validator set) -valSetNumber int64 // sequence number of the current validator set +valSet []Validator // current validator set (last known and verified validator set) +valSetNumber int64 // sequence number of the current validator set valSetHash []byte // hash of the current validator set -valSetTime int64 // time when the current validator set is initialised +valSetTime int64 // time when the current validator set is initialised ``` The light client is initialised with the trusted validator set, for example based on the known validator set hash, validator set sequence number and the validator set init time. The core of the light client logic is captured by the VerifyAndUpdate function that is used to 1) verify if the given header is valid, -and 2) update the validator set (when the given header is valid and it is more recent than the seen headers). +and 2) update the validator set (when the given header is valid and it is more recent than the seen headers). ```golang VerifyAndUpdate(signedHeader SignedHeader): - assertThat signedHeader.valSetNumber >= valSetNumber + assertThat signedHeader.valSetNumber >= valSetNumber if isValid(signedHeader) and signedHeader.Header.Time <= valSetTime + UNBONDING_PERIOD then setValidatorSet(signedHeader) return true @@ -76,7 +75,7 @@ isValid(signedHeader SignedHeader): assertThat Hash(valSetOfTheHeader) == signedHeader.Header.ValSetHash assertThat signedHeader is passing basic validation if votingPower(signedHeader.Commit) > 2/3 * votingPower(valSetOfTheHeader) then return true - else + else return false setValidatorSet(signedHeader SignedHeader): @@ -85,7 +84,7 @@ setValidatorSet(signedHeader SignedHeader): valSet = nextValSet.Validators valSetHash = signedHeader.Header.ValidatorsHash valSetNumber = signedHeader.ValSetNumber - valSetTime = nextValSet.ValSetTime + valSetTime = nextValSet.ValSetTime votingPower(commit Commit): votingPower = 0 @@ -96,9 +95,9 @@ votingPower(commit Commit): votingPower(validatorSet []Validator): for each validator in validatorSet do: - votingPower += validator.VotingPower + votingPower += validator.VotingPower return votingPower - + updateValidatorSet(valSetNumberOfTheHeader): while valSetNumber != valSetNumberOfTheHeader do signedHeader = LastHeader(valSetNumber) @@ -110,5 +109,5 @@ updateValidatorSet(valSetNumberOfTheHeader): Note that in the logic above we assume that the light client will always go upward with respect to header verifications, i.e., that it will always be used to verify more recent headers. In case a light client needs to be used to verify older -headers (go backward) the same mechanisms and similar logic can be used. In case a call to the FullNode or subsequent -checks fail, a light client need to implement some recovery strategy, for example connecting to other FullNode. +headers (go backward) the same mechanisms and similar logic can be used. In case a call to the FullNode or subsequent +checks fail, a light client need to implement some recovery strategy, for example connecting to other FullNode. diff --git a/docs/spec/p2p/connection.md b/docs/spec/p2p/connection.md index f6322fe8f..47366a549 100644 --- a/docs/spec/p2p/connection.md +++ b/docs/spec/p2p/connection.md @@ -4,10 +4,10 @@ `MConnection` is a multiplex connection that supports multiple independent streams with distinct quality of service guarantees atop a single TCP connection. -Each stream is known as a `Channel` and each `Channel` has a globally unique *byte id*. +Each stream is known as a `Channel` and each `Channel` has a globally unique _byte id_. Each `Channel` also has a relative priority that determines the quality of service of the `Channel` compared to other `Channel`s. -The *byte id* and the relative priorities of each `Channel` are configured upon +The _byte id_ and the relative priorities of each `Channel` are configured upon initialization of the connection. The `MConnection` supports three packet types: @@ -53,13 +53,14 @@ Messages are chosen for a batch one at a time from the channel with the lowest r ## Sending Messages There are two methods for sending messages: + ```go func (m MConnection) Send(chID byte, msg interface{}) bool {} func (m MConnection) TrySend(chID byte, msg interface{}) bool {} ``` `Send(chID, msg)` is a blocking call that waits until `msg` is successfully queued -for the channel with the given id byte `chID`. The message `msg` is serialized +for the channel with the given id byte `chID`. The message `msg` is serialized using the `tendermint/wire` submodule's `WriteBinary()` reflection routine. `TrySend(chID, msg)` is a nonblocking call that queues the message msg in the channel @@ -76,8 +77,8 @@ and other higher level thread-safe data used by the reactors. ## Switch/Reactor The `Switch` handles peer connections and exposes an API to receive incoming messages -on `Reactors`. Each `Reactor` is responsible for handling incoming messages of one -or more `Channels`. So while sending outgoing messages is typically performed on the peer, +on `Reactors`. Each `Reactor` is responsible for handling incoming messages of one +or more `Channels`. So while sending outgoing messages is typically performed on the peer, incoming messages are received on the reactor. ```go diff --git a/docs/spec/p2p/node.md b/docs/spec/p2p/node.md index 366b27dd2..2771356af 100644 --- a/docs/spec/p2p/node.md +++ b/docs/spec/p2p/node.md @@ -17,8 +17,9 @@ See [the peer-exchange docs](https://github.com/tendermint/tendermint/blob/maste ## New Full Node A new node needs a few things to connect to the network: + - a list of seeds, which can be provided to Tendermint via config file or flags, -or hardcoded into the software by in-process apps + or hardcoded into the software by in-process apps - a `ChainID`, also called `Network` at the p2p layer - a recent block height, H, and hash, HASH for the blockchain. diff --git a/docs/spec/p2p/peer.md b/docs/spec/p2p/peer.md index dadb4a3a5..f9d2d8bce 100644 --- a/docs/spec/p2p/peer.md +++ b/docs/spec/p2p/peer.md @@ -29,26 +29,26 @@ Both handshakes have configurable timeouts (they should complete quickly). Tendermint implements the Station-to-Station protocol using X25519 keys for Diffie-Helman key-exchange and chacha20poly1305 for encryption. It goes as follows: + - generate an ephemeral X25519 keypair - send the ephemeral public key to the peer - wait to receive the peer's ephemeral public key - compute the Diffie-Hellman shared secret using the peers ephemeral public key and our ephemeral private key - generate two keys to use for encryption (sending and receiving) and a challenge for authentication as follows: - - create a hkdf-sha256 instance with the key being the diffie hellman shared secret, and info parameter as - `TENDERMINT_SECRET_CONNECTION_KEY_AND_CHALLENGE_GEN` - - get 96 bytes of output from hkdf-sha256 - - if we had the smaller ephemeral pubkey, use the first 32 bytes for the key for receiving, the second 32 bytes for sending; else the opposite - - use the last 32 bytes of output for the challenge + - create a hkdf-sha256 instance with the key being the diffie hellman shared secret, and info parameter as + `TENDERMINT_SECRET_CONNECTION_KEY_AND_CHALLENGE_GEN` + - get 96 bytes of output from hkdf-sha256 + - if we had the smaller ephemeral pubkey, use the first 32 bytes for the key for receiving, the second 32 bytes for sending; else the opposite + - use the last 32 bytes of output for the challenge - use a seperate nonce for receiving and sending. Both nonces start at 0, and should support the full 96 bit nonce range - all communications from now on are encrypted in 1024 byte frames, -using the respective secret and nonce. Each nonce is incremented by one after each use. + using the respective secret and nonce. Each nonce is incremented by one after each use. - we now have an encrypted channel, but still need to authenticate - sign the common challenge obtained from the hkdf with our persistent private key - send the amino encoded persistent pubkey and signature to the peer - wait to receive the persistent public key and signature from the peer - verify the signature on the challenge using the peer's persistent public key - If this is an outgoing connection (we dialed the peer) and we used a peer ID, then finally verify that the peer's persistent public key corresponds to the peer ID we dialed, ie. `peer.PubKey.Address() == `. @@ -69,7 +69,6 @@ an optional whitelist which can be managed through the ABCI app - if the whitelist is enabled and the peer does not qualify, the connection is terminated. - ### Tendermint Version Handshake The Tendermint Version Handshake allows the peers to exchange their NodeInfo: @@ -89,6 +88,7 @@ type NodeInfo struct { ``` The connection is disconnected if: + - `peer.NodeInfo.ID` is not equal `peerConn.ID` - `peer.NodeInfo.Version` is not formatted as `X.X.X` where X are integers known as Major, Minor, and Revision - `peer.NodeInfo.Version` Major is not the same as ours @@ -97,7 +97,6 @@ The connection is disconnected if: - `peer.NodeInfo.ListenAddr` is malformed or is a DNS host that cannot be resolved - At this point, if we have not disconnected, the peer is valid. It is added to the switch and hence all reactors via the `AddPeer` method. Note that each reactor may handle multiple channels. diff --git a/docs/spec/reactors/block_sync/impl.md b/docs/spec/reactors/block_sync/impl.md index a96f83b32..ff067b841 100644 --- a/docs/spec/reactors/block_sync/impl.md +++ b/docs/spec/reactors/block_sync/impl.md @@ -1,46 +1,46 @@ ## Blockchain Reactor -* coordinates the pool for syncing -* coordinates the store for persistence -* coordinates the playing of blocks towards the app using a sm.BlockExecutor -* handles switching between fastsync and consensus -* it is a p2p.BaseReactor -* starts the pool.Start() and its poolRoutine() -* registers all the concrete types and interfaces for serialisation +- coordinates the pool for syncing +- coordinates the store for persistence +- coordinates the playing of blocks towards the app using a sm.BlockExecutor +- handles switching between fastsync and consensus +- it is a p2p.BaseReactor +- starts the pool.Start() and its poolRoutine() +- registers all the concrete types and interfaces for serialisation ### poolRoutine -* listens to these channels: - * pool requests blocks from a specific peer by posting to requestsCh, block reactor then sends +- listens to these channels: + - pool requests blocks from a specific peer by posting to requestsCh, block reactor then sends a &bcBlockRequestMessage for a specific height - * pool signals timeout of a specific peer by posting to timeoutsCh - * switchToConsensusTicker to periodically try and switch to consensus - * trySyncTicker to periodically check if we have fallen behind and then catch-up sync - * if there aren't any new blocks available on the pool it skips syncing -* tries to sync the app by taking downloaded blocks from the pool, gives them to the app and stores + - pool signals timeout of a specific peer by posting to timeoutsCh + - switchToConsensusTicker to periodically try and switch to consensus + - trySyncTicker to periodically check if we have fallen behind and then catch-up sync + - if there aren't any new blocks available on the pool it skips syncing +- tries to sync the app by taking downloaded blocks from the pool, gives them to the app and stores them on disk -* implements Receive which is called by the switch/peer - * calls AddBlock on the pool when it receives a new block from a peer +- implements Receive which is called by the switch/peer + - calls AddBlock on the pool when it receives a new block from a peer ## Block Pool -* responsible for downloading blocks from peers -* makeRequestersRoutine() - * removes timeout peers - * starts new requesters by calling makeNextRequester() -* requestRoutine(): - * picks a peer and sends the request, then blocks until: - * pool is stopped by listening to pool.Quit - * requester is stopped by listening to Quit - * request is redone - * we receive a block - * gotBlockCh is strange +- responsible for downloading blocks from peers +- makeRequestersRoutine() + - removes timeout peers + - starts new requesters by calling makeNextRequester() +- requestRoutine(): + - picks a peer and sends the request, then blocks until: + - pool is stopped by listening to pool.Quit + - requester is stopped by listening to Quit + - request is redone + - we receive a block + - gotBlockCh is strange ## Block Store -* persists blocks to disk +- persists blocks to disk # TODO -* How does the switch from bcR to conR happen? Does conR persist blocks to disk too? -* What is the interaction between the consensus and blockchain reactors? +- How does the switch from bcR to conR happen? Does conR persist blocks to disk too? +- What is the interaction between the consensus and blockchain reactors? diff --git a/docs/spec/reactors/block_sync/reactor.md b/docs/spec/reactors/block_sync/reactor.md index 97104eeeb..045bbd400 100644 --- a/docs/spec/reactors/block_sync/reactor.md +++ b/docs/spec/reactors/block_sync/reactor.md @@ -46,11 +46,11 @@ type bcStatusResponseMessage struct { ## Architecture and algorithm -The Blockchain reactor is organised as a set of concurrent tasks: - - Receive routine of Blockchain Reactor - - Task for creating Requesters - - Set of Requesters tasks and - - Controller task. +The Blockchain reactor is organised as a set of concurrent tasks: + +- Receive routine of Blockchain Reactor +- Task for creating Requesters +- Set of Requesters tasks and - Controller task. ![Blockchain Reactor Architecture Diagram](img/bc-reactor.png) @@ -58,41 +58,39 @@ The Blockchain reactor is organised as a set of concurrent tasks: These are the core data structures necessarily to provide the Blockchain Reactor logic. -Requester data structure is used to track assignment of request for `block` at position `height` to a -peer with id equals to `peerID`. +Requester data structure is used to track assignment of request for `block` at position `height` to a peer with id equals to `peerID`. ```go type Requester { - mtx Mutex + mtx Mutex block Block - height int64 - 
 peerID p2p.ID + height int64 + 
peerID p2p.ID redoChannel chan struct{} } ``` -Pool is core data structure that stores last executed block (`height`), assignment of requests to peers (`requesters`), -current height for each peer and number of pending requests for each peer (`peers`), maximum peer height, etc. + +Pool is core data structure that stores last executed block (`height`), assignment of requests to peers (`requesters`), current height for each peer and number of pending requests for each peer (`peers`), maximum peer height, etc. ```go type Pool { - mtx Mutex + mtx Mutex requesters map[int64]*Requester - 
height int64 + height int64 peers map[p2p.ID]*Peer - 
maxPeerHeight int64 

 - 
numPending int32 + maxPeerHeight int64 + numPending int32 store BlockStore - 
requestsChannel chan<- BlockRequest - 
errorsChannel chan<- peerError + requestsChannel chan<- BlockRequest + errorsChannel chan<- peerError } ``` -Peer data structure stores for each peer current `height` and number of pending requests sent to -the peer (`numPending`), etc. +Peer data structure stores for each peer current `height` and number of pending requests sent to the peer (`numPending`), etc. ```go type Peer struct { - id p2p.ID + id p2p.ID height int64 numPending int32 timeout *time.Timer @@ -100,202 +98,202 @@ type Peer struct { } ``` -BlockRequest is internal data structure used to denote current mapping of request for a block at some `height` to -a peer (`PeerID`). - +BlockRequest is internal data structure used to denote current mapping of request for a block at some `height` to a peer (`PeerID`). + ```go type BlockRequest { Height int64 - PeerID p2p.ID + PeerID p2p.ID } ``` ### Receive routine of Blockchain Reactor -It is executed upon message reception on the BlockchainChannel inside p2p receive routine. There is a separate p2p -receive routine (and therefore receive routine of the Blockchain Reactor) executed for each peer. Note that -try to send will not block (returns immediately) if outgoing buffer is full. +It is executed upon message reception on the BlockchainChannel inside p2p receive routine. There is a separate p2p receive routine (and therefore receive routine of the Blockchain Reactor) executed for each peer. Note that try to send will not block (returns immediately) if outgoing buffer is full. ```go handleMsg(pool, m): upon receiving bcBlockRequestMessage m from peer p: - block = load block for height m.Height from pool.store - if block != nil then - try to send BlockResponseMessage(block) to p - else - try to send bcNoBlockResponseMessage(m.Height) to p - - upon receiving bcBlockResponseMessage m from peer p: - pool.mtx.Lock() - requester = pool.requesters[m.Height] - if requester == nil then - error("peer sent us a block we didn't expect") - continue - - if requester.block == nil and requester.peerID == p then + block = load block for height m.Height from pool.store + if block != nil then + try to send BlockResponseMessage(block) to p + else + try to send bcNoBlockResponseMessage(m.Height) to p + + upon receiving bcBlockResponseMessage m from peer p: + pool.mtx.Lock() + requester = pool.requesters[m.Height] + if requester == nil then + error("peer sent us a block we didn't expect") + continue + + if requester.block == nil and requester.peerID == p then requester.block = m - pool.numPending -= 1 // atomic decrement - peer = pool.peers[p] - if peer != nil then - peer.numPending-- - if peer.numPending == 0 then - peer.timeout.Stop() - // NOTE: we don't send Quit signal to the corresponding requester task! - else - trigger peer timeout to expire after peerTimeout - pool.mtx.Unlock() - - + pool.numPending -= 1 // atomic decrement + peer = pool.peers[p] + if peer != nil then + peer.numPending-- + if peer.numPending == 0 then + peer.timeout.Stop() + // NOTE: we don't send Quit signal to the corresponding requester task! + else + trigger peer timeout to expire after peerTimeout + pool.mtx.Unlock() + + upon receiving bcStatusRequestMessage m from peer p: - try to send bcStatusResponseMessage(pool.store.Height) + try to send bcStatusResponseMessage(pool.store.Height) upon receiving bcStatusResponseMessage m from peer p: - pool.mtx.Lock() - peer = pool.peers[p] - if peer != nil then - peer.height = m.height - else - peer = create new Peer data structure with id = p and height = m.Height - pool.peers[p] = peer - - if m.Height > pool.maxPeerHeight then - pool.maxPeerHeight = m.Height - pool.mtx.Unlock() - + pool.mtx.Lock() + peer = pool.peers[p] + if peer != nil then + peer.height = m.height + else + peer = create new Peer data structure with id = p and height = m.Height + pool.peers[p] = peer + + if m.Height > pool.maxPeerHeight then + pool.maxPeerHeight = m.Height + pool.mtx.Unlock() + onTimeout(p): - send error message to pool error channel - peer = pool.peers[p] - peer.didTimeout = true + send error message to pool error channel + peer = pool.peers[p] + peer.didTimeout = true ``` ### Requester tasks -Requester task is responsible for fetching a single block at position `height`. +Requester task is responsible for fetching a single block at position `height`. ```go fetchBlock(height, pool): - while true do - peerID = nil + while true do + peerID = nil block = nil - peer = pickAvailablePeer(height) - peerId = peer.id + peer = pickAvailablePeer(height) + peerId = peer.id enqueue BlockRequest(height, peerID) to pool.requestsChannel - redo = false - while !redo do - select { + redo = false + while !redo do + select { upon receiving Quit message do - return - upon receiving message on redoChannel do - mtx.Lock() + return + upon receiving message on redoChannel do + mtx.Lock() pool.numPending++ - redo = true - mtx.UnLock() - } + redo = true + mtx.UnLock() + } pickAvailablePeer(height): - selectedPeer = nil - while selectedPeer = nil do - pool.mtx.Lock() - for each peer in pool.peers do - if !peer.didTimeout and peer.numPending < maxPendingRequestsPerPeer and peer.height >= height then - peer.numPending++ - selectedPeer = peer - break - pool.mtx.Unlock() - - if selectedPeer = nil then - sleep requestIntervalMS - - return selectedPeer + selectedPeer = nil + while selectedPeer = nil do + pool.mtx.Lock() + for each peer in pool.peers do + if !peer.didTimeout and peer.numPending < maxPendingRequestsPerPeer and peer.height >= height then + peer.numPending++ + selectedPeer = peer + break + pool.mtx.Unlock() + + if selectedPeer = nil then + sleep requestIntervalMS + + return selectedPeer ``` + sleep for requestIntervalMS + ### Task for creating Requesters This task is responsible for continuously creating and starting Requester tasks. + ```go createRequesters(pool): - while true do - if !pool.isRunning then break - if pool.numPending < maxPendingRequests or size(pool.requesters) < maxTotalRequesters then + while true do + if !pool.isRunning then break + if pool.numPending < maxPendingRequests or size(pool.requesters) < maxTotalRequesters then pool.mtx.Lock() nextHeight = pool.height + size(pool.requesters) - requester = create new requester for height nextHeight - pool.requesters[nextHeight] = requester - pool.numPending += 1 // atomic increment - start requester task - pool.mtx.Unlock() - else + requester = create new requester for height nextHeight + pool.requesters[nextHeight] = requester + pool.numPending += 1 // atomic increment + start requester task + pool.mtx.Unlock() + else sleep requestIntervalMS - pool.mtx.Lock() - for each peer in pool.peers do - if !peer.didTimeout && peer.numPending > 0 && peer.curRate < minRecvRate then - send error on pool error channel + pool.mtx.Lock() + for each peer in pool.peers do + if !peer.didTimeout && peer.numPending > 0 && peer.curRate < minRecvRate then + send error on pool error channel peer.didTimeout = true - if peer.didTimeout then - for each requester in pool.requesters do - if requester.getPeerID() == peer then + if peer.didTimeout then + for each requester in pool.requesters do + if requester.getPeerID() == peer then enqueue msg on requestor's redoChannel - delete(pool.peers, peerID) - pool.mtx.Unlock() + delete(pool.peers, peerID) + pool.mtx.Unlock() ``` - -### Main blockchain reactor controller task +### Main blockchain reactor controller task + ```go main(pool): - create trySyncTicker with interval trySyncIntervalMS - create statusUpdateTicker with interval statusUpdateIntervalSeconds - create switchToConsensusTicker with interbal switchToConsensusIntervalSeconds - - while true do - select { + create trySyncTicker with interval trySyncIntervalMS + create statusUpdateTicker with interval statusUpdateIntervalSeconds + create switchToConsensusTicker with interbal switchToConsensusIntervalSeconds + + while true do + select { upon receiving BlockRequest(Height, Peer) on pool.requestsChannel: - try to send bcBlockRequestMessage(Height) to Peer + try to send bcBlockRequestMessage(Height) to Peer upon receiving error(peer) on errorsChannel: - stop peer for error + stop peer for error upon receiving message on statusUpdateTickerChannel: - broadcast bcStatusRequestMessage(bcR.store.Height) // message sent in a separate routine + broadcast bcStatusRequestMessage(bcR.store.Height) // message sent in a separate routine upon receiving message on switchToConsensusTickerChannel: - pool.mtx.Lock() - receivedBlockOrTimedOut = pool.height > 0 || (time.Now() - pool.startTime) > 5 Seconds - ourChainIsLongestAmongPeers = pool.maxPeerHeight == 0 || pool.height >= pool.maxPeerHeight - haveSomePeers = size of pool.peers > 0 + pool.mtx.Lock() + receivedBlockOrTimedOut = pool.height > 0 || (time.Now() - pool.startTime) > 5 Seconds + ourChainIsLongestAmongPeers = pool.maxPeerHeight == 0 || pool.height >= pool.maxPeerHeight + haveSomePeers = size of pool.peers > 0 pool.mtx.Unlock() if haveSomePeers && receivedBlockOrTimedOut && ourChainIsLongestAmongPeers then - switch to consensus mode + switch to consensus mode upon receiving message on trySyncTickerChannel: - for i = 0; i < 10; i++ do - pool.mtx.Lock() + for i = 0; i < 10; i++ do + pool.mtx.Lock() firstBlock = pool.requesters[pool.height].block secondBlock = pool.requesters[pool.height].block if firstBlock == nil or secondBlock == nil then continue pool.mtx.Unlock() - verify firstBlock using LastCommit from secondBlock - if verification failed - pool.mtx.Lock() + verify firstBlock using LastCommit from secondBlock + if verification failed + pool.mtx.Lock() peerID = pool.requesters[pool.height].peerID redoRequestsForPeer(peerId) delete(pool.peers, peerID) - stop peer peerID for error - pool.mtx.Unlock() - else + stop peer peerID for error + pool.mtx.Unlock() + else delete(pool.requesters, pool.height) save firstBlock to store - pool.height++ - execute firstBlock + pool.height++ + execute firstBlock } - + redoRequestsForPeer(pool, peerId): - for each requester in pool.requesters do - if requester.getPeerID() == peerID - enqueue msg on redoChannel for requester + for each requester in pool.requesters do + if requester.getPeerID() == peerID + enqueue msg on redoChannel for requester ``` - + ## Channels Defines `maxMsgSize` for the maximum size of incoming messages, diff --git a/docs/spec/reactors/consensus/consensus-reactor.md b/docs/spec/reactors/consensus/consensus-reactor.md index 0f03b44b7..7be350322 100644 --- a/docs/spec/reactors/consensus/consensus-reactor.md +++ b/docs/spec/reactors/consensus/consensus-reactor.md @@ -1,49 +1,48 @@ # Consensus Reactor -Consensus Reactor defines a reactor for the consensus service. It contains the ConsensusState service that -manages the state of the Tendermint consensus internal state machine. -When Consensus Reactor is started, it starts Broadcast Routine which starts ConsensusState service. -Furthermore, for each peer that is added to the Consensus Reactor, it creates (and manages) the known peer state -(that is used extensively in gossip routines) and starts the following three routines for the peer p: -Gossip Data Routine, Gossip Votes Routine and QueryMaj23Routine. Finally, Consensus Reactor is responsible +Consensus Reactor defines a reactor for the consensus service. It contains the ConsensusState service that +manages the state of the Tendermint consensus internal state machine. +When Consensus Reactor is started, it starts Broadcast Routine which starts ConsensusState service. +Furthermore, for each peer that is added to the Consensus Reactor, it creates (and manages) the known peer state +(that is used extensively in gossip routines) and starts the following three routines for the peer p: +Gossip Data Routine, Gossip Votes Routine and QueryMaj23Routine. Finally, Consensus Reactor is responsible for decoding messages received from a peer and for adequate processing of the message depending on its type and content. -The processing normally consists of updating the known peer state and for some messages -(`ProposalMessage`, `BlockPartMessage` and `VoteMessage`) also forwarding message to ConsensusState module -for further processing. In the following text we specify the core functionality of those separate unit of executions -that are part of the Consensus Reactor. +The processing normally consists of updating the known peer state and for some messages +(`ProposalMessage`, `BlockPartMessage` and `VoteMessage`) also forwarding message to ConsensusState module +for further processing. In the following text we specify the core functionality of those separate unit of executions +that are part of the Consensus Reactor. ## ConsensusState service -Consensus State handles execution of the Tendermint BFT consensus algorithm. It processes votes and proposals, +Consensus State handles execution of the Tendermint BFT consensus algorithm. It processes votes and proposals, and upon reaching agreement, commits blocks to the chain and executes them against the application. The internal state machine receives input from peers, the internal validator and from a timer. Inside Consensus State we have the following units of execution: Timeout Ticker and Receive Routine. -Timeout Ticker is a timer that schedules timeouts conditional on the height/round/step that are processed -by the Receive Routine. - +Timeout Ticker is a timer that schedules timeouts conditional on the height/round/step that are processed +by the Receive Routine. ### Receive Routine of the ConsensusState service Receive Routine of the ConsensusState handles messages which may cause internal consensus state transitions. -It is the only routine that updates RoundState that contains internal consensus state. -Updates (state transitions) happen on timeouts, complete proposals, and 2/3 majorities. -It receives messages from peers, internal validators and from Timeout Ticker -and invokes the corresponding handlers, potentially updating the RoundState. -The details of the protocol (together with formal proofs of correctness) implemented by the Receive Routine are +It is the only routine that updates RoundState that contains internal consensus state. +Updates (state transitions) happen on timeouts, complete proposals, and 2/3 majorities. +It receives messages from peers, internal validators and from Timeout Ticker +and invokes the corresponding handlers, potentially updating the RoundState. +The details of the protocol (together with formal proofs of correctness) implemented by the Receive Routine are discussed in separate document. For understanding of this document -it is sufficient to understand that the Receive Routine manages and updates RoundState data structure that is +it is sufficient to understand that the Receive Routine manages and updates RoundState data structure that is then extensively used by the gossip routines to determine what information should be sent to peer processes. ## Round State RoundState defines the internal consensus state. It contains height, round, round step, a current validator set, -a proposal and proposal block for the current round, locked round and block (if some block is being locked), set of -received votes and last commit and last validators set. +a proposal and proposal block for the current round, locked round and block (if some block is being locked), set of +received votes and last commit and last validators set. ```golang type RoundState struct { - Height int64 + Height int64 Round int Step RoundStepType Validators ValidatorSet @@ -54,10 +53,10 @@ type RoundState struct { LockedBlock Block LockedBlockParts PartSet Votes HeightVoteSet - LastCommit VoteSet + LastCommit VoteSet LastValidators ValidatorSet -} -``` +} +``` Internally, consensus will run as a state machine with the following states: @@ -82,8 +81,8 @@ type PeerRoundState struct { Round int // Round peer is at, -1 if unknown. Step RoundStepType // Step peer is at Proposal bool // True if peer has proposal for this round - ProposalBlockPartsHeader PartSetHeader - ProposalBlockParts BitArray + ProposalBlockPartsHeader PartSetHeader + ProposalBlockParts BitArray ProposalPOLRound int // Proposal's POL round. -1 if none. ProposalPOL BitArray // nil until ProposalPOLMessage received. Prevotes BitArray // All votes peer has for this round @@ -93,19 +92,19 @@ type PeerRoundState struct { CatchupCommitRound int // Round that we have commit for. Not necessarily unique. -1 if none. CatchupCommit BitArray // All commit precommits peer has for this height & CatchupCommitRound } -``` +``` ## Receive method of Consensus reactor -The entry point of the Consensus reactor is a receive method. When a message is received from a peer p, -normally the peer round state is updated correspondingly, and some messages +The entry point of the Consensus reactor is a receive method. When a message is received from a peer p, +normally the peer round state is updated correspondingly, and some messages are passed for further processing, for example to ConsensusState service. We now specify the processing of messages in the receive method of Consensus reactor for each message type. In the following message handler, `rs` and `prs` denote `RoundState` and `PeerRoundState`, respectively. -### NewRoundStepMessage handler +### NewRoundStepMessage handler -``` +``` handleMessage(msg): if msg is from smaller height/round/step then return // Just remember these values. @@ -116,10 +115,10 @@ handleMessage(msg): Update prs with values from msg if prs.Height or prs.Round has been updated then - reset Proposal related fields of the peer state + reset Proposal related fields of the peer state if prs.Round has been updated and msg.Round == prsCatchupCommitRound then prs.Precommits = psCatchupCommit - if prs.Height has been updated then + if prs.Height has been updated then if prsHeight+1 == msg.Height && prsRound == msg.LastCommitRound then prs.LastCommitRound = msg.LastCommitRound prs.LastCommit = prs.Precommits @@ -128,111 +127,111 @@ handleMessage(msg): prs.LastCommit = nil } Reset prs.CatchupCommitRound and prs.CatchupCommit -``` +``` ### CommitStepMessage handler -``` +``` handleMessage(msg): - if prs.Height == msg.Height then + if prs.Height == msg.Height then prs.ProposalBlockPartsHeader = msg.BlockPartsHeader prs.ProposalBlockParts = msg.BlockParts -``` +``` ### HasVoteMessage handler -``` +``` handleMessage(msg): - if prs.Height == msg.Height then + if prs.Height == msg.Height then prs.setHasVote(msg.Height, msg.Round, msg.Type, msg.Index) -``` +``` ### VoteSetMaj23Message handler -``` +``` handleMessage(msg): if prs.Height == msg.Height then Record in rs that a peer claim to have ⅔ majority for msg.BlockID - Send VoteSetBitsMessage showing votes node has for that BlockId -``` + Send VoteSetBitsMessage showing votes node has for that BlockId +``` ### ProposalMessage handler ``` handleMessage(msg): - if prs.Height != msg.Height || prs.Round != msg.Round || prs.Proposal then return + if prs.Height != msg.Height || prs.Round != msg.Round || prs.Proposal then return prs.Proposal = true prs.ProposalBlockPartsHeader = msg.BlockPartsHeader - prs.ProposalBlockParts = empty set + prs.ProposalBlockParts = empty set prs.ProposalPOLRound = msg.POLRound - prs.ProposalPOL = nil + prs.ProposalPOL = nil Send msg through internal peerMsgQueue to ConsensusState service -``` +``` ### ProposalPOLMessage handler -``` +``` handleMessage(msg): if prs.Height != msg.Height or prs.ProposalPOLRound != msg.ProposalPOLRound then return prs.ProposalPOL = msg.ProposalPOL -``` +``` ### BlockPartMessage handler -``` +``` handleMessage(msg): if prs.Height != msg.Height || prs.Round != msg.Round then return - Record in prs that peer has block part msg.Part.Index + Record in prs that peer has block part msg.Part.Index Send msg trough internal peerMsgQueue to ConsensusState service -``` +``` ### VoteMessage handler -``` +``` handleMessage(msg): Record in prs that a peer knows vote with index msg.vote.ValidatorIndex for particular height and round Send msg trough internal peerMsgQueue to ConsensusState service -``` +``` ### VoteSetBitsMessage handler -``` +``` handleMessage(msg): Update prs for the bit-array of votes peer claims to have for the msg.BlockID -``` +``` ## Gossip Data Routine -It is used to send the following messages to the peer: `BlockPartMessage`, `ProposalMessage` and -`ProposalPOLMessage` on the DataChannel. The gossip data routine is based on the local RoundState (`rs`) +It is used to send the following messages to the peer: `BlockPartMessage`, `ProposalMessage` and +`ProposalPOLMessage` on the DataChannel. The gossip data routine is based on the local RoundState (`rs`) and the known PeerRoundState (`prs`). The routine repeats forever the logic shown below: ``` 1a) if rs.ProposalBlockPartsHeader == prs.ProposalBlockPartsHeader and the peer does not have all the proposal parts then - Part = pick a random proposal block part the peer does not have - Send BlockPartMessage(rs.Height, rs.Round, Part) to the peer on the DataChannel + Part = pick a random proposal block part the peer does not have + Send BlockPartMessage(rs.Height, rs.Round, Part) to the peer on the DataChannel if send returns true, record that the peer knows the corresponding block Part - Continue - + Continue + 1b) if (0 < prs.Height) and (prs.Height < rs.Height) then help peer catch up using gossipDataForCatchup function Continue -1c) if (rs.Height != prs.Height) or (rs.Round != prs.Round) then +1c) if (rs.Height != prs.Height) or (rs.Round != prs.Round) then Sleep PeerGossipSleepDuration - Continue + Continue // at this point rs.Height == prs.Height and rs.Round == prs.Round -1d) if (rs.Proposal != nil and !prs.Proposal) then +1d) if (rs.Proposal != nil and !prs.Proposal) then Send ProposalMessage(rs.Proposal) to the peer if send returns true, record that the peer knows Proposal if 0 <= rs.Proposal.POLRound then - polRound = rs.Proposal.POLRound - prevotesBitArray = rs.Votes.Prevotes(polRound).BitArray() + polRound = rs.Proposal.POLRound + prevotesBitArray = rs.Votes.Prevotes(polRound).BitArray() Send ProposalPOLMessage(rs.Height, polRound, prevotesBitArray) - Continue + Continue -2) Sleep PeerGossipSleepDuration +2) Sleep PeerGossipSleepDuration ``` ### Gossip Data For Catchup @@ -240,65 +239,65 @@ and the known PeerRoundState (`prs`). The routine repeats forever the logic show This function is responsible for helping peer catch up if it is at the smaller height (prs.Height < rs.Height). The function executes the following logic: - if peer does not have all block parts for prs.ProposalBlockPart then + if peer does not have all block parts for prs.ProposalBlockPart then blockMeta = Load Block Metadata for height prs.Height from blockStore if (!blockMeta.BlockID.PartsHeader == prs.ProposalBlockPartsHeader) then Sleep PeerGossipSleepDuration return - Part = pick a random proposal block part the peer does not have - Send BlockPartMessage(prs.Height, prs.Round, Part) to the peer on the DataChannel + Part = pick a random proposal block part the peer does not have + Send BlockPartMessage(prs.Height, prs.Round, Part) to the peer on the DataChannel if send returns true, record that the peer knows the corresponding block Part return - else Sleep PeerGossipSleepDuration - + else Sleep PeerGossipSleepDuration + ## Gossip Votes Routine It is used to send the following message: `VoteMessage` on the VoteChannel. -The gossip votes routine is based on the local RoundState (`rs`) +The gossip votes routine is based on the local RoundState (`rs`) and the known PeerRoundState (`prs`). The routine repeats forever the logic shown below: ``` 1a) if rs.Height == prs.Height then - if prs.Step == RoundStepNewHeight then - vote = random vote from rs.LastCommit the peer does not have - Send VoteMessage(vote) to the peer + if prs.Step == RoundStepNewHeight then + vote = random vote from rs.LastCommit the peer does not have + Send VoteMessage(vote) to the peer if send returns true, continue - - if prs.Step <= RoundStepPrevote and prs.Round != -1 and prs.Round <= rs.Round then + + if prs.Step <= RoundStepPrevote and prs.Round != -1 and prs.Round <= rs.Round then Prevotes = rs.Votes.Prevotes(prs.Round) - vote = random vote from Prevotes the peer does not have - Send VoteMessage(vote) to the peer + vote = random vote from Prevotes the peer does not have + Send VoteMessage(vote) to the peer if send returns true, continue - if prs.Step <= RoundStepPrecommit and prs.Round != -1 and prs.Round <= rs.Round then - Precommits = rs.Votes.Precommits(prs.Round) - vote = random vote from Precommits the peer does not have - Send VoteMessage(vote) to the peer + if prs.Step <= RoundStepPrecommit and prs.Round != -1 and prs.Round <= rs.Round then + Precommits = rs.Votes.Precommits(prs.Round) + vote = random vote from Precommits the peer does not have + Send VoteMessage(vote) to the peer if send returns true, continue - - if prs.ProposalPOLRound != -1 then + + if prs.ProposalPOLRound != -1 then PolPrevotes = rs.Votes.Prevotes(prs.ProposalPOLRound) - vote = random vote from PolPrevotes the peer does not have - Send VoteMessage(vote) to the peer - if send returns true, continue + vote = random vote from PolPrevotes the peer does not have + Send VoteMessage(vote) to the peer + if send returns true, continue 1b) if prs.Height != 0 and rs.Height == prs.Height+1 then - vote = random vote from rs.LastCommit peer does not have - Send VoteMessage(vote) to the peer + vote = random vote from rs.LastCommit peer does not have + Send VoteMessage(vote) to the peer if send returns true, continue - + 1c) if prs.Height != 0 and rs.Height >= prs.Height+2 then - Commit = get commit from BlockStore for prs.Height - vote = random vote from Commit the peer does not have - Send VoteMessage(vote) to the peer + Commit = get commit from BlockStore for prs.Height + vote = random vote from Commit the peer does not have + Send VoteMessage(vote) to the peer if send returns true, continue -2) Sleep PeerGossipSleepDuration +2) Sleep PeerGossipSleepDuration ``` ## QueryMaj23Routine -It is used to send the following message: `VoteSetMaj23Message`. `VoteSetMaj23Message` is sent to indicate that a given +It is used to send the following message: `VoteSetMaj23Message`. `VoteSetMaj23Message` is sent to indicate that a given BlockID has seen +2/3 votes. This routine is based on the local RoundState (`rs`) and the known PeerRoundState (`prs`). The routine repeats forever the logic shown below. @@ -324,8 +323,8 @@ BlockID has seen +2/3 votes. This routine is based on the local RoundState (`rs` Send m to peer Sleep PeerQueryMaj23SleepDuration -1d) if prs.CatchupCommitRound != -1 and 0 < prs.Height and - prs.Height <= blockStore.Height() then +1d) if prs.CatchupCommitRound != -1 and 0 < prs.Height and + prs.Height <= blockStore.Height() then Commit = LoadCommit(prs.Height) m = VoteSetMaj23Message(prs.Height,Commit.Round,Precommit,Commit.blockId) Send m to peer @@ -339,14 +338,14 @@ BlockID has seen +2/3 votes. This routine is based on the local RoundState (`rs` The Broadcast routine subscribes to an internal event bus to receive new round steps, votes messages and proposal heartbeat messages, and broadcasts messages to peers upon receiving those events. It broadcasts `NewRoundStepMessage` or `CommitStepMessage` upon new round state event. Note that -broadcasting these messages does not depend on the PeerRoundState; it is sent on the StateChannel. -Upon receiving VoteMessage it broadcasts `HasVoteMessage` message to its peers on the StateChannel. +broadcasting these messages does not depend on the PeerRoundState; it is sent on the StateChannel. +Upon receiving VoteMessage it broadcasts `HasVoteMessage` message to its peers on the StateChannel. `ProposalHeartbeatMessage` is sent the same way on the StateChannel. ## Channels Defines 4 channels: state, data, vote and vote_set_bits. Each channel -has `SendQueueCapacity` and `RecvBufferCapacity` and +has `SendQueueCapacity` and `RecvBufferCapacity` and `RecvMessageCapacity` set to `maxMsgSize`. Sending incorrectly encoded data will result in stopping the peer. diff --git a/docs/spec/reactors/consensus/consensus.md b/docs/spec/reactors/consensus/consensus.md index 4ea619b51..a1cf17bcb 100644 --- a/docs/spec/reactors/consensus/consensus.md +++ b/docs/spec/reactors/consensus/consensus.md @@ -23,7 +23,7 @@ processes using `BlockPartMessage`. Validators in Tendermint communicate by peer-to-peer gossiping protocol. Each validator is connected only to a subset of processes called peers. By the gossiping protocol, a validator send to its peers -all needed information (`ProposalMessage`, `VoteMessage` and `BlockPartMessage`) so they can +all needed information (`ProposalMessage`, `VoteMessage` and `BlockPartMessage`) so they can reach agreement on some block, and also obtain the content of the chosen block (block parts). As part of the gossiping protocol, processes also send auxiliary messages that inform peers about the executed steps of the core consensus algorithm (`NewRoundStepMessage` and `CommitStepMessage`), and diff --git a/docs/spec/reactors/consensus/proposer-selection.md b/docs/spec/reactors/consensus/proposer-selection.md index 649d3dd21..b5e0b35af 100644 --- a/docs/spec/reactors/consensus/proposer-selection.md +++ b/docs/spec/reactors/consensus/proposer-selection.md @@ -1,6 +1,6 @@ # Proposer selection procedure in Tendermint -This document specifies the Proposer Selection Procedure that is used in Tendermint to choose a round proposer. +This document specifies the Proposer Selection Procedure that is used in Tendermint to choose a round proposer. As Tendermint is “leader-based protocol”, the proposer selection is critical for its correct functioning. Let denote with `proposer_p(h,r)` a process returned by the Proposer Selection Procedure at the process p, at height h and round r. Then the Proposer Selection procedure should fulfill the following properties: @@ -9,13 +9,13 @@ and round r. Then the Proposer Selection procedure should fulfill the following p and q, for each height h, and each round r, proposer_p(h,r) = proposer_q(h,r) -`Liveness`: In every consecutive sequence of rounds of size K (K is system parameter), at least a -single round has an honest proposer. +`Liveness`: In every consecutive sequence of rounds of size K (K is system parameter), at least a +single round has an honest proposer. -`Fairness`: The proposer selection is proportional to the validator voting power, i.e., a validator with more -voting power is selected more frequently, proportional to its power. More precisely, given a set of processes -with the total voting power N, during a sequence of rounds of size N, every process is proposer in a number of rounds -equal to its voting power. +`Fairness`: The proposer selection is proportional to the validator voting power, i.e., a validator with more +voting power is selected more frequently, proportional to its power. More precisely, given a set of processes +with the total voting power N, during a sequence of rounds of size N, every process is proposer in a number of rounds +equal to its voting power. We now look at a few particular cases to understand better how fairness should be implemented. If we have 4 processes with the following voting power distribution (p0,4), (p1, 2), (p2, 2), (p3, 2) at some round r, @@ -27,20 +27,20 @@ Let consider now the following scenario where a total voting power of faulty pro p0: (p0,3), (p1, 1), (p2, 1), (p3, 1), (p4, 1), (p5, 1), (p6, 1), (p7, 1). In this case the sequence of proposer selections looks like this: -`p0, p1, p2, p3, p0, p4, p5, p6, p7, p0, p0, p1, p2, p3, p0, p4, p5, p6, p7, p0, etc` +`p0, p1, p2, p3, p0, p4, p5, p6, p7, p0, p0, p1, p2, p3, p0, p4, p5, p6, p7, p0, etc` In this case, we see that a number of rounds coordinated by a faulty process is proportional to its voting power. -We consider also the case where we have voting power uniformly distributed among processes, i.e., we have 10 processes -each with voting power of 1. And let consider that there are 3 faulty processes with consecutive addresses, +We consider also the case where we have voting power uniformly distributed among processes, i.e., we have 10 processes +each with voting power of 1. And let consider that there are 3 faulty processes with consecutive addresses, for example the first 3 processes are faulty. Then the sequence looks like this: `p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, etc` -In this case, we have 3 consecutive rounds with a faulty proposer. +In this case, we have 3 consecutive rounds with a faulty proposer. One special case we consider is the case where a single honest process p0 has most of the voting power, for example: (p0,100), (p1, 2), (p2, 3), (p3, 4). Then the sequence of proposer selection looks like this: p0, p0, p0, p0, p0, p0, p0, p0, p0, p0, p0, p0, p0, p1, p0, p0, p0, p0, p0, etc -This basically means that almost all rounds have the same proposer. But in this case, the process p0 has anyway enough +This basically means that almost all rounds have the same proposer. But in this case, the process p0 has anyway enough voting power to decide whatever he wants, so the fact that he coordinates almost all rounds seems correct. diff --git a/docs/spec/reactors/mempool/concurrency.md b/docs/spec/reactors/mempool/concurrency.md index 991113e6d..a6870db9b 100644 --- a/docs/spec/reactors/mempool/concurrency.md +++ b/docs/spec/reactors/mempool/concurrency.md @@ -2,7 +2,7 @@ Look at the concurrency model this uses... -* Receiving CheckTx -* Broadcasting new tx -* Interfaces with consensus engine, reap/update while checking -* Calling the ABCI app (ordering. callbacks. how proxy works alongside the blockchain proxy which actually writes blocks) +- Receiving CheckTx +- Broadcasting new tx +- Interfaces with consensus engine, reap/update while checking +- Calling the ABCI app (ordering. callbacks. how proxy works alongside the blockchain proxy which actually writes blocks) diff --git a/docs/spec/reactors/mempool/config.md b/docs/spec/reactors/mempool/config.md index 776149ba0..3e3c0d373 100644 --- a/docs/spec/reactors/mempool/config.md +++ b/docs/spec/reactors/mempool/config.md @@ -11,12 +11,12 @@ Flag: `--mempool.recheck_empty=false` Environment: `TM_MEMPOOL_RECHECK_EMPTY=false` Config: + ``` [mempool] recheck_empty = false ``` - ## Recheck `--mempool.recheck=false` (default: true) diff --git a/docs/spec/reactors/mempool/functionality.md b/docs/spec/reactors/mempool/functionality.md index 85c3dc58d..8c9847e8c 100644 --- a/docs/spec/reactors/mempool/functionality.md +++ b/docs/spec/reactors/mempool/functionality.md @@ -6,26 +6,25 @@ consensus reactor when it is selected as the block proposer. There are two sides to the mempool state: -* External: get, check, and broadcast new transactions -* Internal: return valid transaction, update list after block commit - +- External: get, check, and broadcast new transactions +- Internal: return valid transaction, update list after block commit ## External functionality External functionality is exposed via network interfaces to potentially untrusted actors. -* CheckTx - triggered via RPC or P2P -* Broadcast - gossip messages after a successful check +- CheckTx - triggered via RPC or P2P +- Broadcast - gossip messages after a successful check ## Internal functionality Internal functionality is exposed via method calls to other code compiled into the tendermint binary. -* Reap - get tx to propose in next block -* Update - remove tx that were included in last block -* ABCI.CheckTx - call ABCI app to validate the tx +- Reap - get tx to propose in next block +- Update - remove tx that were included in last block +- ABCI.CheckTx - call ABCI app to validate the tx What does it provide the consensus reactor? What guarantees does it need from the ABCI app? diff --git a/docs/spec/reactors/mempool/messages.md b/docs/spec/reactors/mempool/messages.md index 9a624dff1..117fc5f2f 100644 --- a/docs/spec/reactors/mempool/messages.md +++ b/docs/spec/reactors/mempool/messages.md @@ -35,12 +35,12 @@ Request (`POST http://gaia.zone:26657/`): ```json { - "id": "", - "jsonrpc": "2.0", - "method": "broadcast_sync", - "params": { + "id": "", + "jsonrpc": "2.0", + "method": "broadcast_sync", + "params": { "tx": "F012A4BC68..." - } + } } ``` @@ -48,14 +48,14 @@ Response: ```json { - "error": "", - "result": { - "hash": "E39AAB7A537ABAA237831742DCE1117F187C3C52", - "log": "", - "data": "", - "code": 0 - }, - "id": "", - "jsonrpc": "2.0" + "error": "", + "result": { + "hash": "E39AAB7A537ABAA237831742DCE1117F187C3C52", + "log": "", + "data": "", + "code": 0 + }, + "id": "", + "jsonrpc": "2.0" } ``` diff --git a/docs/spec/reactors/pex/pex.md b/docs/spec/reactors/pex/pex.md index 9e00101ae..26f1fa8bb 100644 --- a/docs/spec/reactors/pex/pex.md +++ b/docs/spec/reactors/pex/pex.md @@ -95,6 +95,7 @@ remove from address book completely. ## Select Peers to Exchange When we’re asked for peers, we select them as follows: + - select at most `maxGetSelection` peers - try to select at least `minGetSelection` peers - if we have less than that, select them all. - select a random, unbiased `getSelectionPercent` of the peers @@ -126,4 +127,3 @@ to use it in the PEX. See the [trustmetric](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-006-trust-metric.md) and [trustmetric useage](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-007-trust-metric-usage.md) architecture docs for more details. - diff --git a/docs/spec/software/abci.md b/docs/spec/software/abci.md index 4323d3948..aacdb43df 100644 --- a/docs/spec/software/abci.md +++ b/docs/spec/software/abci.md @@ -44,7 +44,6 @@ Thus, during Commit, it is safe to reset the QueryState and the CheckTxState to Note, however, that it is not possible to send transactions to Tendermint during Commit - if your app tries to send a `/broadcast_tx` to Tendermint during Commit, it will deadlock. - ## EndBlock Validator Updates Updates to the Tendermint validator set can be made by returning `Validator` @@ -60,12 +59,12 @@ message PubKey { string type bytes data } - ``` The `pub_key` currently supports two types: - - `type = "ed25519" and `data = ` - - `type = "secp256k1" and `data = <33-byte OpenSSL compressed public key>` + +- `type = "ed25519" and`data = ` +- `type = "secp256k1" and `data = <33-byte OpenSSL compressed public key>` If the address is provided, it must match the address of the pubkey, as specified [here](/docs/spec/blockchain/encoding.md#Addresses) @@ -87,9 +86,9 @@ following rules: - if power is 0, the validator must already exist, and will be removed from the validator set - if power is non-0: - - if the validator does not already exist, it will be added to the validator - set with the given power - - if the validator does already exist, its power will be adjusted to the given power + - if the validator does not already exist, it will be added to the validator + set with the given power + - if the validator does already exist, its power will be adjusted to the given power ## InitChain Validator Updates @@ -114,10 +113,10 @@ features. These are: When Tendermint connects to a peer, it sends two queries to the ABCI application using the following paths, with no additional data: - - `/p2p/filter/addr/`, where `` denote the IP address and - the port of the connection - - `p2p/filter/id/`, where `` is the peer node ID (ie. the - pubkey.Address() for the peer's PubKey) +- `/p2p/filter/addr/`, where `` denote the IP address and + the port of the connection +- `p2p/filter/id/`, where `` is the peer node ID (ie. the + pubkey.Address() for the peer's PubKey) If either of these queries return a non-zero ABCI code, Tendermint will refuse to connect to the peer. @@ -128,11 +127,9 @@ On startup, Tendermint calls Info on the Query connection to get the latest committed state of the app. The app MUST return information consistent with the last block it succesfully completed Commit for. -If the app succesfully committed block H but not H+1, then `last_block_height = -H` and `last_block_app_hash = `. If the app +If the app succesfully committed block H but not H+1, then `last_block_height = H` and `last_block_app_hash = `. If the app failed during the Commit of block H, then `last_block_height = H-1` and -`last_block_app_hash = `. +`last_block_app_hash = `. We now distinguish three heights, and describe how Tendermint syncs itself with the app. @@ -165,24 +162,24 @@ If `storeBlockHeight > stateBlockHeight+1`, panic Now, the meat: If `storeBlockHeight == stateBlockHeight && appBlockHeight < storeBlockHeight`, - replay all blocks in full from `appBlockHeight` to `storeBlockHeight`. - This happens if we completed processing the block, but the app forgot its height. +replay all blocks in full from `appBlockHeight` to `storeBlockHeight`. +This happens if we completed processing the block, but the app forgot its height. If `storeBlockHeight == stateBlockHeight && appBlockHeight == storeBlockHeight`, we're done - This happens if we crashed at an opportune spot. +This happens if we crashed at an opportune spot. If `storeBlockHeight == stateBlockHeight+1` - This happens if we started processing the block but didn't finish. +This happens if we started processing the block but didn't finish. - If `appBlockHeight < stateBlockHeight` - replay all blocks in full from `appBlockHeight` to `storeBlockHeight-1`, - and replay the block at `storeBlockHeight` using the WAL. - This happens if the app forgot the last block it committed. + If `appBlockHeight < stateBlockHeight` + replay all blocks in full from `appBlockHeight` to `storeBlockHeight-1`, + and replay the block at `storeBlockHeight` using the WAL. + This happens if the app forgot the last block it committed. - If `appBlockHeight == stateBlockHeight`, - replay the last block (storeBlockHeight) in full. - This happens if we crashed before the app finished Commit + If `appBlockHeight == stateBlockHeight`, + replay the last block (storeBlockHeight) in full. + This happens if we crashed before the app finished Commit - If appBlockHeight == storeBlockHeight { - update the state using the saved ABCI responses but dont run the block against the real app. - This happens if we crashed after the app finished Commit but before Tendermint saved the state. + If appBlockHeight == storeBlockHeight { + update the state using the saved ABCI responses but dont run the block against the real app. + This happens if we crashed after the app finished Commit but before Tendermint saved the state. diff --git a/docs/stop-words.txt b/docs/stop-words.txt new file mode 100644 index 000000000..7f90eca32 --- /dev/null +++ b/docs/stop-words.txt @@ -0,0 +1,6 @@ +investor +invest +investing +token distribution +atom distribution +distribution of atoms diff --git a/docs/tendermint-core/block-structure.md b/docs/tendermint-core/block-structure.md index 803805529..f58e83aad 100644 --- a/docs/tendermint-core/block-structure.md +++ b/docs/tendermint-core/block-structure.md @@ -13,11 +13,11 @@ A [Block](https://godoc.org/github.com/tendermint/tendermint/types#Block) contains: -- a [Header](#header) contains merkle hashes for various chain states -- the - [Data](https://godoc.org/github.com/tendermint/tendermint/types#Data) - is all transactions which are to be processed -- the [LastCommit](#commit) > 2/3 signatures for the last block +- a [Header](#header) contains merkle hashes for various chain states +- the + [Data](https://godoc.org/github.com/tendermint/tendermint/types#Data) + is all transactions which are to be processed +- the [LastCommit](#commit) > 2/3 signatures for the last block The signatures returned along with block `H` are those validating block `H-1`. This can be a little confusing, but we must also consider that @@ -66,7 +66,7 @@ effects of running that transaction will be first visible in the `AppHash` from the block header at height `H+1`. Like the `LastCommit` issue, this is a requirement of the immutability -of the block chain, as the application only applies transactions *after* +of the block chain, as the application only applies transactions _after_ they are commited to the chain. ## Commit @@ -90,7 +90,7 @@ you look at the code, you will notice that we need to provide the `chainID` of the blockchain in order to properly calculate the votes. This is to protect anyone from swapping votes between chains to fake (or frame) a validator. Also note that this `chainID` is in the -`genesis.json` from *Tendermint*, not the `genesis.json` from the +`genesis.json` from _Tendermint_, not the `genesis.json` from the basecoin app ([that is a different chainID...](https://github.com/cosmos/cosmos-sdk/issues/32)). diff --git a/docs/tendermint-core/light-client-protocol.md b/docs/tendermint-core/light-client-protocol.md index 6d905be32..7318ad16f 100644 --- a/docs/tendermint-core/light-client-protocol.md +++ b/docs/tendermint-core/light-client-protocol.md @@ -18,13 +18,13 @@ proofs](./merkle.md#iavl-tree). ## Properties -- You get the full collateralized security benefits of Tendermint; No - need to wait for confirmations. -- You get the full speed benefits of Tendermint; transactions - commit instantly. -- You can get the most recent version of the application state - non-interactively (without committing anything to the blockchain). - For example, this means that you can get the most recent value of a - name from the name-registry without worrying about fork censorship - attacks, without posting a commit and waiting for confirmations. - It's fast, secure, and free! +- You get the full collateralized security benefits of Tendermint; No + need to wait for confirmations. +- You get the full speed benefits of Tendermint; transactions + commit instantly. +- You can get the most recent version of the application state + non-interactively (without committing anything to the blockchain). + For example, this means that you can get the most recent value of a + name from the name-registry without worrying about fork censorship + attacks, without posting a commit and waiting for confirmations. + It's fast, secure, and free! diff --git a/docs/tendermint-core/running-in-production.md b/docs/tendermint-core/running-in-production.md index eeab25c3c..76465b8e7 100644 --- a/docs/tendermint-core/running-in-production.md +++ b/docs/tendermint-core/running-in-production.md @@ -135,33 +135,34 @@ Tendermint, replay will fail with panic. Recovering from data corruption can be hard and time-consuming. Here are two approaches you can take: -1) Delete the WAL file and restart Tendermint. It will attempt to sync with other peers. -2) Try to repair the WAL file manually: +1. Delete the WAL file and restart Tendermint. It will attempt to sync with other peers. +2. Try to repair the WAL file manually: - 1. Create a backup of the corrupted WAL file: +1) Create a backup of the corrupted WAL file: ``` cp "$TMHOME/data/cs.wal/wal" > /tmp/corrupted_wal_backup ``` - 2. Use `./scripts/wal2json` to create a human-readable version +2. Use `./scripts/wal2json` to create a human-readable version ``` ./scripts/wal2json/wal2json "$TMHOME/data/cs.wal/wal" > /tmp/corrupted_wal ``` - 3. Search for a "CORRUPTED MESSAGE" line. - 4. By looking at the previous message and the message after the corrupted one - and looking at the logs, try to rebuild the message. If the consequent - messages are marked as corrupted too (this may happen if length header - got corrupted or some writes did not make it to the WAL ~ truncation), - then remove all the lines starting from the corrupted one and restart - Tendermint. +3. Search for a "CORRUPTED MESSAGE" line. +4. By looking at the previous message and the message after the corrupted one + and looking at the logs, try to rebuild the message. If the consequent + messages are marked as corrupted too (this may happen if length header + got corrupted or some writes did not make it to the WAL ~ truncation), + then remove all the lines starting from the corrupted one and restart + Tendermint. ``` $EDITOR /tmp/corrupted_wal ``` - 5. After editing, convert this file back into binary form by running: + +5. After editing, convert this file back into binary form by running: ``` ./scripts/json2wal/json2wal /tmp/corrupted_wal $TMHOME/data/cs.wal/wal diff --git a/docs/tendermint-core/secure-p2p.md b/docs/tendermint-core/secure-p2p.md index 692fe9ef7..1d90e0588 100644 --- a/docs/tendermint-core/secure-p2p.md +++ b/docs/tendermint-core/secure-p2p.md @@ -61,9 +61,9 @@ Authenticated encryption is enabled by default. ## Additional Reading -- [Implementation](https://github.com/tendermint/tendermint/blob/64bae01d007b5bee0d0827ab53259ffd5910b4e6/p2p/conn/secret_connection.go#L47) -- [Original STS paper by Whitfield Diffie, Paul C. van Oorschot and - Michael J. - Wiener](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.216.6107&rep=rep1&type=pdf) -- [Further work on secret - handshakes](https://dominictarr.github.io/secret-handshake-paper/shs.pdf) +- [Implementation](https://github.com/tendermint/tendermint/blob/64bae01d007b5bee0d0827ab53259ffd5910b4e6/p2p/conn/secret_connection.go#L47) +- [Original STS paper by Whitfield Diffie, Paul C. van Oorschot and + Michael J. + Wiener](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.216.6107&rep=rep1&type=pdf) +- [Further work on secret + handshakes](https://dominictarr.github.io/secret-handshake-paper/shs.pdf) diff --git a/docs/tendermint-core/using-tendermint.md b/docs/tendermint-core/using-tendermint.md index b3ea26248..71b773f07 100644 --- a/docs/tendermint-core/using-tendermint.md +++ b/docs/tendermint-core/using-tendermint.md @@ -39,20 +39,20 @@ definition](https://github.com/tendermint/tendermint/blob/master/types/genesis.g #### Fields -- `genesis_time`: Official time of blockchain start. -- `chain_id`: ID of the blockchain. This must be unique for - every blockchain. If your testnet blockchains do not have unique - chain IDs, you will have a bad time. -- `validators`: -- `pub_key`: The first element specifies the `pub_key` type. 1 - == Ed25519. The second element are the pubkey bytes. -- `power`: The validator's voting power. -- `name`: Name of the validator (optional). -- `app_hash`: The expected application hash (as returned by the - `ResponseInfo` ABCI message) upon genesis. If the app's hash does - not match, Tendermint will panic. -- `app_state`: The application state (e.g. initial distribution - of tokens). +- `genesis_time`: Official time of blockchain start. +- `chain_id`: ID of the blockchain. This must be unique for + every blockchain. If your testnet blockchains do not have unique + chain IDs, you will have a bad time. +- `validators`: +- `pub_key`: The first element specifies the `pub_key` type. 1 + == Ed25519. The second element are the pubkey bytes. +- `power`: The validator's voting power. +- `name`: Name of the validator (optional). +- `app_hash`: The expected application hash (as returned by the + `ResponseInfo` ABCI message) upon genesis. If the app's hash does + not match, Tendermint will panic. +- `app_state`: The application state (e.g. initial distribution + of tokens). #### Sample genesis.json diff --git a/docs/tendermint-core/validators.md b/docs/tendermint-core/validators.md index 0c1d7d89a..5513886ad 100644 --- a/docs/tendermint-core/validators.md +++ b/docs/tendermint-core/validators.md @@ -2,7 +2,7 @@ Validators are responsible for committing new blocks in the blockchain. These validators participate in the consensus protocol by broadcasting -*votes* which contain cryptographic signatures signed by each +_votes_ which contain cryptographic signatures signed by each validator's private key. Some Proof-of-Stake consensus algorithms aim to create a "completely" @@ -28,12 +28,12 @@ There are two ways to become validator. ## Committing a Block -*+2/3 is short for "more than 2/3"* +_+2/3 is short for "more than 2/3"_ A block is committed when +2/3 of the validator set sign [precommit votes](../spec/blockchain/blockchain.md#vote) for that block at the same `round`. The +2/3 set of precommit votes is called a -[*commit*](../spec/blockchain/blockchain.md#commit). While any +2/3 set of +[_commit_](../spec/blockchain/blockchain.md#commit). While any +2/3 set of precommits for the same block at the same height&round can serve as validation, the canonical commit is included in the next block (see [LastCommit](../spec/blockchain/blockchain.md#last-commit)). diff --git a/docs/tools/benchmarking.md b/docs/tools/benchmarking.md index 20c368e29..691d3b6ed 100644 --- a/docs/tools/benchmarking.md +++ b/docs/tools/benchmarking.md @@ -23,7 +23,7 @@ Blocks/sec 0.818 0.386 1 9 [Install Tendermint](../introduction/install) This currently is setup to work on tendermint's develop branch. Please ensure you are on that. (If not, update `tendermint` and `tmlibs` in gopkg.toml to use - the master branch.) +the master branch.) then run: @@ -32,7 +32,7 @@ tendermint init tendermint node --proxy_app=kvstore ``` -``` +``` tm-bench localhost:26657 ``` diff --git a/docs/tools/monitoring.md b/docs/tools/monitoring.md index 5b0ffd071..bd0105c8e 100644 --- a/docs/tools/monitoring.md +++ b/docs/tools/monitoring.md @@ -26,6 +26,7 @@ use `kvstore`: docker run -it --rm -v "/tmp:/tendermint" tendermint/tendermint init docker run -it --rm -v "/tmp:/tendermint" -p "26657:26657" --name=tm tendermint/tendermint node --proxy_app=kvstore ``` + ``` docker run -it --rm -p "26670:26670" --link=tm tendermint/monitor tm:26657 ``` @@ -71,7 +72,7 @@ Flags: Run `tm-monitor` and visit http://localhost:26670 You should see the list of the available RPC endpoints: -``` +``` http://localhost:26670/status http://localhost:26670/status/network http://localhost:26670/monitor?endpoint=_ diff --git a/docs/yarn.lock b/docs/yarn.lock new file mode 100644 index 000000000..4f453ed47 --- /dev/null +++ b/docs/yarn.lock @@ -0,0 +1,2611 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@azu/format-text@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@azu/format-text/-/format-text-1.0.1.tgz#6967350a94640f6b02855169bd897ce54d6cebe2" + +"@azu/style-format@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@azu/style-format/-/style-format-1.0.0.tgz#e70187f8a862e191b1bce6c0268f13acd3a56b20" + dependencies: + "@azu/format-text" "^1.0.1" + +"@sindresorhus/is@^0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" + +"@textlint/ast-node-types@^4.0.2", "@textlint/ast-node-types@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@textlint/ast-node-types/-/ast-node-types-4.0.3.tgz#b51c87bb86022323f764fbdc976b173f19261cc5" + +"@textlint/ast-traverse@^2.0.8", "@textlint/ast-traverse@^2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@textlint/ast-traverse/-/ast-traverse-2.0.9.tgz#4bf427cf01b7195013e75d27540a77ad68c363d9" + dependencies: + "@textlint/ast-node-types" "^4.0.3" + +"@textlint/feature-flag@^3.0.4", "@textlint/feature-flag@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@textlint/feature-flag/-/feature-flag-3.0.5.tgz#3783e0f2661053d2a74fdad775993395a2d530b4" + dependencies: + map-like "^2.0.0" + +"@textlint/fixer-formatter@^3.0.7": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@textlint/fixer-formatter/-/fixer-formatter-3.0.8.tgz#90ef804c60b9e694c8c048a06febbf1f331abd49" + dependencies: + "@textlint/kernel" "^3.0.0" + chalk "^1.1.3" + debug "^2.1.0" + diff "^2.2.2" + interop-require "^1.0.0" + is-file "^1.0.0" + string-width "^1.0.1" + text-table "^0.2.0" + try-resolve "^1.0.1" + +"@textlint/kernel@^2.0.9": + version "2.0.9" + resolved "https://registry.yarnpkg.com/@textlint/kernel/-/kernel-2.0.9.tgz#a4471b7969e192551230c35ea9fae32d80128ee0" + dependencies: + "@textlint/ast-node-types" "^4.0.2" + "@textlint/ast-traverse" "^2.0.8" + "@textlint/feature-flag" "^3.0.4" + "@types/bluebird" "^3.5.18" + bluebird "^3.5.1" + debug "^2.6.6" + deep-equal "^1.0.1" + object-assign "^4.1.1" + structured-source "^3.0.2" + +"@textlint/kernel@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@textlint/kernel/-/kernel-3.0.0.tgz#ba10962acff64f17b9e5fce8089a40f1f8880dcd" + dependencies: + "@textlint/ast-node-types" "^4.0.3" + "@textlint/ast-traverse" "^2.0.9" + "@textlint/feature-flag" "^3.0.5" + "@types/bluebird" "^3.5.18" + bluebird "^3.5.1" + debug "^2.6.6" + deep-equal "^1.0.1" + map-like "^2.0.0" + object-assign "^4.1.1" + structured-source "^3.0.2" + +"@textlint/linter-formatter@^3.0.7": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@textlint/linter-formatter/-/linter-formatter-3.0.8.tgz#030aa03ff3d85dda94ca9fa9e6bf824f9c1cb7ef" + dependencies: + "@azu/format-text" "^1.0.1" + "@azu/style-format" "^1.0.0" + "@textlint/kernel" "^3.0.0" + chalk "^1.0.0" + concat-stream "^1.5.1" + js-yaml "^3.2.4" + optionator "^0.8.1" + pluralize "^2.0.0" + string-width "^1.0.1" + string.prototype.padstart "^3.0.0" + strip-ansi "^3.0.1" + table "^3.7.8" + text-table "^0.2.0" + try-resolve "^1.0.1" + xml-escape "^1.0.0" + +"@textlint/markdown-to-ast@^6.0.8": + version "6.0.9" + resolved "https://registry.yarnpkg.com/@textlint/markdown-to-ast/-/markdown-to-ast-6.0.9.tgz#e7c89e5ad15d17dcd8e5a62758358936827658fa" + dependencies: + "@textlint/ast-node-types" "^4.0.3" + debug "^2.1.3" + remark-frontmatter "^1.2.0" + remark-parse "^5.0.0" + structured-source "^3.0.2" + traverse "^0.6.6" + unified "^6.1.6" + +"@textlint/text-to-ast@^3.0.8": + version "3.0.9" + resolved "https://registry.yarnpkg.com/@textlint/text-to-ast/-/text-to-ast-3.0.9.tgz#dcb63f09cc79ea2096fc823c3b6cd07c79a060b5" + dependencies: + "@textlint/ast-node-types" "^4.0.3" + +"@textlint/textlint-plugin-markdown@^4.0.10": + version "4.0.10" + resolved "https://registry.yarnpkg.com/@textlint/textlint-plugin-markdown/-/textlint-plugin-markdown-4.0.10.tgz#a99b4a308067597e89439a9e87bc1c4a7f4d076b" + dependencies: + "@textlint/markdown-to-ast" "^6.0.8" + +"@textlint/textlint-plugin-text@^3.0.10": + version "3.0.10" + resolved "https://registry.yarnpkg.com/@textlint/textlint-plugin-text/-/textlint-plugin-text-3.0.10.tgz#619600bdc352d33a68e7a73d77d58b0c52b2a44f" + dependencies: + "@textlint/text-to-ast" "^3.0.8" + +"@types/bluebird@^3.5.18": + version "3.5.23" + resolved "https://registry.yarnpkg.com/@types/bluebird/-/bluebird-3.5.23.tgz#e805da976b76892b2b2e50eec29e84914c730670" + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + +adverb-where@0.0.9: + version "0.0.9" + resolved "https://registry.yarnpkg.com/adverb-where/-/adverb-where-0.0.9.tgz#09c5cddd8d503b9fe5f76e0b8dc5c70a8f193e34" + +aggregate-error@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-1.0.0.tgz#888344dad0220a72e3af50906117f48771925fac" + dependencies: + clean-stack "^1.0.0" + indent-string "^3.0.0" + +ajv-keywords@^1.0.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" + +ajv@^4.7.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +ajv@^5.1.0: + version "5.5.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" + dependencies: + co "^4.6.0" + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + dependencies: + color-convert "^1.9.0" + +anymatch@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" + dependencies: + micromatch "^2.1.5" + normalize-path "^2.0.0" + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + dependencies: + arr-flatten "^1.0.1" + +arr-flatten@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + +array-iterate@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/array-iterate/-/array-iterate-1.1.2.tgz#f66a57e84426f8097f4197fbb6c051b8e5cdf7d8" + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + +arrify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + +aws4@^1.6.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + +bail@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.3.tgz#63cfb9ddbac829b02a3128cd53224be78e6c21a3" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + dependencies: + tweetnacl "^0.14.3" + +binary-extensions@^1.0.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" + +bluebird@^3.0.5, bluebird@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + +boundary@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/boundary/-/boundary-1.0.1.tgz#4d67dc2602c0cc16dd9bce7ebf87e948290f5812" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + +builtin-modules@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + +cacheable-request@^2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d" + dependencies: + clone-response "1.0.2" + get-stream "3.0.0" + http-cache-semantics "3.8.1" + keyv "3.0.0" + lowercase-keys "1.0.0" + normalize-url "2.0.1" + responselike "1.0.2" + +camelcase@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + +capture-stack-trace@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +ccount@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.3.tgz#f1cec43f332e2ea5a569fd46f9f5bde4e6102aff" + +chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +character-entities-html4@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.2.tgz#c44fdde3ce66b52e8d321d6c1bf46101f0150610" + +character-entities-legacy@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.2.tgz#7c6defb81648498222c9855309953d05f4d63a9c" + +character-entities@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.2.tgz#58c8f371c0774ef0ba9b2aca5f00d8f100e6e363" + +character-reference-invalid@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.2.tgz#21e421ad3d84055952dab4a43a04e73cd425d3ed" + +charenc@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" + +chokidar@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + +chownr@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" + +circular-json@^0.3.1: + version "0.3.3" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" + +clean-stack@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-1.3.0.tgz#9e821501ae979986c46b1d66d2d432db2fd4ae31" + +clone-response@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + dependencies: + mimic-response "^1.0.0" + +co@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/co/-/co-3.1.0.tgz#4ea54ea5a08938153185e15210c68d9092bc1b78" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +collapse-white-space@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.4.tgz#ce05cf49e54c3277ae573036a26851ba430a0091" + +color-convert@^1.9.0: + version "1.9.2" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147" + dependencies: + color-name "1.1.1" + +color-name@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" + +combined-stream@1.0.6, combined-stream@~1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" + dependencies: + delayed-stream "~1.0.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@^1.5.1: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +create-error-class@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" + dependencies: + capture-stack-trace "^1.0.0" + +crypt@~0.0.1: + version "0.0.2" + resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +debug@^2.1.0, debug@^2.1.2, debug@^2.1.3, debug@^2.6.6: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +debug@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + dependencies: + ms "2.0.0" + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + +decompress-response@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" + dependencies: + mimic-response "^1.0.0" + +deep-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +define-properties@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" + dependencies: + foreach "^2.0.5" + object-keys "^1.0.8" + +del@^2.0.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" + dependencies: + globby "^5.0.0" + is-path-cwd "^1.0.0" + is-path-in-cwd "^1.0.0" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + rimraf "^2.2.8" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + +diff@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/diff/-/diff-2.2.3.tgz#60eafd0d28ee906e4e8ff0a52c1229521033bf99" + +dns-packet@^1.1.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" + dependencies: + ip "^1.1.0" + safe-buffer "^5.0.1" + +dns-socket@^1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/dns-socket/-/dns-socket-1.6.3.tgz#5268724fad4aa46ad9c5ca4ffcd16e1de5342aab" + dependencies: + dns-packet "^1.1.0" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + +e-prime@^0.10.2: + version "0.10.2" + resolved "https://registry.yarnpkg.com/e-prime/-/e-prime-0.10.2.tgz#ea9375eb985636de88013c7a9fb129ad9e15eff8" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.4.3: + version "1.12.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" + dependencies: + es-to-primitive "^1.1.1" + function-bind "^1.1.1" + has "^1.0.1" + is-callable "^1.1.3" + is-regex "^1.0.4" + +es-to-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" + dependencies: + is-callable "^1.1.1" + is-date-object "^1.0.1" + is-symbol "^1.0.1" + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + dependencies: + is-posix-bracket "^0.1.0" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + dependencies: + fill-range "^2.1.0" + +extend@^3.0.0, extend@~3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + dependencies: + is-extglob "^1.0.0" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + +fast-deep-equal@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +fault@^1.0.0, fault@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.2.tgz#c3d0fec202f172a3a4d414042ad2bb5e2a3ffbaa" + dependencies: + format "^0.2.2" + +file-entry-cache@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" + dependencies: + flat-cache "^1.2.1" + object-assign "^4.0.1" + +filename-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + +fill-range@^2.1.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^3.0.0" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +find-up@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + dependencies: + locate-path "^2.0.0" + +flat-cache@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481" + dependencies: + circular-json "^0.3.1" + del "^2.0.2" + graceful-fs "^4.1.2" + write "^0.2.1" + +fn-name@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fn-name/-/fn-name-2.0.1.tgz#5214d7537a4d06a4a301c0cc262feb84188002e7" + +for-in@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + dependencies: + for-in "^1.0.1" + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" + dependencies: + asynckit "^0.4.0" + combined-stream "1.0.6" + mime-types "^2.1.12" + +format@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" + +from2@^2.1.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs-minipass@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" + dependencies: + minipass "^2.2.1" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.0.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426" + dependencies: + nan "^2.9.2" + node-pre-gyp "^0.10.0" + +function-bind@^1.0.2, function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +get-stdin@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" + +get-stream@3.0.0, get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + dependencies: + assert-plus "^1.0.0" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + dependencies: + is-glob "^2.0.0" + +glob@^7.0.3, glob@^7.0.5, glob@^7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globby@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" + dependencies: + array-union "^1.0.1" + arrify "^1.0.0" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +got@^6.7.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" + dependencies: + create-error-class "^3.0.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-redirect "^1.0.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + lowercase-keys "^1.0.0" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + unzip-response "^2.0.1" + url-parse-lax "^1.0.0" + +got@^8.0.0: + version "8.3.2" + resolved "https://registry.yarnpkg.com/got/-/got-8.3.2.tgz#1d23f64390e97f776cac52e5b936e5f514d2e937" + dependencies: + "@sindresorhus/is" "^0.7.0" + cacheable-request "^2.1.1" + decompress-response "^3.3.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + into-stream "^3.1.0" + is-retry-allowed "^1.1.0" + isurl "^1.0.0-alpha5" + lowercase-keys "^1.0.0" + mimic-response "^1.0.0" + p-cancelable "^0.4.0" + p-timeout "^2.0.1" + pify "^3.0.0" + safe-buffer "^5.1.1" + timed-out "^4.0.1" + url-parse-lax "^3.0.0" + url-to-options "^1.0.1" + +graceful-fs@^4.1.2: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + +har-validator@~5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" + dependencies: + ajv "^5.1.0" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + +has-symbol-support-x@^1.4.1: + version "1.4.2" + resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" + +has-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" + +has-to-string-tag-x@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" + dependencies: + has-symbol-support-x "^1.4.1" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +has@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + dependencies: + function-bind "^1.1.1" + +hosted-git-info@^2.1.4: + version "2.7.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" + +http-cache-semantics@3.8.1: + version "3.8.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +iconv-lite@^0.4.4: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + dependencies: + safer-buffer ">= 2.1.2 < 3" + +ignore-walk@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + dependencies: + minimatch "^3.0.4" + +ignore@^3.2.0: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + +indent-string@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + +interop-require@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/interop-require/-/interop-require-1.0.0.tgz#e53103679944c88d7e6105b62a9f4475c783971e" + +into-stream@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6" + dependencies: + from2 "^2.1.1" + p-is-promise "^1.1.0" + +ip-regex@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + +ip@^1.1.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + +is-absolute-url@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" + +is-alphabetical@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.2.tgz#1fa6e49213cb7885b75d15862fb3f3d96c884f41" + +is-alphanumeric@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz#4a9cef71daf4c001c1d81d63d140cf53fd6889f4" + +is-alphanumerical@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.2.tgz#1138e9ae5040158dc6ff76b820acd6b7a181fd40" + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.1.4, is-buffer@^1.1.5, is-buffer@~1.1.1: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + +is-callable@^1.1.1, is-callable@^1.1.3: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75" + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + +is-decimal@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.2.tgz#894662d6a8709d307f3a276ca4339c8fa5dff0ff" + +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + +is-empty@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-empty/-/is-empty-1.2.0.tgz#de9bb5b278738a05a0b09a57e1fb4d4a341a9f6b" + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + +is-file@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-file/-/is-file-1.0.0.tgz#28a44cfbd9d3db193045f22b65fce8edf9620596" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + dependencies: + is-extglob "^1.0.0" + +is-hexadecimal@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.2.tgz#b6e710d7d07bb66b98cb8cece5c9b4921deeb835" + +is-hidden@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-hidden/-/is-hidden-1.1.1.tgz#82ee6a93aeef3fb007ad5b9457c0584d45329f38" + +is-ip@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-2.0.0.tgz#68eea07e8a0a0a94c2d080dd674c731ab2a461ab" + dependencies: + ip-regex "^2.0.0" + +is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + dependencies: + kind-of "^3.0.2" + +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + +is-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" + +is-online@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-online/-/is-online-7.0.0.tgz#7e2408c0ae1e7e37ba8d50bdb237260d32bfd96e" + dependencies: + got "^6.7.1" + p-any "^1.0.0" + p-timeout "^1.0.0" + public-ip "^2.3.0" + +is-path-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" + +is-path-in-cwd@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52" + dependencies: + is-path-inside "^1.0.0" + +is-path-inside@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" + dependencies: + path-is-inside "^1.0.1" + +is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + +is-redirect@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" + +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + dependencies: + has "^1.0.1" + +is-relative-url@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-relative-url/-/is-relative-url-2.0.0.tgz#72902d7fe04b3d4792e7db15f9db84b7204c9cef" + dependencies: + is-absolute-url "^2.0.0" + +is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" + +is-stream@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +is-symbol@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + +is-whitespace-character@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.2.tgz#ede53b4c6f6fb3874533751ec9280d01928d03ed" + +is-word-character@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.2.tgz#46a5dac3f2a1840898b91e576cd40d493f3ae553" + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isemail@^3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/isemail/-/isemail-3.1.3.tgz#64f37fc113579ea12523165c3ebe3a71a56ce571" + dependencies: + punycode "2.x.x" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +isurl@^1.0.0-alpha5: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" + dependencies: + has-to-string-tag-x "^1.2.0" + is-object "^1.0.1" + +js-yaml@^3.12.0, js-yaml@^3.2.4, js-yaml@^3.6.1: + version "3.12.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +json-buffer@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + +json-schema-traverse@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + dependencies: + minimist "^1.2.0" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +keyv@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" + dependencies: + json-buffer "3.0.0" + +kind-of@^3.0.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +link-check@^4.1.0: + version "4.4.4" + resolved "https://registry.yarnpkg.com/link-check/-/link-check-4.4.4.tgz#08dbb881b70c23f1c173889c3a34d682c2e68c1a" + dependencies: + is-relative-url "^2.0.0" + isemail "^3.1.2" + ms "^2.1.1" + request "^2.87.0" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + +load-plugin@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/load-plugin/-/load-plugin-2.2.2.tgz#ebc7599491ff33e5077719fbe051d5725a9f7a89" + dependencies: + npm-prefix "^1.2.0" + resolve-from "^4.0.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +lodash@^4.0.0, lodash@^4.17.4: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + +log-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" + dependencies: + chalk "^1.0.0" + +longest-streak@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.2.tgz#2421b6ba939a443bb9ffebf596585a50b4c38e2e" + +lowercase-keys@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" + +lowercase-keys@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + +map-like@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/map-like/-/map-like-2.0.0.tgz#94496d49ad333c0dc3234b27adbbd1e8535953b4" + +markdown-escapes@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.2.tgz#e639cbde7b99c841c0bacc8a07982873b46d2122" + +markdown-extensions@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/markdown-extensions/-/markdown-extensions-1.1.1.tgz#fea03b539faeaee9b4ef02a3769b455b189f7fc3" + +markdown-table@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.2.tgz#c78db948fa879903a41bce522e3b96f801c63786" + +math-random@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.1.tgz#8b3aac588b8a66e4975e3cdea67f7bb329601fac" + +md5@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" + dependencies: + charenc "~0.0.1" + crypt "~0.0.1" + is-buffer "~1.1.1" + +mdast-util-compact@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-compact/-/mdast-util-compact-1.0.1.tgz#cdb5f84e2b6a2d3114df33bd05d9cb32e3c4083a" + dependencies: + unist-util-modify-children "^1.0.0" + unist-util-visit "^1.1.0" + +micromatch@^2.1.5: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +mime-db@~1.35.0: + version "1.35.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.35.0.tgz#0569d657466491283709663ad379a99b90d9ab47" + +mime-types@^2.1.12, mime-types@~2.1.17: + version "2.1.19" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.19.tgz#71e464537a7ef81c15f2db9d97e913fc0ff606f0" + dependencies: + mime-db "~1.35.0" + +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + +minimatch@^3.0.2, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +minipass@^2.2.1, minipass@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.3.tgz#a7dcc8b7b833f5d368759cce544dccb55f50f233" + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.0.tgz#11e13658ce46bc3a70a267aac58359d1e0c29ceb" + dependencies: + minipass "^2.2.1" + +mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +ms@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + +nan@^2.9.2: + version "2.10.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" + +needle@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.1.tgz#b5e325bd3aae8c2678902fa296f729455d1d3a7d" + dependencies: + debug "^2.1.2" + iconv-lite "^0.4.4" + sax "^1.2.4" + +nlcst-to-string@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/nlcst-to-string/-/nlcst-to-string-2.0.2.tgz#7125af4d4d369850c697192a658f01f36af9937b" + +no-cliches@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/no-cliches/-/no-cliches-0.1.0.tgz#f4eb81a551fecde813f8c611e35e64a5118dc38c" + +node-pre-gyp@^0.10.0: + version "0.10.3" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.3.2: + version "2.4.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.0, normalize-path@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-url@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" + dependencies: + prepend-http "^2.0.0" + query-string "^5.0.1" + sort-keys "^2.0.0" + +npm-bundled@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.3.tgz#7e71703d973af3370a9591bafe3a63aca0be2308" + +npm-packlist@^1.1.6: + version "1.1.11" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.11.tgz#84e8c683cbe7867d34b1d357d893ce29e28a02de" + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + +npm-prefix@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/npm-prefix/-/npm-prefix-1.2.0.tgz#e619455f7074ba54cc66d6d0d37dd9f1be6bcbc0" + dependencies: + rc "^1.1.0" + shellsubstitute "^1.1.0" + untildify "^2.1.0" + +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +oauth-sign@~0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.8: + version "1.0.12" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2" + +object.assign@^4.0.4: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +optionator@^0.8.0, optionator@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-tmpdir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-any@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-any/-/p-any-1.1.0.tgz#1d03835c7eed1e34b8e539c47b7b60d0d015d4e1" + dependencies: + p-some "^2.0.0" + +p-cancelable@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + +p-is-promise@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + dependencies: + p-try "^1.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + dependencies: + p-limit "^1.1.0" + +p-some@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/p-some/-/p-some-2.0.1.tgz#65d87c8b154edbcf5221d167778b6d2e150f6f06" + dependencies: + aggregate-error "^1.0.0" + +p-timeout@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" + dependencies: + p-finally "^1.0.0" + +p-timeout@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038" + dependencies: + p-finally "^1.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + +parse-entities@^1.0.2, parse-entities@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.1.2.tgz#9eaf719b29dc3bd62246b4332009072e01527777" + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +passive-voice@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/passive-voice/-/passive-voice-0.1.0.tgz#16ff91ae40ba0e92c43e671763fdc842a70270b1" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-is-inside@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + +path-to-glob-pattern@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-to-glob-pattern/-/path-to-glob-pattern-1.0.2.tgz#473e6a3a292a9d13fbae3edccee72d3baba8c619" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + dependencies: + pify "^3.0.0" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +pluralize@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-2.0.0.tgz#72b726aa6fac1edeee42256c7d8dc256b335677f" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + +prepend-http@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + +prettier@^1.13.7: + version "1.14.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.2.tgz#0ac1c6e1a90baa22a62925f41963c841983282f9" + +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + +public-ip@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/public-ip/-/public-ip-2.4.0.tgz#f00c028a15366d8c798e47efab6acd09a17666da" + dependencies: + dns-socket "^1.6.2" + got "^8.0.0" + is-ip "^2.0.0" + pify "^3.0.0" + +punycode@2.x.x: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +qs@~6.5.1: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + +query-string@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" + dependencies: + decode-uri-component "^0.2.0" + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +randomatic@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.0.tgz#36f2ca708e9e567f5ed2ec01949026d50aa10116" + dependencies: + is-number "^4.0.0" + kind-of "^6.0.0" + math-random "^1.0.1" + +rc-config-loader@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/rc-config-loader/-/rc-config-loader-2.0.2.tgz#46eb2f98fb5b2aa7b1119d66c0554de5133f1bc1" + dependencies: + debug "^3.1.0" + js-yaml "^3.12.0" + json5 "^1.0.1" + object-assign "^4.1.0" + object-keys "^1.0.12" + path-exists "^3.0.0" + require-from-string "^2.0.2" + +rc@^1.1.0, rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +read-pkg-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" + dependencies: + find-up "^2.0.0" + read-pkg "^3.0.0" + +read-pkg@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + +readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +regex-cache@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + dependencies: + is-equal-shallow "^0.1.3" + +remark-cli@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/remark-cli/-/remark-cli-5.0.0.tgz#9feefd06474f3d0ff132df21b5334c546df12ab6" + dependencies: + markdown-extensions "^1.1.0" + remark "^9.0.0" + unified-args "^5.0.0" + +remark-frontmatter@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/remark-frontmatter/-/remark-frontmatter-1.2.0.tgz#67905d178c0fe531ed12c57b98759f101fc2c1b5" + dependencies: + fault "^1.0.1" + xtend "^4.0.1" + +remark-lint-no-dead-urls@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/remark-lint-no-dead-urls/-/remark-lint-no-dead-urls-0.3.0.tgz#b640ecbb4ccaf780afe28c8d13e79f5dc6769449" + dependencies: + is-online "^7.0.0" + is-relative-url "^2.0.0" + link-check "^4.1.0" + unified-lint-rule "^1.0.1" + unist-util-visit "^1.1.3" + +remark-lint-write-good@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/remark-lint-write-good/-/remark-lint-write-good-1.0.3.tgz#daa4cf122212cfa06e437702ef7b43a12875bd5d" + dependencies: + nlcst-to-string "^2.0.0" + unified-lint-rule "^1.0.1" + unist-util-visit "^1.1.1" + write-good "^0.11.1" + +remark-parse@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-5.0.0.tgz#4c077f9e499044d1d5c13f80d7a98cf7b9285d95" + dependencies: + collapse-white-space "^1.0.2" + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + is-word-character "^1.0.0" + markdown-escapes "^1.0.0" + parse-entities "^1.1.0" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + trim "0.0.1" + trim-trailing-lines "^1.0.0" + unherit "^1.0.4" + unist-util-remove-position "^1.0.0" + vfile-location "^2.0.0" + xtend "^4.0.1" + +remark-stringify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-5.0.0.tgz#336d3a4d4a6a3390d933eeba62e8de4bd280afba" + dependencies: + ccount "^1.0.0" + is-alphanumeric "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + longest-streak "^2.0.1" + markdown-escapes "^1.0.0" + markdown-table "^1.1.0" + mdast-util-compact "^1.0.0" + parse-entities "^1.0.2" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + stringify-entities "^1.0.1" + unherit "^1.0.4" + xtend "^4.0.1" + +remark@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/remark/-/remark-9.0.0.tgz#c5cfa8ec535c73a67c4b0f12bfdbd3a67d8b2f60" + dependencies: + remark-parse "^5.0.0" + remark-stringify "^5.0.0" + unified "^6.0.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.5.0, repeat-string@^1.5.2, repeat-string@^1.5.4: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +replace-ext@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" + +request@^2.87.0: + version "2.87.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.6.0" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.1" + forever-agent "~0.6.1" + form-data "~2.3.1" + har-validator "~5.0.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.17" + oauth-sign "~0.8.2" + performance-now "^2.1.0" + qs "~6.5.1" + safe-buffer "^5.1.1" + tough-cookie "~2.3.3" + tunnel-agent "^0.6.0" + uuid "^3.1.0" + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + +responselike@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" + dependencies: + lowercase-keys "^1.0.0" + +rimraf@^2.2.8, rimraf@^2.6.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + +safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + +"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + +"semver@2 || 3 || 4 || 5", semver@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" + +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +shellsubstitute@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shellsubstitute/-/shellsubstitute-1.2.0.tgz#e4f702a50c518b0f6fe98451890d705af29b6b70" + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +slice-ansi@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" + +sliced@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sliced/-/sliced-1.0.1.tgz#0b3a662b5d04c3177b1926bea82b03f837a2ef41" + +sort-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" + dependencies: + is-plain-obj "^1.0.0" + +spdx-correct@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9" + +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" + +split-lines@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/split-lines/-/split-lines-1.1.0.tgz#3abba8f598614142f9db8d27ab6ab875662a1e09" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +sshpk@^1.7.0: + version "1.14.2" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + safer-buffer "^2.0.2" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +state-toggle@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.1.tgz#c3cb0974f40a6a0f8e905b96789eb41afa1cde3a" + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + +string-width@^1.0.0, string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string.prototype.padstart@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.padstart/-/string.prototype.padstart-3.0.0.tgz#5bcfad39f4649bb2d031292e19bcf0b510d4b242" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.4.3" + function-bind "^1.0.2" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + dependencies: + safe-buffer "~5.1.0" + +stringify-entities@^1.0.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-1.3.2.tgz#a98417e5471fd227b3e45d3db1861c11caf668f7" + dependencies: + character-entities-html4 "^1.0.0" + character-entities-legacy "^1.0.0" + is-alphanumerical "^1.0.0" + is-hexadecimal "^1.0.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +structured-source@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/structured-source/-/structured-source-3.0.2.tgz#dd802425e0f53dc4a6e7aca3752901a1ccda7af5" + dependencies: + boundary "^1.0.1" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^4.1.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" + dependencies: + has-flag "^2.0.0" + +supports-color@^5.3.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + dependencies: + has-flag "^3.0.0" + +table@^3.7.8: + version "3.8.3" + resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" + dependencies: + ajv "^4.7.0" + ajv-keywords "^1.0.0" + chalk "^1.1.1" + lodash "^4.0.0" + slice-ansi "0.0.4" + string-width "^2.0.0" + +tar@^4: + version "4.4.6" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.6.tgz#63110f09c00b4e60ac8bcfe1bf3c8660235fbc9b" + dependencies: + chownr "^1.0.1" + fs-minipass "^1.2.5" + minipass "^2.3.3" + minizlib "^1.1.0" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.2" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + +textlint-rule-helper@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/textlint-rule-helper/-/textlint-rule-helper-2.0.0.tgz#95cb4696c95c4258d2e3389e9e64b849f9721382" + dependencies: + unist-util-visit "^1.1.0" + +textlint-rule-stop-words@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/textlint-rule-stop-words/-/textlint-rule-stop-words-1.0.3.tgz#fe2f40cbe5837331b2a09fdec57cc71758093bf0" + dependencies: + lodash "^4.17.4" + split-lines "^1.1.0" + textlint-rule-helper "^2.0.0" + +textlint@^10.2.1: + version "10.2.1" + resolved "https://registry.yarnpkg.com/textlint/-/textlint-10.2.1.tgz#ee22b7967d59cef7c74a04a5f4e8883134e5c79d" + dependencies: + "@textlint/ast-node-types" "^4.0.2" + "@textlint/ast-traverse" "^2.0.8" + "@textlint/feature-flag" "^3.0.4" + "@textlint/fixer-formatter" "^3.0.7" + "@textlint/kernel" "^2.0.9" + "@textlint/linter-formatter" "^3.0.7" + "@textlint/textlint-plugin-markdown" "^4.0.10" + "@textlint/textlint-plugin-text" "^3.0.10" + "@types/bluebird" "^3.5.18" + bluebird "^3.0.5" + debug "^2.1.0" + deep-equal "^1.0.1" + file-entry-cache "^2.0.0" + get-stdin "^5.0.1" + glob "^7.1.1" + interop-require "^1.0.0" + is-file "^1.0.0" + log-symbols "^1.0.2" + map-like "^2.0.0" + md5 "^2.2.1" + mkdirp "^0.5.0" + object-assign "^4.0.1" + optionator "^0.8.0" + path-to-glob-pattern "^1.0.2" + rc-config-loader "^2.0.1" + read-pkg "^1.1.0" + read-pkg-up "^3.0.0" + structured-source "^3.0.2" + try-resolve "^1.0.1" + unique-concat "^0.2.2" + +timed-out@^4.0.0, timed-out@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + +to-vfile@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/to-vfile/-/to-vfile-2.2.0.tgz#342d1705e6df526d569b1fc8bfa29f1f36d6c416" + dependencies: + is-buffer "^1.1.4" + vfile "^2.0.0" + x-is-function "^1.0.4" + +too-wordy@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/too-wordy/-/too-wordy-0.1.4.tgz#8e7b20a7b7a4d8fc3759f4e00c4929993d1b12f0" + +tough-cookie@~2.3.3: + version "2.3.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" + dependencies: + punycode "^1.4.1" + +traverse@^0.6.6: + version "0.6.6" + resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" + +trim-trailing-lines@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.1.tgz#e0ec0810fd3c3f1730516b45f49083caaf2774d9" + +trim@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" + +trough@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.3.tgz#e29bd1614c6458d44869fc28b255ab7857ef7c24" + +try-resolve@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/try-resolve/-/try-resolve-1.0.1.tgz#cfde6fabd72d63e5797cfaab873abbe8e700e912" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + +unherit@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.1.tgz#132748da3e88eab767e08fabfbb89c5e9d28628c" + dependencies: + inherits "^2.0.1" + xtend "^4.0.1" + +unified-args@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/unified-args/-/unified-args-5.1.0.tgz#1889200e072998a662e6e84d817d6f4b5f448dd1" + dependencies: + camelcase "^4.0.0" + chalk "^2.0.0" + chokidar "^1.5.1" + json5 "^0.5.1" + minimist "^1.2.0" + text-table "^0.2.0" + unified-engine "^5.1.0" + +unified-engine@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/unified-engine/-/unified-engine-5.1.0.tgz#30db83bcc76c821f773bb5a8a491aa0e2471e3d1" + dependencies: + concat-stream "^1.5.1" + debug "^3.1.0" + fault "^1.0.0" + fn-name "^2.0.1" + glob "^7.0.3" + ignore "^3.2.0" + is-empty "^1.0.0" + is-hidden "^1.0.1" + is-object "^1.0.1" + js-yaml "^3.6.1" + load-plugin "^2.0.0" + parse-json "^4.0.0" + to-vfile "^2.0.0" + trough "^1.0.0" + unist-util-inspect "^4.1.2" + vfile-reporter "^4.0.0" + vfile-statistics "^1.1.0" + x-is-function "^1.0.4" + x-is-string "^0.1.0" + xtend "^4.0.1" + +unified-lint-rule@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/unified-lint-rule/-/unified-lint-rule-1.0.3.tgz#e302b0c4a7ac428c0980e049a500e59528001299" + dependencies: + wrapped "^1.0.1" + +unified@^6.0.0, unified@^6.1.6: + version "6.2.0" + resolved "https://registry.yarnpkg.com/unified/-/unified-6.2.0.tgz#7fbd630f719126d67d40c644b7e3f617035f6dba" + dependencies: + bail "^1.0.0" + extend "^3.0.0" + is-plain-obj "^1.1.0" + trough "^1.0.0" + vfile "^2.0.0" + x-is-string "^0.1.0" + +unique-concat@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/unique-concat/-/unique-concat-0.2.2.tgz#9210f9bdcaacc5e1e3929490d7c019df96f18712" + +unist-util-inspect@^4.1.2: + version "4.1.3" + resolved "https://registry.yarnpkg.com/unist-util-inspect/-/unist-util-inspect-4.1.3.tgz#39470e6d77485db285966df78431219aa1287822" + dependencies: + is-empty "^1.0.0" + +unist-util-is@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-2.1.2.tgz#1193fa8f2bfbbb82150633f3a8d2eb9a1c1d55db" + +unist-util-modify-children@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/unist-util-modify-children/-/unist-util-modify-children-1.1.2.tgz#c7f1b91712554ee59c47a05b551ed3e052a4e2d1" + dependencies: + array-iterate "^1.0.0" + +unist-util-remove-position@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-1.1.2.tgz#86b5dad104d0bbfbeb1db5f5c92f3570575c12cb" + dependencies: + unist-util-visit "^1.1.0" + +unist-util-stringify-position@^1.0.0, unist-util-stringify-position@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz#3f37fcf351279dcbca7480ab5889bb8a832ee1c6" + +unist-util-visit-parents@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-2.0.1.tgz#63fffc8929027bee04bfef7d2cce474f71cb6217" + dependencies: + unist-util-is "^2.1.2" + +unist-util-visit@^1.1.0, unist-util-visit@^1.1.1, unist-util-visit@^1.1.3: + version "1.4.0" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.0.tgz#1cb763647186dc26f5e1df5db6bd1e48b3cc2fb1" + dependencies: + unist-util-visit-parents "^2.0.0" + +untildify@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-2.1.0.tgz#17eb2807987f76952e9c0485fc311d06a826a2e0" + dependencies: + os-homedir "^1.0.0" + +unzip-response@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + dependencies: + prepend-http "^1.0.1" + +url-parse-lax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" + dependencies: + prepend-http "^2.0.0" + +url-to-options@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +uuid@^3.1.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vfile-location@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-2.0.3.tgz#083ba80e50968e8d420be49dd1ea9a992131df77" + +vfile-message@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-1.0.1.tgz#51a2ccd8a6b97a7980bb34efb9ebde9632e93677" + dependencies: + unist-util-stringify-position "^1.1.1" + +vfile-reporter@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/vfile-reporter/-/vfile-reporter-4.0.0.tgz#ea6f0ae1342f4841573985e05f941736f27de9da" + dependencies: + repeat-string "^1.5.0" + string-width "^1.0.0" + supports-color "^4.1.0" + unist-util-stringify-position "^1.0.0" + vfile-statistics "^1.1.0" + +vfile-statistics@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/vfile-statistics/-/vfile-statistics-1.1.1.tgz#a22fd4eb844c9eaddd781ad3b3246db88375e2e3" + +vfile@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-2.3.0.tgz#e62d8e72b20e83c324bc6c67278ee272488bf84a" + dependencies: + is-buffer "^1.1.4" + replace-ext "1.0.0" + unist-util-stringify-position "^1.0.0" + vfile-message "^1.0.0" + +weasel-words@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/weasel-words/-/weasel-words-0.1.1.tgz#7137946585c73fe44882013853bd000c5d687a4e" + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + dependencies: + string-width "^1.0.2 || 2" + +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +wrapped@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wrapped/-/wrapped-1.0.1.tgz#c783d9d807b273e9b01e851680a938c87c907242" + dependencies: + co "3.1.0" + sliced "^1.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +write-good@^0.11.1: + version "0.11.3" + resolved "https://registry.yarnpkg.com/write-good/-/write-good-0.11.3.tgz#8eeb5da9a8e155dafb1325d27eba33cb67d24d8c" + dependencies: + adverb-where "0.0.9" + e-prime "^0.10.2" + no-cliches "^0.1.0" + object.assign "^4.0.4" + passive-voice "^0.1.0" + too-wordy "^0.1.4" + weasel-words "^0.1.1" + +write@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" + dependencies: + mkdirp "^0.5.1" + +x-is-function@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/x-is-function/-/x-is-function-1.0.4.tgz#5d294dc3d268cbdd062580e0c5df77a391d1fa1e" + +x-is-string@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82" + +xml-escape@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/xml-escape/-/xml-escape-1.1.0.tgz#3904c143fa8eb3a0030ec646d2902a2f1b706c44" + +xtend@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + +yallist@^3.0.0, yallist@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9"