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": "" | |||
} |