From 2edc68c59bea3e242011b5bb308df2f399d5548b Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 6 Jun 2018 13:07:17 -0700 Subject: [PATCH 1/5] use all fields in abci types --- types/protobuf.go | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/types/protobuf.go b/types/protobuf.go index bfe3df343..d1b0ae7fb 100644 --- a/types/protobuf.go +++ b/types/protobuf.go @@ -32,20 +32,26 @@ type tm2pb struct{} func (tm2pb) Header(header *Header) abci.Header { return abci.Header{ - ChainID: header.ChainID, - Height: header.Height, - Time: header.Time.Unix(), - NumTxs: int32(header.NumTxs), // XXX: overflow + ChainID: header.ChainID, + Height: header.Height, + + Time: header.Time.Unix(), + NumTxs: int32(header.NumTxs), // XXX: overflow + TotalTxs: header.NumTxs, + LastBlockHash: header.LastBlockID.Hash, ValidatorsHash: header.ValidatorsHash, AppHash: header.AppHash, + + // Proposer: TODO } } func (tm2pb) Validator(val *Validator) abci.Validator { return abci.Validator{ - PubKey: TM2PB.PubKey(val.PubKey), - Power: val.VotingPower, + Address: val.PubKey.Address(), + PubKey: TM2PB.PubKey(val.PubKey), + Power: val.VotingPower, } } @@ -101,28 +107,24 @@ func (tm2pb) Evidence(ev Evidence, valSet *ValidatorSet, evTime time.Time) abci. panic(val) } - abciEvidence := abci.Evidence{ - Validator: abci.Validator{ - Address: ev.Address(), - PubKey: TM2PB.PubKey(val.PubKey), - Power: val.VotingPower, - }, - Height: ev.Height(), - Time: evTime.Unix(), - TotalVotingPower: valSet.TotalVotingPower(), - } - // set type + var evType string switch ev.(type) { case *DuplicateVoteEvidence: - abciEvidence.Type = ABCIEvidenceTypeDuplicateVote + evType = ABCIEvidenceTypeDuplicateVote case *MockGoodEvidence, MockGoodEvidence: - abciEvidence.Type = ABCIEvidenceTypeMockGood + evType = ABCIEvidenceTypeMockGood default: panic(fmt.Sprintf("Unknown evidence type: %v %v", ev, reflect.TypeOf(ev))) } - return abciEvidence + return abci.Evidence{ + Type: evType, + Validator: TM2PB.Validator(val), + Height: ev.Height(), + Time: evTime.Unix(), + TotalVotingPower: valSet.TotalVotingPower(), + } } func (tm2pb) ValidatorFromPubKeyAndPower(pubkey crypto.PubKey, power int64) abci.Validator { From 8e45348737d3c5b412a470385d2654f245ea2efa Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 6 Jun 2018 15:47:04 -0700 Subject: [PATCH 2/5] update for abci v0.11.0 release. let InitChain update validators --- CHANGELOG.md | 16 +++++++++++-- Gopkg.lock | 8 +++---- Gopkg.toml | 2 +- consensus/replay.go | 18 ++++++++++++-- consensus/replay_test.go | 51 ++++++++++++++++++++++++++++++++++++++++ types/protobuf.go | 16 +++++++++++++ version/version.go | 6 ++--- 7 files changed, 105 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3de2157e..97c3dfb44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,20 @@ # Changelog -## 0.19.10 +## 0.20.0 + +*June 6th, 2018* + +BREAKING CHANGES + +- [abci] Upgrade to + [v0.11.0](https://github.com/tendermint/abci/blob/master/CHANGELOG.md#0110) + +NOTE: this release does not break any blockchain data structures or +protocols other than the ABCI messages between Tendermint and the application. + +Applications that upgrade for ABCI v0.11.0 should be able to continue running Tendermint +v0.20.0 on blockchains created with v0.19.X -*TBD* ## 0.19.9 diff --git a/Gopkg.lock b/Gopkg.lock index 20ae605a1..f21e38427 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -238,8 +238,8 @@ "server", "types" ] - revision = "7cf66f570e2f47b286985e2d688b0a400c028e4c" - version = "v0.11.0-rc6" + revision = "ebee2fe114020aa49c70bbbae50b7079fc7e7b90" + version = "v0.11.0" [[projects]] branch = "master" @@ -313,7 +313,7 @@ branch = "master" name = "golang.org/x/sys" packages = ["unix"] - revision = "c11f84a56e43e20a78cee75a7c034031ecf57d1f" + revision = "9527bec2660bd847c050fda93a0f0c6dee0800bb" [[projects]] name = "golang.org/x/text" @@ -374,6 +374,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "132980da98fc92b6c0dd988df07316a340f5fc91ee77593cf984ade4e3e5fd62" + inputs-digest = "ae6792578b0664708339f4c05e020687d6b799ad6f8282394b919de69e403d1f" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index f95fb2961..77a6af9b5 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -71,7 +71,7 @@ [[constraint]] name = "github.com/tendermint/abci" - version = "~0.11.0-rc6" + version = "~0.11.0" [[constraint]] name = "github.com/tendermint/go-crypto" diff --git a/consensus/replay.go b/consensus/replay.go index b30a1f151..c0c300b37 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -267,17 +267,31 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight // If appBlockHeight == 0 it means that we are at genesis and hence should send InitChain if appBlockHeight == 0 { validators := types.TM2PB.Validators(state.Validators) + csParams := types.TM2PB.ConsensusParams(h.genDoc.ConsensusParams) req := abci.RequestInitChain{ Time: h.genDoc.GenesisTime.Unix(), // TODO ChainId: h.genDoc.ChainID, - ConsensusParams: types.TM2PB.ConsensusParams(h.genDoc.ConsensusParams), + ConsensusParams: csParams, Validators: validators, AppStateBytes: h.genDoc.AppStateJSON, } - _, err := proxyApp.Consensus().InitChainSync(req) + res, err := proxyApp.Consensus().InitChainSync(req) if err != nil { return nil, err } + + // update the state + if len(res.Validators) > 0 { + vals, err := types.PB2TM.Validators(res.Validators) + if err != nil { + return nil, err + } + state.Validators = types.NewValidatorSet(vals) + } + if res.ConsensusParams != nil { + // TODO + } + sm.SaveState(h.stateDB, state) } // First handle edge cases and constraints on the storeBlockHeight diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 2836110c1..725568ed5 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -13,6 +13,7 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tendermint/abci/example/kvstore" @@ -634,3 +635,53 @@ func (bs *mockBlockStore) LoadBlockCommit(height int64) *types.Commit { func (bs *mockBlockStore) LoadSeenCommit(height int64) *types.Commit { return bs.commits[height-1] } + +//---------------------------------------- + +func TestInitChainUpdateValidators(t *testing.T) { + val, _ := types.RandValidator(true, 10) + vals := types.NewValidatorSet([]*types.Validator{val}) + app := &initChainApp{vals: types.TM2PB.Validators(vals)} + clientCreator := proxy.NewLocalClientCreator(app) + + config := ResetConfig("proxy_test_") + privVal := privval.LoadFilePV(config.PrivValidatorFile()) + stateDB, state, store := stateAndStore(config, privVal.GetPubKey()) + + oldValAddr := state.Validators.Validators[0].Address + + // now start the app using the handshake - it should sync + genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile()) + handshaker := NewHandshaker(stateDB, state, store, genDoc) + proxyApp := proxy.NewAppConns(clientCreator, handshaker) + if err := proxyApp.Start(); err != nil { + t.Fatalf("Error starting proxy app connections: %v", err) + } + defer proxyApp.Stop() + + // reload the state, check the validator set was updated + state = sm.LoadState(stateDB) + + newValAddr := state.Validators.Validators[0].Address + expectValAddr := val.Address + assert.NotEqual(t, oldValAddr, newValAddr) + assert.Equal(t, newValAddr, expectValAddr) +} + +func newInitChainApp(vals []abci.Validator) *initChainApp { + return &initChainApp{ + vals: vals, + } +} + +// returns the vals on InitChain +type initChainApp struct { + abci.BaseApplication + vals []abci.Validator +} + +func (ica *initChainApp) InitChain(req abci.RequestInitChain) abci.ResponseInitChain { + return abci.ResponseInitChain{ + Validators: ica.vals, + } +} diff --git a/types/protobuf.go b/types/protobuf.go index d1b0ae7fb..730c60052 100644 --- a/types/protobuf.go +++ b/types/protobuf.go @@ -167,3 +167,19 @@ func (pb2tm) PubKey(pubKey abci.PubKey) (crypto.PubKey, error) { return nil, fmt.Errorf("Unknown pubkey type %v", pubKey.Type) } } + +func (pb2tm) Validators(vals []abci.Validator) ([]*Validator, error) { + tmVals := make([]*Validator, len(vals)) + for i, v := range vals { + pub, err := PB2TM.PubKey(v.PubKey) + if err != nil { + return nil, err + } + tmVals[i] = &Validator{ + Address: v.Address, + PubKey: pub, + VotingPower: v.Power, + } + } + return tmVals, nil +} diff --git a/version/version.go b/version/version.go index 1296657da..2c9c88c09 100644 --- a/version/version.go +++ b/version/version.go @@ -3,14 +3,14 @@ package version // Version components const ( Maj = "0" - Min = "19" - Fix = "10" + Min = "20" + Fix = "0" ) var ( // Version is the current version of Tendermint // Must be a string because scripts like dist.sh read this file. - Version = "0.19.10-dev" + Version = "0.20.0-dev" // GitCommit is the current HEAD set using ldflags. GitCommit string From 8be27494bb7e8a0f492ee8db99bf190790ccd45d Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 6 Jun 2018 16:11:58 -0700 Subject: [PATCH 3/5] update abci spec. add address spec --- docs/spec/blockchain/encoding.md | 37 ++++++++++++++++++++++++++------ docs/spec/consensus/abci.md | 25 +++++++++++++++------ 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/docs/spec/blockchain/encoding.md b/docs/spec/blockchain/encoding.md index c60f7035f..aa1842107 100644 --- a/docs/spec/blockchain/encoding.md +++ b/docs/spec/blockchain/encoding.md @@ -49,14 +49,14 @@ spec](https://github.com/tendermint/go-amino#computing-the-prefix-and-disambigua In what follows, we provide the type names and prefix bytes directly. Notice that when encoding byte-arrays, the length of the byte-array is appended to the PrefixBytes. Thus the encoding of a byte array becomes ` - `. In other words, to encode any type listed below you do not need to be -familiar with amino encoding. -You can simply use below table and concatenate Prefix || Length (of raw bytes) || raw bytes + `. In other words, to encode any type listed below you do not need to be +familiar with amino encoding. +You can simply use below table and concatenate Prefix || Length (of raw bytes) || raw bytes ( while || stands for byte concatenation here). | Type | Name | Prefix | Length | -| ---- | ---- | ------ | ----- | -| PubKeyEd25519 | tendermint/PubKeyEd25519 | 0x1624DE62 | 0x20 | +| ---- | ---- | ------ | ----- | +| PubKeyEd25519 | tendermint/PubKeyEd25519 | 0x1624DE62 | 0x20 | | PubKeyLedgerEd25519 | tendermint/PubKeyLedgerEd25519 | 0x5C3453B2 | 0x20 | | PubKeySecp256k1 | tendermint/PubKeySecp256k1 | 0xEB5AE982 | 0x21 | | PrivKeyEd25519 | tendermint/PrivKeyEd25519 | 0xA3288912 | 0x40 | @@ -69,7 +69,7 @@ You can simply use below table and concatenate Prefix || Length (of raw bytes) | ### Examples 1. For example, the 33-byte (or 0x21-byte in hex) Secp256k1 pubkey -`020BD40F225A57ED383B440CF073BC5539D0341F5767D2BF2D78406D00475A2EE9` +`020BD40F225A57ED383B440CF073BC5539D0341F5767D2BF2D78406D00475A2EE9` would be encoded as `EB5AE98221020BD40F225A57ED383B440CF073BC5539D0341F5767D2BF2D78406D00475A2EE9` @@ -78,6 +78,31 @@ would be encoded as would be encoded as `16E1FEEA46304402201CD4B8C764D2FD8AF23ECFE6666CA8A53886D47754D951295D2D311E1FEA33BF02201E0F906BB1CF2C30EAACFFB032A7129358AFF96B9F79B06ACFFB18AC90C2ADD7` +### Addresses + +Addresses for each public key types are computed as follows: + +#### Ed25519 + +RIPEMD160 hash of the Amino encoded public key: + +``` +address = RIPEMD160(AMINO(pubkey)) +``` + +NOTE: this will soon change to the truncated 20-bytes of the SHA256 of the raw +public key + +#### Secp256k1 + +RIPEMD160 hash of the SHA256 hash of the OpenSSL compressed public key: + +``` +address = RIPEMD160(SHA256(pubkey)) +``` + +This is the same as Bitcoin. + ## Other Common Types ### BitArray diff --git a/docs/spec/consensus/abci.md b/docs/spec/consensus/abci.md index aa09cf3ad..e6d865400 100644 --- a/docs/spec/consensus/abci.md +++ b/docs/spec/consensus/abci.md @@ -52,20 +52,33 @@ objects in the `ResponseBeginBlock`: ``` message Validator { - bytes pub_key = 1; - int64 power = 2; + bytes address = 1; + PubKey pub_key = 2; + int64 power = 3; } + +message PubKey { + string type = 1; + bytes data = 2; +} + ``` -The `pub_key` is the Amino encoded public key for the validator. For details on -Amino encoded public keys, see the [section of the encoding spec](https://github.com/tendermint/tendermint/blob/master/docs/spec/blockchain/encoding.md#public-key-cryptography). +The `pub_key` currently supports two types: + - `type = "ed25519" and `data = ` + - `type = "secp256k1" and `data = <33-byte OpenSSL compressed public key>` +If the address is provided, it must match the address of the pubkey, as +specified [here](/docs/spec/blockchain/encoding.md#Addresses) + +(Note: In the v0.19 series, the `pub_key` is the [Amino encoded public +key](/docs/spec/blockchain/encoding.md#public-key-cryptography). For Ed25519 pubkeys, the Amino prefix is always "1624DE6220". For example, the 32-byte Ed25519 pubkey `76852933A4686A721442E931A8415F62F5F1AEDF4910F1F252FB393F74C40C85` would be Amino encoded as -`1624DE622076852933A4686A721442E931A8415F62F5F1AEDF4910F1F252FB393F74C40C85` +`1624DE622076852933A4686A721442E931A8415F62F5F1AEDF4910F1F252FB393F74C40C85`) -(Note: in old versions of Tendermint (pre-v0.19.0), the pubkey is just prefixed with a +(Note: In old versions of Tendermint (pre-v0.19.0), the pubkey is just prefixed with a single type byte, so for ED25519 we'd have `pub_key = 0x1 | pub`) The `power` is the new voting power for the validator, with the From 6b8613b3e77de0711a4f646a55ef795e571e7d69 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 6 Jun 2018 16:12:14 -0700 Subject: [PATCH 4/5] ResponseEndBlock: ensure Address matches PubKey if provided --- state/execution.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/state/execution.go b/state/execution.go index 435b29ee6..ec37e0d13 100644 --- a/state/execution.go +++ b/state/execution.go @@ -1,6 +1,7 @@ package state import ( + "bytes" "fmt" fail "github.com/ebuchman/fail-test" @@ -278,6 +279,16 @@ func updateValidators(currentSet *types.ValidatorSet, updates []abci.Validator) } address := pubkey.Address() + + // If the app provided an address too, it must match. + // This is just a sanity check. + if len(v.Address) > 0 { + if !bytes.Equal(address, v.Address) { + return fmt.Errorf("Validator.Address (%X) does not match PubKey.Address (%X)", + v.Address, address) + } + } + power := int64(v.Power) // mind the overflow from int64 if power < 0 { From 89925501f353d7ebd9742909ed40820cc72c05da Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 6 Jun 2018 16:13:51 -0700 Subject: [PATCH 5/5] p2p/filter/pubkey -> p2p/filter/id --- CHANGELOG.md | 2 ++ docs/spec/consensus/abci.md | 2 +- node/node.go | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97c3dfb44..019b8e04d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ BREAKING CHANGES - [abci] Upgrade to [v0.11.0](https://github.com/tendermint/abci/blob/master/CHANGELOG.md#0110) +- [abci] Query path for filtering peers by node ID changed from + `p2p/filter/pubkey/` to `p2p/filter/id/` NOTE: this release does not break any blockchain data structures or protocols other than the ABCI messages between Tendermint and the application. diff --git a/docs/spec/consensus/abci.md b/docs/spec/consensus/abci.md index e6d865400..2669a78e1 100644 --- a/docs/spec/consensus/abci.md +++ b/docs/spec/consensus/abci.md @@ -107,7 +107,7 @@ using the following paths, with no additional data: - `/p2p/filter/addr/`, where `` denote the IP address and the port of the connection - - `p2p/filter/pubkey/`, where `` is the peer node ID (ie. the + - `p2p/filter/id/`, where `` is the peer node ID (ie. the pubkey.Address() for the peer's PubKey) If either of these queries return a non-zero ABCI code, Tendermint will refuse diff --git a/node/node.go b/node/node.go index a8b5c9b2f..efeb17ee0 100644 --- a/node/node.go +++ b/node/node.go @@ -302,7 +302,7 @@ func NewNode(config *cfg.Config, return nil }) sw.SetIDFilter(func(id p2p.ID) error { - resQuery, err := proxyApp.Query().QuerySync(abci.RequestQuery{Path: cmn.Fmt("/p2p/filter/pubkey/%s", id)}) + resQuery, err := proxyApp.Query().QuerySync(abci.RequestQuery{Path: cmn.Fmt("/p2p/filter/id/%s", id)}) if err != nil { return err }