Bucky/aminoifypull/1347/head v0.19.0-rc3
@ -0,0 +1,114 @@ | |||||
# Light client | |||||
A light client is a process that connects to the Tendermint Full Node(s) and then tries to verify the Merkle proofs | |||||
about the blockchain application. In this document we describe mechanisms that ensures that the Tendermint light client | |||||
has the same level of security as Full Node processes (without being itself a Full Node). | |||||
To be able to validate a Merkle proof, a light client needs to validate the blockchain header that contains the root app hash. | |||||
Validating a blockchain header in Tendermint consists in verifying that the header is committed (signed) by >2/3 of the | |||||
voting power of the corresponding validator set. As the validator set is a dynamic set (it is changing), one of the | |||||
core functionality of the light client is updating the current validator set, that is then used to verify the | |||||
blockchain header, and further the corresponding Merkle proofs. | |||||
For the purpose of this light client specification, we assume that the Tendermint Full Node exposes the following functions over | |||||
Tendermint RPC: | |||||
```golang | |||||
Header(height int64) (SignedHeader, error) // returns signed header for the given height | |||||
Validators(height int64) (ResultValidators, error) // returns validator set for the given height | |||||
LastHeader(valSetNumber int64) (SignedHeader, error) // returns last header signed by the validator set with the given validator set number | |||||
type SignedHeader struct { | |||||
Header Header | |||||
Commit Commit | |||||
ValSetNumber int64 | |||||
} | |||||
type ResultValidators struct { | |||||
BlockHeight int64 | |||||
Validators []Validator | |||||
// time the current validator set is initialised, i.e, time of the last validator change before header BlockHeight | |||||
ValSetTime int64 | |||||
} | |||||
``` | |||||
We assume that Tendermint keeps track of the validator set changes and that each time a validator set is changed it is | |||||
being assigned the next sequence number. We can call this number the validator set sequence number. Tendermint also remembers | |||||
the Time from the header when the next validator set is initialised (starts to be in power), and we refer to this time | |||||
as validator set init time. | |||||
Furthermore, we assume that each validator set change is signed (committed) by the current validator set. More precisely, | |||||
given a block `H` that contains transactions that are modifying the current validator set, the Merkle root hash of the next | |||||
validator set (modified based on transactions from block H) will be in block `H+1` (and signed by the current validator | |||||
set), and then starting from the block `H+2`, it will be signed by the next validator set. | |||||
Note that the real Tendermint RPC API is slightly different (for example, response messages contain more data and function | |||||
names are slightly different); we shortened (and modified) it for the purpose of this document to make the spec more | |||||
clear and simple. Furthermore, note that in case of the third function, the returned header has `ValSetNumber` equals to | |||||
`valSetNumber+1`. | |||||
Locally, light client manages the following state: | |||||
```golang | |||||
valSet []Validator // current validator set (last known and verified validator set) | |||||
valSetNumber int64 // sequence number of the current validator set | |||||
valSetHash []byte // hash of the current validator set | |||||
valSetTime int64 // time when the current validator set is initialised | |||||
``` | |||||
The light client is initialised with the trusted validator set, for example based on the known validator set hash, | |||||
validator set sequence number and the validator set init time. | |||||
The core of the light client logic is captured by the VerifyAndUpdate function that is used to 1) verify if the given header is valid, | |||||
and 2) update the validator set (when the given header is valid and it is more recent than the seen headers). | |||||
```golang | |||||
VerifyAndUpdate(signedHeader SignedHeader): | |||||
assertThat signedHeader.valSetNumber >= valSetNumber | |||||
if isValid(signedHeader) and signedHeader.Header.Time <= valSetTime + UNBONDING_PERIOD then | |||||
setValidatorSet(signedHeader) | |||||
return true | |||||
else | |||||
updateValidatorSet(signedHeader.ValSetNumber) | |||||
return VerifyAndUpdate(signedHeader) | |||||
isValid(signedHeader SignedHeader): | |||||
valSetOfTheHeader = Validators(signedHeader.Header.Height) | |||||
assertThat Hash(valSetOfTheHeader) == signedHeader.Header.ValSetHash | |||||
assertThat signedHeader is passing basic validation | |||||
if votingPower(signedHeader.Commit) > 2/3 * votingPower(valSetOfTheHeader) then return true | |||||
else | |||||
return false | |||||
setValidatorSet(signedHeader SignedHeader): | |||||
nextValSet = Validators(signedHeader.Header.Height) | |||||
assertThat Hash(nextValSet) == signedHeader.Header.ValidatorsHash | |||||
valSet = nextValSet.Validators | |||||
valSetHash = signedHeader.Header.ValidatorsHash | |||||
valSetNumber = signedHeader.ValSetNumber | |||||
valSetTime = nextValSet.ValSetTime | |||||
votingPower(commit Commit): | |||||
votingPower = 0 | |||||
for each precommit in commit.Precommits do: | |||||
if precommit.ValidatorAddress is in valSet and signature of the precommit verifies then | |||||
votingPower += valSet[precommit.ValidatorAddress].VotingPower | |||||
return votingPower | |||||
votingPower(validatorSet []Validator): | |||||
for each validator in validatorSet do: | |||||
votingPower += validator.VotingPower | |||||
return votingPower | |||||
updateValidatorSet(valSetNumberOfTheHeader): | |||||
while valSetNumber != valSetNumberOfTheHeader do | |||||
signedHeader = LastHeader(valSetNumber) | |||||
if isValid(signedHeader) then | |||||
setValidatorSet(signedHeader) | |||||
else return error | |||||
return | |||||
``` | |||||
Note that in the logic above we assume that the light client will always go upward with respect to header verifications, | |||||
i.e., that it will always be used to verify more recent headers. In case a light client needs to be used to verify older | |||||
headers (go backward) the same mechanisms and similar logic can be used. In case a call to the FullNode or subsequent | |||||
checks fail, a light client need to implement some recovery strategy, for example connecting to other FullNode. |
@ -0,0 +1,218 @@ | |||||
package proxy_test | |||||
import ( | |||||
"testing" | |||||
"time" | |||||
"github.com/stretchr/testify/assert" | |||||
"github.com/tendermint/tendermint/lite" | |||||
"github.com/tendermint/tendermint/lite/proxy" | |||||
"github.com/tendermint/tendermint/types" | |||||
) | |||||
var ( | |||||
deadBeefTxs = types.Txs{[]byte("DE"), []byte("AD"), []byte("BE"), []byte("EF")} | |||||
deadBeefHash = deadBeefTxs.Hash() | |||||
testTime1 = time.Date(2018, 1, 1, 1, 1, 1, 1, time.UTC) | |||||
testTime2 = time.Date(2017, 1, 2, 1, 1, 1, 1, time.UTC) | |||||
) | |||||
var hdrHeight11 = &types.Header{ | |||||
Height: 11, | |||||
Time: testTime1, | |||||
ValidatorsHash: []byte("Tendermint"), | |||||
} | |||||
func TestValidateBlock(t *testing.T) { | |||||
tests := []struct { | |||||
block *types.Block | |||||
commit lite.Commit | |||||
wantErr string | |||||
}{ | |||||
{ | |||||
block: nil, wantErr: "non-nil Block", | |||||
}, | |||||
{ | |||||
block: &types.Block{}, wantErr: "nil Header", | |||||
}, | |||||
{ | |||||
block: &types.Block{Header: new(types.Header)}, | |||||
}, | |||||
// Start Header.Height mismatch test | |||||
{ | |||||
block: &types.Block{Header: &types.Header{Height: 10}}, | |||||
commit: lite.Commit{Header: &types.Header{Height: 11}}, | |||||
wantErr: "don't match - 10 vs 11", | |||||
}, | |||||
{ | |||||
block: &types.Block{Header: &types.Header{Height: 11}}, | |||||
commit: lite.Commit{Header: &types.Header{Height: 11}}, | |||||
}, | |||||
// End Header.Height mismatch test | |||||
// Start Header.Hash mismatch test | |||||
{ | |||||
block: &types.Block{Header: hdrHeight11}, | |||||
commit: lite.Commit{Header: &types.Header{Height: 11}}, | |||||
wantErr: "Headers don't match", | |||||
}, | |||||
{ | |||||
block: &types.Block{Header: hdrHeight11}, | |||||
commit: lite.Commit{Header: hdrHeight11}, | |||||
}, | |||||
// End Header.Hash mismatch test | |||||
// Start Header.Data hash mismatch test | |||||
{ | |||||
block: &types.Block{ | |||||
Header: &types.Header{Height: 11}, | |||||
Data: &types.Data{Txs: []types.Tx{[]byte("0xDE"), []byte("AD")}}, | |||||
}, | |||||
commit: lite.Commit{ | |||||
Header: &types.Header{Height: 11}, | |||||
Commit: &types.Commit{BlockID: types.BlockID{Hash: []byte("0xDEADBEEF")}}, | |||||
}, | |||||
wantErr: "Data hash doesn't match header", | |||||
}, | |||||
{ | |||||
block: &types.Block{ | |||||
Header: &types.Header{Height: 11, DataHash: deadBeefHash}, | |||||
Data: &types.Data{Txs: deadBeefTxs}, | |||||
}, | |||||
commit: lite.Commit{ | |||||
Header: &types.Header{Height: 11}, | |||||
Commit: &types.Commit{BlockID: types.BlockID{Hash: []byte("DEADBEEF")}}, | |||||
}, | |||||
}, | |||||
// End Header.Data hash mismatch test | |||||
} | |||||
for i, tt := range tests { | |||||
err := proxy.ValidateBlock(tt.block, tt.commit) | |||||
if tt.wantErr != "" { | |||||
if err == nil { | |||||
assert.FailNowf(t, "Unexpectedly passed", "#%d", i) | |||||
} else { | |||||
assert.Contains(t, err.Error(), tt.wantErr, "#%d should contain the substring\n\n", i) | |||||
} | |||||
continue | |||||
} | |||||
assert.Nil(t, err, "#%d: expecting a nil error", i) | |||||
} | |||||
} | |||||
func TestValidateBlockMeta(t *testing.T) { | |||||
tests := []struct { | |||||
meta *types.BlockMeta | |||||
commit lite.Commit | |||||
wantErr string | |||||
}{ | |||||
{ | |||||
meta: nil, wantErr: "non-nil BlockMeta", | |||||
}, | |||||
{ | |||||
meta: &types.BlockMeta{}, wantErr: "non-nil Header", | |||||
}, | |||||
{ | |||||
meta: &types.BlockMeta{Header: new(types.Header)}, | |||||
}, | |||||
// Start Header.Height mismatch test | |||||
{ | |||||
meta: &types.BlockMeta{Header: &types.Header{Height: 10}}, | |||||
commit: lite.Commit{Header: &types.Header{Height: 11}}, | |||||
wantErr: "don't match - 10 vs 11", | |||||
}, | |||||
{ | |||||
meta: &types.BlockMeta{Header: &types.Header{Height: 11}}, | |||||
commit: lite.Commit{Header: &types.Header{Height: 11}}, | |||||
}, | |||||
// End Header.Height mismatch test | |||||
// Start Headers don't match test | |||||
{ | |||||
meta: &types.BlockMeta{Header: hdrHeight11}, | |||||
commit: lite.Commit{Header: &types.Header{Height: 11}}, | |||||
wantErr: "Headers don't match", | |||||
}, | |||||
{ | |||||
meta: &types.BlockMeta{Header: hdrHeight11}, | |||||
commit: lite.Commit{Header: hdrHeight11}, | |||||
}, | |||||
{ | |||||
meta: &types.BlockMeta{ | |||||
Header: &types.Header{ | |||||
Height: 11, | |||||
ValidatorsHash: []byte("lite-test"), | |||||
// TODO: should be able to use empty time after Amino upgrade | |||||
Time: testTime1, | |||||
}, | |||||
}, | |||||
commit: lite.Commit{ | |||||
Header: &types.Header{Height: 11, DataHash: deadBeefHash}, | |||||
}, | |||||
wantErr: "Headers don't match", | |||||
}, | |||||
{ | |||||
meta: &types.BlockMeta{ | |||||
Header: &types.Header{ | |||||
Height: 11, DataHash: deadBeefHash, | |||||
ValidatorsHash: []byte("Tendermint"), | |||||
Time: testTime1, | |||||
}, | |||||
}, | |||||
commit: lite.Commit{ | |||||
Header: &types.Header{ | |||||
Height: 11, DataHash: deadBeefHash, | |||||
ValidatorsHash: []byte("Tendermint"), | |||||
Time: testTime2, | |||||
}, | |||||
Commit: &types.Commit{BlockID: types.BlockID{Hash: []byte("DEADBEEF")}}, | |||||
}, | |||||
wantErr: "Headers don't match", | |||||
}, | |||||
{ | |||||
meta: &types.BlockMeta{ | |||||
Header: &types.Header{ | |||||
Height: 11, DataHash: deadBeefHash, | |||||
ValidatorsHash: []byte("Tendermint"), | |||||
Time: testTime2, | |||||
}, | |||||
}, | |||||
commit: lite.Commit{ | |||||
Header: &types.Header{ | |||||
Height: 11, DataHash: deadBeefHash, | |||||
ValidatorsHash: []byte("Tendermint-x"), | |||||
Time: testTime2, | |||||
}, | |||||
Commit: &types.Commit{BlockID: types.BlockID{Hash: []byte("DEADBEEF")}}, | |||||
}, | |||||
wantErr: "Headers don't match", | |||||
}, | |||||
// End Headers don't match test | |||||
} | |||||
for i, tt := range tests { | |||||
err := proxy.ValidateBlockMeta(tt.meta, tt.commit) | |||||
if tt.wantErr != "" { | |||||
if err == nil { | |||||
assert.FailNowf(t, "Unexpectedly passed", "#%d: wanted error %q", i, tt.wantErr) | |||||
} else { | |||||
assert.Contains(t, err.Error(), tt.wantErr, "#%d should contain the substring\n\n", i) | |||||
} | |||||
continue | |||||
} | |||||
assert.Nil(t, err, "#%d: expecting a nil error", i) | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
package node | |||||
import ( | |||||
amino "github.com/tendermint/go-amino" | |||||
crypto "github.com/tendermint/go-crypto" | |||||
) | |||||
var cdc = amino.NewCodec() | |||||
func init() { | |||||
crypto.RegisterAmino(cdc) | |||||
} |
@ -0,0 +1,72 @@ | |||||
package dummy | |||||
import ( | |||||
p2p "github.com/tendermint/tendermint/p2p" | |||||
tmconn "github.com/tendermint/tendermint/p2p/conn" | |||||
cmn "github.com/tendermint/tmlibs/common" | |||||
) | |||||
type peer struct { | |||||
cmn.BaseService | |||||
kv map[string]interface{} | |||||
} | |||||
var _ p2p.Peer = (*peer)(nil) | |||||
// NewPeer creates new dummy peer. | |||||
func NewPeer() *peer { | |||||
p := &peer{ | |||||
kv: make(map[string]interface{}), | |||||
} | |||||
p.BaseService = *cmn.NewBaseService(nil, "peer", p) | |||||
return p | |||||
} | |||||
// ID always returns dummy. | |||||
func (p *peer) ID() p2p.ID { | |||||
return p2p.ID("dummy") | |||||
} | |||||
// IsOutbound always returns false. | |||||
func (p *peer) IsOutbound() bool { | |||||
return false | |||||
} | |||||
// IsPersistent always returns false. | |||||
func (p *peer) IsPersistent() bool { | |||||
return false | |||||
} | |||||
// NodeInfo always returns empty node info. | |||||
func (p *peer) NodeInfo() p2p.NodeInfo { | |||||
return p2p.NodeInfo{} | |||||
} | |||||
// Status always returns empry connection status. | |||||
func (p *peer) Status() tmconn.ConnectionStatus { | |||||
return tmconn.ConnectionStatus{} | |||||
} | |||||
// Send does not do anything and just returns true. | |||||
func (p *peer) Send(byte, []byte) bool { | |||||
return true | |||||
} | |||||
// TrySend does not do anything and just returns true. | |||||
func (p *peer) TrySend(byte, []byte) bool { | |||||
return true | |||||
} | |||||
// Set records value under key specified in the map. | |||||
func (p *peer) Set(key string, value interface{}) { | |||||
p.kv[key] = value | |||||
} | |||||
// Get returns a value associated with the key. Nil is returned if no value | |||||
// found. | |||||
func (p *peer) Get(key string) interface{} { | |||||
if value, ok := p.kv[key]; ok { | |||||
return value | |||||
} | |||||
return nil | |||||
} |
@ -0,0 +1,31 @@ | |||||
package core | |||||
import ( | |||||
ctypes "github.com/tendermint/tendermint/rpc/core/types" | |||||
) | |||||
// Get node health. Returns empty result (200 OK) on success, no response - in | |||||
// case of an error. | |||||
// | |||||
// ```shell | |||||
// curl 'localhost:46657/health' | |||||
// ``` | |||||
// | |||||
// ```go | |||||
// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket") | |||||
// result, err := client.Health() | |||||
// ``` | |||||
// | |||||
// > The above command returns JSON structured like this: | |||||
// | |||||
// ```json | |||||
// { | |||||
// "error": "", | |||||
// "result": {}, | |||||
// "id": "", | |||||
// "jsonrpc": "2.0" | |||||
// } | |||||
// ``` | |||||
func Health() (*ctypes.ResultHealth, error) { | |||||
return &ctypes.ResultHealth{}, nil | |||||
} |
@ -1,35 +0,0 @@ | |||||
#! /bin/bash | |||||
set -ex | |||||
set +u | |||||
if [[ "$DEP" == "" ]]; then | |||||
DEP=$GOPATH/src/github.com/tendermint/tendermint/Gopkg.lock | |||||
fi | |||||
set -u | |||||
set -u | |||||
function getVendoredVersion() { | |||||
grep -A100 "$LIB" "$DEP" | grep revision | head -n1 | grep -o '"[^"]\+"' | cut -d '"' -f 2 | |||||
} | |||||
# fetch and checkout vendored dep | |||||
lib=$1 | |||||
echo "----------------------------------" | |||||
echo "Getting $lib ..." | |||||
go get -t "github.com/tendermint/$lib/..." | |||||
VENDORED=$(getVendoredVersion "$lib") | |||||
cd "$GOPATH/src/github.com/tendermint/$lib" || exit | |||||
MASTER=$(git rev-parse origin/master) | |||||
if [[ "$VENDORED" != "$MASTER" ]]; then | |||||
echo "... VENDORED != MASTER ($VENDORED != $MASTER)" | |||||
echo "... Checking out commit $VENDORED" | |||||
git checkout "$VENDORED" &> /dev/null | |||||
fi |
@ -0,0 +1,181 @@ | |||||
package main | |||||
import ( | |||||
"encoding/json" | |||||
"fmt" | |||||
"io/ioutil" | |||||
"os" | |||||
"path/filepath" | |||||
"time" | |||||
"github.com/tendermint/go-amino" | |||||
crypto "github.com/tendermint/go-crypto" | |||||
cmn "github.com/tendermint/tmlibs/common" | |||||
"github.com/tendermint/tendermint/p2p" | |||||
"github.com/tendermint/tendermint/types" | |||||
priv_val "github.com/tendermint/tendermint/types/priv_validator" | |||||
) | |||||
type GenesisValidator struct { | |||||
PubKey Data `json:"pub_key"` | |||||
Power int64 `json:"power"` | |||||
Name string `json:"name"` | |||||
} | |||||
type Genesis struct { | |||||
GenesisTime time.Time `json:"genesis_time"` | |||||
ChainID string `json:"chain_id"` | |||||
ConsensusParams *types.ConsensusParams `json:"consensus_params,omitempty"` | |||||
Validators []GenesisValidator `json:"validators"` | |||||
AppHash cmn.HexBytes `json:"app_hash"` | |||||
AppStateJSON json.RawMessage `json:"app_state,omitempty"` | |||||
AppOptions json.RawMessage `json:"app_options,omitempty"` // DEPRECATED | |||||
} | |||||
type NodeKey struct { | |||||
PrivKey Data `json:"priv_key"` | |||||
} | |||||
type PrivVal struct { | |||||
Address cmn.HexBytes `json:"address"` | |||||
LastHeight int64 `json:"last_height"` | |||||
LastRound int `json:"last_round"` | |||||
LastStep int8 `json:"last_step"` | |||||
PubKey Data `json:"pub_key"` | |||||
PrivKey Data `json:"priv_key"` | |||||
} | |||||
type Data struct { | |||||
Type string `json:"type"` | |||||
Data cmn.HexBytes `json:"data"` | |||||
} | |||||
func convertNodeKey(cdc *amino.Codec, jsonBytes []byte) ([]byte, error) { | |||||
var nodeKey NodeKey | |||||
err := json.Unmarshal(jsonBytes, &nodeKey) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
var privKey crypto.PrivKeyEd25519 | |||||
copy(privKey[:], nodeKey.PrivKey.Data) | |||||
nodeKeyNew := p2p.NodeKey{privKey} | |||||
bz, err := cdc.MarshalJSON(nodeKeyNew) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return bz, nil | |||||
} | |||||
func convertPrivVal(cdc *amino.Codec, jsonBytes []byte) ([]byte, error) { | |||||
var privVal PrivVal | |||||
err := json.Unmarshal(jsonBytes, &privVal) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
var privKey crypto.PrivKeyEd25519 | |||||
copy(privKey[:], privVal.PrivKey.Data) | |||||
var pubKey crypto.PubKeyEd25519 | |||||
copy(pubKey[:], privVal.PubKey.Data) | |||||
privValNew := priv_val.FilePV{ | |||||
Address: pubKey.Address(), | |||||
PubKey: pubKey, | |||||
LastHeight: privVal.LastHeight, | |||||
LastRound: privVal.LastRound, | |||||
LastStep: privVal.LastStep, | |||||
PrivKey: privKey, | |||||
} | |||||
bz, err := cdc.MarshalJSON(privValNew) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return bz, nil | |||||
} | |||||
func convertGenesis(cdc *amino.Codec, jsonBytes []byte) ([]byte, error) { | |||||
var genesis Genesis | |||||
err := json.Unmarshal(jsonBytes, &genesis) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
genesisNew := types.GenesisDoc{ | |||||
GenesisTime: genesis.GenesisTime, | |||||
ChainID: genesis.ChainID, | |||||
ConsensusParams: genesis.ConsensusParams, | |||||
// Validators | |||||
AppHash: genesis.AppHash, | |||||
AppStateJSON: genesis.AppStateJSON, | |||||
} | |||||
if genesis.AppOptions != nil { | |||||
genesisNew.AppStateJSON = genesis.AppOptions | |||||
} | |||||
for _, v := range genesis.Validators { | |||||
var pubKey crypto.PubKeyEd25519 | |||||
copy(pubKey[:], v.PubKey.Data) | |||||
genesisNew.Validators = append( | |||||
genesisNew.Validators, | |||||
types.GenesisValidator{ | |||||
PubKey: pubKey, | |||||
Power: v.Power, | |||||
Name: v.Name, | |||||
}, | |||||
) | |||||
} | |||||
bz, err := cdc.MarshalJSON(genesisNew) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
return bz, nil | |||||
} | |||||
func main() { | |||||
cdc := amino.NewCodec() | |||||
crypto.RegisterAmino(cdc) | |||||
args := os.Args[1:] | |||||
if len(args) != 1 { | |||||
fmt.Println("Please specify a file to convert") | |||||
os.Exit(1) | |||||
} | |||||
filePath := args[0] | |||||
fileName := filepath.Base(filePath) | |||||
fileBytes, err := ioutil.ReadFile(filePath) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
var bz []byte | |||||
switch fileName { | |||||
case "node_key.json": | |||||
bz, err = convertNodeKey(cdc, fileBytes) | |||||
case "priv_validator.json": | |||||
bz, err = convertPrivVal(cdc, fileBytes) | |||||
case "genesis.json": | |||||
bz, err = convertGenesis(cdc, fileBytes) | |||||
default: | |||||
fmt.Println("Expected file name to be in (node_key.json, priv_validator.json, genesis.json)") | |||||
os.Exit(1) | |||||
} | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
fmt.Println(string(bz)) | |||||
} |
@ -1,9 +0,0 @@ | |||||
#! /bin/bash | |||||
# This is a sample bash script for a ABCI application | |||||
cd app/ | |||||
git clone https://github.com/tendermint/nomnomcoin.git | |||||
cd nomnomcoin | |||||
npm install . | |||||
node app.js --eyes="unix:///data/tendermint/data/data.sock" |
@ -1,53 +0,0 @@ | |||||
{ | |||||
"id": "", | |||||
"val_set_id": "anon", | |||||
"validators": [ | |||||
{ | |||||
"validator": { | |||||
"id": "mach1", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "BE8933DFF1600C026E34718F1785A4CDEAB90C35698B394E38B6947AE91DE116" | |||||
} | |||||
}, | |||||
"p2p_addr": "", | |||||
"rpc_addr": "" | |||||
}, | |||||
{ | |||||
"validator": { | |||||
"id": "mach2", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "6DC534465323126587D2A2A93B59D689B717073B1DE968A25A6EF13D595318AD" | |||||
} | |||||
}, | |||||
"p2p_addr": "", | |||||
"rpc_addr": "", | |||||
"index": 1 | |||||
}, | |||||
{ | |||||
"validator": { | |||||
"id": "mach3", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "AE67AC697D135AA0B4601EA57EAAB3FEBF4BAA4F229C45A598C2985B12FCD1A1" | |||||
} | |||||
}, | |||||
"p2p_addr": "", | |||||
"rpc_addr": "", | |||||
"index": 2 | |||||
}, | |||||
{ | |||||
"validator": { | |||||
"id": "mach4", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "9EBC8F58CED4B46DCD5AB8ABA591DD253CD7CB5037273FDA32BC0B6461C4EFD9" | |||||
} | |||||
}, | |||||
"p2p_addr": "", | |||||
"rpc_addr": "", | |||||
"index": 3 | |||||
} | |||||
] | |||||
} |
@ -1,39 +1,39 @@ | |||||
{ | { | ||||
"app_hash": "", | |||||
"chain_id": "chain-9ujDWI", | |||||
"genesis_time": "2016-06-24T20:01:19.322Z", | |||||
"validators": [ | |||||
{ | |||||
"power": 1, | |||||
"name": "mach1", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "BE8933DFF1600C026E34718F1785A4CDEAB90C35698B394E38B6947AE91DE116" | |||||
} | |||||
}, | |||||
{ | |||||
"power": 1, | |||||
"name": "mach2", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "6DC534465323126587D2A2A93B59D689B717073B1DE968A25A6EF13D595318AD" | |||||
} | |||||
}, | |||||
{ | |||||
"power": 1, | |||||
"name": "mach3", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "AE67AC697D135AA0B4601EA57EAAB3FEBF4BAA4F229C45A598C2985B12FCD1A1" | |||||
} | |||||
}, | |||||
{ | |||||
"power": 1, | |||||
"name": "mach4", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "9EBC8F58CED4B46DCD5AB8ABA591DD253CD7CB5037273FDA32BC0B6461C4EFD9" | |||||
} | |||||
} | |||||
] | |||||
"genesis_time": "2016-06-24T20:01:19.322Z", | |||||
"chain_id": "chain-9ujDWI", | |||||
"validators": [ | |||||
{ | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "vokz3/FgDAJuNHGPF4Wkzeq5DDVpizlOOLaUeukd4RY=" | |||||
}, | |||||
"power": 1, | |||||
"name": "mach1" | |||||
}, | |||||
{ | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "bcU0RlMjEmWH0qKpO1nWibcXBzsd6WiiWm7xPVlTGK0=" | |||||
}, | |||||
"power": 1, | |||||
"name": "mach2" | |||||
}, | |||||
{ | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "rmesaX0TWqC0YB6lfqqz/r9Lqk8inEWlmMKYWxL80aE=" | |||||
}, | |||||
"power": 1, | |||||
"name": "mach3" | |||||
}, | |||||
{ | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "nryPWM7UtG3NWrirpZHdJTzXy1A3Jz/aMrwLZGHE79k=" | |||||
}, | |||||
"power": 1, | |||||
"name": "mach4" | |||||
} | |||||
], | |||||
"app_hash": "" | |||||
} | } |
@ -1 +1,6 @@ | |||||
{"priv_key":{"type":"ed25519","data":"06962D169F314ABB9D05AE5A04B46E48F0FBD8F1830149B47493910CBDCA7796096E5B94CD179F545AE3C281D9BF5C9E0E3D8FF719048B62F7849094CFFA8591"}} | |||||
{ | |||||
"priv_key": { | |||||
"type": "954568A3288910", | |||||
"value": "BpYtFp8xSrudBa5aBLRuSPD72PGDAUm0dJORDL3Kd5YJbluUzRefVFrjwoHZv1yeDj2P9xkEi2L3hJCUz/qFkQ==" | |||||
} | |||||
} |
@ -1,14 +1,14 @@ | |||||
{ | { | ||||
"address": "0E6925C3EE4C599DFF1536A5071AF4A26DF33635", | |||||
"last_height": 0, | |||||
"last_round": 0, | |||||
"last_step": 0, | |||||
"priv_key": { | |||||
"type": "ed25519", | |||||
"data": "547AA07C7A8CE16C5CB2A40C6C26D15B0A32960410A9F1EA6E50B636F1AB389ABE8933DFF1600C026E34718F1785A4CDEAB90C35698B394E38B6947AE91DE116" | |||||
"address": "7E9D1FB08EDBAFCF116638D4C8FAFAEE2ABE1AAA", | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "vokz3/FgDAJuNHGPF4Wkzeq5DDVpizlOOLaUeukd4RY=" | |||||
}, | }, | ||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "BE8933DFF1600C026E34718F1785A4CDEAB90C35698B394E38B6947AE91DE116" | |||||
"last_height": 0, | |||||
"last_round": 0, | |||||
"last_step": 0, | |||||
"priv_key": { | |||||
"type": "954568A3288910", | |||||
"value": "VHqgfHqM4WxcsqQMbCbRWwoylgQQqfHqblC2NvGrOJq+iTPf8WAMAm40cY8XhaTN6rkMNWmLOU44tpR66R3hFg==" | |||||
} | } | ||||
} | } |
@ -1,39 +1,39 @@ | |||||
{ | { | ||||
"app_hash": "", | |||||
"chain_id": "chain-9ujDWI", | |||||
"genesis_time": "2016-06-24T20:01:19.322Z", | |||||
"validators": [ | |||||
{ | |||||
"power": 1, | |||||
"name": "mach1", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "BE8933DFF1600C026E34718F1785A4CDEAB90C35698B394E38B6947AE91DE116" | |||||
} | |||||
}, | |||||
{ | |||||
"power": 1, | |||||
"name": "mach2", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "6DC534465323126587D2A2A93B59D689B717073B1DE968A25A6EF13D595318AD" | |||||
} | |||||
}, | |||||
{ | |||||
"power": 1, | |||||
"name": "mach3", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "AE67AC697D135AA0B4601EA57EAAB3FEBF4BAA4F229C45A598C2985B12FCD1A1" | |||||
} | |||||
}, | |||||
{ | |||||
"power": 1, | |||||
"name": "mach4", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "9EBC8F58CED4B46DCD5AB8ABA591DD253CD7CB5037273FDA32BC0B6461C4EFD9" | |||||
} | |||||
} | |||||
] | |||||
"genesis_time": "2016-06-24T20:01:19.322Z", | |||||
"chain_id": "chain-9ujDWI", | |||||
"validators": [ | |||||
{ | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "vokz3/FgDAJuNHGPF4Wkzeq5DDVpizlOOLaUeukd4RY=" | |||||
}, | |||||
"power": 1, | |||||
"name": "mach1" | |||||
}, | |||||
{ | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "bcU0RlMjEmWH0qKpO1nWibcXBzsd6WiiWm7xPVlTGK0=" | |||||
}, | |||||
"power": 1, | |||||
"name": "mach2" | |||||
}, | |||||
{ | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "rmesaX0TWqC0YB6lfqqz/r9Lqk8inEWlmMKYWxL80aE=" | |||||
}, | |||||
"power": 1, | |||||
"name": "mach3" | |||||
}, | |||||
{ | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "nryPWM7UtG3NWrirpZHdJTzXy1A3Jz/aMrwLZGHE79k=" | |||||
}, | |||||
"power": 1, | |||||
"name": "mach4" | |||||
} | |||||
], | |||||
"app_hash": "" | |||||
} | } |
@ -1 +1,6 @@ | |||||
{"priv_key":{"type":"ed25519","data":"B8CE8B0D5138C10208526ABDADCE91C735FCCC4186E06E0972EC35E64973428A45EBC61F24CE1B91B3D26AFBAB11C2789EF04CBAC28183619C01116B66A9C528"}} | |||||
{ | |||||
"priv_key": { | |||||
"type": "954568A3288910", | |||||
"value": "uM6LDVE4wQIIUmq9rc6RxzX8zEGG4G4Jcuw15klzQopF68YfJM4bkbPSavurEcJ4nvBMusKBg2GcARFrZqnFKA==" | |||||
} | |||||
} |
@ -1,14 +1,14 @@ | |||||
{ | { | ||||
"address": "99DBBD2AFC28FB5BAC5574AFAF0D9C806CED3B55", | |||||
"last_height": 0, | |||||
"last_round": 0, | |||||
"last_step": 0, | |||||
"priv_key": { | |||||
"type": "ed25519", | |||||
"data": "D047889E60502FC3129D0AB7F334B1838ED9ED1ECD99CBB96B71AD5ABF5A81436DC534465323126587D2A2A93B59D689B717073B1DE968A25A6EF13D595318AD" | |||||
"address": "8893D14FE09F1157E39CD34B98036048D51B4985", | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "bcU0RlMjEmWH0qKpO1nWibcXBzsd6WiiWm7xPVlTGK0=" | |||||
}, | }, | ||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "6DC534465323126587D2A2A93B59D689B717073B1DE968A25A6EF13D595318AD" | |||||
"last_height": 0, | |||||
"last_round": 0, | |||||
"last_step": 0, | |||||
"priv_key": { | |||||
"type": "954568A3288910", | |||||
"value": "0EeInmBQL8MSnQq38zSxg47Z7R7Nmcu5a3GtWr9agUNtxTRGUyMSZYfSoqk7WdaJtxcHOx3paKJabvE9WVMYrQ==" | |||||
} | } | ||||
} | } |
@ -1,39 +1,39 @@ | |||||
{ | { | ||||
"app_hash": "", | |||||
"chain_id": "chain-9ujDWI", | |||||
"genesis_time": "2016-06-24T20:01:19.322Z", | |||||
"validators": [ | |||||
{ | |||||
"power": 1, | |||||
"name": "mach1", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "BE8933DFF1600C026E34718F1785A4CDEAB90C35698B394E38B6947AE91DE116" | |||||
} | |||||
}, | |||||
{ | |||||
"power": 1, | |||||
"name": "mach2", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "6DC534465323126587D2A2A93B59D689B717073B1DE968A25A6EF13D595318AD" | |||||
} | |||||
}, | |||||
{ | |||||
"power": 1, | |||||
"name": "mach3", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "AE67AC697D135AA0B4601EA57EAAB3FEBF4BAA4F229C45A598C2985B12FCD1A1" | |||||
} | |||||
}, | |||||
{ | |||||
"power": 1, | |||||
"name": "mach4", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "9EBC8F58CED4B46DCD5AB8ABA591DD253CD7CB5037273FDA32BC0B6461C4EFD9" | |||||
} | |||||
} | |||||
] | |||||
"genesis_time": "2016-06-24T20:01:19.322Z", | |||||
"chain_id": "chain-9ujDWI", | |||||
"validators": [ | |||||
{ | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "vokz3/FgDAJuNHGPF4Wkzeq5DDVpizlOOLaUeukd4RY=" | |||||
}, | |||||
"power": 1, | |||||
"name": "mach1" | |||||
}, | |||||
{ | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "bcU0RlMjEmWH0qKpO1nWibcXBzsd6WiiWm7xPVlTGK0=" | |||||
}, | |||||
"power": 1, | |||||
"name": "mach2" | |||||
}, | |||||
{ | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "rmesaX0TWqC0YB6lfqqz/r9Lqk8inEWlmMKYWxL80aE=" | |||||
}, | |||||
"power": 1, | |||||
"name": "mach3" | |||||
}, | |||||
{ | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "nryPWM7UtG3NWrirpZHdJTzXy1A3Jz/aMrwLZGHE79k=" | |||||
}, | |||||
"power": 1, | |||||
"name": "mach4" | |||||
} | |||||
], | |||||
"app_hash": "" | |||||
} | } |
@ -1 +1,6 @@ | |||||
{"priv_key":{"type":"ed25519","data":"913DE8AC6D18922A53F6B0196EF023B4693FECFBB565E084F0B4941768F3DAE892B35ADD954562FE071C465BC244B2AFAED4A270EC849269341473CE192DE682"}} | |||||
{ | |||||
"priv_key": { | |||||
"type": "954568A3288910", | |||||
"value": "kT3orG0YkipT9rAZbvAjtGk/7Pu1ZeCE8LSUF2jz2uiSs1rdlUVi/gccRlvCRLKvrtSicOyEkmk0FHPOGS3mgg==" | |||||
} | |||||
} |
@ -1,14 +1,14 @@ | |||||
{ | { | ||||
"address": "4C5F061DAC28660853904A66705B12CA2B317572", | |||||
"last_height": 0, | |||||
"last_round": 0, | |||||
"last_step": 0, | |||||
"priv_key": { | |||||
"type": "ed25519", | |||||
"data": "C1A4E47F349FC5F556F4A9A27BA776B94424C312BAA6CF6EE44B867348D7C3F2AE67AC697D135AA0B4601EA57EAAB3FEBF4BAA4F229C45A598C2985B12FCD1A1" | |||||
"address": "7C747D7E002932B3864E3FBE9AC04287043F66A0", | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "rmesaX0TWqC0YB6lfqqz/r9Lqk8inEWlmMKYWxL80aE=" | |||||
}, | }, | ||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "AE67AC697D135AA0B4601EA57EAAB3FEBF4BAA4F229C45A598C2985B12FCD1A1" | |||||
"last_height": 0, | |||||
"last_round": 0, | |||||
"last_step": 0, | |||||
"priv_key": { | |||||
"type": "954568A3288910", | |||||
"value": "waTkfzSfxfVW9Kmie6d2uUQkwxK6ps9u5EuGc0jXw/KuZ6xpfRNaoLRgHqV+qrP+v0uqTyKcRaWYwphbEvzRoQ==" | |||||
} | } | ||||
} | } |
@ -1,39 +1,39 @@ | |||||
{ | { | ||||
"app_hash": "", | |||||
"chain_id": "chain-9ujDWI", | |||||
"genesis_time": "2016-06-24T20:01:19.322Z", | |||||
"validators": [ | |||||
{ | |||||
"power": 1, | |||||
"name": "mach1", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "BE8933DFF1600C026E34718F1785A4CDEAB90C35698B394E38B6947AE91DE116" | |||||
} | |||||
}, | |||||
{ | |||||
"power": 1, | |||||
"name": "mach2", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "6DC534465323126587D2A2A93B59D689B717073B1DE968A25A6EF13D595318AD" | |||||
} | |||||
}, | |||||
{ | |||||
"power": 1, | |||||
"name": "mach3", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "AE67AC697D135AA0B4601EA57EAAB3FEBF4BAA4F229C45A598C2985B12FCD1A1" | |||||
} | |||||
}, | |||||
{ | |||||
"power": 1, | |||||
"name": "mach4", | |||||
"pub_key": { | |||||
"type": "ed25519", | |||||
"data": "9EBC8F58CED4B46DCD5AB8ABA591DD253CD7CB5037273FDA32BC0B6461C4EFD9" | |||||
} | |||||
} | |||||
] | |||||
"genesis_time": "2016-06-24T20:01:19.322Z", | |||||
"chain_id": "chain-9ujDWI", | |||||
"validators": [ | |||||
{ | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "vokz3/FgDAJuNHGPF4Wkzeq5DDVpizlOOLaUeukd4RY=" | |||||
}, | |||||
"power": 1, | |||||
"name": "mach1" | |||||
}, | |||||
{ | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "bcU0RlMjEmWH0qKpO1nWibcXBzsd6WiiWm7xPVlTGK0=" | |||||
}, | |||||
"power": 1, | |||||
"name": "mach2" | |||||
}, | |||||
{ | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "rmesaX0TWqC0YB6lfqqz/r9Lqk8inEWlmMKYWxL80aE=" | |||||
}, | |||||
"power": 1, | |||||
"name": "mach3" | |||||
}, | |||||
{ | |||||
"pub_key": { | |||||
"type": "AC26791624DE60", | |||||
"value": "nryPWM7UtG3NWrirpZHdJTzXy1A3Jz/aMrwLZGHE79k=" | |||||
}, | |||||
"power": 1, | |||||
"name": "mach4" | |||||
} | |||||
], | |||||
"app_hash": "" | |||||
} | } |