Release/v0.22.4pull/1974/head v0.22.4
@ -1,9 +1,329 @@ | |||||
We are working to finalize an updated Tendermint specification with formal | |||||
proofs of safety and liveness. | |||||
# Byzantine Consensus Algorithm | |||||
In the meantime, see the [description in the | |||||
docs](http://tendermint.readthedocs.io/en/master/specification/byzantine-consensus-algorithm.html). | |||||
## Terms | |||||
There are also relevant but somewhat outdated descriptions in Jae Kwon's [original | |||||
whitepaper](https://tendermint.com/static/docs/tendermint.pdf) and Ethan Buchman's [master's | |||||
thesis](https://atrium.lib.uoguelph.ca/xmlui/handle/10214/9769). | |||||
- 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 `<nil>` 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* | |||||
(`Propose`, `Prevote`, and `Precommit`), along with two special steps | |||||
`Commit` and `NewHeight`. | |||||
In the optimal scenario, the order of steps is: | |||||
``` | |||||
NewHeight -> (Propose -> Prevote -> Precommit)+ -> Commit -> NewHeight ->... | |||||
``` | |||||
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 `<nil>` 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 | |||||
parameters over each successive round. | |||||
## State Machine Diagram | |||||
``` | |||||
+-------------------------------------+ | |||||
v |(Wait til `CommmitTime+timeoutCommit`) | |||||
+-----------+ +-----+-----+ | |||||
+----------> | Propose +--------------+ | NewHeight | | |||||
| +-----------+ | +-----------+ | |||||
| | ^ | |||||
|(Else, after timeoutPrecommit) v | | |||||
+-----+-----+ +-----------+ | | |||||
| Precommit | <------------------------+ Prevote | | | |||||
+-----+-----+ +-----------+ | | |||||
|(When +2/3 Precommits for block found) | | |||||
v | | |||||
+--------------------------------------------------------------------+ | |||||
| Commit | | |||||
| | | |||||
| * Set CommitTime = now; | | |||||
| * Wait for block, then stage/save/commit block; | | |||||
+--------------------------------------------------------------------+ | |||||
``` | |||||
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 | |||||
validator-nodes) have an associated state (the current height, round, | |||||
and step) and work to make progress. | |||||
Between two nodes there exists a `Connection`, and multiplexed on top of | |||||
this connection are fairly throttled `Channel`s of information. An | |||||
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) | |||||
There's more, but let's not get ahead of ourselves here. | |||||
## Proposals | |||||
A proposal is signed and published by the designated proposer at each | |||||
round. The proposer is chosen by a deterministic and non-choking round | |||||
robin selection algorithm that selects proposers in proportion to their | |||||
voting power (see | |||||
[implementation](https://github.com/tendermint/tendermint/blob/develop/types/validator_set.go)). | |||||
A proposal at `(H,R)` is composed of a block and an optional latest | |||||
`PoLC-Round < R` which is included iff the proposer knows of one. This | |||||
hints the network to allow nodes to unlock (when safe) to ensure the | |||||
liveness property. | |||||
## State Machine Spec | |||||
### Propose Step (height:H,round:R) | |||||
Upon entering `Propose`: - The designated proposer proposes a block at | |||||
`(H,R)`. | |||||
The `Propose` step ends: - After `timeoutProposeR` after entering | |||||
`Propose`. --> goto `Prevote(H,R)` - After receiving proposal block | |||||
and all prevotes at `PoLC-Round`. --> goto `Prevote(H,R)` - After | |||||
[common exit conditions](#common-exit-conditions) | |||||
### Prevote Step (height:H,round:R) | |||||
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 `<nil>`. | |||||
The `Prevote` step ends: - After +2/3 prevotes for a particular block or | |||||
`<nil>`. -->; goto `Precommit(H,R)` - After `timeoutPrevote` after | |||||
receiving any +2/3 prevotes. --> goto `Precommit(H,R)` - After | |||||
[common exit conditions](#common-exit-conditions) | |||||
### 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 | |||||
`<nil>`, it unlocks and precommits `<nil>`. - Else, it keeps the lock | |||||
unchanged and precommits `<nil>`. | |||||
A precommit for `<nil>` means "I didn’t see a PoLC for this round, but I | |||||
did get +2/3 prevotes and waited a bit". | |||||
The Precommit step ends: - After +2/3 precommits for `<nil>`. --> | |||||
goto `Propose(H,R+1)` - After `timeoutPrecommit` after receiving any | |||||
+2/3 precommits. --> goto `Propose(H,R+1)` - After [common exit | |||||
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)` | |||||
### Commit Step (height:H) | |||||
- 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)` | |||||
## Proofs | |||||
### Proof of Safety | |||||
Assume that at most -1/3 of the voting power of validators is byzantine. | |||||
If a validator commits block `B` at round `R`, it's because it saw +2/3 | |||||
of precommits at round `R`. This implies that 1/3+ of honest nodes are | |||||
still locked at round `R' > R`. These locked validators will remain | |||||
locked until they see a PoLC at `R' > R`, but this won't happen because | |||||
1/3+ are locked and honest, so at most -2/3 are available to vote for | |||||
anything other than `B`. | |||||
### Proof of Liveness | |||||
If 1/3+ honest validators are locked on two different blocks from | |||||
different rounds, a proposers' `PoLC-Round` will eventually cause nodes | |||||
locked from the earlier round to unlock. Eventually, the designated | |||||
proposer will be one that is aware of a PoLC at the later round. Also, | |||||
`timeoutProposalR` increments with round `R`, while the size of a | |||||
proposal are capped, so eventually the network is able to "fully gossip" | |||||
the whole proposal (e.g. the block & PoLC). | |||||
### Proof of Fork Accountability | |||||
Define the JSet (justification-vote-set) at height `H` of a validator | |||||
`V1` to be all the votes signed by the validator at `H` along with | |||||
justification PoLC prevotes for each lock change. For example, if `V1` | |||||
signed the following precommits: `Precommit(B1 @ round 0)`, | |||||
`Precommit(<nil> @ round 1)`, `Precommit(B2 @ round 4)` (note that no | |||||
precommits were signed for rounds 2 and 3, and that's ok), | |||||
`Precommit(B1 @ round 0)` must be justified by a PoLC at round 0, and | |||||
`Precommit(B2 @ round 4)` must be justified by a PoLC at round 4; but | |||||
the precommit for `<nil>` at round 1 is not a lock-change by definition | |||||
so the JSet for `V1` need not include any prevotes at round 1, 2, or 3 | |||||
(unless `V1` happened to have prevoted for those rounds). | |||||
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 | |||||
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. | |||||
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. | |||||
Either we will find 1/3+ who cannot justify at least one of their votes, | |||||
and/or, we will find 1/3+ who had double-signed. | |||||
### Alternative algorithm | |||||
Alternatively, we can take the JSet of a commit to be the "full commit". | |||||
That is, if light clients and validators do not consider a block to be | |||||
committed unless the JSet of the commit is also known, then we get the | |||||
desirable property that if there ever is a fork (e.g. there are two | |||||
conflicting "full commits"), then 1/3+ of the validators are immediately | |||||
punishable for double-signing. | |||||
There are many ways to ensure that the gossip network efficiently share | |||||
the JSet of a commit. One solution is to add a new message type that | |||||
tells peers that this node has (or does not have) a +2/3 majority for B | |||||
(or) at (H,R), and a bitarray of which votes contributed towards that | |||||
majority. Peers can react by responding with appropriate votes. | |||||
We will implement such an algorithm for the next iteration of the | |||||
Tendermint consensus protocol. | |||||
Other potential improvements include adding more data in votes such as | |||||
the last known PoLC round that caused a lock change, and the last voted | |||||
round/step (or, we may require that validators not skip any votes). This | |||||
may make JSet verification/gossip logic easier to implement. | |||||
### Censorship Attacks | |||||
Due to the definition of a block | |||||
[commit](../../tendermint-core/validator.md#commiting-a-block), any 1/3+ coalition of | |||||
validators can halt the blockchain by not broadcasting their votes. Such | |||||
a coalition can also censor particular transactions by rejecting blocks | |||||
that include these transactions, though this would result in a | |||||
significant proportion of block proposals to be rejected, which would | |||||
slow down the rate of block commits of the blockchain, reducing its | |||||
utility and value. The malicious coalition might also broadcast votes in | |||||
a trickle so as to grind blockchain block commits to a near halt, or | |||||
engage in any combination of these attacks. | |||||
If a global active adversary were also involved, it can partition the | |||||
network in such a way that it may appear that the wrong subset of | |||||
validators were responsible for the slowdown. This is not just a | |||||
limitation of Tendermint, but rather a limitation of all consensus | |||||
protocols whose network is potentially controlled by an active | |||||
adversary. | |||||
### Overcoming Forks and Censorship Attacks | |||||
For these types of attacks, a subset of the validators through external | |||||
means should coordinate to sign a reorg-proposal that chooses a fork | |||||
(and any evidence thereof) and the initial subset of validators with | |||||
their signatures. Validators who sign such a reorg-proposal forego its | |||||
collateral on all other forks. Clients should verify the signatures on | |||||
the reorg-proposal, verify any evidence, and make a judgement or prompt | |||||
the end-user for a decision. For example, a phone wallet app may prompt | |||||
the user with a security warning, while a refrigerator may accept any | |||||
reorg-proposal signed by +1/2 of the original validators. | |||||
No non-synchronous Byzantine fault-tolerant algorithm can come to | |||||
consensus when 1/3+ of validators are dishonest, yet a fork assumes that | |||||
1/3+ of validators have already been dishonest by double-signing or | |||||
lock-changing without justification. So, signing the reorg-proposal is a | |||||
coordination problem that cannot be solved by any non-synchronous | |||||
protocol (i.e. automatically, and without making assumptions about the | |||||
reliability of the underlying network). It must be provided by means | |||||
external to the weakly-synchronous Tendermint consensus algorithm. For | |||||
now, we leave the problem of reorg-proposal coordination to human | |||||
coordination via internet media. Validators must take care to ensure | |||||
that there are no significant network partitions, to avoid situations | |||||
where two conflicting reorg-proposals are signed. | |||||
Assuming that the external coordination medium and protocol is robust, | |||||
it follows that forks are less of a concern than [censorship | |||||
attacks](#censorship-attacks). |
@ -1,218 +0,0 @@ | |||||
Block Structure | |||||
=============== | |||||
The tendermint consensus engine records all agreements by a | |||||
supermajority of nodes into a blockchain, which is replicated among all | |||||
nodes. This blockchain is accessible via various rpc endpoints, mainly | |||||
``/block?height=`` to get the full block, as well as | |||||
``/blockchain?minHeight=_&maxHeight=_`` to get a list of headers. But | |||||
what exactly is stored in these blocks? | |||||
Block | |||||
~~~~~ | |||||
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 | |||||
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 the ``Header`` also contains the ``LastCommitHash``. It would be | |||||
impossible for a Header to include the commits that sign it, as it would | |||||
cause an infinite loop here. But when we get block ``H``, we find | |||||
``Header.LastCommitHash``, which must match the hash of ``LastCommit``. | |||||
Header | |||||
~~~~~~ | |||||
The | |||||
`Header <https://godoc.org/github.com/tendermint/tendermint/types#Header>`__ | |||||
contains lots of information (follow link for up-to-date info). Notably, | |||||
it maintains the ``Height``, the ``LastBlockID`` (to make it a chain), | |||||
and hashes of the data, the app state, and the validator set. This is | |||||
important as the only item that is signed by the validators is the | |||||
``Header``, and all other data must be validated against one of the | |||||
merkle hashes in the ``Header``. | |||||
The ``DataHash`` can provide a nice check on the | |||||
`Data <https://godoc.org/github.com/tendermint/tendermint/types#Data>`__ | |||||
returned in this same block. If you are subscribed to new blocks, via | |||||
tendermint RPC, in order to display or process the new transactions you | |||||
should at least validate that the ``DataHash`` is valid. If it is | |||||
important to verify autheniticity, you must wait for the ``LastCommit`` | |||||
from the next block to make sure the block header (including | |||||
``DataHash``) was properly signed. | |||||
The ``ValidatorHash`` contains a hash of the current | |||||
`Validators <https://godoc.org/github.com/tendermint/tendermint/types#Validator>`__. | |||||
Tracking all changes in the validator set is complex, but a client can | |||||
quickly compare this hash with the `hash of the currently known | |||||
validators <https://godoc.org/github.com/tendermint/tendermint/types#ValidatorSet.Hash>`__ | |||||
to see if there have been changes. | |||||
The ``AppHash`` serves as the basis for validating any merkle proofs | |||||
that come from the ABCI application. It represents the | |||||
state of the actual application, rather that the state of the blockchain | |||||
itself. This means it's necessary in order to perform any business | |||||
logic, such as verifying an account balance. | |||||
**Note** After the transactions are committed to a block, they still | |||||
need to be processed in a separate step, which happens between the | |||||
blocks. If you find a given transaction in the block at height ``H``, | |||||
the 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* | |||||
they are commited to the chain. | |||||
Commit | |||||
~~~~~~ | |||||
The | |||||
`Commit <https://godoc.org/github.com/tendermint/tendermint/types#Commit>`__ | |||||
contains a set of | |||||
`Votes <https://godoc.org/github.com/tendermint/tendermint/types#Vote>`__ | |||||
that were made by the validator set to reach consensus on this block. | |||||
This is the key to the security in any PoS system, and actually no data | |||||
that cannot be traced back to a block header with a valid set of Votes | |||||
can be trusted. Thus, getting the Commit data and verifying the votes is | |||||
extremely important. | |||||
As mentioned above, in order to find the ``precommit votes`` for block | |||||
header ``H``, we need to query block ``H+1``. Then we need to check the | |||||
votes, make sure they really are for that block, and properly formatted. | |||||
Much of this code is implemented in Go in the | |||||
`light-client <https://github.com/tendermint/light-client>`__ package. | |||||
If 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 | |||||
basecoin app (`that is a different | |||||
chainID... <https://github.com/cosmos/cosmos-sdk/issues/32>`__). | |||||
Once we have those votes, and we calculated the proper `sign | |||||
bytes <https://godoc.org/github.com/tendermint/tendermint/types#Vote.WriteSignBytes>`__ | |||||
using the chainID and a `nice helper | |||||
function <https://godoc.org/github.com/tendermint/tendermint/types#SignBytes>`__, | |||||
we can verify them. The light client is responsible for maintaining a | |||||
set of validators that we trust. Each vote only stores the validators | |||||
``Address``, as well as the ``Signature``. Assuming we have a local copy | |||||
of the trusted validator set, we can look up the ``Public Key`` of the | |||||
validator given its ``Address``, then verify that the ``Signature`` | |||||
matches the ``SignBytes`` and ``Public Key``. Then we sum up the total | |||||
voting power of all validators, whose votes fulfilled all these | |||||
stringent requirements. If the total number of voting power for a single | |||||
block is greater than 2/3 of all voting power, then we can finally trust | |||||
the block header, the AppHash, and the proof we got from the ABCI | |||||
application. | |||||
Vote Sign Bytes | |||||
^^^^^^^^^^^^^^^ | |||||
The ``sign-bytes`` of a vote is produced by taking a | |||||
`stable-json <https://github.com/substack/json-stable-stringify>`__-like | |||||
deterministic JSON `wire <./wire-protocol.html>`__ encoding of | |||||
the vote (excluding the ``Signature`` field), and wrapping it with | |||||
``{"chain_id":"my_chain","vote":...}``. | |||||
For example, a precommit vote might have the following ``sign-bytes``: | |||||
.. code:: json | |||||
{"chain_id":"my_chain","vote":{"block_hash":"611801F57B4CE378DF1A3FFF1216656E89209A99","block_parts_header":{"hash":"B46697379DBE0774CC2C3B656083F07CA7E0F9CE","total":123},"height":1234,"round":1,"type":2}} | |||||
Block Hash | |||||
~~~~~~~~~~ | |||||
The `block | |||||
hash <https://godoc.org/github.com/tendermint/tendermint/types#Block.Hash>`__ | |||||
is the `Simple Tree hash <./merkle.html#simple-tree-with-dictionaries>`__ | |||||
of the fields of the block ``Header`` encoded as a list of | |||||
``KVPair``\ s. | |||||
Transaction | |||||
~~~~~~~~~~~ | |||||
A transaction is any sequence of bytes. It is up to your | |||||
ABCI application to accept or reject transactions. | |||||
BlockID | |||||
~~~~~~~ | |||||
Many of these data structures refer to the | |||||
`BlockID <https://godoc.org/github.com/tendermint/tendermint/types#BlockID>`__, | |||||
which is the ``BlockHash`` (hash of the block header, also referred to | |||||
by the next block) along with the ``PartSetHeader``. The | |||||
``PartSetHeader`` is explained below and is used internally to | |||||
orchestrate the p2p propogation. For clients, it is basically opaque | |||||
bytes, but they must match for all votes. | |||||
PartSetHeader | |||||
~~~~~~~~~~~~~ | |||||
The | |||||
`PartSetHeader <https://godoc.org/github.com/tendermint/tendermint/types#PartSetHeader>`__ | |||||
contains the total number of pieces in a | |||||
`PartSet <https://godoc.org/github.com/tendermint/tendermint/types#PartSet>`__, | |||||
and the Merkle root hash of those pieces. | |||||
PartSet | |||||
~~~~~~~ | |||||
PartSet is used to split a byteslice of data into parts (pieces) for | |||||
transmission. By splitting data into smaller parts and computing a | |||||
Merkle root hash on the list, you can verify that a part is legitimately | |||||
part of the complete data, and the part can be forwarded to other peers | |||||
before all the parts are known. In short, it's a fast way to securely | |||||
propagate a large chunk of data (like a block) over a gossip network. | |||||
PartSet was inspired by the LibSwift project. | |||||
Usage: | |||||
.. code:: go | |||||
data := RandBytes(2 << 20) // Something large | |||||
partSet := NewPartSetFromData(data) | |||||
partSet.Total() // Total number of 4KB parts | |||||
partSet.Count() // Equal to the Total, since we already have all the parts | |||||
partSet.Hash() // The Merkle root hash | |||||
partSet.BitArray() // A BitArray of partSet.Total() 1's | |||||
header := partSet.Header() // Send this to the peer | |||||
header.Total // Total number of parts | |||||
header.Hash // The merkle root hash | |||||
// Now we'll reconstruct the data from the parts | |||||
partSet2 := NewPartSetFromHeader(header) | |||||
partSet2.Total() // Same total as partSet.Total() | |||||
partSet2.Count() // Zero, since this PartSet doesn't have any parts yet. | |||||
partSet2.Hash() // Same hash as in partSet.Hash() | |||||
partSet2.BitArray() // A BitArray of partSet.Total() 0's | |||||
// In a gossip network the parts would arrive in arbitrary order, perhaps | |||||
// in response to explicit requests for parts, or optimistically in response | |||||
// to the receiving peer's partSet.BitArray(). | |||||
for !partSet2.IsComplete() { | |||||
part := receivePartFromGossipNetwork() | |||||
added, err := partSet2.AddPart(part) | |||||
if err != nil { | |||||
// A wrong part, | |||||
// the merkle trail does not hash to partSet2.Hash() | |||||
} else if !added { | |||||
// A duplicate part already received | |||||
} | |||||
} | |||||
data2, _ := ioutil.ReadAll(partSet2.GetReader()) | |||||
bytes.Equal(data, data2) // true |
@ -1,349 +0,0 @@ | |||||
Byzantine Consensus Algorithm | |||||
============================= | |||||
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 ``<nil>`` 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* | |||||
(``Propose``, ``Prevote``, and ``Precommit``), along with two special | |||||
steps ``Commit`` and ``NewHeight``. | |||||
In the optimal scenario, the order of steps is: | |||||
:: | |||||
NewHeight -> (Propose -> Prevote -> Precommit)+ -> Commit -> NewHeight ->... | |||||
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 ``<nil>`` 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 | |||||
parameters over each successive round. | |||||
State Machine Diagram | |||||
--------------------- | |||||
:: | |||||
+-------------------------------------+ | |||||
v |(Wait til `CommmitTime+timeoutCommit`) | |||||
+-----------+ +-----+-----+ | |||||
+----------> | Propose +--------------+ | NewHeight | | |||||
| +-----------+ | +-----------+ | |||||
| | ^ | |||||
|(Else, after timeoutPrecommit) v | | |||||
+-----+-----+ +-----------+ | | |||||
| Precommit | <------------------------+ Prevote | | | |||||
+-----+-----+ +-----------+ | | |||||
|(When +2/3 Precommits for block found) | | |||||
v | | |||||
+--------------------------------------------------------------------+ | |||||
| Commit | | |||||
| | | |||||
| * Set CommitTime = now; | | |||||
| * Wait for block, then stage/save/commit block; | | |||||
+--------------------------------------------------------------------+ | |||||
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 | |||||
validator-nodes) have an associated state (the current height, round, | |||||
and step) and work to make progress. | |||||
Between two nodes there exists a ``Connection``, and multiplexed on top | |||||
of this connection are fairly throttled ``Channel``\ s of information. | |||||
An 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) | |||||
There's more, but let's not get ahead of ourselves here. | |||||
Proposals | |||||
--------- | |||||
A proposal is signed and published by the designated proposer at each | |||||
round. The proposer is chosen by a deterministic and non-choking round | |||||
robin selection algorithm that selects proposers in proportion to their | |||||
voting power. (see | |||||
`implementation <https://github.com/tendermint/tendermint/blob/develop/types/validator_set.go>`__) | |||||
A proposal at ``(H,R)`` is composed of a block and an optional latest | |||||
``PoLC-Round < R`` which is included iff the proposer knows of one. This | |||||
hints the network to allow nodes to unlock (when safe) to ensure the | |||||
liveness property. | |||||
State Machine Spec | |||||
------------------ | |||||
Propose Step (height:H,round:R) | |||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||||
Upon entering ``Propose``: - The designated proposer proposes a block at | |||||
``(H,R)``. | |||||
The ``Propose`` step ends: - After ``timeoutProposeR`` after entering | |||||
``Propose``. --> goto ``Prevote(H,R)`` - After receiving proposal block | |||||
and all prevotes at ``PoLC-Round``. --> goto ``Prevote(H,R)`` - After | |||||
`common exit conditions <#common-exit-conditions>`__ | |||||
Prevote Step (height:H,round:R) | |||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||||
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 ``<nil>``. | |||||
The ``Prevote`` step ends: - After +2/3 prevotes for a particular block | |||||
or ``<nil>``. --> goto ``Precommit(H,R)`` - After ``timeoutPrevote`` | |||||
after receiving any +2/3 prevotes. --> goto ``Precommit(H,R)`` - After | |||||
`common exit conditions <#common-exit-conditions>`__ | |||||
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 ``<nil>``, it unlocks and precommits ``<nil>``. - Else, it keeps the | |||||
lock unchanged and precommits ``<nil>``. | |||||
A precommit for ``<nil>`` means "I didn’t see a PoLC for this round, but | |||||
I did get +2/3 prevotes and waited a bit". | |||||
The Precommit step ends: - After +2/3 precommits for ``<nil>``. --> goto | |||||
``Propose(H,R+1)`` - After ``timeoutPrecommit`` after receiving any +2/3 | |||||
precommits. --> goto ``Propose(H,R+1)`` - After `common exit | |||||
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)`` | |||||
Commit Step (height:H) | |||||
~~~~~~~~~~~~~~~~~~~~~~ | |||||
- 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)`` | |||||
Proofs | |||||
------ | |||||
Proof of Safety | |||||
~~~~~~~~~~~~~~~ | |||||
Assume that at most -1/3 of the voting power of validators is byzantine. | |||||
If a validator commits block ``B`` at round ``R``, it's because it saw | |||||
+2/3 of precommits at round ``R``. This implies that 1/3+ of honest | |||||
nodes are still locked at round ``R' > R``. These locked validators will | |||||
remain locked until they see a PoLC at ``R' > R``, but this won't happen | |||||
because 1/3+ are locked and honest, so at most -2/3 are available to | |||||
vote for anything other than ``B``. | |||||
Proof of Liveness | |||||
~~~~~~~~~~~~~~~~~ | |||||
If 1/3+ honest validators are locked on two different blocks from | |||||
different rounds, a proposers' ``PoLC-Round`` will eventually cause | |||||
nodes locked from the earlier round to unlock. Eventually, the | |||||
designated proposer will be one that is aware of a PoLC at the later | |||||
round. Also, ``timeoutProposalR`` increments with round ``R``, while the | |||||
size of a proposal are capped, so eventually the network is able to | |||||
"fully gossip" the whole proposal (e.g. the block & PoLC). | |||||
Proof of Fork Accountability | |||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||||
Define the JSet (justification-vote-set) at height ``H`` of a validator | |||||
``V1`` to be all the votes signed by the validator at ``H`` along with | |||||
justification PoLC prevotes for each lock change. For example, if ``V1`` | |||||
signed the following precommits: ``Precommit(B1 @ round 0)``, | |||||
``Precommit(<nil> @ round 1)``, ``Precommit(B2 @ round 4)`` (note that | |||||
no precommits were signed for rounds 2 and 3, and that's ok), | |||||
``Precommit(B1 @ round 0)`` must be justified by a PoLC at round 0, and | |||||
``Precommit(B2 @ round 4)`` must be justified by a PoLC at round 4; but | |||||
the precommit for ``<nil>`` at round 1 is not a lock-change by | |||||
definition so the JSet for ``V1`` need not include any prevotes at round | |||||
1, 2, or 3 (unless ``V1`` happened to have prevoted for those rounds). | |||||
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 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. | |||||
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. | |||||
Either we will find 1/3+ who cannot justify at least one of their votes, | |||||
and/or, we will find 1/3+ who had double-signed. | |||||
Alternative algorithm | |||||
~~~~~~~~~~~~~~~~~~~~~ | |||||
Alternatively, we can take the JSet of a commit to be the "full commit". | |||||
That is, if light clients and validators do not consider a block to be | |||||
committed unless the JSet of the commit is also known, then we get the | |||||
desirable property that if there ever is a fork (e.g. there are two | |||||
conflicting "full commits"), then 1/3+ of the validators are immediately | |||||
punishable for double-signing. | |||||
There are many ways to ensure that the gossip network efficiently share | |||||
the JSet of a commit. One solution is to add a new message type that | |||||
tells peers that this node has (or does not have) a +2/3 majority for B | |||||
(or ) at (H,R), and a bitarray of which votes contributed towards that | |||||
majority. Peers can react by responding with appropriate votes. | |||||
We will implement such an algorithm for the next iteration of the | |||||
Tendermint consensus protocol. | |||||
Other potential improvements include adding more data in votes such as | |||||
the last known PoLC round that caused a lock change, and the last voted | |||||
round/step (or, we may require that validators not skip any votes). This | |||||
may make JSet verification/gossip logic easier to implement. | |||||
Censorship Attacks | |||||
~~~~~~~~~~~~~~~~~~ | |||||
Due to the definition of a block | |||||
`commit <validators.html#commiting-a-block>`__, any 1/3+ | |||||
coalition of validators can halt the blockchain by not broadcasting | |||||
their votes. Such a coalition can also censor particular transactions by | |||||
rejecting blocks that include these transactions, though this would | |||||
result in a significant proportion of block proposals to be rejected, | |||||
which would slow down the rate of block commits of the blockchain, | |||||
reducing its utility and value. The malicious coalition might also | |||||
broadcast votes in a trickle so as to grind blockchain block commits to | |||||
a near halt, or engage in any combination of these attacks. | |||||
If a global active adversary were also involved, it can partition the | |||||
network in such a way that it may appear that the wrong subset of | |||||
validators were responsible for the slowdown. This is not just a | |||||
limitation of Tendermint, but rather a limitation of all consensus | |||||
protocols whose network is potentially controlled by an active | |||||
adversary. | |||||
Overcoming Forks and Censorship Attacks | |||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||||
For these types of attacks, a subset of the validators through external | |||||
means should coordinate to sign a reorg-proposal that chooses a fork | |||||
(and any evidence thereof) and the initial subset of validators with | |||||
their signatures. Validators who sign such a reorg-proposal forego its | |||||
collateral on all other forks. Clients should verify the signatures on | |||||
the reorg-proposal, verify any evidence, and make a judgement or prompt | |||||
the end-user for a decision. For example, a phone wallet app may prompt | |||||
the user with a security warning, while a refrigerator may accept any | |||||
reorg-proposal signed by +1/2 of the original validators. | |||||
No non-synchronous Byzantine fault-tolerant algorithm can come to | |||||
consensus when 1/3+ of validators are dishonest, yet a fork assumes that | |||||
1/3+ of validators have already been dishonest by double-signing or | |||||
lock-changing without justification. So, signing the reorg-proposal is a | |||||
coordination problem that cannot be solved by any non-synchronous | |||||
protocol (i.e. automatically, and without making assumptions about the | |||||
reliability of the underlying network). It must be provided by means | |||||
external to the weakly-synchronous Tendermint consensus algorithm. For | |||||
now, we leave the problem of reorg-proposal coordination to human | |||||
coordination via internet media. Validators must take care to ensure | |||||
that there are no significant network partitions, to avoid situations | |||||
where two conflicting reorg-proposals are signed. | |||||
Assuming that the external coordination medium and protocol is robust, | |||||
it follows that forks are less of a concern than `censorship | |||||
attacks <#censorship-attacks>`__. |
@ -1,70 +0,0 @@ | |||||
Corruption | |||||
========== | |||||
Important step | |||||
-------------- | |||||
Make sure you have a backup of the Tendermint data directory. | |||||
Possible causes | |||||
--------------- | |||||
Remember that most corruption is caused by hardware issues: | |||||
- RAID controllers with faulty / worn out battery backup, and an unexpected power loss | |||||
- Hard disk drives with write-back cache enabled, and an unexpected power loss | |||||
- Cheap SSDs with insufficient power-loss protection, and an unexpected power-loss | |||||
- Defective RAM | |||||
- Defective or overheating CPU(s) | |||||
Other causes can be: | |||||
- Database systems configured with fsync=off and an OS crash or power loss | |||||
- Filesystems configured to use write barriers plus a storage layer that ignores write barriers. LVM is a particular culprit. | |||||
- Tendermint bugs | |||||
- Operating system bugs | |||||
- Admin error | |||||
- directly modifying Tendermint data-directory contents | |||||
(Source: https://wiki.postgresql.org/wiki/Corruption) | |||||
WAL Corruption | |||||
-------------- | |||||
If consensus WAL is corrupted at the lastest height and you are trying to start | |||||
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. Create a backup of the corrupted WAL file: | |||||
.. code:: bash | |||||
cp "$TMHOME/data/cs.wal/wal" > /tmp/corrupted_wal_backup | |||||
2. Use ./scripts/wal2json to create a human-readable version | |||||
.. code:: bash | |||||
./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. | |||||
.. code:: bash | |||||
$EDITOR /tmp/corrupted_wal | |||||
5. After editing, convert this file back into binary form by running: | |||||
.. code:: bash | |||||
./scripts/json2wal/json2wal /tmp/corrupted_wal > "$TMHOME/data/cs.wal/wal" |
@ -1,71 +0,0 @@ | |||||
Genesis | |||||
======= | |||||
The genesis.json file in ``$TMHOME/config`` defines the initial TendermintCore | |||||
state upon genesis of the blockchain (`see | |||||
definition <https://github.com/tendermint/tendermint/blob/master/types/genesis.go>`__). | |||||
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). | |||||
Sample genesis.json | |||||
~~~~~~~~~~~~~~~~~~~ | |||||
.. code:: json | |||||
{ | |||||
"genesis_time": "2016-02-05T06:02:31.526Z", | |||||
"chain_id": "chain-tTH4mi", | |||||
"validators": [ | |||||
{ | |||||
"pub_key": [ | |||||
1, | |||||
"9BC5112CB9614D91CE423FA8744885126CD9D08D9FC9D1F42E552D662BAA411E" | |||||
], | |||||
"power": 1, | |||||
"name": "mach1" | |||||
}, | |||||
{ | |||||
"pub_key": [ | |||||
1, | |||||
"F46A5543D51F31660D9F59653B4F96061A740FF7433E0DC1ECBC30BE8494DE06" | |||||
], | |||||
"power": 1, | |||||
"name": "mach2" | |||||
}, | |||||
{ | |||||
"pub_key": [ | |||||
1, | |||||
"0E7B423C1635FD07C0FC3603B736D5D27953C1C6CA865BB9392CD79DE1A682BB" | |||||
], | |||||
"power": 1, | |||||
"name": "mach3" | |||||
}, | |||||
{ | |||||
"pub_key": [ | |||||
1, | |||||
"4F49237B9A32EB50682EDD83C48CE9CDB1D02A7CFDADCFF6EC8C1FAADB358879" | |||||
], | |||||
"power": 1, | |||||
"name": "mach4" | |||||
} | |||||
], | |||||
"app_hash": "15005165891224E721CB664D15CB972240F5703F", | |||||
"app_state": { | |||||
{"account": "Bob", "coins": 5000} | |||||
} | |||||
} |
@ -1,33 +0,0 @@ | |||||
Light Client Protocol | |||||
===================== | |||||
Light clients are an important part of the complete blockchain system | |||||
for most applications. Tendermint provides unique speed and security | |||||
properties for light client applications. | |||||
See our `lite package | |||||
<https://godoc.org/github.com/tendermint/tendermint/lite>`__. | |||||
Overview | |||||
-------- | |||||
The objective of the light client protocol is to get a | |||||
`commit <./validators.html#committing-a-block>`__ for a recent | |||||
`block hash <./block-structure.html#block-hash>`__ where the commit | |||||
includes a majority of signatures from the last known validator set. | |||||
From there, all the application state is verifiable with `merkle | |||||
proofs <./merkle.html#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! |
@ -1,88 +0,0 @@ | |||||
Merkle | |||||
====== | |||||
For an overview of Merkle trees, see | |||||
`wikipedia <https://en.wikipedia.org/wiki/Merkle_tree>`__. | |||||
There are two types of Merkle trees used in Tendermint. | |||||
- **IAVL+ Tree**: An immutable self-balancing binary | |||||
tree for persistent application state | |||||
- **Simple Tree**: A simple compact binary tree for | |||||
a static list of items | |||||
IAVL+ Tree | |||||
---------- | |||||
The purpose of this data structure is to provide persistent storage for | |||||
key-value pairs (e.g. account state, name-registrar data, and | |||||
per-contract data) such that a deterministic merkle root hash can be | |||||
computed. The tree is balanced using a variant of the `AVL | |||||
algorithm <http://en.wikipedia.org/wiki/AVL_tree>`__ so all operations | |||||
are O(log(n)). | |||||
Nodes of this tree are immutable and indexed by its hash. Thus any node | |||||
serves as an immutable snapshot which lets us stage uncommitted | |||||
transactions from the mempool cheaply, and we can instantly roll back to | |||||
the last committed state to process transactions of a newly committed | |||||
block (which may not be the same set of transactions as those from the | |||||
mempool). | |||||
In an AVL tree, the heights of the two child subtrees of any node differ | |||||
by at most one. Whenever this condition is violated upon an update, the | |||||
tree is rebalanced by creating O(log(n)) new nodes that point to | |||||
unmodified nodes of the old tree. In the original AVL algorithm, inner | |||||
nodes can also hold key-value pairs. The AVL+ algorithm (note the plus) | |||||
modifies the AVL algorithm to keep all values on leaf nodes, while only | |||||
using branch-nodes to store keys. This simplifies the algorithm while | |||||
minimizing the size of merkle proofs | |||||
In Ethereum, the analog is the `Patricia | |||||
trie <http://en.wikipedia.org/wiki/Radix_tree>`__. There are tradeoffs. | |||||
Keys do not need to be hashed prior to insertion in IAVL+ trees, so this | |||||
provides faster iteration in the key space which may benefit some | |||||
applications. The logic is simpler to implement, requiring only two | |||||
types of nodes -- inner nodes and leaf nodes. The IAVL+ tree is a binary | |||||
tree, so merkle proofs are much shorter than the base 16 Patricia trie. | |||||
On the other hand, while IAVL+ trees provide a deterministic merkle root | |||||
hash, it depends on the order of updates. In practice this shouldn't be | |||||
a problem, since you can efficiently encode the tree structure when | |||||
serializing the tree contents. | |||||
Simple Tree | |||||
----------- | |||||
For merkelizing smaller static lists, use the 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. | |||||
:: | |||||
Simple Tree with 6 items Simple Tree with 7 items | |||||
* * | |||||
/ \ / \ | |||||
/ \ / \ | |||||
/ \ / \ | |||||
/ \ / \ | |||||
* * * * | |||||
/ \ / \ / \ / \ | |||||
/ \ / \ / \ / \ | |||||
/ \ / \ / \ / \ | |||||
* h2 * h5 * * * h6 | |||||
/ \ / \ / \ / \ / \ | |||||
h0 h1 h3 h4 h0 h1 h2 h3 h4 h5 | |||||
Simple Tree with Dictionaries | |||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||||
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 | |||||
similarly derived. |
@ -1 +0,0 @@ | |||||
Spec moved to [docs/spec](https://github.com/tendermint/tendermint/tree/master/docs/spec). |
@ -1,172 +0,0 @@ | |||||
Wire Protocol | |||||
============= | |||||
The `Tendermint wire protocol <https://github.com/tendermint/go-wire>`__ | |||||
encodes data in `c-style binary <#binary>`__ and `JSON <#json>`__ form. | |||||
Supported types | |||||
--------------- | |||||
- Primitive types | |||||
- ``uint8`` (aka ``byte``), ``uint16``, ``uint32``, ``uint64`` | |||||
- ``int8``, ``int16``, ``int32``, ``int64`` | |||||
- ``uint``, ``int``: variable length (un)signed integers | |||||
- ``string``, ``[]byte`` | |||||
- ``time`` | |||||
- Derived types | |||||
- structs | |||||
- var-length arrays of a particular type | |||||
- fixed-length arrays of a particular type | |||||
- interfaces: registered union types preceded by a ``type byte`` | |||||
- pointers | |||||
Binary | |||||
------ | |||||
**Fixed-length primitive types** are encoded with 1,2,3, or 4 big-endian | |||||
bytes. - ``uint8`` (aka ``byte``), ``uint16``, ``uint32``, ``uint64``: | |||||
takes 1,2,3, and 4 bytes respectively - ``int8``, ``int16``, ``int32``, | |||||
``int64``: takes 1,2,3, and 4 bytes respectively - ``time``: ``int64`` | |||||
representation of nanoseconds since epoch | |||||
**Variable-length integers** are encoded with a single leading byte | |||||
representing the length of the following big-endian bytes. For signed | |||||
negative integers, the most significant bit of the leading byte is a 1. | |||||
- ``uint``: 1-byte length prefixed variable-size (0 ~ 255 bytes) | |||||
unsigned integers | |||||
- ``int``: 1-byte length prefixed variable-size (0 ~ 127 bytes) signed | |||||
integers | |||||
NOTE: While the number 0 (zero) is encoded with a single byte ``x00``, | |||||
the number 1 (one) takes two bytes to represent: ``x0101``. This isn't | |||||
the most efficient representation, but the rules are easier to remember. | |||||
+---------------+----------------+----------------+ | |||||
| number | binary | binary ``int`` | | |||||
| | ``uint`` | | | |||||
+===============+================+================+ | |||||
| 0 | ``x00`` | ``x00`` | | |||||
+---------------+----------------+----------------+ | |||||
| 1 | ``x0101`` | ``x0101`` | | |||||
+---------------+----------------+----------------+ | |||||
| 2 | ``x0102`` | ``x0102`` | | |||||
+---------------+----------------+----------------+ | |||||
| 256 | ``x020100`` | ``x020100`` | | |||||
+---------------+----------------+----------------+ | |||||
| 2^(127\ *8)-1 | ``x800100...`` | overflow | | |||||
| \| | | | | |||||
| ``x7FFFFF...` | | | | |||||
| ` | | | | |||||
| \| | | | | |||||
| ``x7FFFFF...` | | | | |||||
| ` | | | | |||||
| \| \| | | | | |||||
| 2^(127*\ 8) | | | | |||||
+---------------+----------------+----------------+ | |||||
| 2^(255\*8)-1 | | |||||
| \| | | |||||
| ``xFFFFFF...` | | |||||
| ` | | |||||
| \| overflow | | |||||
| \| \| -1 \| | | |||||
| n/a \| | | |||||
| ``x8101`` \| | | |||||
| \| -2 \| n/a | | |||||
| \| ``x8102`` | | |||||
| \| \| -256 \| | | |||||
| n/a \| | | |||||
| ``x820100`` | | |||||
| \| | | |||||
+---------------+----------------+----------------+ | |||||
**Structures** are encoded by encoding the field values in order of | |||||
declaration. | |||||
.. code:: go | |||||
type Foo struct { | |||||
MyString string | |||||
MyUint32 uint32 | |||||
} | |||||
var foo = Foo{"626172", math.MaxUint32} | |||||
/* The binary representation of foo: | |||||
0103626172FFFFFFFF | |||||
0103: `int` encoded length of string, here 3 | |||||
626172: 3 bytes of string "bar" | |||||
FFFFFFFF: 4 bytes of uint32 MaxUint32 | |||||
*/ | |||||
**Variable-length arrays** are encoded with a leading ``int`` denoting | |||||
the length of the array followed by the binary representation of the | |||||
items. **Fixed-length arrays** are similar but aren't preceded by the | |||||
leading ``int``. | |||||
.. code:: go | |||||
foos := []Foo{foo, foo} | |||||
/* The binary representation of foos: | |||||
01020103626172FFFFFFFF0103626172FFFFFFFF | |||||
0102: `int` encoded length of array, here 2 | |||||
0103626172FFFFFFFF: the first `foo` | |||||
0103626172FFFFFFFF: the second `foo` | |||||
*/ | |||||
foos := [2]Foo{foo, foo} // fixed-length array | |||||
/* The binary representation of foos: | |||||
0103626172FFFFFFFF0103626172FFFFFFFF | |||||
0103626172FFFFFFFF: the first `foo` | |||||
0103626172FFFFFFFF: the second `foo` | |||||
*/ | |||||
**Interfaces** can represent one of any number of concrete types. The | |||||
concrete types of an interface must first be declared with their | |||||
corresponding ``type byte``. An interface is then encoded with the | |||||
leading ``type byte``, then the binary encoding of the underlying | |||||
concrete type. | |||||
NOTE: The byte ``x00`` is reserved for the ``nil`` interface value and | |||||
``nil`` pointer values. | |||||
.. code:: go | |||||
type Animal interface{} | |||||
type Dog uint32 | |||||
type Cat string | |||||
RegisterInterface( | |||||
struct{ Animal }{}, // Convenience for referencing the 'Animal' interface | |||||
ConcreteType{Dog(0), 0x01}, // Register the byte 0x01 to denote a Dog | |||||
ConcreteType{Cat(""), 0x02}, // Register the byte 0x02 to denote a Cat | |||||
) | |||||
var animal Animal = Dog(02) | |||||
/* The binary representation of animal: | |||||
010102 | |||||
01: the type byte for a `Dog` | |||||
0102: the bytes of Dog(02) | |||||
*/ | |||||
**Pointers** are encoded with a single leading byte ``x00`` for ``nil`` | |||||
pointers, otherwise encoded with a leading byte ``x01`` followed by the | |||||
binary encoding of the value pointed to. | |||||
NOTE: It's easy to convert pointer types into interface types, since the | |||||
``type byte`` ``x00`` is always ``nil``. | |||||
JSON | |||||
---- | |||||
The JSON codec is compatible with the ```binary`` <#binary>`__ codec, | |||||
and is fairly intuitive if you're already familiar with golang's JSON | |||||
encoding. Some quirks are noted below: | |||||
- variable-length and fixed-length bytes are encoded as uppercase | |||||
hexadecimal strings | |||||
- interface values are encoded as an array of two items: | |||||
``[type_byte, concrete_value]`` | |||||
- times are encoded as rfc2822 strings |
@ -0,0 +1,206 @@ | |||||
# Block Structure | |||||
The tendermint consensus engine records all agreements by a | |||||
supermajority of nodes into a blockchain, which is replicated among all | |||||
nodes. This blockchain is accessible via various rpc endpoints, mainly | |||||
`/block?height=` to get the full block, as well as | |||||
`/blockchain?minHeight=_&maxHeight=_` to get a list of headers. But what | |||||
exactly is stored in these blocks? | |||||
## Block | |||||
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 | |||||
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 | |||||
the `Header` also contains the `LastCommitHash`. It would be impossible | |||||
for a Header to include the commits that sign it, as it would cause an | |||||
infinite loop here. But when we get block `H`, we find | |||||
`Header.LastCommitHash`, which must match the hash of `LastCommit`. | |||||
## Header | |||||
The | |||||
[Header](https://godoc.org/github.com/tendermint/tendermint/types#Header) | |||||
contains lots of information (follow link for up-to-date info). Notably, | |||||
it maintains the `Height`, the `LastBlockID` (to make it a chain), and | |||||
hashes of the data, the app state, and the validator set. This is | |||||
important as the only item that is signed by the validators is the | |||||
`Header`, and all other data must be validated against one of the merkle | |||||
hashes in the `Header`. | |||||
The `DataHash` can provide a nice check on the | |||||
[Data](https://godoc.org/github.com/tendermint/tendermint/types#Data) | |||||
returned in this same block. If you are subscribed to new blocks, via | |||||
tendermint RPC, in order to display or process the new transactions you | |||||
should at least validate that the `DataHash` is valid. If it is | |||||
important to verify autheniticity, you must wait for the `LastCommit` | |||||
from the next block to make sure the block header (including `DataHash`) | |||||
was properly signed. | |||||
The `ValidatorHash` contains a hash of the current | |||||
[Validators](https://godoc.org/github.com/tendermint/tendermint/types#Validator). | |||||
Tracking all changes in the validator set is complex, but a client can | |||||
quickly compare this hash with the [hash of the currently known | |||||
validators](https://godoc.org/github.com/tendermint/tendermint/types#ValidatorSet.Hash) | |||||
to see if there have been changes. | |||||
The `AppHash` serves as the basis for validating any merkle proofs that | |||||
come from the ABCI application. It represents the state of the actual | |||||
application, rather that the state of the blockchain itself. This means | |||||
it's necessary in order to perform any business logic, such as verifying | |||||
an account balance. | |||||
**Note** After the transactions are committed to a block, they still | |||||
need to be processed in a separate step, which happens between the | |||||
blocks. If you find a given transaction in the block at height `H`, the | |||||
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* | |||||
they are commited to the chain. | |||||
## Commit | |||||
The | |||||
[Commit](https://godoc.org/github.com/tendermint/tendermint/types#Commit) | |||||
contains a set of | |||||
[Votes](https://godoc.org/github.com/tendermint/tendermint/types#Vote) | |||||
that were made by the validator set to reach consensus on this block. | |||||
This is the key to the security in any PoS system, and actually no data | |||||
that cannot be traced back to a block header with a valid set of Votes | |||||
can be trusted. Thus, getting the Commit data and verifying the votes is | |||||
extremely important. | |||||
As mentioned above, in order to find the `precommit votes` for block | |||||
header `H`, we need to query block `H+1`. Then we need to check the | |||||
votes, make sure they really are for that block, and properly formatted. | |||||
Much of this code is implemented in Go in the | |||||
[light-client](https://github.com/tendermint/light-client) package. If | |||||
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 | |||||
basecoin app ([that is a different | |||||
chainID...](https://github.com/cosmos/cosmos-sdk/issues/32)). | |||||
Once we have those votes, and we calculated the proper [sign | |||||
bytes](https://godoc.org/github.com/tendermint/tendermint/types#Vote.WriteSignBytes) | |||||
using the chainID and a [nice helper | |||||
function](https://godoc.org/github.com/tendermint/tendermint/types#SignBytes), | |||||
we can verify them. The light client is responsible for maintaining a | |||||
set of validators that we trust. Each vote only stores the validators | |||||
`Address`, as well as the `Signature`. Assuming we have a local copy of | |||||
the trusted validator set, we can look up the `Public Key` of the | |||||
validator given its `Address`, then verify that the `Signature` matches | |||||
the `SignBytes` and `Public Key`. Then we sum up the total voting power | |||||
of all validators, whose votes fulfilled all these stringent | |||||
requirements. If the total number of voting power for a single block is | |||||
greater than 2/3 of all voting power, then we can finally trust the | |||||
block header, the AppHash, and the proof we got from the ABCI | |||||
application. | |||||
### Vote Sign Bytes | |||||
The `sign-bytes` of a vote is produced by taking a | |||||
[stable-json](https://github.com/substack/json-stable-stringify)-like | |||||
deterministic JSON [wire](./wire-protocol.html) encoding of the vote | |||||
(excluding the `Signature` field), and wrapping it with | |||||
`{"chain_id":"my_chain","vote":...}`. | |||||
For example, a precommit vote might have the following `sign-bytes`: | |||||
``` | |||||
{"chain_id":"my_chain","vote":{"block_hash":"611801F57B4CE378DF1A3FFF1216656E89209A99","block_parts_header":{"hash":"B46697379DBE0774CC2C3B656083F07CA7E0F9CE","total":123},"height":1234,"round":1,"type":2}} | |||||
``` | |||||
## Block Hash | |||||
The [block | |||||
hash](https://godoc.org/github.com/tendermint/tendermint/types#Block.Hash) | |||||
is the [Simple Tree hash](./merkle.html#simple-tree-with-dictionaries) | |||||
of the fields of the block `Header` encoded as a list of `KVPair`s. | |||||
## Transaction | |||||
A transaction is any sequence of bytes. It is up to your ABCI | |||||
application to accept or reject transactions. | |||||
## BlockID | |||||
Many of these data structures refer to the | |||||
[BlockID](https://godoc.org/github.com/tendermint/tendermint/types#BlockID), | |||||
which is the `BlockHash` (hash of the block header, also referred to by | |||||
the next block) along with the `PartSetHeader`. The `PartSetHeader` is | |||||
explained below and is used internally to orchestrate the p2p | |||||
propogation. For clients, it is basically opaque bytes, but they must | |||||
match for all votes. | |||||
## PartSetHeader | |||||
The | |||||
[PartSetHeader](https://godoc.org/github.com/tendermint/tendermint/types#PartSetHeader) | |||||
contains the total number of pieces in a | |||||
[PartSet](https://godoc.org/github.com/tendermint/tendermint/types#PartSet), | |||||
and the Merkle root hash of those pieces. | |||||
## PartSet | |||||
PartSet is used to split a byteslice of data into parts (pieces) for | |||||
transmission. By splitting data into smaller parts and computing a | |||||
Merkle root hash on the list, you can verify that a part is legitimately | |||||
part of the complete data, and the part can be forwarded to other peers | |||||
before all the parts are known. In short, it's a fast way to securely | |||||
propagate a large chunk of data (like a block) over a gossip network. | |||||
PartSet was inspired by the LibSwift project. | |||||
Usage: | |||||
``` | |||||
data := RandBytes(2 << 20) // Something large | |||||
partSet := NewPartSetFromData(data) | |||||
partSet.Total() // Total number of 4KB parts | |||||
partSet.Count() // Equal to the Total, since we already have all the parts | |||||
partSet.Hash() // The Merkle root hash | |||||
partSet.BitArray() // A BitArray of partSet.Total() 1's | |||||
header := partSet.Header() // Send this to the peer | |||||
header.Total // Total number of parts | |||||
header.Hash // The merkle root hash | |||||
// Now we'll reconstruct the data from the parts | |||||
partSet2 := NewPartSetFromHeader(header) | |||||
partSet2.Total() // Same total as partSet.Total() | |||||
partSet2.Count() // Zero, since this PartSet doesn't have any parts yet. | |||||
partSet2.Hash() // Same hash as in partSet.Hash() | |||||
partSet2.BitArray() // A BitArray of partSet.Total() 0's | |||||
// In a gossip network the parts would arrive in arbitrary order, perhaps | |||||
// in response to explicit requests for parts, or optimistically in response | |||||
// to the receiving peer's partSet.BitArray(). | |||||
for !partSet2.IsComplete() { | |||||
part := receivePartFromGossipNetwork() | |||||
added, err := partSet2.AddPart(part) | |||||
if err != nil { | |||||
// A wrong part, | |||||
// the merkle trail does not hash to partSet2.Hash() | |||||
} else if !added { | |||||
// A duplicate part already received | |||||
} | |||||
} | |||||
data2, _ := ioutil.ReadAll(partSet2.GetReader()) | |||||
bytes.Equal(data, data2) // true | |||||
``` |
@ -0,0 +1,30 @@ | |||||
# Light Client Protocol | |||||
Light clients are an important part of the complete blockchain system | |||||
for most applications. Tendermint provides unique speed and security | |||||
properties for light client applications. | |||||
See our [lite | |||||
package](https://godoc.org/github.com/tendermint/tendermint/lite). | |||||
## Overview | |||||
The objective of the light client protocol is to get a | |||||
[commit](./validators.md#committing-a-block) for a recent [block | |||||
hash](../spec/consensus/consensus.md.md#block-hash) where the commit includes a | |||||
majority of signatures from the last known validator set. From there, | |||||
all the application state is verifiable with [merkle | |||||
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! |
@ -1,10 +1,10 @@ | |||||
gen_query_parser: | gen_query_parser: | ||||
@go get github.com/pointlander/peg | |||||
go get -u -v github.com/pointlander/peg | |||||
peg -inline -switch query.peg | peg -inline -switch query.peg | ||||
fuzzy_test: | fuzzy_test: | ||||
@go get github.com/dvyukov/go-fuzz/go-fuzz | |||||
@go get github.com/dvyukov/go-fuzz/go-fuzz-build | |||||
go get -u -v github.com/dvyukov/go-fuzz/go-fuzz | |||||
go get -u -v github.com/dvyukov/go-fuzz/go-fuzz-build | |||||
go-fuzz-build github.com/tendermint/tendermint/libs/pubsub/query/fuzz_test | go-fuzz-build github.com/tendermint/tendermint/libs/pubsub/query/fuzz_test | ||||
go-fuzz -bin=./fuzz_test-fuzz.zip -workdir=./fuzz_test/output | go-fuzz -bin=./fuzz_test-fuzz.zip -workdir=./fuzz_test/output | ||||
@ -0,0 +1,41 @@ | |||||
#!/usr/bin/env bash | |||||
# XXX: this script is intended to be run from | |||||
# an MacOS machine | |||||
# as written, this script will install | |||||
# tendermint core from master branch | |||||
REPO=github.com/tendermint/tendermint | |||||
# change this to a specific release or branch | |||||
BRANCH=master | |||||
if ! [ -x "$(command -v brew)" ]; then | |||||
echo 'Error: brew is not installed, to install brew' >&2 | |||||
echo 'follow the instructions here: https://docs.brew.sh/Installation' >&2 | |||||
exit 1 | |||||
fi | |||||
if ! [ -x "$(command -v go)" ]; then | |||||
echo 'Error: go is not installed, to install go follow' >&2 | |||||
echo 'the instructions here: https://golang.org/doc/install#tarball' >&2 | |||||
echo 'ALSO MAKE SURE TO SETUP YOUR $GOPATH and $GOBIN in your ~/.profile: https://github.com/golang/go/wiki/SettingGOPATH' >&2 | |||||
exit 1 | |||||
fi | |||||
if ! [ -x "$(command -v make)" ]; then | |||||
echo 'Make not installed, installing using brew...' | |||||
brew install make | |||||
fi | |||||
# get the code and move into repo | |||||
go get $REPO | |||||
cd $GOPATH/src/$REPO | |||||
# build & install | |||||
git checkout $BRANCH | |||||
# XXX: uncomment if branch isn't master | |||||
# git fetch origin $BRANCH | |||||
make get_tools | |||||
make get_vendor_deps | |||||
make install |
@ -1,77 +0,0 @@ | |||||
#!/usr/bin/env bash | |||||
set -euo pipefail | |||||
if [ "$CIRCLE_BRANCH" == "" ]; then | |||||
echo "this script is meant to be run on CircleCI, exiting" | |||||
echo 1 | |||||
fi | |||||
# check for changes in the `rpc/core` directory | |||||
did_rpc_change=$(git diff --name-status $CIRCLE_BRANCH origin/master | grep rpc/core) | |||||
if [ "$did_rpc_change" == "" ]; then | |||||
echo "no changes detected in rpc/core, exiting" | |||||
exit 0 | |||||
else | |||||
echo "changes detected in rpc/core, continuing" | |||||
fi | |||||
# only run this script on changes to rpc/core committed to develop | |||||
if [ "$CIRCLE_BRANCH" != "master" ]; then | |||||
echo "the branch being built isn't master, exiting" | |||||
exit 0 | |||||
else | |||||
echo "on master, building the RPC docs" | |||||
fi | |||||
# godoc2md used to convert the go documentation from | |||||
# `rpc/core` into a markdown file consumed by Slate | |||||
go get github.com/davecheney/godoc2md | |||||
# slate works via forks, and we'll be committing to | |||||
# master branch, which will trigger our fork to run | |||||
# the `./deploy.sh` and publish via the `gh-pages` branch | |||||
slate_repo=github.com/tendermint/slate | |||||
slate_path="$GOPATH"/src/"$slate_repo" | |||||
if [ ! -d "$slate_path" ]; then | |||||
git clone https://"$slate_repo".git $slate_path | |||||
fi | |||||
# the main file we need to update if rpc/core changed | |||||
destination="$slate_path"/source/index.html.md | |||||
# we remove it then re-create it with the latest changes | |||||
rm $destination | |||||
header="--- | |||||
title: RPC Reference | |||||
language_tabs: | |||||
- shell | |||||
- go | |||||
toc_footers: | |||||
- <a href='https://tendermint.com/'>Tendermint</a> | |||||
- <a href='https://github.com/lord/slate'>Documentation Powered by Slate</a> | |||||
search: true | |||||
---" | |||||
# write header to the main slate file | |||||
echo "$header" > "$destination" | |||||
# generate a markdown from the godoc comments, using a template | |||||
rpc_docs=$(godoc2md -template rpc/core/doc_template.txt github.com/tendermint/tendermint/rpc/core | grep -v -e "pipe.go" -e "routes.go" -e "dev.go" | sed 's$/src/target$https://github.com/tendermint/tendermint/tree/master/rpc/core$') | |||||
# append core RPC docs | |||||
echo "$rpc_docs" >> "$destination" | |||||
# commit the changes | |||||
cd $slate_path | |||||
git config --global user.email "github@tendermint.com" | |||||
git config --global user.name "tenderbot" | |||||
git commit -a -m "Update tendermint RPC docs via CircleCI" | |||||
git push -q https://${GITHUB_ACCESS_TOKEN}@github.com/tendermint/slate.git master |
@ -0,0 +1,3 @@ | |||||
# tools | |||||
Tools for working with tendermint and associated technologies. Documentation can be found in the `README.md` of each the `tm-bench/` and `tm-monitor/` directories. |
@ -0,0 +1,4 @@ | |||||
BUILD | |||||
RPMS | |||||
SPECS | |||||
tmp |
@ -0,0 +1,204 @@ | |||||
Tendermint Core | |||||
License: Apache2.0 | |||||
Apache License | |||||
Version 2.0, January 2004 | |||||
http://www.apache.org/licenses/ | |||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |||||
1. Definitions. | |||||
"License" shall mean the terms and conditions for use, reproduction, | |||||
and distribution as defined by Sections 1 through 9 of this document. | |||||
"Licensor" shall mean the copyright owner or entity authorized by | |||||
the copyright owner that is granting the License. | |||||
"Legal Entity" shall mean the union of the acting entity and all | |||||
other entities that control, are controlled by, or are under common | |||||
control with that entity. For the purposes of this definition, | |||||
"control" means (i) the power, direct or indirect, to cause the | |||||
direction or management of such entity, whether by contract or | |||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the | |||||
outstanding shares, or (iii) beneficial ownership of such entity. | |||||
"You" (or "Your") shall mean an individual or Legal Entity | |||||
exercising permissions granted by this License. | |||||
"Source" form shall mean the preferred form for making modifications, | |||||
including but not limited to software source code, documentation | |||||
source, and configuration files. | |||||
"Object" form shall mean any form resulting from mechanical | |||||
transformation or translation of a Source form, including but | |||||
not limited to compiled object code, generated documentation, | |||||
and conversions to other media types. | |||||
"Work" shall mean the work of authorship, whether in Source or | |||||
Object form, made available under the License, as indicated by a | |||||
copyright notice that is included in or attached to the work | |||||
(an example is provided in the Appendix below). | |||||
"Derivative Works" shall mean any work, whether in Source or Object | |||||
form, that is based on (or derived from) the Work and for which the | |||||
editorial revisions, annotations, elaborations, or other modifications | |||||
represent, as a whole, an original work of authorship. For the purposes | |||||
of this License, Derivative Works shall not include works that remain | |||||
separable from, or merely link (or bind by name) to the interfaces of, | |||||
the Work and Derivative Works thereof. | |||||
"Contribution" shall mean any work of authorship, including | |||||
the original version of the Work and any modifications or additions | |||||
to that Work or Derivative Works thereof, that is intentionally | |||||
submitted to Licensor for inclusion in the Work by the copyright owner | |||||
or by an individual or Legal Entity authorized to submit on behalf of | |||||
the copyright owner. For the purposes of this definition, "submitted" | |||||
means any form of electronic, verbal, or written communication sent | |||||
to the Licensor or its representatives, including but not limited to | |||||
communication on electronic mailing lists, source code control systems, | |||||
and issue tracking systems that are managed by, or on behalf of, the | |||||
Licensor for the purpose of discussing and improving the Work, but | |||||
excluding communication that is conspicuously marked or otherwise | |||||
designated in writing by the copyright owner as "Not a Contribution." | |||||
"Contributor" shall mean Licensor and any individual or Legal Entity | |||||
on behalf of whom a Contribution has been received by Licensor and | |||||
subsequently incorporated within the Work. | |||||
2. Grant of Copyright License. Subject to the terms and conditions of | |||||
this License, each Contributor hereby grants to You a perpetual, | |||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||||
copyright license to reproduce, prepare Derivative Works of, | |||||
publicly display, publicly perform, sublicense, and distribute the | |||||
Work and such Derivative Works in Source or Object form. | |||||
3. Grant of Patent License. Subject to the terms and conditions of | |||||
this License, each Contributor hereby grants to You a perpetual, | |||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||||
(except as stated in this section) patent license to make, have made, | |||||
use, offer to sell, sell, import, and otherwise transfer the Work, | |||||
where such license applies only to those patent claims licensable | |||||
by such Contributor that are necessarily infringed by their | |||||
Contribution(s) alone or by combination of their Contribution(s) | |||||
with the Work to which such Contribution(s) was submitted. If You | |||||
institute patent litigation against any entity (including a | |||||
cross-claim or counterclaim in a lawsuit) alleging that the Work | |||||
or a Contribution incorporated within the Work constitutes direct | |||||
or contributory patent infringement, then any patent licenses | |||||
granted to You under this License for that Work shall terminate | |||||
as of the date such litigation is filed. | |||||
4. Redistribution. You may reproduce and distribute copies of the | |||||
Work or Derivative Works thereof in any medium, with or without | |||||
modifications, and in Source or Object form, provided that You | |||||
meet the following conditions: | |||||
(a) You must give any other recipients of the Work or | |||||
Derivative Works a copy of this License; and | |||||
(b) You must cause any modified files to carry prominent notices | |||||
stating that You changed the files; and | |||||
(c) You must retain, in the Source form of any Derivative Works | |||||
that You distribute, all copyright, patent, trademark, and | |||||
attribution notices from the Source form of the Work, | |||||
excluding those notices that do not pertain to any part of | |||||
the Derivative Works; and | |||||
(d) If the Work includes a "NOTICE" text file as part of its | |||||
distribution, then any Derivative Works that You distribute must | |||||
include a readable copy of the attribution notices contained | |||||
within such NOTICE file, excluding those notices that do not | |||||
pertain to any part of the Derivative Works, in at least one | |||||
of the following places: within a NOTICE text file distributed | |||||
as part of the Derivative Works; within the Source form or | |||||
documentation, if provided along with the Derivative Works; or, | |||||
within a display generated by the Derivative Works, if and | |||||
wherever such third-party notices normally appear. The contents | |||||
of the NOTICE file are for informational purposes only and | |||||
do not modify the License. You may add Your own attribution | |||||
notices within Derivative Works that You distribute, alongside | |||||
or as an addendum to the NOTICE text from the Work, provided | |||||
that such additional attribution notices cannot be construed | |||||
as modifying the License. | |||||
You may add Your own copyright statement to Your modifications and | |||||
may provide additional or different license terms and conditions | |||||
for use, reproduction, or distribution of Your modifications, or | |||||
for any such Derivative Works as a whole, provided Your use, | |||||
reproduction, and distribution of the Work otherwise complies with | |||||
the conditions stated in this License. | |||||
5. Submission of Contributions. Unless You explicitly state otherwise, | |||||
any Contribution intentionally submitted for inclusion in the Work | |||||
by You to the Licensor shall be under the terms and conditions of | |||||
this License, without any additional terms or conditions. | |||||
Notwithstanding the above, nothing herein shall supersede or modify | |||||
the terms of any separate license agreement you may have executed | |||||
with Licensor regarding such Contributions. | |||||
6. Trademarks. This License does not grant permission to use the trade | |||||
names, trademarks, service marks, or product names of the Licensor, | |||||
except as required for reasonable and customary use in describing the | |||||
origin of the Work and reproducing the content of the NOTICE file. | |||||
7. Disclaimer of Warranty. Unless required by applicable law or | |||||
agreed to in writing, Licensor provides the Work (and each | |||||
Contributor provides its Contributions) on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||||
implied, including, without limitation, any warranties or conditions | |||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |||||
PARTICULAR PURPOSE. You are solely responsible for determining the | |||||
appropriateness of using or redistributing the Work and assume any | |||||
risks associated with Your exercise of permissions under this License. | |||||
8. Limitation of Liability. In no event and under no legal theory, | |||||
whether in tort (including negligence), contract, or otherwise, | |||||
unless required by applicable law (such as deliberate and grossly | |||||
negligent acts) or agreed to in writing, shall any Contributor be | |||||
liable to You for damages, including any direct, indirect, special, | |||||
incidental, or consequential damages of any character arising as a | |||||
result of this License or out of the use or inability to use the | |||||
Work (including but not limited to damages for loss of goodwill, | |||||
work stoppage, computer failure or malfunction, or any and all | |||||
other commercial damages or losses), even if such Contributor | |||||
has been advised of the possibility of such damages. | |||||
9. Accepting Warranty or Additional Liability. While redistributing | |||||
the Work or Derivative Works thereof, You may choose to offer, | |||||
and charge a fee for, acceptance of support, warranty, indemnity, | |||||
or other liability obligations and/or rights consistent with this | |||||
License. However, in accepting such obligations, You may act only | |||||
on Your own behalf and on Your sole responsibility, not on behalf | |||||
of any other Contributor, and only if You agree to indemnify, | |||||
defend, and hold each Contributor harmless for any liability | |||||
incurred by, or claims asserted against, such Contributor by reason | |||||
of your accepting any such warranty or additional liability. | |||||
END OF TERMS AND CONDITIONS | |||||
APPENDIX: How to apply the Apache License to your work. | |||||
To apply the Apache License to your work, attach the following | |||||
boilerplate notice, with the fields enclosed by brackets "{}" | |||||
replaced with your own identifying information. (Don't include | |||||
the brackets!) The text should be enclosed in the appropriate | |||||
comment syntax for the file format. We also recommend that a | |||||
file or class name and description of purpose be included on the | |||||
same "printed page" as the copyright notice for easier | |||||
identification within third-party archives. | |||||
Copyright 2016 All in Bits, Inc | |||||
Licensed under the Apache License, Version 2.0 (the "License"); | |||||
you may not use this file except in compliance with the License. | |||||
You may obtain a copy of the License at | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. |
@ -0,0 +1,289 @@ | |||||
## | |||||
# Extra checks, because we do not use autoconf. | |||||
## | |||||
requirements_check = true | |||||
gpg_check = false | |||||
go_min_version = 1.9.4 | |||||
gpg_key = 2122CBE9 | |||||
ifeq ($(requirements_check),true) | |||||
ifndef GOPATH | |||||
$(error GOPATH not set) | |||||
else | |||||
go_version := $(shell go version | sed "s/^.* go\([0-9\.]*\) .*$$/\1/" ) | |||||
$(info Found go version $(go_version)) | |||||
go_version_check := $(shell echo -e "$(go_min_version)\n$(go_version)" | sort -V | head -1) | |||||
ifneq ($(go_min_version),$(go_version_check)) | |||||
$(error go version go_min_version or above is required) | |||||
endif | |||||
endif | |||||
ifeq ($(gpg_check),true) | |||||
gpg_check := $(shell gpg -K | grep '/$(gpg_key) ' | sed 's,^.*/\($(gpg_key)\) .*$$,\1,') | |||||
ifneq ($(gpg_check),$(gpg_key)) | |||||
$(error GPG key $(gpg_key) not found.) | |||||
else | |||||
$(info GPG key $(gpg_key) found) | |||||
endif | |||||
ifndef GPG_PASSPHRASE | |||||
$(error GPG_PASSPHRASE not set) | |||||
endif | |||||
endif | |||||
endif | |||||
### | |||||
# Here comes the real deal | |||||
### | |||||
binaries = tendermint basecoind ethermint gaia | |||||
build-binaries = build-tendermint build-basecoind build-ethermint build-gaia | |||||
package-rpm = package-rpm-tendermint package-rpm-basecoind package-rpm-ethermint package-rpm-gaia | |||||
install-rpm = install-rpm-tendermint install-rpm-basecoind install-rpm-ethermint install-rpm-gaia | |||||
package-deb = package-deb-tendermint package-deb-basecoind package-deb-ethermint package-deb-gaia | |||||
install-deb = install-deb-tendermint install-deb-basecoind install-deb-ethermint install-deb-gaia | |||||
all: $(binaries) | |||||
build: $(build-binaries) | |||||
package: $(package-rpm) $(package-deb) | |||||
install: $(install-rpm) $(install-deb) | |||||
$(binaries): %: build-% package-rpm-% package-deb-% | |||||
### | |||||
# Build the binaries | |||||
### | |||||
git-branch: | |||||
$(eval GIT_BRANCH=$(shell echo $${GIT_BRANCH:-master})) | |||||
gopath-setup: | |||||
test -d $(GOPATH) || mkdir -p $(GOPATH) | |||||
test -d $(GOPATH)/bin || mkdir -p $(GOPATH)/bin | |||||
test -d $(GOPATH)/src || mkdir -p $(GOPATH)/src | |||||
build-tendermint: git-branch gopath-setup | |||||
@echo "*** Building tendermint" | |||||
go get -d -u github.com/tendermint/tendermint/cmd/tendermint | |||||
cd $(GOPATH)/src/github.com/tendermint/tendermint && git checkout "$(GIT_BRANCH)" && git pull | |||||
export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/tendermint/tendermint get_tools get_vendor_deps build | |||||
cp $(GOPATH)/src/github.com/tendermint/tendermint/build/tendermint $(GOPATH)/bin | |||||
@echo "*** Built tendermint" | |||||
build-ethermint: git-branch gopath-setup | |||||
@echo "*** Building ethermint" | |||||
go get -d -u github.com/tendermint/ethermint/cmd/ethermint | |||||
cd $(GOPATH)/src/github.com/tendermint/ethermint && git checkout "$(GIT_BRANCH)" && git pull | |||||
export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/tendermint/ethermint get_vendor_deps build | |||||
cp $(GOPATH)/src/github.com/tendermint/ethermint/build/ethermint $(GOPATH)/bin | |||||
@echo "*** Built ethermint" | |||||
build-gaia: git-branch gopath-setup | |||||
@echo "*** Building gaia" | |||||
go get -d -u go github.com/cosmos/gaia || echo "Workaround for go downloads." | |||||
cd $(GOPATH)/src/github.com/cosmos/gaia && git checkout "$(GIT_BRANCH)" && git pull | |||||
export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/cosmos/gaia get_vendor_deps install | |||||
@echo "*** Built gaia" | |||||
build-basecoind: git-branch gopath-setup | |||||
@echo "*** Building basecoind from cosmos-sdk" | |||||
go get -d -u github.com/cosmos/cosmos-sdk/examples/basecoin/cmd/basecoind | |||||
cd $(GOPATH)/src/github.com/cosmos/cosmos-sdk && git checkout "$(GIT_BRANCH)" && git pull | |||||
export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/cosmos/cosmos-sdk get_tools get_vendor_deps build | |||||
cp $(GOPATH)/src/github.com/cosmos/cosmos-sdk/build/basecoind $(GOPATH)/bin/basecoind | |||||
@echo "*** Built basecoind from cosmos-sdk" | |||||
### | |||||
# Prepare package files | |||||
### | |||||
# set app_version | |||||
version-%: | |||||
@echo "Checking if binary exists" | |||||
test -f $(GOPATH)/bin/$* | |||||
@echo "BUILD_NUMBER is $(BUILD_NUMBER)" | |||||
test -n "$(BUILD_NUMBER)" | |||||
$(eval $*_version=$(shell $(GOPATH)/bin/$* version | head -1 | cut -d- -f1 | sed 's/^\(ethermint:\s*\|\)\(v\|\)//' | tr -d '\t ' )) | |||||
# set build_folder | |||||
folder-%: version-% | |||||
$(eval build_folder=BUILD/$*-$($*_version)-$(BUILD_NUMBER)) | |||||
# clean up folder structure for package files | |||||
prepare-files = rm -rf $(build_folder) && mkdir -p $(build_folder) && cp -r ./$(1)/* $(build_folder) && mkdir -p $(build_folder)/usr/bin && cp $(GOPATH)/bin/$(1) $(build_folder)/usr/bin | |||||
## | |||||
## Package customizations for the different applications | |||||
## | |||||
prepare-tendermint = | |||||
prepare-ethermint = mkdir -p $(build_folder)/etc/ethermint && \ | |||||
cp $(GOPATH)/src/github.com/tendermint/ethermint/setup/genesis.json $(build_folder)/etc/ethermint/genesis.json && \ | |||||
cp -r $(GOPATH)/src/github.com/tendermint/ethermint/setup/keystore $(build_folder)/etc/ethermint | |||||
prepare-gaia = | |||||
prepare-basecoind = cp $(GOPATH)/bin/basecoind $(build_folder)/usr/bin | |||||
### | |||||
# Package the binary for CentOS/RedHat (RPM) and Debian/Ubuntu (DEB) | |||||
### | |||||
# Depends on rpmbuild, sorry, this can only be built on CentOS/RedHat machines. | |||||
package-rpm-%: folder-% | |||||
@echo "*** Packaging RPM $* version $($*_version)" | |||||
$(call prepare-files,$*) | |||||
$(call prepare-$*) | |||||
rm -rf $(build_folder)/DEBIAN | |||||
mkdir -p $(build_folder)/usr/share/licenses/$* | |||||
cp ./LICENSE $(build_folder)/usr/share/licenses/$*/LICENSE | |||||
chmod -Rf a+rX,u+w,g-w,o-w $(build_folder) | |||||
mkdir -p {SPECS,tmp} | |||||
./generate-spec $* spectemplates SPECS | |||||
sed -i "s/@VERSION@/$($*_version)/" SPECS/$*.spec | |||||
sed -i "s/@BUILD_NUMBER@/$(BUILD_NUMBER)/" SPECS/$*.spec | |||||
sed -i "s/@PACKAGE_NAME@/$*/" SPECS/$*.spec | |||||
rpmbuild -bb SPECS/$*.spec --define "_topdir `pwd`" --define "_tmppath `pwd`/tmp" | |||||
./sign RPMS/x86_64/$*-$($*_version)-$(BUILD_NUMBER).x86_64.rpm "$(gpg_key)" "`which gpg`" | |||||
rpm -Kv RPMS/x86_64/$*-$($*_version)-$(BUILD_NUMBER).x86_64.rpm || echo "rpm returns non-zero exist for some reason. ($?)" | |||||
@echo "*** Packaged RPM $* version $($*_version)" | |||||
package-deb-%: folder-% | |||||
@echo "*** Packaging DEB $* version $($*_version)-$(BUILD_NUMBER)" | |||||
$(call prepare-files,$*) | |||||
$(call prepare-$*) | |||||
mkdir -p $(build_folder)/usr/share/doc/$* | |||||
cp $(build_folder)/DEBIAN/copyright $(build_folder)/usr/share/doc/$* | |||||
chmod -Rf a+rX,u+w,g-w,o-w $(build_folder) | |||||
sed -i "s/@VERSION@/$($*_version)-$(BUILD_NUMBER)/" $(build_folder)/DEBIAN/changelog | |||||
sed -i "s/@STABILITY@/stable/" $(build_folder)/DEBIAN/changelog | |||||
sed -i "s/@DATETIMESTAMP@/`date +%a,\ %d\ %b\ %Y\ %T\ %z`/" $(build_folder)/DEBIAN/changelog | |||||
sed -i "s/@VERSION@/$($*_version)-$(BUILD_NUMBER)/" $(build_folder)/DEBIAN/control | |||||
gzip -c $(build_folder)/DEBIAN/changelog > $(build_folder)/usr/share/doc/$*/changelog.Debian.gz | |||||
gzip -c $(build_folder)/DEBIAN/changelog > $(build_folder)/usr/share/doc/$*/changelog.Debian.amd64.gz | |||||
sed -i "s/@INSTALLEDSIZE@/`du -ks $(build_folder) | cut -f 1`/" $(build_folder)/DEBIAN/control | |||||
cd $(build_folder) && tar --owner=root --group=root -cvJf ../../tmp/data.tar.xz --exclude DEBIAN * | |||||
cd $(build_folder)/DEBIAN && tar --owner=root --group=root -cvzf ../../../tmp/control.tar.gz * | |||||
echo "2.0" > tmp/debian-binary | |||||
cp ./_gpg tmp/ | |||||
cd tmp && sed -i "s/@DATETIMESTAMP@/`date +%a\ %b\ %d\ %T\ %Y`/" _gpg | |||||
cd tmp && sed -i "s/@BINMD5@/`md5sum debian-binary | cut -d\ -f1`/" _gpg | |||||
cd tmp && sed -i "s/@BINSHA1@/`sha1sum debian-binary | cut -d\ -f1`/" _gpg | |||||
cd tmp && sed -i "s/@BINSIZE@/`stat -c %s debian-binary | cut -d\ -f1`/" _gpg | |||||
cd tmp && sed -i "s/@CONMD5@/`md5sum control.tar.gz | cut -d\ -f1`/" _gpg | |||||
cd tmp && sed -i "s/@CONSHA1@/`sha1sum control.tar.gz | cut -d\ -f1`/" _gpg | |||||
cd tmp && sed -i "s/@CONSIZE@/`stat -c %s control.tar.gz | cut -d\ -f1`/" _gpg | |||||
cd tmp && sed -i "s/@DATMD5@/`md5sum data.tar.xz | cut -d\ -f1`/" _gpg | |||||
cd tmp && sed -i "s/@DATSHA1@/`sha1sum data.tar.xz | cut -d\ -f1`/" _gpg | |||||
cd tmp && sed -i "s/@DATSIZE@/`stat -c %s data.tar.xz | cut -d\ -f1`/" _gpg | |||||
gpg --batch --passphrase "$(GPG_PASSPHRASE)" --clearsign tmp/_gpg | |||||
mv tmp/_gpg.asc tmp/_gpgbuilder | |||||
ar r tmp/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb tmp/debian-binary tmp/control.tar.gz tmp/data.tar.xz tmp/_gpgbuilder | |||||
mv tmp/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb RPMS/ | |||||
rm tmp/debian-binary tmp/control.tar.gz tmp/data.tar.xz tmp/_gpgbuilder tmp/_gpg | |||||
@echo "*** Packaged DEB $* version $($*_version)-$(BUILD_NUMBER)" | |||||
install-rpm-%: version-% | |||||
#Make sure your host has the IAM role to read/write the S3 bucket OR that you set up ~/.boto | |||||
@echo "*** Uploading $*-$($*_version)-$(BUILD_NUMBER).x86_64.rpm to AWS $(DEVOPS_PATH)CentOS repository" | |||||
aws s3 sync s3://tendermint-packages/$(DEVOPS_PATH)centos/ tmp/s3/ --delete | |||||
mkdir -p tmp/s3/7/os/x86_64/Packages | |||||
cp RPMS/x86_64/$*-$($*_version)-$(BUILD_NUMBER).x86_64.rpm tmp/s3/7/os/x86_64/Packages | |||||
cp ./RPM-GPG-KEY-Tendermint tmp/s3/7/os/x86_64/ | |||||
cp ./tendermint.repo tmp/s3/7/os/x86_64/ | |||||
rm -f tmp/s3/7/os/x86_64/repodata/*.bz2 tmp/s3/7/os/x86_64/repodata/*.gz tmp/s3/7/os/x86_64/repodata/repomd.xml.asc | |||||
createrepo tmp/s3/7/os/x86_64/Packages -u https://tendermint-packages.interblock.io/$(DEVOPS_PATH)centos/7/os/x86_64/Packages -o tmp/s3/7/os/x86_64 --update -S --repo Tendermint --content tendermint --content basecoind --content ethermint | |||||
gpg --batch --passphrase "$(GPG_PASSPHRASE)" --detach-sign -a tmp/s3/7/os/x86_64/repodata/repomd.xml | |||||
aws s3 sync tmp/s3/ s3://tendermint-packages/$(DEVOPS_PATH)centos/ --delete --acl public-read | |||||
@echo "*** Uploaded $* to AWS $(DEVOPS_PATH)CentOS repository" | |||||
install-deb-%: version-% | |||||
@echo "*** Uploading $*-$($*_version)-$(BUILD_NUMBER)_amd64.deb to AWS $(DEVOPS_PATH)Debian repository" | |||||
@echo "Testing if $*-$($*_version)-$(BUILD_NUMBER)_amd64.deb is already uploaded" | |||||
test ! -f tmp/debian-s3/pool/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb | |||||
aws s3 sync s3://tendermint-packages/$(DEVOPS_PATH)debian/ tmp/debian-s3/ --delete | |||||
@echo "Testing if $*-$($*_version)-$(BUILD_NUMBER)_amd64.deb is already uploaded" | |||||
test ! -f tmp/debian-s3/pool/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb | |||||
cp ./tendermint.list tmp/debian-s3/ | |||||
mkdir -p tmp/debian-s3/pool tmp/debian-s3/dists/stable/main/binary-amd64 | |||||
cp RPMS/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb tmp/debian-s3/pool | |||||
cp ./Release_amd64 tmp/debian-s3/dists/stable/main/binary-amd64/Release | |||||
#Packages / Packages.gz | |||||
echo > tmp/Package | |||||
echo "Filename: pool/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb" >> tmp/Package | |||||
echo "MD5sum: `md5sum RPMS/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb | cut -d\ -f 1`" >> tmp/Package | |||||
echo "SHA1: `sha1sum RPMS/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb | cut -d\ -f 1`" >> tmp/Package | |||||
echo "SHA256: `sha256sum RPMS/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb | cut -d\ -f 1`" >> tmp/Package | |||||
echo "Size: `stat -c %s RPMS/$*-$($*_version)-$(BUILD_NUMBER)_amd64.deb | cut -d\ -f 1`" >> tmp/Package | |||||
cat BUILD/$*-$($*_version)-$(BUILD_NUMBER)/DEBIAN/control >> tmp/Package | |||||
cat tmp/Package >> tmp/debian-s3/dists/stable/main/binary-amd64/Packages | |||||
rm -f tmp/debian-s3/dists/stable/main/binary-amd64/Packages.gz | |||||
gzip -c tmp/debian-s3/dists/stable/main/binary-amd64/Packages > tmp/debian-s3/dists/stable/main/binary-amd64/Packages.gz | |||||
rm -f tmp/Package | |||||
#main / Release / InRelease / Release.gpg | |||||
cp ./Release tmp/debian-s3/dists/stable/main/Release | |||||
rm -f tmp/debian-s3/dists/stable/main/InRelease | |||||
rm -f tmp/debian-s3/dists/stable/main/Release.gpg | |||||
echo "MD5Sum:" >> tmp/debian-s3/dists/stable/main/Release | |||||
cd tmp/debian-s3/dists/stable/main && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; md5sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release | |||||
echo "SHA1:" >> tmp/debian-s3/dists/stable/main/Release | |||||
cd tmp/debian-s3/dists/stable/main && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; sha1sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release | |||||
echo "SHA256:" >> tmp/debian-s3/dists/stable/main/Release | |||||
cd tmp/debian-s3/dists/stable/main && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; sha256sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release | |||||
gpg --batch --passphrase "$(GPG_PASSPHRASE)" --digest-algo SHA256 -b -a tmp/debian-s3/dists/stable/main/Release | |||||
mv tmp/debian-s3/dists/stable/main/Release.asc tmp/debian-s3/dists/stable/main/Release.gpg | |||||
gpg --batch --passphrase "$(GPG_PASSPHRASE)" --digest-algo SHA512 --clearsign tmp/debian-s3/dists/stable/main/Release | |||||
mv tmp/debian-s3/dists/stable/main/Release.asc tmp/debian-s3/dists/stable/main/InRelease | |||||
#stable / Release / InRelease / Release.gpg | |||||
cp ./Release tmp/debian-s3/dists/stable/Release | |||||
rm -f tmp/debian-s3/dists/stable/InRelease | |||||
rm -f tmp/debian-s3/dists/stable/Release.gpg | |||||
echo "MD5Sum:" >> tmp/debian-s3/dists/stable/Release | |||||
cd tmp/debian-s3/dists/stable && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; md5sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release | |||||
echo "SHA1:" >> tmp/debian-s3/dists/stable/Release | |||||
cd tmp/debian-s3/dists/stable && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; sha1sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release | |||||
echo "SHA256:" >> tmp/debian-s3/dists/stable/Release | |||||
cd tmp/debian-s3/dists/stable && for f in `find . -type f | sed 's/^.\///'` ; do test "$$f" == "Release" && continue ; echo -n " " ; sha256sum $$f | sed "s/ / `stat -c %s $$f` /" ; done >> Release | |||||
gpg --batch --passphrase "$(GPG_PASSPHRASE)" --digest-algo SHA256 -b -a tmp/debian-s3/dists/stable/Release | |||||
mv tmp/debian-s3/dists/stable/Release.asc tmp/debian-s3/dists/stable/Release.gpg | |||||
gpg --batch --passphrase "$(GPG_PASSPHRASE)" --digest-algo SHA512 --clearsign tmp/debian-s3/dists/stable/Release | |||||
mv tmp/debian-s3/dists/stable/Release.asc tmp/debian-s3/dists/stable/InRelease | |||||
aws s3 sync tmp/debian-s3/ s3://tendermint-packages/$(DEVOPS_PATH)debian/ --delete --acl public-read | |||||
@echo "*** Uploaded $*-$($*_version)-$(BUILD_NUMBER)_amd64.deb to AWS $(DEVOPS_PATH)Debian repository" | |||||
mostlyclean: | |||||
rm -rf {BUILDROOT,SOURCES,SPECS,SRPMS,tmp} | |||||
clean: mostlyclean | |||||
rm -rf {BUILD,RPMS} | |||||
distclean: clean | |||||
rm -rf $(GOPATH)/src/github.com/tendermint/tendermint | |||||
rm -rf $(GOPATH)/src/github.com/cosmos/cosmos-sdk | |||||
rm -rf $(GOPATH)/src/github.com/tendermint/ethermint | |||||
rm -rf $(GOPATH)/bin/tendermint | |||||
rm -rf $(GOPATH)/bin/basecoind | |||||
rm -rf $(GOPATH)/bin/ethermint | |||||
rm -rf $(GOPATH)/bin/gaia | |||||
.PHONY : clean | |||||
@ -0,0 +1,19 @@ | |||||
-----BEGIN PGP PUBLIC KEY BLOCK----- | |||||
Version: GnuPG v2.0.22 (GNU/Linux) | |||||
mQENBFk97ngBCADaiPQFKJI7zWYdUKqC490DzY9g9LatsWoJErK5LuMXwEnF5i+a | |||||
UkygueukA4C5U7L71l5EeOB9rtb6AbkF4IEZsmmp93APec/3Vfbac9xvK4dBdiht | |||||
F8SrazPdHeR6AKcZH8ZpG/+mdONvGb/gEgtxVjaeIJFpCbjKLlKEXazh2zamhhth | |||||
q+Nn/17QmI3KBiaGqQK5w4kGZ4mZPy6fXMQhW5dDMq9f4anlGIAYi9O53dVxsx2S | |||||
5d+NHuGer5Ps0u6WMJi/e+UT2EGwzP6ygOxkIjyhMFuVftabOtSSrRHHetw8UAaI | |||||
N/RPn2gSbQtOQ7unzHDXp3/o6/r2nDEErPyJABEBAAG0LkdyZWcgU3phYm8gKFRl | |||||
bmRlcm1pbnQpIDxncmVnQHBoaWxvc29iZWFyLmNvbT6JATkEEwECACMFAlk97ngC | |||||
GwMHCwkIBwMCAQYVCAIJCgsEFgIDAQIeAQIXgAAKCRDIkIHIISLL6bX/CACXTKmO | |||||
u5XgvJICH0pHNeVS5/4Om1Rsg1xNmEkGFBP8N2fqn576exbOLgWLSyNHTEyrJNoc | |||||
iTeUtod2qqbVGwRgWm1zeiP8NBYiQ9SUbqskIqcPavJNGWIxsCB0p/odoZah8xSj | |||||
tGrkoyoxrc+7z2JgKYK8SVSkJXQkzuc5/ZlY85ci5gPKQhlo5YDqGo+4U9n/Ieo5 | |||||
nkF8LBalFC2j7A7sQNroEicpulpGhIq3jyUHtadX01z3pNzuX+wfHX9futoet0YS | |||||
tG2007WoPGV0whGnoKxmk0JhwzhscC2XNtJl1GZcwqOOlPU9eGtZuPKj/HBAlRtz | |||||
4xTOAcklpg8soqRA | |||||
=jNDW | |||||
-----END PGP PUBLIC KEY BLOCK----- |
@ -0,0 +1,7 @@ | |||||
Origin: Tendermint | |||||
Label: Tendermint | |||||
Suite: stable | |||||
Date: Fri, 16 Jun 2017 19:44:00 UTC | |||||
Architectures: amd64 | |||||
Components: main | |||||
Description: Tendermint repository |
@ -0,0 +1,5 @@ | |||||
Archive: stable | |||||
Component: main | |||||
Origin: Tendermint | |||||
Label: Tendermint | |||||
Architecture: amd64 |
@ -0,0 +1,8 @@ | |||||
Version: 4 | |||||
Signer: | |||||
Date: @DATETIMESTAMP@ | |||||
Role: builder | |||||
Files: | |||||
@BINMD5@ @BINSHA1@ @BINSIZE@ debian-binary | |||||
@CONMD5@ @CONSHA1@ @CONSIZE@ control.tar.gz | |||||
@DATMD5@ @DATSHA1@ @DATSIZE@ data.tar.xz |
@ -0,0 +1,6 @@ | |||||
basecoind (@VERSION@) @STABILITY@; urgency=medium | |||||
* Automatic build. See https://github.com/cosmos/cosmos-sdk for more information. | |||||
-- Greg Szabo <greg@philosobear.com> @DATETIMESTAMP@ | |||||
@ -0,0 +1 @@ | |||||
9 |
@ -0,0 +1,14 @@ | |||||
Source: basecoind | |||||
Section: net | |||||
Priority: optional | |||||
Maintainer: Greg Szabo <greg@philosobear.com> | |||||
Build-Depends: debhelper (>=9) | |||||
Standards-Version: 3.9.6 | |||||
Homepage: https://tendermint.com | |||||
Package: basecoind | |||||
Architecture: amd64 | |||||
Version: @VERSION@ | |||||
Installed-Size: @INSTALLEDSIZE@ | |||||
Description: basecoind is a Proof-of-Stake cryptocurrency and framework | |||||
Basecoind is an ABCI application designed to be used with the Tendermint consensus engine to form a Proof-of-Stake cryptocurrency. It also provides a general purpose framework for extending the feature-set of the cryptocurrency by implementing plugins. | |||||
@ -0,0 +1,21 @@ | |||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ | |||||
Upstream-Name: basecoind | |||||
Source: https://github.com/cosmos/cosmos-sdk | |||||
Files: * | |||||
Copyright: 2017 All In Bits, Inc. | |||||
License: Apache-2.0 | |||||
Licensed under the Apache License, Version 2.0 (the "License"); | |||||
you may not use this file except in compliance with the License. | |||||
You may obtain a copy of the License at | |||||
. | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
. | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
. | |||||
On Debian systems, the full text of the Apache License 2.0 can be found | |||||
in the file `/usr/share/common-licenses/Apache-2.0'. |
@ -0,0 +1,41 @@ | |||||
#!/bin/sh | |||||
# postinst script for basecoind | |||||
# | |||||
set -e | |||||
# summary of how this script can be called: | |||||
# * <postinst> `configure' <most-recently-configured-version> | |||||
# * <old-postinst> `abort-upgrade' <new version> | |||||
# * <conflictor's-postinst> `abort-remove' `in-favour' <package> | |||||
# <new-version> | |||||
# * <postinst> `abort-remove' | |||||
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour' | |||||
# <failed-install-package> <version> `removing' | |||||
# <conflicting-package> <version> | |||||
# for details, see https://www.debian.org/doc/debian-policy/ or | |||||
# the debian-policy package | |||||
case "$1" in | |||||
configure) | |||||
chown basecoind.basecoind /etc/basecoind | |||||
sudo -Hu basecoind basecoind node init --home /etc/basecoind 2B24DEE2364762300168DF19B6C18BCE2D399EA2 | |||||
systemctl daemon-reload | |||||
;; | |||||
abort-upgrade|abort-remove|abort-deconfigure) | |||||
;; | |||||
*) | |||||
echo "postinst called with unknown argument \`$1'" >&2 | |||||
exit 1 | |||||
;; | |||||
esac | |||||
# dh_installdeb will replace this with shell code automatically | |||||
# generated by other debhelper scripts. | |||||
#DEBHELPER# | |||||
exit 0 |
@ -0,0 +1,41 @@ | |||||
#!/bin/sh | |||||
# postrm script for basecoin | |||||
# | |||||
set -e | |||||
# summary of how this script can be called: | |||||
# * <postrm> `remove' | |||||
# * <postrm> `purge' | |||||
# * <old-postrm> `upgrade' <new-version> | |||||
# * <new-postrm> `failed-upgrade' <old-version> | |||||
# * <new-postrm> `abort-install' | |||||
# * <new-postrm> `abort-install' <old-version> | |||||
# * <new-postrm> `abort-upgrade' <old-version> | |||||
# * <disappearer's-postrm> `disappear' <overwriter> | |||||
# <overwriter-version> | |||||
# for details, see https://www.debian.org/doc/debian-policy/ or | |||||
# the debian-policy package | |||||
case "$1" in | |||||
upgrade|failed-upgrade|abort-upgrade) | |||||
systemctl daemon-reload | |||||
;; | |||||
purge|remove|abort-install|disappear) | |||||
systemctl daemon-reload | |||||
;; | |||||
*) | |||||
echo "postrm called with unknown argument \`$1'" >&2 | |||||
exit 1 | |||||
;; | |||||
esac | |||||
# dh_installdeb will replace this with shell code automatically | |||||
# generated by other debhelper scripts. | |||||
#DEBHELPER# | |||||
exit 0 |
@ -0,0 +1,38 @@ | |||||
#!/bin/sh | |||||
# preinst script for basecoind | |||||
# | |||||
set -e | |||||
# summary of how this script can be called: | |||||
# * <new-preinst> `install' | |||||
# * <new-preinst> `install' <old-version> | |||||
# * <new-preinst> `upgrade' <old-version> | |||||
# * <old-preinst> `abort-upgrade' <new-version> | |||||
# for details, see https://www.debian.org/doc/debian-policy/ or | |||||
# the debian-policy package | |||||
case "$1" in | |||||
install|upgrade) | |||||
if ! grep -q '^basecoind:' /etc/passwd ; then | |||||
useradd -k /dev/null -r -m -b /etc basecoind | |||||
chmod 755 /etc/basecoind | |||||
fi | |||||
;; | |||||
abort-upgrade) | |||||
;; | |||||
*) | |||||
echo "preinst called with unknown argument \`$1'" >&2 | |||||
exit 1 | |||||
;; | |||||
esac | |||||
# dh_installdeb will replace this with shell code automatically | |||||
# generated by other debhelper scripts. | |||||
#DEBHELPER# | |||||
exit 0 |
@ -0,0 +1,38 @@ | |||||
#!/bin/sh | |||||
# prerm script for basecoin | |||||
# | |||||
set -e | |||||
# summary of how this script can be called: | |||||
# * <prerm> `remove' | |||||
# * <old-prerm> `upgrade' <new-version> | |||||
# * <new-prerm> `failed-upgrade' <old-version> | |||||
# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version> | |||||
# * <deconfigured's-prerm> `deconfigure' `in-favour' | |||||
# <package-being-installed> <version> `removing' | |||||
# <conflicting-package> <version> | |||||
# for details, see https://www.debian.org/doc/debian-policy/ or | |||||
# the debian-policy package | |||||
case "$1" in | |||||
remove|upgrade|deconfigure) | |||||
systemctl stop basecoind 2> /dev/null || : | |||||
;; | |||||
failed-upgrade) | |||||
;; | |||||
*) | |||||
echo "prerm called with unknown argument \`$1'" >&2 | |||||
exit 1 | |||||
;; | |||||
esac | |||||
# dh_installdeb will replace this with shell code automatically | |||||
# generated by other debhelper scripts. | |||||
#DEBHELPER# | |||||
exit 0 |
@ -0,0 +1,2 @@ | |||||
disable basecoind.service | |||||
@ -0,0 +1,18 @@ | |||||
[Unit] | |||||
Description=Basecoind | |||||
Requires=network-online.target | |||||
After=network-online.target | |||||
[Service] | |||||
Environment="BCHOME=/etc/basecoind" | |||||
Restart=on-failure | |||||
User=basecoind | |||||
Group=basecoind | |||||
PermissionsStartOnly=true | |||||
ExecStart=/usr/bin/basecoind start | |||||
ExecReload=/bin/kill -HUP $MAINPID | |||||
KillSignal=SIGTERM | |||||
[Install] | |||||
WantedBy=multi-user.target | |||||
@ -0,0 +1,12 @@ | |||||
{ | |||||
"address": "1B1BE55F969F54064628A63B9559E7C21C925165", | |||||
"priv_key": { | |||||
"type": "ed25519", | |||||
"data": "C70D6934B4F55F1B7BC33B56B9CA8A2061384AFC19E91E44B40C4BBA182953D1619D3678599971ED29C7529DDD4DA537B97129893598A17C82E3AC9A8BA95279" | |||||
}, | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "619D3678599971ED29C7529DDD4DA537B97129893598A17C82E3AC9A8BA95279" | |||||
} | |||||
} | |||||
@ -0,0 +1,12 @@ | |||||
{ | |||||
"address": "1DA7C74F9C219229FD54CC9F7386D5A3839F0090", | |||||
"priv_key": { | |||||
"type": "ed25519", | |||||
"data": "34BAE9E65CE8245FAD035A0E3EED9401BDE8785FFB3199ACCF8F5B5DDF7486A8352195DA90CB0B90C24295B90AEBA25A5A71BC61BAB2FE2387241D439698B7B8" | |||||
}, | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "352195DA90CB0B90C24295B90AEBA25A5A71BC61BAB2FE2387241D439698B7B8" | |||||
} | |||||
} | |||||
@ -0,0 +1,6 @@ | |||||
ethermint (@VERSION@) @STABILITY@; urgency=medium | |||||
* Automatic build. See https://github.com/tendermint/tendermint for more information. | |||||
-- Greg Szabo <greg@philosobear.com> @DATETIMESTAMP@ | |||||
@ -0,0 +1 @@ | |||||
9 |
@ -0,0 +1,15 @@ | |||||
Source: ethermint | |||||
Section: net | |||||
Priority: optional | |||||
Maintainer: Greg Szabo <greg@philosobear.com> | |||||
Build-Depends: debhelper (>=9) | |||||
Depends: tendermint (>=0.11.0) | |||||
Standards-Version: 3.9.6 | |||||
Homepage: https://tendermint.com | |||||
Package: ethermint | |||||
Architecture: amd64 | |||||
Version: @VERSION@ | |||||
Installed-Size: @INSTALLEDSIZE@ | |||||
Description: ethermint enables ethereum as an ABCI application on tendermint and the COSMOS hub | |||||
Ethermint enables ethereum to run as an ABCI application on tendermint and the COSMOS hub. This application allows you to get all the benefits of ethereum without having to run your own miners. | |||||
@ -0,0 +1,21 @@ | |||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ | |||||
Upstream-Name: ethermint | |||||
Source: https://github.com/tendermint/ethermint | |||||
Files: * | |||||
Copyright: 2017 All In Bits, Inc. | |||||
License: Apache-2.0 | |||||
Licensed under the Apache License, Version 2.0 (the "License"); | |||||
you may not use this file except in compliance with the License. | |||||
You may obtain a copy of the License at | |||||
. | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
. | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
. | |||||
On Debian systems, the full text of the Apache License 2.0 can be found | |||||
in the file `/usr/share/common-licenses/Apache-2.0'. |
@ -0,0 +1,46 @@ | |||||
#!/bin/sh | |||||
# postinst script for ethermint | |||||
# | |||||
set -e | |||||
# summary of how this script can be called: | |||||
# * <postinst> `configure' <most-recently-configured-version> | |||||
# * <old-postinst> `abort-upgrade' <new version> | |||||
# * <conflictor's-postinst> `abort-remove' `in-favour' <package> | |||||
# <new-version> | |||||
# * <postinst> `abort-remove' | |||||
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour' | |||||
# <failed-install-package> <version> `removing' | |||||
# <conflicting-package> <version> | |||||
# for details, see https://www.debian.org/doc/debian-policy/ or | |||||
# the debian-policy package | |||||
case "$1" in | |||||
configure) | |||||
chown ethermint.ethermint /etc/ethermint | |||||
chown ethermint.ethermint /etc/ethermint/genesis.json | |||||
chown ethermint.ethermint /etc/ethermint/keystore | |||||
chown ethermint.ethermint /etc/ethermint/keystore/UTC--2016-10-21T22-30-03.071787745Z--7eff122b94897ea5b0e2a9abf47b86337fafebdc | |||||
sudo -Hu ethermint /usr/bin/ethermint --datadir /etc/ethermint init /etc/ethermint/genesis.json | |||||
sudo -Hu ethermint tendermint init --home /etc/ethermint | |||||
systemctl daemon-reload | |||||
;; | |||||
abort-upgrade|abort-remove|abort-deconfigure) | |||||
;; | |||||
*) | |||||
echo "postinst called with unknown argument \`$1'" >&2 | |||||
exit 1 | |||||
;; | |||||
esac | |||||
# dh_installdeb will replace this with shell code automatically | |||||
# generated by other debhelper scripts. | |||||
#DEBHELPER# | |||||
exit 0 |
@ -0,0 +1,41 @@ | |||||
#!/bin/sh | |||||
# postrm script for ethermint | |||||
# | |||||
set -e | |||||
# summary of how this script can be called: | |||||
# * <postrm> `remove' | |||||
# * <postrm> `purge' | |||||
# * <old-postrm> `upgrade' <new-version> | |||||
# * <new-postrm> `failed-upgrade' <old-version> | |||||
# * <new-postrm> `abort-install' | |||||
# * <new-postrm> `abort-install' <old-version> | |||||
# * <new-postrm> `abort-upgrade' <old-version> | |||||
# * <disappearer's-postrm> `disappear' <overwriter> | |||||
# <overwriter-version> | |||||
# for details, see https://www.debian.org/doc/debian-policy/ or | |||||
# the debian-policy package | |||||
case "$1" in | |||||
upgrade|failed-upgrade|abort-upgrade) | |||||
systemctl daemon-reload | |||||
;; | |||||
purge|remove|abort-install|disappear) | |||||
systemctl daemon-reload | |||||
;; | |||||
*) | |||||
echo "postrm called with unknown argument \`$1'" >&2 | |||||
exit 1 | |||||
;; | |||||
esac | |||||
# dh_installdeb will replace this with shell code automatically | |||||
# generated by other debhelper scripts. | |||||
#DEBHELPER# | |||||
exit 0 |
@ -0,0 +1,38 @@ | |||||
#!/bin/sh | |||||
# preinst script for ethermint | |||||
# | |||||
set -e | |||||
# summary of how this script can be called: | |||||
# * <new-preinst> `install' | |||||
# * <new-preinst> `install' <old-version> | |||||
# * <new-preinst> `upgrade' <old-version> | |||||
# * <old-preinst> `abort-upgrade' <new-version> | |||||
# for details, see https://www.debian.org/doc/debian-policy/ or | |||||
# the debian-policy package | |||||
case "$1" in | |||||
install|upgrade) | |||||
if ! grep -q '^ethermint:' /etc/passwd ; then | |||||
useradd -k /dev/null -r -m -b /etc ethermint | |||||
chmod 755 /etc/ethermint | |||||
fi | |||||
;; | |||||
abort-upgrade) | |||||
;; | |||||
*) | |||||
echo "preinst called with unknown argument \`$1'" >&2 | |||||
exit 1 | |||||
;; | |||||
esac | |||||
# dh_installdeb will replace this with shell code automatically | |||||
# generated by other debhelper scripts. | |||||
#DEBHELPER# | |||||
exit 0 |
@ -0,0 +1,38 @@ | |||||
#!/bin/sh | |||||
# prerm script for ethermint | |||||
# | |||||
set -e | |||||
# summary of how this script can be called: | |||||
# * <prerm> `remove' | |||||
# * <old-prerm> `upgrade' <new-version> | |||||
# * <new-prerm> `failed-upgrade' <old-version> | |||||
# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version> | |||||
# * <deconfigured's-prerm> `deconfigure' `in-favour' | |||||
# <package-being-installed> <version> `removing' | |||||
# <conflicting-package> <version> | |||||
# for details, see https://www.debian.org/doc/debian-policy/ or | |||||
# the debian-policy package | |||||
case "$1" in | |||||
remove|upgrade|deconfigure) | |||||
systemctl stop ethermint 2> /dev/null || : | |||||
;; | |||||
failed-upgrade) | |||||
;; | |||||
*) | |||||
echo "prerm called with unknown argument \`$1'" >&2 | |||||
exit 1 | |||||
;; | |||||
esac | |||||
# dh_installdeb will replace this with shell code automatically | |||||
# generated by other debhelper scripts. | |||||
#DEBHELPER# | |||||
exit 0 |
@ -0,0 +1,2 @@ | |||||
disable ethermint.service | |||||
@ -0,0 +1,17 @@ | |||||
[Unit] | |||||
Description=Ethermint | |||||
Requires=network-online.target | |||||
After=network-online.target | |||||
[Service] | |||||
Restart=on-failure | |||||
User=ethermint | |||||
Group=ethermint | |||||
PermissionsStartOnly=true | |||||
ExecStart=/usr/bin/ethermint --datadir /etc/ethermint | |||||
ExecReload=/bin/kill -HUP $MAINPID | |||||
KillSignal=SIGTERM | |||||
[Install] | |||||
WantedBy=multi-user.target | |||||
@ -0,0 +1,6 @@ | |||||
gaia (@VERSION@) @STABILITY@; urgency=medium | |||||
* Automatic build. See https://github.com/tendermint/basecoin for more information. | |||||
-- Greg Szabo <greg@philosobear.com> @DATETIMESTAMP@ | |||||
@ -0,0 +1 @@ | |||||
9 |
@ -0,0 +1,14 @@ | |||||
Source: gaia | |||||
Section: net | |||||
Priority: optional | |||||
Maintainer: Greg Szabo <greg@philosobear.com> | |||||
Build-Depends: debhelper (>=9) | |||||
Standards-Version: 3.9.6 | |||||
Homepage: https://cosmos.network | |||||
Package: gaia | |||||
Architecture: amd64 | |||||
Version: @VERSION@ | |||||
Installed-Size: @INSTALLEDSIZE@ | |||||
Description: gaia - Tendermint Cosmos delegation game chain | |||||
Gaia description comes later. | |||||
@ -0,0 +1,21 @@ | |||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ | |||||
Upstream-Name: gaia | |||||
Source: https://github.com/cosmos/gaia | |||||
Files: * | |||||
Copyright: 2017 All In Bits, Inc. | |||||
License: Apache-2.0 | |||||
Licensed under the Apache License, Version 2.0 (the "License"); | |||||
you may not use this file except in compliance with the License. | |||||
You may obtain a copy of the License at | |||||
. | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
. | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
. | |||||
On Debian systems, the full text of the Apache License 2.0 can be found | |||||
in the file `/usr/share/common-licenses/Apache-2.0'. |
@ -0,0 +1,41 @@ | |||||
#!/bin/sh | |||||
# postinst script for gaia | |||||
# | |||||
set -e | |||||
# summary of how this script can be called: | |||||
# * <postinst> `configure' <most-recently-configured-version> | |||||
# * <old-postinst> `abort-upgrade' <new version> | |||||
# * <conflictor's-postinst> `abort-remove' `in-favour' <package> | |||||
# <new-version> | |||||
# * <postinst> `abort-remove' | |||||
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour' | |||||
# <failed-install-package> <version> `removing' | |||||
# <conflicting-package> <version> | |||||
# for details, see https://www.debian.org/doc/debian-policy/ or | |||||
# the debian-policy package | |||||
case "$1" in | |||||
configure) | |||||
chown gaia.gaia /etc/gaia | |||||
sudo -Hu gaia gaia node init --home /etc/gaia 2B24DEE2364762300168DF19B6C18BCE2D399EA2 | |||||
systemctl daemon-reload | |||||
;; | |||||
abort-upgrade|abort-remove|abort-deconfigure) | |||||
;; | |||||
*) | |||||
echo "postinst called with unknown argument \`$1'" >&2 | |||||
exit 1 | |||||
;; | |||||
esac | |||||
# dh_installdeb will replace this with shell code automatically | |||||
# generated by other debhelper scripts. | |||||
#DEBHELPER# | |||||
exit 0 |
@ -0,0 +1,41 @@ | |||||
#!/bin/sh | |||||
# postrm script for gaia | |||||
# | |||||
set -e | |||||
# summary of how this script can be called: | |||||
# * <postrm> `remove' | |||||
# * <postrm> `purge' | |||||
# * <old-postrm> `upgrade' <new-version> | |||||
# * <new-postrm> `failed-upgrade' <old-version> | |||||
# * <new-postrm> `abort-install' | |||||
# * <new-postrm> `abort-install' <old-version> | |||||
# * <new-postrm> `abort-upgrade' <old-version> | |||||
# * <disappearer's-postrm> `disappear' <overwriter> | |||||
# <overwriter-version> | |||||
# for details, see https://www.debian.org/doc/debian-policy/ or | |||||
# the debian-policy package | |||||
case "$1" in | |||||
upgrade|failed-upgrade|abort-upgrade) | |||||
systemctl daemon-reload | |||||
;; | |||||
purge|remove|abort-install|disappear) | |||||
systemctl daemon-reload | |||||
;; | |||||
*) | |||||
echo "postrm called with unknown argument \`$1'" >&2 | |||||
exit 1 | |||||
;; | |||||
esac | |||||
# dh_installdeb will replace this with shell code automatically | |||||
# generated by other debhelper scripts. | |||||
#DEBHELPER# | |||||
exit 0 |
@ -0,0 +1,38 @@ | |||||
#!/bin/sh | |||||
# preinst script for gaia | |||||
# | |||||
set -e | |||||
# summary of how this script can be called: | |||||
# * <new-preinst> `install' | |||||
# * <new-preinst> `install' <old-version> | |||||
# * <new-preinst> `upgrade' <old-version> | |||||
# * <old-preinst> `abort-upgrade' <new-version> | |||||
# for details, see https://www.debian.org/doc/debian-policy/ or | |||||
# the debian-policy package | |||||
case "$1" in | |||||
install|upgrade) | |||||
if ! grep -q '^gaia:' /etc/passwd ; then | |||||
useradd -k /dev/null -r -m -b /etc gaia | |||||
chmod 755 /etc/gaia | |||||
fi | |||||
;; | |||||
abort-upgrade) | |||||
;; | |||||
*) | |||||
echo "preinst called with unknown argument \`$1'" >&2 | |||||
exit 1 | |||||
;; | |||||
esac | |||||
# dh_installdeb will replace this with shell code automatically | |||||
# generated by other debhelper scripts. | |||||
#DEBHELPER# | |||||
exit 0 |
@ -0,0 +1,38 @@ | |||||
#!/bin/sh | |||||
# prerm script for gaia | |||||
# | |||||
set -e | |||||
# summary of how this script can be called: | |||||
# * <prerm> `remove' | |||||
# * <old-prerm> `upgrade' <new-version> | |||||
# * <new-prerm> `failed-upgrade' <old-version> | |||||
# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version> | |||||
# * <deconfigured's-prerm> `deconfigure' `in-favour' | |||||
# <package-being-installed> <version> `removing' | |||||
# <conflicting-package> <version> | |||||
# for details, see https://www.debian.org/doc/debian-policy/ or | |||||
# the debian-policy package | |||||
case "$1" in | |||||
remove|upgrade|deconfigure) | |||||
systemctl stop gaia 2> /dev/null || : | |||||
;; | |||||
failed-upgrade) | |||||
;; | |||||
*) | |||||
echo "prerm called with unknown argument \`$1'" >&2 | |||||
exit 1 | |||||
;; | |||||
esac | |||||
# dh_installdeb will replace this with shell code automatically | |||||
# generated by other debhelper scripts. | |||||
#DEBHELPER# | |||||
exit 0 |
@ -0,0 +1,2 @@ | |||||
disable gaia.service | |||||
@ -0,0 +1,17 @@ | |||||
[Unit] | |||||
Description=Gaia | |||||
Requires=network-online.target | |||||
After=network-online.target | |||||
[Service] | |||||
Restart=on-failure | |||||
User=gaia | |||||
Group=gaia | |||||
PermissionsStartOnly=true | |||||
ExecStart=/usr/bin/gaia node start --home=/etc/gaia | |||||
ExecReload=/bin/kill -HUP $MAINPID | |||||
KillSignal=SIGTERM | |||||
[Install] | |||||
WantedBy=multi-user.target | |||||
@ -0,0 +1,12 @@ | |||||
{ | |||||
"address": "1B1BE55F969F54064628A63B9559E7C21C925165", | |||||
"priv_key": { | |||||
"type": "ed25519", | |||||
"data": "C70D6934B4F55F1B7BC33B56B9CA8A2061384AFC19E91E44B40C4BBA182953D1619D3678599971ED29C7529DDD4DA537B97129893598A17C82E3AC9A8BA95279" | |||||
}, | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "619D3678599971ED29C7529DDD4DA537B97129893598A17C82E3AC9A8BA95279" | |||||
} | |||||
} | |||||
@ -0,0 +1,12 @@ | |||||
{ | |||||
"address": "1DA7C74F9C219229FD54CC9F7386D5A3839F0090", | |||||
"priv_key": { | |||||
"type": "ed25519", | |||||
"data": "34BAE9E65CE8245FAD035A0E3EED9401BDE8785FFB3199ACCF8F5B5DDF7486A8352195DA90CB0B90C24295B90AEBA25A5A71BC61BAB2FE2387241D439698B7B8" | |||||
}, | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "352195DA90CB0B90C24295B90AEBA25A5A71BC61BAB2FE2387241D439698B7B8" | |||||
} | |||||
} | |||||
@ -0,0 +1,36 @@ | |||||
#!/bin/bash | |||||
if [ $# -ne 3 ]; then | |||||
echo "Usage: $0 <application> <template_source_dir> <SPEC_dir>" | |||||
exit 1 | |||||
fi | |||||
app=$1 | |||||
src=$2 | |||||
dst=$3 | |||||
# Find spectemplate | |||||
if [ ! -f "$src/$app.spec" ]; then | |||||
if [ ! -f "$src/app-template.spec" ]; then | |||||
echo "Source template not found." | |||||
exit 1 | |||||
else | |||||
srcfile="$src/app-template.spec" | |||||
fi | |||||
else | |||||
srcfile="$src/$app.spec" | |||||
fi | |||||
# Copy spectemplate to SPECS | |||||
cp "$srcfile" "$dst/$app.spec" | |||||
# Apply any variables defined in .data | |||||
if [ -f "$src/$app.data" ]; then | |||||
srcdata="$src/$app.data" | |||||
source "$srcdata" | |||||
for var in `grep -v -e ^# -e ^\s*$ "$srcdata" | grep = | sed 's/\s*=.*$//'` | |||||
do | |||||
sed -i "s\\@${var}@\\${!var}\\g" "$dst/$app.spec" | |||||
done | |||||
fi | |||||
@ -0,0 +1,26 @@ | |||||
#!/usr/bin/expect -f | |||||
set timeout 3 | |||||
set PACKAGE [lindex $argv 0] | |||||
set GPG_NAME [lindex $argv 1] | |||||
set GPG_PATH [lindex $argv 2] | |||||
set GPG_PASSPHRASE $env(GPG_PASSPHRASE) | |||||
if {[llength $argv] == 0} { | |||||
send_user "Usage: ./sign <rpm_package> <gpg_key> <gpg_binary>\n" | |||||
exit 1 | |||||
} | |||||
send_user "\nSigning $PACKAGE\n" | |||||
spawn rpmsign --resign $PACKAGE --define "_signature gpg" --define "_gpg_name $GPG_NAME" --define "_gpgbin $GPG_PATH" | |||||
expect { | |||||
timeout { send_user "\nTimeout signing $PACKAGE\n"; exit 1 } | |||||
"Enter pass phrase:" | |||||
} | |||||
send "$GPG_PASSPHRASE\r" | |||||
expect { | |||||
timeout { send_user "\nTimeout signing $PACKAGE\n"; exit 1 } | |||||
"Pass phrase is good." | |||||
} | |||||
interact | |||||
sleep 3 | |||||
@ -0,0 +1,55 @@ | |||||
Version: @VERSION@ | |||||
Release: @BUILD_NUMBER@ | |||||
%define __spec_install_post %{nil} | |||||
%define debug_package %{nil} | |||||
%define __os_install_post %{nil} | |||||
Name: @PACKAGE_NAME@ | |||||
Summary: @PACKAGE_SUMMARY@ | |||||
License: Apache 2.0 | |||||
URL: @PACKAGE_URL@ | |||||
Packager: Greg Szabo | |||||
@PACKAGE_ADDITIONAL_HEADER@ | |||||
%description | |||||
@PACKAGE_DESCRIPTION@ | |||||
%pre | |||||
if ! %{__grep} -q '^%{name}:' /etc/passwd ; then | |||||
useradd -r -b %{_sysconfdir} %{name} | |||||
mkdir -p %{_sysconfdir}/%{name} | |||||
chmod 755 %{_sysconfdir}/%{name} | |||||
chown %{name}.%{name} %{_sysconfdir}/%{name} | |||||
fi | |||||
%prep | |||||
# Nothing to do here. - It is done in the Makefile. | |||||
%build | |||||
# Nothing to do here. | |||||
%install | |||||
cd %{name}-%{version}-%{release} | |||||
%{__cp} -a * %{buildroot} | |||||
%post | |||||
sudo -Hu %{name} %{name} node init --home %{_sysconfdir}/%{name} 2B24DEE2364762300168DF19B6C18BCE2D399EA2 | |||||
systemctl daemon-reload | |||||
%preun | |||||
systemctl stop %{name} 2> /dev/null || : | |||||
%postun | |||||
systemctl daemon-reload | |||||
%files | |||||
%ghost %attr(0755, %{name}, %{name}) %dir %{_sysconfdir}/%{name} | |||||
%{_bindir}/* | |||||
%{_sysconfdir}/systemd/system/* | |||||
%{_sysconfdir}/systemd/system-preset/* | |||||
%dir %{_datadir}/%{name} | |||||
%{_datadir}/%{name}/* | |||||
%dir %{_defaultlicensedir}/%{name} | |||||
%doc %{_defaultlicensedir}/%{name}/LICENSE | |||||
@ -0,0 +1,5 @@ | |||||
PACKAGE_SUMMARY="basecoind is a Proof-of-Stake cryptocurrency and framework" | |||||
PACKAGE_URL="https://cosmos.network/" | |||||
PACKAGE_ADDITIONAL_HEADER="Provides: basecoind" | |||||
PACKAGE_DESCRIPTION="Basecoind is an ABCI application designed to be used with the Tendermint consensus engine to form a Proof-of-Stake cryptocurrency. It also provides a general purpose framework for extending the feature-set of the cryptocurrency by implementing plugins." | |||||
@ -0,0 +1,5 @@ | |||||
PACKAGE_SUMMARY="ethermint enables ethereum as an ABCI application on tendermint and the COSMOS hub" | |||||
PACKAGE_URL="https://tendermint.com/" | |||||
PACKAGE_ADDITIONAL_HEADER="Provides: ethermint" | |||||
PACKAGE_DESCRIPTION="Ethermint enables ethereum to run as an ABCI application on tendermint and the COSMOS hub. This application allows you to get all the benefits of ethereum without having to run your own miners." | |||||
@ -0,0 +1,60 @@ | |||||
Version: @VERSION@ | |||||
Release: @BUILD_NUMBER@ | |||||
%define __spec_install_post %{nil} | |||||
%define debug_package %{nil} | |||||
%define __os_install_post %{nil} | |||||
Name: @PACKAGE_NAME@ | |||||
Summary: @PACKAGE_SUMMARY@ | |||||
License: Apache 2.0 | |||||
URL: @PACKAGE_URL@ | |||||
Packager: Greg Szabo | |||||
Requires: tendermint >= 0.11.0 | |||||
@PACKAGE_ADDITIONAL_HEADER@ | |||||
%description | |||||
@PACKAGE_DESCRIPTION@ | |||||
%pre | |||||
if ! %{__grep} -q '^%{name}:' /etc/passwd ; then | |||||
useradd -r -b %{_sysconfdir} %{name} | |||||
mkdir -p %{_sysconfdir}/%{name} | |||||
chmod 755 %{_sysconfdir}/%{name} | |||||
chown %{name}.%{name} %{_sysconfdir}/%{name} | |||||
fi | |||||
%prep | |||||
# Nothing to do here. - It is done in the Makefile. | |||||
%build | |||||
# Nothing to do here. | |||||
%install | |||||
cd %{name}-%{version}-%{release} | |||||
%{__cp} -a * %{buildroot} | |||||
%post | |||||
sudo -Hu %{name} tendermint init --home %{_sysconfdir}/%{name} | |||||
sudo -Hu %{name} %{name} --datadir %{_sysconfdir}/%{name} init %{_sysconfdir}/%{name}/genesis.json | |||||
systemctl daemon-reload | |||||
%preun | |||||
systemctl stop %{name} 2> /dev/null || : | |||||
systemctl stop %{name}-service 2> /dev/null || : | |||||
%postun | |||||
systemctl daemon-reload | |||||
%files | |||||
%attr(0755, %{name}, %{name}) %dir %{_sysconfdir}/%{name} | |||||
%config(noreplace) %attr(0644, %{name}, %{name}) %{_sysconfdir}/%{name}/genesis.json | |||||
%attr(0755, %{name}, %{name}) %dir %{_sysconfdir}/%{name}/keystore | |||||
%attr(0644, %{name}, %{name}) %{_sysconfdir}/%{name}/keystore/* | |||||
%{_bindir}/* | |||||
%{_sysconfdir}/systemd/system/* | |||||
%{_sysconfdir}/systemd/system-preset/* | |||||
%dir %{_defaultlicensedir}/%{name} | |||||
%doc %{_defaultlicensedir}/%{name}/LICENSE | |||||
@ -0,0 +1,5 @@ | |||||
PACKAGE_SUMMARY="gaia - Tendermint Cosmos delegation game chain" | |||||
PACKAGE_URL="https://cosmos.network/" | |||||
PACKAGE_ADDITIONAL_HEADER="" | |||||
PACKAGE_DESCRIPTION="Gaia description comes later." | |||||
@ -0,0 +1,31 @@ | |||||
Version: @VERSION@ | |||||
Release: @BUILD_NUMBER@ | |||||
%define __spec_install_post %{nil} | |||||
%define debug_package %{nil} | |||||
%define __os_install_post %{nil} | |||||
Name: tendermint | |||||
Summary: securely and consistently replicate an application on many machines | |||||
License: Apache 2.0 | |||||
URL: https://tendermint.com/ | |||||
Packager: Greg Szabo | |||||
%description | |||||
Tendermint is software for securely and consistently replicating an application on many machines. By securely, we mean that Tendermint works even if up to 1/3 of machines fail in arbitrary ways. By consistently, we mean that every non-faulty machine sees the same transaction log and computes the same state. | |||||
%prep | |||||
# Nothing to do here. - It is done in the Makefile. | |||||
%build | |||||
# Nothing to do here. | |||||
%install | |||||
cd %{name}-%{version}-%{release} | |||||
%{__cp} -a * %{buildroot} | |||||
%files | |||||
%{_bindir}/tendermint | |||||
%dir %{_defaultlicensedir}/%{name} | |||||
%doc %{_defaultlicensedir}/%{name}/LICENSE | |||||
@ -0,0 +1 @@ | |||||
deb http://tendermint-packages.s3-website-us-west-1.amazonaws.com/debian stable main |
@ -0,0 +1,12 @@ | |||||
#This is the .repo file for the Tendermint CentOS repositories. | |||||
#Although it has only been tested under CentOS 7, it should work under Fedora and RedHat 7 too. | |||||
#Currently only 64-bit packages are built. | |||||
[tendermint] | |||||
name=Tendermint stable releases repository | |||||
baseurl=https://do9rmxapsag1v.cloudfront.net/centos/7/os/x86_64 | |||||
gpgcheck=1 | |||||
gpgkey=https://do9rmxapsag1v.cloudfront.net/centos/7/os/x86_64/RPM-GPG-KEY-Tendermint | |||||
enabled=1 | |||||
#sslverify = 1 | |||||
@ -0,0 +1,6 @@ | |||||
tendermint (@VERSION@) @STABILITY@; urgency=medium | |||||
* Automatic build. See https://github.com/tendermint/tendermint for more information. | |||||
-- Greg Szabo <greg@philosobear.com> @DATETIMESTAMP@ | |||||
@ -0,0 +1 @@ | |||||
9 |
@ -0,0 +1,14 @@ | |||||
Source: tendermint | |||||
Section: net | |||||
Priority: optional | |||||
Maintainer: Greg Szabo <greg@philosobear.com> | |||||
Build-Depends: debhelper (>=9) | |||||
Standards-Version: 3.9.6 | |||||
Homepage: https://tendermint.com | |||||
Package: tendermint | |||||
Architecture: amd64 | |||||
Version: @VERSION@ | |||||
Installed-Size: @INSTALLEDSIZE@ | |||||
Description: securely and consistently replicate an application on many machines | |||||
Tendermint is software for securely and consistently replicating an application on many machines. By securely, we mean that Tendermint works even if up to 1/3 of machines fail in arbitrary ways. By consistently, we mean that every non-faulty machine sees the same transaction log and computes the same state. | |||||
@ -0,0 +1,21 @@ | |||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ | |||||
Upstream-Name: tendermint | |||||
Source: https://github.com/tendermint/tendermint | |||||
Files: * | |||||
Copyright: 2017 All In Bits, Inc. | |||||
License: Apache-2.0 | |||||
Licensed under the Apache License, Version 2.0 (the "License"); | |||||
you may not use this file except in compliance with the License. | |||||
You may obtain a copy of the License at | |||||
. | |||||
http://www.apache.org/licenses/LICENSE-2.0 | |||||
. | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. | |||||
. | |||||
On Debian systems, the full text of the Apache License 2.0 can be found | |||||
in the file `/usr/share/common-licenses/Apache-2.0'. |
@ -0,0 +1,192 @@ | |||||
Copyright (C) 2017 Tendermint | |||||
Apache License | |||||
Version 2.0, January 2004 | |||||
https://www.apache.org/licenses/ | |||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |||||
1. Definitions. | |||||
"License" shall mean the terms and conditions for use, reproduction, | |||||
and distribution as defined by Sections 1 through 9 of this document. | |||||
"Licensor" shall mean the copyright owner or entity authorized by | |||||
the copyright owner that is granting the License. | |||||
"Legal Entity" shall mean the union of the acting entity and all | |||||
other entities that control, are controlled by, or are under common | |||||
control with that entity. For the purposes of this definition, | |||||
"control" means (i) the power, direct or indirect, to cause the | |||||
direction or management of such entity, whether by contract or | |||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the | |||||
outstanding shares, or (iii) beneficial ownership of such entity. | |||||
"You" (or "Your") shall mean an individual or Legal Entity | |||||
exercising permissions granted by this License. | |||||
"Source" form shall mean the preferred form for making modifications, | |||||
including but not limited to software source code, documentation | |||||
source, and configuration files. | |||||
"Object" form shall mean any form resulting from mechanical | |||||
transformation or translation of a Source form, including but | |||||
not limited to compiled object code, generated documentation, | |||||
and conversions to other media types. | |||||
"Work" shall mean the work of authorship, whether in Source or | |||||
Object form, made available under the License, as indicated by a | |||||
copyright notice that is included in or attached to the work | |||||
(an example is provided in the Appendix below). | |||||
"Derivative Works" shall mean any work, whether in Source or Object | |||||
form, that is based on (or derived from) the Work and for which the | |||||
editorial revisions, annotations, elaborations, or other modifications | |||||
represent, as a whole, an original work of authorship. For the purposes | |||||
of this License, Derivative Works shall not include works that remain | |||||
separable from, or merely link (or bind by name) to the interfaces of, | |||||
the Work and Derivative Works thereof. | |||||
"Contribution" shall mean any work of authorship, including | |||||
the original version of the Work and any modifications or additions | |||||
to that Work or Derivative Works thereof, that is intentionally | |||||
submitted to Licensor for inclusion in the Work by the copyright owner | |||||
or by an individual or Legal Entity authorized to submit on behalf of | |||||
the copyright owner. For the purposes of this definition, "submitted" | |||||
means any form of electronic, verbal, or written communication sent | |||||
to the Licensor or its representatives, including but not limited to | |||||
communication on electronic mailing lists, source code control systems, | |||||
and issue tracking systems that are managed by, or on behalf of, the | |||||
Licensor for the purpose of discussing and improving the Work, but | |||||
excluding communication that is conspicuously marked or otherwise | |||||
designated in writing by the copyright owner as "Not a Contribution." | |||||
"Contributor" shall mean Licensor and any individual or Legal Entity | |||||
on behalf of whom a Contribution has been received by Licensor and | |||||
subsequently incorporated within the Work. | |||||
2. Grant of Copyright License. Subject to the terms and conditions of | |||||
this License, each Contributor hereby grants to You a perpetual, | |||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||||
copyright license to reproduce, prepare Derivative Works of, | |||||
publicly display, publicly perform, sublicense, and distribute the | |||||
Work and such Derivative Works in Source or Object form. | |||||
3. Grant of Patent License. Subject to the terms and conditions of | |||||
this License, each Contributor hereby grants to You a perpetual, | |||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||||
(except as stated in this section) patent license to make, have made, | |||||
use, offer to sell, sell, import, and otherwise transfer the Work, | |||||
where such license applies only to those patent claims licensable | |||||
by such Contributor that are necessarily infringed by their | |||||
Contribution(s) alone or by combination of their Contribution(s) | |||||
with the Work to which such Contribution(s) was submitted. If You | |||||
institute patent litigation against any entity (including a | |||||
cross-claim or counterclaim in a lawsuit) alleging that the Work | |||||
or a Contribution incorporated within the Work constitutes direct | |||||
or contributory patent infringement, then any patent licenses | |||||
granted to You under this License for that Work shall terminate | |||||
as of the date such litigation is filed. | |||||
4. Redistribution. You may reproduce and distribute copies of the | |||||
Work or Derivative Works thereof in any medium, with or without | |||||
modifications, and in Source or Object form, provided that You | |||||
meet the following conditions: | |||||
(a) You must give any other recipients of the Work or | |||||
Derivative Works a copy of this License; and | |||||
(b) You must cause any modified files to carry prominent notices | |||||
stating that You changed the files; and | |||||
(c) You must retain, in the Source form of any Derivative Works | |||||
that You distribute, all copyright, patent, trademark, and | |||||
attribution notices from the Source form of the Work, | |||||
excluding those notices that do not pertain to any part of | |||||
the Derivative Works; and | |||||
(d) If the Work includes a "NOTICE" text file as part of its | |||||
distribution, then any Derivative Works that You distribute must | |||||
include a readable copy of the attribution notices contained | |||||
within such NOTICE file, excluding those notices that do not | |||||
pertain to any part of the Derivative Works, in at least one | |||||
of the following places: within a NOTICE text file distributed | |||||
as part of the Derivative Works; within the Source form or | |||||
documentation, if provided along with the Derivative Works; or, | |||||
within a display generated by the Derivative Works, if and | |||||
wherever such third-party notices normally appear. The contents | |||||
of the NOTICE file are for informational purposes only and | |||||
do not modify the License. You may add Your own attribution | |||||
notices within Derivative Works that You distribute, alongside | |||||
or as an addendum to the NOTICE text from the Work, provided | |||||
that such additional attribution notices cannot be construed | |||||
as modifying the License. | |||||
You may add Your own copyright statement to Your modifications and | |||||
may provide additional or different license terms and conditions | |||||
for use, reproduction, or distribution of Your modifications, or | |||||
for any such Derivative Works as a whole, provided Your use, | |||||
reproduction, and distribution of the Work otherwise complies with | |||||
the conditions stated in this License. | |||||
5. Submission of Contributions. Unless You explicitly state otherwise, | |||||
any Contribution intentionally submitted for inclusion in the Work | |||||
by You to the Licensor shall be under the terms and conditions of | |||||
this License, without any additional terms or conditions. | |||||
Notwithstanding the above, nothing herein shall supersede or modify | |||||
the terms of any separate license agreement you may have executed | |||||
with Licensor regarding such Contributions. | |||||
6. Trademarks. This License does not grant permission to use the trade | |||||
names, trademarks, service marks, or product names of the Licensor, | |||||
except as required for reasonable and customary use in describing the | |||||
origin of the Work and reproducing the content of the NOTICE file. | |||||
7. Disclaimer of Warranty. Unless required by applicable law or | |||||
agreed to in writing, Licensor provides the Work (and each | |||||
Contributor provides its Contributions) on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||||
implied, including, without limitation, any warranties or conditions | |||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |||||
PARTICULAR PURPOSE. You are solely responsible for determining the | |||||
appropriateness of using or redistributing the Work and assume any | |||||
risks associated with Your exercise of permissions under this License. | |||||
8. Limitation of Liability. In no event and under no legal theory, | |||||
whether in tort (including negligence), contract, or otherwise, | |||||
unless required by applicable law (such as deliberate and grossly | |||||
negligent acts) or agreed to in writing, shall any Contributor be | |||||
liable to You for damages, including any direct, indirect, special, | |||||
incidental, or consequential damages of any character arising as a | |||||
result of this License or out of the use or inability to use the | |||||
Work (including but not limited to damages for loss of goodwill, | |||||
work stoppage, computer failure or malfunction, or any and all | |||||
other commercial damages or losses), even if such Contributor | |||||
has been advised of the possibility of such damages. | |||||
9. Accepting Warranty or Additional Liability. While redistributing | |||||
the Work or Derivative Works thereof, You may choose to offer, | |||||
and charge a fee for, acceptance of support, warranty, indemnity, | |||||
or other liability obligations and/or rights consistent with this | |||||
License. However, in accepting such obligations, You may act only | |||||
on Your own behalf and on Your sole responsibility, not on behalf | |||||
of any other Contributor, and only if You agree to indemnify, | |||||
defend, and hold each Contributor harmless for any liability | |||||
incurred by, or claims asserted against, such Contributor by reason | |||||
of your accepting any such warranty or additional liability. | |||||
END OF TERMS AND CONDITIONS | |||||
Licensed under the Apache License, Version 2.0 (the "License"); | |||||
you may not use this file except in compliance with the License. | |||||
You may obtain a copy of the License at | |||||
https://www.apache.org/licenses/LICENSE-2.0 | |||||
Unless required by applicable law or agreed to in writing, software | |||||
distributed under the License is distributed on an "AS IS" BASIS, | |||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
See the License for the specific language governing permissions and | |||||
limitations under the License. |
@ -0,0 +1,290 @@ | |||||
Using Kubernetes | |||||
================ | |||||
.. figure:: assets/t_plus_k.png | |||||
:alt: Tendermint plus Kubernetes | |||||
Tendermint plus Kubernetes | |||||
This should primarily be used for testing purposes or for | |||||
tightly-defined chains operated by a single stakeholder (see `the | |||||
security precautions <#security>`__). If your desire is to launch an | |||||
application with many stakeholders, consider using our set of Ansible | |||||
scripts. | |||||
Quick Start | |||||
----------- | |||||
For either platform, see the `requirements <https://github.com/kubernetes/minikube#requirements>`__ | |||||
MacOS | |||||
^^^^^ | |||||
:: | |||||
curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/kubectl | |||||
curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.18.0/minikube-darwin-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/ | |||||
minikube start | |||||
git clone https://github.com/tendermint/tools.git && cd tools/mintnet-kubernetes/examples/basecoin && make create | |||||
Linux | |||||
^^^^^ | |||||
:: | |||||
curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/kubectl | |||||
curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.18.0/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/ | |||||
minikube start | |||||
git clone https://github.com/tendermint/tools.git && cd tools/mintnet-kubernetes/examples/basecoin && make create | |||||
Verify it worked | |||||
~~~~~~~~~~~~~~~~ | |||||
**Using a shell:** | |||||
First wait until all the pods are ``Running``: | |||||
``kubectl get pods -w -o wide -L tm`` | |||||
then query the Tendermint app logs from the first pod: | |||||
``kubectl logs -c tm -f tm-0`` | |||||
finally, use our `Rest API <../specification/rpc.html>`__ to fetch the status of the second pod's Tendermint app. | |||||
Note we are using ``kubectl exec`` because pods are not exposed (and should not be) to the | |||||
outer network: | |||||
``kubectl exec -c tm tm-0 -- curl -s http://tm-1.basecoin:26657/status | json_pp`` | |||||
**Using the dashboard:** | |||||
:: | |||||
minikube dashboard | |||||
Clean up | |||||
~~~~~~~~ | |||||
:: | |||||
make destroy | |||||
Usage | |||||
----- | |||||
Setup a Kubernetes cluster | |||||
^^^^^^^^^^^^^^^^^^^^^^^^^^ | |||||
- locally using `Minikube <https://github.com/kubernetes/minikube>`__ | |||||
- on GCE with a single click in the web UI | |||||
- on AWS using `Kubernetes | |||||
Operations <https://github.com/kubernetes/kops/blob/master/docs/aws.md>`__ | |||||
- on Linux machines (Digital Ocean) using | |||||
`kubeadm <https://kubernetes.io/docs/getting-started-guides/kubeadm/>`__ | |||||
- on AWS, Azure, GCE or bare metal using `Kargo | |||||
(Ansible) <https://kubernetes.io/docs/getting-started-guides/kargo/>`__ | |||||
Please refer to `the official | |||||
documentation <https://kubernetes.io/docs/getting-started-guides/>`__ | |||||
for overview and comparison of different options. | |||||
Kubernetes on Digital Ocean | |||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||||
Available options: | |||||
- `kubeadm (alpha) <https://kubernetes.io/docs/getting-started-guides/kubeadm/>`__ | |||||
- `kargo <https://kubernetes.io/docs/getting-started-guides/kargo/>`__ | |||||
- `rancher <http://rancher.com/>`__ | |||||
- `terraform <https://github.com/hermanjunge/kubernetes-digitalocean-terraform>`__ | |||||
As you can see, there is no single tool for creating a cluster on DO. | |||||
Therefore, choose the one you know and comfortable working with. If you know | |||||
and used `terraform <https://www.terraform.io/>`__ before, then choose it. If you | |||||
know Ansible, then pick kargo. If none of these seem familiar to you, go with | |||||
``kubeadm``. Rancher is a beautiful UI for deploying and managing containers in | |||||
production. | |||||
Kubernetes on Google Cloud Engine | |||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |||||
Review the `Official Documentation <https://kubernetes.io/docs/getting-started-guides/gce/>`__ for Kubernetes on Google Compute | |||||
Engine. | |||||
**Create a cluster** | |||||
The recommended way is to use `Google Container | |||||
Engine <https://cloud.google.com/container-engine/>`__. You should be able | |||||
to create a fully fledged cluster with just a few clicks. | |||||
**Connect to it** | |||||
Install ``gcloud`` as a part of `Google Cloud SDK <https://cloud.google.com/sdk/>`__. | |||||
Make sure you have credentials for GCloud by running ``gcloud auth login``. | |||||
In order to make API calls against GCE, you must also run ``gcloud auth | |||||
application-default login``. | |||||
Press ``Connect``: | |||||
.. figure:: assets/gce1.png | |||||
and execute the first command in your shell. Then start a proxy by | |||||
executing ``kubectl` proxy``. | |||||
.. figure:: assets/gce2.png | |||||
Now you should be able to run ``kubectl`` command to create resources, get | |||||
resource info, logs, etc. | |||||
**Make sure you have Kubernetes >= 1.5, because you will be using | |||||
StatefulSets, which is a beta feature in 1.5.** | |||||
Create a configuration file | |||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |||||
Download a template: | |||||
:: | |||||
curl -Lo app.yaml https://github.com/tendermint/tools/raw/master/mintnet-kubernetes/app.template.yaml | |||||
Open ``app.yaml`` in your favorite editor and configure your app | |||||
container (navigate to ``- name: app``). Kubernetes DSL (Domain Specific | |||||
Language) is very simple, so it should be easy. You will need to set | |||||
Docker image, command and/or run arguments. Replace variables prefixed | |||||
with ``YOUR_APP`` with corresponding values. Set genesis time to now and | |||||
preferable chain ID in ConfigMap. | |||||
Please note if you are changing ``replicas`` number, do not forget to | |||||
update ``validators`` set in ConfigMap. You will be able to scale the | |||||
cluster up or down later, but new pods (nodes) won't become validators | |||||
automatically. | |||||
Deploy your application | |||||
^^^^^^^^^^^^^^^^^^^^^^^ | |||||
:: | |||||
kubectl create -f ./app.yaml | |||||
Observe your cluster | |||||
^^^^^^^^^^^^^^^^^^^^ | |||||
`web UI <https://github.com/kubernetes/dashboard>`__ | |||||
The easiest way to access Dashboard is to use ``kubectl``. Run the following | |||||
command in your desktop environment: | |||||
:: | |||||
kubectl proxy | |||||
``kubectl`` will handle authentication with apiserver and make Dashboard | |||||
available at http://localhost:8001/ui | |||||
**shell** | |||||
List all the pods: | |||||
:: | |||||
kubectl get pods -o wide -L tm | |||||
StatefulSet details: | |||||
:: | |||||
kubectl describe statefulsets tm | |||||
First pod details: | |||||
:: | |||||
kubectl describe pod tm-0 | |||||
Tendermint app logs from the first pod: | |||||
:: | |||||
kubectl logs tm-0 -c tm -f | |||||
App logs from the first pod: | |||||
:: | |||||
kubectl logs tm-0 -c app -f | |||||
Status of the second pod's Tendermint app: | |||||
:: | |||||
kubectl exec -c tm tm-0 -- curl -s http://tm-1.<YOUR_APP_NAME>:26657/status | json_pp | |||||
Security | |||||
-------- | |||||
Due to the nature of Kubernetes, where you typically have a single | |||||
master, the master could be a SPOF (Single Point Of Failure). Therefore, | |||||
you need to make sure only authorized people can access it. And these | |||||
people themselves had taken basic measures in order not to get hacked. | |||||
These are the best practices: | |||||
- all access to the master is over TLS | |||||
- access to the API Server is X.509 certificate or token based | |||||
- etcd is not exposed directly to the cluster | |||||
- ensure that images are free of vulnerabilities | |||||
(`1 <https://github.com/coreos/clair>`__) | |||||
- ensure that only authorized images are used in your environment | |||||
- disable direct access to Kubernetes nodes (no SSH) | |||||
- define resource quota | |||||
Resources: | |||||
- https://kubernetes.io/docs/admin/accessing-the-api/ | |||||
- http://blog.kubernetes.io/2016/08/security-best-practices-kubernetes-deployment.html | |||||
- https://blog.openshift.com/securing-kubernetes/ | |||||
Fault tolerance | |||||
--------------- | |||||
Having a single master (API server) is a bad thing also because if | |||||
something happens to it, you risk being left without an access to the | |||||
application. | |||||
To avoid that you can `run Kubernetes in multiple | |||||
zones <https://kubernetes.io/docs/admin/multiple-zones/>`__, each zone | |||||
running an `API | |||||
server <https://kubernetes.io/docs/admin/high-availability/>`__ and load | |||||
balance requests between them. Do not forget to make sure only one | |||||
instance of scheduler and controller-manager are running at once. | |||||
Running in multiple zones is a lightweight version of a broader `Cluster | |||||
Federation feature <https://kubernetes.io/docs/admin/federation/>`__. | |||||
Federated deployments could span across multiple regions (not zones). We | |||||
haven't tried this feature yet, so any feedback is highly appreciated! | |||||
Especially, related to additional latency and cost of exchanging data | |||||
between the regions. | |||||
Resources: | |||||
- https://kubernetes.io/docs/admin/high-availability/ | |||||
Starting process | |||||
---------------- | |||||
.. figure:: assets/statefulset.png | |||||
:alt: StatefulSet | |||||
StatefulSet | |||||
Init containers (``tm-gen-validator``) are run before all other | |||||
containers, creating public-private key pair for each pod. Every ``tm`` | |||||
container then asks other pods for their public keys, which are served | |||||
with nginx (``pub-key`` container). When ``tm`` container have all the | |||||
keys, it forms a genesis file and starts the Tendermint process. |
@ -0,0 +1,265 @@ | |||||
--- | |||||
apiVersion: v1 | |||||
kind: Service | |||||
metadata: | |||||
annotations: | |||||
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" | |||||
name: YOUR_APP_NAME | |||||
labels: | |||||
app: YOUR_APP_NAME | |||||
spec: | |||||
ports: | |||||
- port: 26656 | |||||
name: p2p | |||||
- port: 26657 | |||||
name: rpc | |||||
clusterIP: None | |||||
selector: | |||||
app: tm | |||||
--- | |||||
apiVersion: v1 | |||||
kind: ConfigMap | |||||
metadata: | |||||
name: tm-config | |||||
data: | |||||
seeds: "tm-0,tm-1,tm-2,tm-3" | |||||
validators: "tm-0,tm-1,tm-2,tm-3" | |||||
validator.power: "10" | |||||
genesis.json: |- | |||||
{ | |||||
"genesis_time": "2017-01-02T10:10:10.164Z", | |||||
"chain_id": "chain-B5XXm5", | |||||
"validators": [], | |||||
"app_hash": "" | |||||
} | |||||
pub_key_nginx.conf: |- | |||||
server { | |||||
listen 80 default_server; | |||||
listen [::]:80 default_server ipv6only=on; | |||||
location /pub_key.json { root /usr/share/nginx/; } | |||||
} | |||||
--- | |||||
apiVersion: policy/v1beta1 | |||||
kind: PodDisruptionBudget | |||||
metadata: | |||||
name: tm-budget | |||||
spec: | |||||
selector: | |||||
matchLabels: | |||||
app: tm | |||||
minAvailable: 2 | |||||
--- | |||||
apiVersion: apps/v1beta1 | |||||
kind: StatefulSet | |||||
metadata: | |||||
name: tm | |||||
spec: | |||||
serviceName: YOUR_APP_NAME | |||||
replicas: 4 | |||||
template: | |||||
metadata: | |||||
labels: | |||||
app: tm | |||||
version: v1 | |||||
annotations: | |||||
pod.beta.kubernetes.io/init-containers: '[{ | |||||
"name": "tm-gen-validator", | |||||
"image": "tendermint/tendermint:0.10.0", | |||||
"imagePullPolicy": "IfNotPresent", | |||||
"command": ["bash", "-c", " | |||||
set -ex\n | |||||
if [ ! -f /tendermint/priv_validator.json ]; then\n | |||||
tendermint gen_validator > /tendermint/priv_validator.json\n | |||||
# pub_key.json will be served by pub-key container\n | |||||
cat /tendermint/priv_validator.json | jq \".pub_key\" > /tendermint/pub_key.json\n | |||||
fi\n | |||||
"], | |||||
"volumeMounts": [ | |||||
{"name": "tmdir", "mountPath": "/tendermint"} | |||||
] | |||||
}]' | |||||
spec: | |||||
containers: | |||||
- name: tm | |||||
imagePullPolicy: IfNotPresent | |||||
image: tendermint/tendermint:0.10.0 | |||||
resources: | |||||
requests: | |||||
cpu: 50m | |||||
memory: 128Mi | |||||
limits: | |||||
cpu: 100m | |||||
memory: 256Mi | |||||
ports: | |||||
- containerPort: 26656 | |||||
name: p2p | |||||
- containerPort: 26657 | |||||
name: rpc | |||||
env: | |||||
- name: SEEDS | |||||
valueFrom: | |||||
configMapKeyRef: | |||||
name: tm-config | |||||
key: seeds | |||||
- name: VALIDATOR_POWER | |||||
valueFrom: | |||||
configMapKeyRef: | |||||
name: tm-config | |||||
key: validator.power | |||||
- name: VALIDATORS | |||||
valueFrom: | |||||
configMapKeyRef: | |||||
name: tm-config | |||||
key: validators | |||||
- name: TMHOME | |||||
value: /tendermint | |||||
command: | |||||
- bash | |||||
- "-c" | |||||
- | | |||||
set -ex | |||||
# copy template | |||||
cp /etc/tendermint/genesis.json /tendermint/genesis.json | |||||
# fill genesis file with validators | |||||
IFS=',' read -ra VALS_ARR <<< "$VALIDATORS" | |||||
fqdn_suffix=$(hostname -f | sed 's#[^.]*\.\(\)#\1#') | |||||
for v in "${VALS_ARR[@]}"; do | |||||
# wait until validator generates priv/pub key pair | |||||
set +e | |||||
curl -s --fail "http://$v.$fqdn_suffix/pub_key.json" > /dev/null | |||||
ERR=$? | |||||
while [ "$ERR" != 0 ]; do | |||||
sleep 5 | |||||
curl -s --fail "http://$v.$fqdn_suffix/pub_key.json" > /dev/null | |||||
ERR=$? | |||||
done | |||||
set -e | |||||
# add validator to genesis file along with its pub_key | |||||
curl -s "http://$v.$fqdn_suffix/pub_key.json" | jq ". as \$k | {pub_key: \$k, amount: $VALIDATOR_POWER, name: \"$v\"}" > pub_validator.json | |||||
cat /tendermint/genesis.json | jq ".validators |= .+ [$(cat pub_validator.json)]" > tmpgenesis && mv tmpgenesis /tendermint/genesis.json | |||||
rm pub_validator.json | |||||
done | |||||
# construct seeds | |||||
IFS=',' read -ra SEEDS_ARR <<< "$SEEDS" | |||||
seeds=() | |||||
for s in "${SEEDS_ARR[@]}"; do | |||||
seeds+=("$s.$fqdn_suffix:26656") | |||||
done | |||||
seeds=$(IFS=','; echo "${seeds[*]}") | |||||
tendermint node --p2p.seeds="$seeds" --moniker="`hostname`" --proxy_app="unix:///socks/app.sock" | |||||
volumeMounts: | |||||
- name: tmdir | |||||
mountPath: /tendermint | |||||
- mountPath: /etc/tendermint/genesis.json | |||||
name: configdir | |||||
subPath: genesis.json | |||||
- name: socksdir | |||||
mountPath: /socks | |||||
- name: app | |||||
imagePullPolicy: IfNotPresent | |||||
image: YOUR_APP_IMAGE | |||||
args: ["--addr=\"unix:///socks/app.sock\""] | |||||
volumeMounts: | |||||
- name: socksdir | |||||
mountPath: /socks | |||||
######## OR ######## | |||||
# | |||||
# - name: app | |||||
# imagePullPolicy: IfNotPresent | |||||
# image: golang:1.7.5 | |||||
# resources: | |||||
# requests: | |||||
# cpu: YOUR_APP_CPU_REQ | |||||
# memory: YOUR_APP_MEM_REQ | |||||
# limits: | |||||
# cpu: YOUR_APP_CPU_LIMIT | |||||
# memory: YOUR_APP_MEM_LIMIT | |||||
# command: | |||||
# - bash | |||||
# - "-c" | |||||
# - | | |||||
# set -ex | |||||
# go get -d YOUR_APP_PACKAGE | |||||
# cd $GOPATH/YOUR_APP_PACKAGE | |||||
# make install | |||||
# | |||||
# rm -f /socks/app.sock # remove old socket | |||||
# YOUR_APP_EXEC --addr="unix:///socks/app.sock" | |||||
# volumeMounts: | |||||
# - name: socksdir | |||||
# mountPath: /socks | |||||
######## OPTIONALLY ######## | |||||
# | |||||
# - name: data | |||||
# imagePullPolicy: IfNotPresent | |||||
# image: golang:1.7.5 | |||||
# command: | |||||
# - bash | |||||
# - "-c" | |||||
# - | | |||||
# set -ex | |||||
# go get github.com/tendermint/merkleeyes/cmd/merkleeyes | |||||
# rm -f /socks/data.sock # remove old socket | |||||
# merkleeyes server --address="unix:///socks/data.sock" | |||||
# volumeMounts: | |||||
# - name: socksdir | |||||
# mountPath: /socks | |||||
- name: pub-key | |||||
imagePullPolicy: IfNotPresent | |||||
image: nginx:1.11.9 | |||||
resources: | |||||
requests: | |||||
cpu: 10m | |||||
memory: 12Mi | |||||
limits: | |||||
cpu: 20m | |||||
memory: 24Mi | |||||
ports: | |||||
- containerPort: 80 | |||||
name: pub-key | |||||
command: | |||||
- bash | |||||
- "-c" | |||||
- | | |||||
set -ex | |||||
# fixes 403 Permission Denied (open() "/tendermint/pub_key.json" failed (13: Permission denied)) | |||||
# => we cannot serve from /tendermint, so we copy the file | |||||
mkdir -p /usr/share/nginx | |||||
cp /tendermint/pub_key.json /usr/share/nginx/pub_key.json | |||||
nginx -g "daemon off;" | |||||
volumeMounts: | |||||
- name: tmdir | |||||
mountPath: /tendermint | |||||
- mountPath: /etc/nginx/conf.d/pub_key.conf | |||||
name: configdir | |||||
subPath: pub_key_nginx.conf | |||||
volumes: | |||||
- name: configdir | |||||
configMap: | |||||
name: tm-config | |||||
- name: socksdir | |||||
emptyDir: {} | |||||
volumeClaimTemplates: | |||||
- metadata: | |||||
name: tmdir | |||||
annotations: | |||||
volume.alpha.kubernetes.io/storage-class: anything | |||||
spec: | |||||
accessModes: ["ReadWriteOnce"] | |||||
resources: | |||||
requests: | |||||
storage: 2Gi |
@ -0,0 +1,10 @@ | |||||
create: | |||||
@echo "==> Creating deployment" | |||||
@kubectl create -f app.yaml | |||||
destroy: | |||||
@echo "==> Destroying deployment" | |||||
@kubectl delete -f app.yaml | |||||
@kubectl delete pvc -l app=tm | |||||
.PHONY: create destroy |
@ -0,0 +1,42 @@ | |||||
# Basecoin example | |||||
This is an example of using [basecoin](https://github.com/tendermint/basecoin). | |||||
## Usage | |||||
``` | |||||
make create | |||||
``` | |||||
### Check account balance and send a transaction | |||||
1. wait until all the pods are `Running`. | |||||
``` | |||||
kubectl get pods -w -o wide -L tm | |||||
``` | |||||
2. wait until app starts. | |||||
``` | |||||
kubectl logs -c app -f tm-0 | |||||
``` | |||||
3. get account's address of the second pod | |||||
``` | |||||
ADDR=`kubectl exec -c app tm-1 -- cat /app/key.json | jq ".address" | tr -d "\""` | |||||
``` | |||||
4. send 5 coins to it from the first pod | |||||
``` | |||||
kubectl exec -c app tm-0 -- basecoin tx send --to "0x$ADDR" --amount 5mycoin --from /app/key.json --chain_id chain-tTH4mi | |||||
``` | |||||
## Clean up | |||||
``` | |||||
make destroy | |||||
``` |
@ -0,0 +1,334 @@ | |||||
--- | |||||
apiVersion: v1 | |||||
kind: Service | |||||
metadata: | |||||
annotations: | |||||
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" | |||||
name: basecoin | |||||
labels: | |||||
app: basecoin | |||||
spec: | |||||
ports: | |||||
- port: 26656 | |||||
name: p2p | |||||
- port: 26657 | |||||
name: rpc | |||||
clusterIP: None | |||||
selector: | |||||
app: tm | |||||
--- | |||||
apiVersion: v1 | |||||
kind: ConfigMap | |||||
metadata: | |||||
name: tm-config | |||||
data: | |||||
seeds: "tm-0,tm-1,tm-2,tm-3" | |||||
validators: "tm-0,tm-1,tm-2,tm-3" | |||||
validator.power: "10" | |||||
genesis.json: |- | |||||
{ | |||||
"genesis_time": "2016-02-05T06:02:31.526Z", | |||||
"chain_id": "chain-tTH4mi", | |||||
"validators": [], | |||||
"app_hash": "" | |||||
} | |||||
pub_key_nginx.conf: |- | |||||
server { | |||||
listen 80 default_server; | |||||
listen [::]:80 default_server ipv6only=on; | |||||
location /pub_key.json { root /usr/share/nginx/; } | |||||
location /app_pub_key.json { root /usr/share/nginx/; } | |||||
} | |||||
--- | |||||
apiVersion: v1 | |||||
kind: ConfigMap | |||||
metadata: | |||||
name: app-config | |||||
data: | |||||
genesis.json: |- | |||||
{ | |||||
"chain_id": "chain-tTH4mi", | |||||
"app_options": { | |||||
"accounts": [ | |||||
{ | |||||
"pub_key": "tm-0", | |||||
"coins": [ | |||||
{ | |||||
"denom": "mycoin", | |||||
"amount": 1000000000 | |||||
} | |||||
] | |||||
}, | |||||
{ | |||||
"pub_key": "tm-1", | |||||
"coins": [ | |||||
{ | |||||
"denom": "mycoin", | |||||
"amount": 1000000000 | |||||
} | |||||
] | |||||
}, | |||||
{ | |||||
"pub_key": "tm-2", | |||||
"coins": [ | |||||
{ | |||||
"denom": "mycoin", | |||||
"amount": 1000000000 | |||||
} | |||||
] | |||||
}, | |||||
{ | |||||
"pub_key": "tm-3", | |||||
"coins": [ | |||||
{ | |||||
"denom": "mycoin", | |||||
"amount": 1000000000 | |||||
} | |||||
] | |||||
} | |||||
] | |||||
} | |||||
} | |||||
--- | |||||
apiVersion: policy/v1beta1 | |||||
kind: PodDisruptionBudget | |||||
metadata: | |||||
name: tm-budget | |||||
spec: | |||||
selector: | |||||
matchLabels: | |||||
app: tm | |||||
minAvailable: 2 | |||||
--- | |||||
apiVersion: apps/v1beta1 | |||||
kind: StatefulSet | |||||
metadata: | |||||
name: tm | |||||
spec: | |||||
serviceName: basecoin | |||||
replicas: 4 | |||||
template: | |||||
metadata: | |||||
labels: | |||||
app: tm | |||||
annotations: | |||||
pod.beta.kubernetes.io/init-containers: '[{ | |||||
"name": "tm-gen-validator", | |||||
"image": "tendermint/tendermint:0.10.0", | |||||
"imagePullPolicy": "IfNotPresent", | |||||
"command": ["bash", "-c", " | |||||
set -ex\n | |||||
if [ ! -f /tendermint/priv_validator.json ]; then\n | |||||
tendermint gen_validator > /tendermint/priv_validator.json\n | |||||
# pub_key.json will be served by pub-key container\n | |||||
cat /tendermint/priv_validator.json | jq \".pub_key\" > /tendermint/pub_key.json\n | |||||
fi\n | |||||
"], | |||||
"volumeMounts": [ | |||||
{"name": "tmdir", "mountPath": "/tendermint"} | |||||
] | |||||
}, | |||||
{ | |||||
"name": "app-gen-key", | |||||
"image": "tendermint/basecoin:0.5.1", | |||||
"imagePullPolicy": "IfNotPresent", | |||||
"command": ["bash", "-c", " | |||||
set -ex\n | |||||
if [ ! -f /app/key.json ]; then\n | |||||
basecoin key new > /app/key.json\n | |||||
# pub_key.json will be served by app-pub-key container\n | |||||
cat /app/key.json | jq \".pub_key\" > /app/pub_key.json\n | |||||
fi\n | |||||
"], | |||||
"volumeMounts": [ | |||||
{"name": "appdir", "mountPath": "/app"} | |||||
] | |||||
}]' | |||||
spec: | |||||
containers: | |||||
- name: tm | |||||
imagePullPolicy: IfNotPresent | |||||
image: tendermint/tendermint:0.10.0 | |||||
ports: | |||||
- containerPort: 26656 | |||||
name: p2p | |||||
- containerPort: 26657 | |||||
name: rpc | |||||
env: | |||||
- name: SEEDS | |||||
valueFrom: | |||||
configMapKeyRef: | |||||
name: tm-config | |||||
key: seeds | |||||
- name: VALIDATOR_POWER | |||||
valueFrom: | |||||
configMapKeyRef: | |||||
name: tm-config | |||||
key: validator.power | |||||
- name: VALIDATORS | |||||
valueFrom: | |||||
configMapKeyRef: | |||||
name: tm-config | |||||
key: validators | |||||
- name: TMHOME | |||||
value: /tendermint | |||||
command: | |||||
- bash | |||||
- "-c" | |||||
- | | |||||
set -ex | |||||
# copy template | |||||
cp /etc/tendermint/genesis.json /tendermint/genesis.json | |||||
# fill genesis file with validators | |||||
IFS=',' read -ra VALS_ARR <<< "$VALIDATORS" | |||||
fqdn_suffix=$(hostname -f | sed 's#[^.]*\.\(\)#\1#') | |||||
for v in "${VALS_ARR[@]}"; do | |||||
# wait until validator generates priv/pub key pair | |||||
set +e | |||||
curl -s --fail "http://$v.$fqdn_suffix/pub_key.json" > /dev/null | |||||
ERR=$? | |||||
while [ "$ERR" != 0 ]; do | |||||
sleep 5 | |||||
curl -s --fail "http://$v.$fqdn_suffix/pub_key.json" > /dev/null | |||||
ERR=$? | |||||
done | |||||
set -e | |||||
# add validator to genesis file along with its pub_key | |||||
curl -s "http://$v.$fqdn_suffix/pub_key.json" | jq ". as \$k | {pub_key: \$k, amount: $VALIDATOR_POWER, name: \"$v\"}" > pub_validator.json | |||||
cat /tendermint/genesis.json | jq ".validators |= .+ [$(cat pub_validator.json)]" > tmpgenesis && mv tmpgenesis /tendermint/genesis.json | |||||
rm pub_validator.json | |||||
done | |||||
# construct seeds | |||||
IFS=',' read -ra SEEDS_ARR <<< "$SEEDS" | |||||
seeds=() | |||||
for s in "${SEEDS_ARR[@]}"; do | |||||
seeds+=("$s.$fqdn_suffix:26656") | |||||
done | |||||
seeds=$(IFS=','; echo "${seeds[*]}") | |||||
tendermint node --p2p.seeds="$seeds" --moniker="`hostname`" --proxy_app="unix:///socks/app.sock" | |||||
volumeMounts: | |||||
- name: tmdir | |||||
mountPath: /tendermint | |||||
- mountPath: /etc/tendermint/genesis.json | |||||
name: tmconfigdir | |||||
subPath: genesis.json | |||||
- name: socksdir | |||||
mountPath: /socks | |||||
- name: app | |||||
imagePullPolicy: IfNotPresent | |||||
image: tendermint/basecoin:0.5.1 | |||||
env: | |||||
- name: BCHOME | |||||
value: /app | |||||
workingDir: /app | |||||
command: | |||||
- bash | |||||
- "-c" | |||||
- | | |||||
set -ex | |||||
# replace "tm-N" with public keys in genesis file | |||||
cp /etc/app/genesis.json genesis.json | |||||
fqdn_suffix=$(hostname -f | sed 's#[^.]*\.\(\)#\1#') | |||||
# for every "base/account" | |||||
i=0 | |||||
length=$(cat genesis.json | jq ".app_options.accounts | length") | |||||
while [[ $i -lt $length ]]; do | |||||
# extract pod name ("tm-0") | |||||
pod=$(cat genesis.json | jq -r ".app_options.accounts[$i].pub_key") | |||||
# wait until pod starts to serve its pub_key | |||||
set +e | |||||
curl -s --fail "http://$pod.$fqdn_suffix/app_pub_key.json" > /dev/null | |||||
ERR=$? | |||||
while [ "$ERR" != 0 ]; do | |||||
sleep 5 | |||||
curl -s --fail "http://$pod.$fqdn_suffix/app_pub_key.json" > /dev/null | |||||
ERR=$? | |||||
done | |||||
set -e | |||||
# get its pub_key | |||||
curl -s "http://$pod.$fqdn_suffix/app_pub_key.json" | jq "." > k.json | |||||
# replace pod name with it ("tm-0" => "{"type": ..., "data": ...}") | |||||
cat genesis.json | jq ".app_options.accounts[$i].pub_key = $(cat k.json | jq '.')" > tmpgenesis && mv tmpgenesis genesis.json | |||||
rm -f k.json | |||||
i=$((i+1)) | |||||
done | |||||
rm -f /socks/app.sock # remove old socket | |||||
basecoin start --address="unix:///socks/app.sock" --without-tendermint | |||||
volumeMounts: | |||||
- name: appdir | |||||
mountPath: /app | |||||
- mountPath: /etc/app/genesis.json | |||||
name: appconfigdir | |||||
subPath: genesis.json | |||||
- name: socksdir | |||||
mountPath: /socks | |||||
- name: pub-key | |||||
imagePullPolicy: IfNotPresent | |||||
image: nginx:latest | |||||
ports: | |||||
- containerPort: 80 | |||||
command: | |||||
- bash | |||||
- "-c" | |||||
- | | |||||
set -ex | |||||
# fixes 403 Permission Denied (open() "/tendermint/pub_key.json" failed (13: Permission denied)) | |||||
# => we cannot serve from /tendermint, so we copy the file | |||||
mkdir -p /usr/share/nginx | |||||
cp /tendermint/pub_key.json /usr/share/nginx/pub_key.json | |||||
cp /app/pub_key.json /usr/share/nginx/app_pub_key.json | |||||
nginx -g "daemon off;" | |||||
volumeMounts: | |||||
- name: tmdir | |||||
mountPath: /tendermint | |||||
- name: appdir | |||||
mountPath: /app | |||||
- mountPath: /etc/nginx/conf.d/pub_key.conf | |||||
name: tmconfigdir | |||||
subPath: pub_key_nginx.conf | |||||
volumes: | |||||
- name: tmconfigdir | |||||
configMap: | |||||
name: tm-config | |||||
- name: appconfigdir | |||||
configMap: | |||||
name: app-config | |||||
- name: socksdir | |||||
emptyDir: {} | |||||
volumeClaimTemplates: | |||||
- metadata: | |||||
name: tmdir | |||||
annotations: | |||||
volume.alpha.kubernetes.io/storage-class: anything | |||||
spec: | |||||
accessModes: [ "ReadWriteOnce" ] | |||||
resources: | |||||
requests: | |||||
storage: 2Gi | |||||
- metadata: | |||||
name: appdir | |||||
annotations: | |||||
volume.alpha.kubernetes.io/storage-class: anything | |||||
spec: | |||||
accessModes: [ "ReadWriteOnce" ] | |||||
resources: | |||||
requests: | |||||
storage: 12Mi |
@ -0,0 +1,100 @@ | |||||
**OUTDATED** | |||||
# Using with lightclient | |||||
We have an awesome cluster running, let's try to test this out without | |||||
relying on executing commands on the cluster. Rather, we can connect to the | |||||
rpc interface with the `light-client` package and execute commands locally, | |||||
or even proxy our webapp to the kubernetes backend. | |||||
## Setup | |||||
In order to get this working, we need to know a few pieces of info, | |||||
the chain id of tendermint, the chain id of basecoin, and an account | |||||
with a bit of cash.... | |||||
### Tendermint Chain ID | |||||
`kubectl exec -c tm tm-0 -- curl -s http://tm-1.basecoin:26657/status | json_pp | grep network` | |||||
set TM_CHAIN with the value there | |||||
### Basecoin Chain ID | |||||
`kubectl exec -c app tm-1 -- grep -A1 chainID /app/genesis.json` | |||||
set BC_CHAIN with the value there | |||||
### Expose tendermint rpc | |||||
We need to be able to reach the tendermint rpc interface from our shell. | |||||
`kubectl port-forward tm-0 26657:26657` | |||||
### Start basecoin-proxy | |||||
Using this info, let's connect our proxy and get going | |||||
`proxy-basecoin -tmchain=$TM_CHAIN -chain=$BC_CHAIN -rpc=localhost:26657` | |||||
## Basecoin accounts | |||||
Well, we can connect, but we don't have a registered account yet... | |||||
Let's look around, then use the cli to send some money from one of | |||||
the validators to our client's address so we can play. | |||||
**TODO** we can add some of our known accounts (from `/keys`) into | |||||
the genesis file, so we can skip all the kubectl money fiddling here. | |||||
We will want to start with money on some known non-validators. | |||||
### Getting validator info (kubectl) | |||||
The basecoin app deployment starts with 1000 "blank" coin in an account of | |||||
each validator. Let's get the address of the first validator | |||||
`kubectl exec -c app tm-1 -- grep address /app/key.json` | |||||
Store this info as VAL1_ADDR | |||||
### Querying state (proxy) | |||||
The proxy can read any public info via the tendermint rpc, so let's check | |||||
out this account. | |||||
`curl localhost:8108/query/account/$VAL1_ADDR` | |||||
Now, let's make out own account.... | |||||
`curl -XPOST http://localhost:8108/keys/ -d '{"name": "k8demo", "passphrase": "1234567890"}'` | |||||
(or pick your own user and password). Remember the address you get here. You can | |||||
always find it out later by calling: | |||||
`curl http://localhost:8108/keys/k8demo` | |||||
and store it in DEMO_ADDR, which is empty at first | |||||
`curl localhost:8108/query/account/$DEMO_ADDR` | |||||
### "Stealing" validator cash (kubectl) | |||||
Run one command, that will be signed, now we have money | |||||
`kubectl exec -c app tm-0 -- basecoin tx send --to <k8demo-address> --amount 500` | |||||
### Using our money | |||||
Returning to our remote shell, we have a remote account with some money. | |||||
Let's see that. | |||||
`curl localhost:8108/query/account/$DEMO_ADDR` | |||||
Cool. Now we need to send it to a second account. | |||||
`curl -XPOST http://localhost:8108/keys/ -d '{"name": "buddy", "passphrase": "1234567890"}'` | |||||
and store the resulting address in BUDDY_ADDR | |||||
**TODO** finish this | |||||
@ -0,0 +1,10 @@ | |||||
create: | |||||
@echo "==> Creating deployment" | |||||
@kubectl create -f app.yaml | |||||
destroy: | |||||
@echo "==> Destroying deployment" | |||||
@kubectl delete -f app.yaml | |||||
@kubectl delete pvc -l app=tm | |||||
.PHONY: create destroy |