Compare commits

...

21 Commits

Author SHA1 Message Date
Christopher Goes
73de99ecab Set evidence.Type 2018-05-31 06:35:26 +02:00
Christopher Goes
2046b346a1 Merge pull request #1652 from tendermint/cwgoes/abci-evidence-type-constants
Constants for evidence types
2018-05-31 05:24:47 +02:00
Christopher Goes
c9514f077b DUPLICATE_VOTE evidence type 2018-05-31 05:24:15 +02:00
Christopher Goes
3bf9a7dc50 Validator public key, not address (somehow this was lost in cherry-pick) 2018-05-29 07:51:28 +02:00
Christopher Goes
53b0c67f75 Switch to tagged ABCI version 2018-05-29 05:49:33 +02:00
Christopher Goes
3b8c1ae119 Pin to an ABCI version 2018-05-29 04:59:18 +02:00
Christopher Goes
849ffaf43d Cherry-pick 2018-05-29 01:02:44 +02:00
Christopher Goes
058867669e Pass validator set to ExecCommitBlock, update testcases 2018-05-29 00:50:44 +02:00
Christopher Goes
923e0b02bf Retarget 2018-05-29 00:49:57 +02:00
Zach
ec34c8f9d2 docs: update ABCI output (#1635) 2018-05-28 22:06:02 +04:00
Anton Kaliaev
6004587347 expect all tags to be strings (#1498)
* expect all tags to be strings

Refs #1369

* port changes from https://github.com/tendermint/tmlibs/pull/204

Refs #1369
2018-05-28 14:37:11 +04:00
Zach
7f20eb5f8e generate RPC docs using Slate (#1612)
* generate RPC docs using Slate (#691)

* update changelog

* skip if branch not develop

* slate: only build if rpc/core has changes

* fetch develop to compare against

* slate: build on master only

* [rpc/core] use original repo, not fork in README
2018-05-25 15:59:24 +04:00
Anton Kaliaev
eeabb4c06b Merge pull request #1607 from tendermint/1600-wal-bug
[wal] small fixes in SearchEndHeight & replay logic
2018-05-25 15:41:57 +04:00
Anton Kaliaev
4da81aa0b7 commented out TestPEXReactorRunning 2018-05-25 15:11:32 +04:00
Anton Kaliaev
67068a34f2 log requesting addresses 2018-05-25 15:11:32 +04:00
Anton Kaliaev
2a0e9f93ce provide arg to error
BEFORE:

```
E[05-24|11:55:37.229] Dialing failed                               pex=0 addr=022ec801d79025caab3afbbf816d92ff8450d040@127.0.0.2:6593 err="Connect to self: <nil>" attempts=0
```

AFTER:

```
E[05-24|11:55:37.229] Dialing failed                               pex=0 addr=022ec801d79025caab3afbbf816d92ff8450d040@127.0.0.2:6593 err="Connect to self: 022ec801d79025caab3afbbf816d92ff8450d040@127.0.0.2:6593" attempts=0
```
2018-05-25 15:11:32 +04:00
Anton Kaliaev
708f35e5c1 do not look for height in older files if we've seen height - 1
Refs #1600
2018-05-25 15:11:15 +04:00
Anton Kaliaev
f3f5c7f472 we must only return io.EOF to progress to the next file in auto.Group
since we never write msg partially, if we've encountered io.EOF in the
middle of the msg, we must abort
2018-05-25 15:10:51 +04:00
Anton Kaliaev
68f6226bea data is corrupted, but this requires manual intervention
i.e., can't be skipped

and we should only return DataCorruptionError if we can skip a msg safely
2018-05-25 15:10:51 +04:00
Anton Kaliaev
118b86b1ef fix nil panic error
msg is nil and if we continue executing, we'll get nil exception at
`msg.Msg.(....)`
2018-05-25 15:10:51 +04:00
Anton Kaliaev
b9afcbe3a2 fix typo 2018-05-25 15:10:51 +04:00
29 changed files with 329 additions and 386 deletions

View File

@@ -77,6 +77,22 @@ jobs:
paths:
- "bin/abci*"
build_slate:
<<: *defaults
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v1-pkg-cache
- restore_cache:
key: v1-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: slate docs
command: |
set -ex
export PATH="$GOBIN:$PATH"
make build-slate
lint:
<<: *defaults
steps:
@@ -180,6 +196,9 @@ workflows:
test-suite:
jobs:
- setup_dependencies
- build_slate:
requires:
- setup_dependencies
- setup_abci:
requires:
- setup_dependencies

View File

@@ -1,13 +1,23 @@
# Changelog
## 0.20.0
BREAKING:
- [libs/pubsub] TagMap#Get returns a string value
- [libs/pubsub] NewTagMap accepts a map of strings
## 0.19.6
*TBD*
FEATURES
- [rpc] the RPC documentation is now published to https://tendermint.github.io/slate
IMPROVEMENTS:
- [consensus] consensus reactor now receives events from a separate event bus,
which is not dependant on external RPC load
- [consensus/wal] do not look for height in older files if we've seen height - 1
## 0.19.5

6
Gopkg.lock generated
View File

@@ -238,8 +238,8 @@
"server",
"types"
]
revision = "78a8905690ef54f9d57e3b2b0ee7ad3a04ef3f1f"
version = "v0.10.3"
revision = "f9dce537281ffba5d1e047e6729429f7e5fb90c9"
version = "v0.11.0-rc0"
[[projects]]
branch = "master"
@@ -382,6 +382,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "d85c98dcac32cc1fe05d006aa75e8985f6447a150a041b972a673a65e7681da9"
inputs-digest = "90dc14750c1499107a3e6728ae696f9977f56bee2855c2f1c0a14831a165cc0e"
solver-name = "gps-cdcl"
solver-version = 1

View File

@@ -71,7 +71,7 @@
[[constraint]]
name = "github.com/tendermint/abci"
version = "~0.10.3"
version = "0.11.0-rc0"
[[constraint]]
name = "github.com/tendermint/go-crypto"

View File

@@ -226,8 +226,11 @@ sentry-stop:
@if [ -z "$(DO_API_TOKEN)" ]; then echo "DO_API_TOKEN environment variable not set." ; false ; fi
cd networks/remote/terraform && terraform destroy -var DO_API_TOKEN="$(DO_API_TOKEN)" -var SSH_KEY_FILE="$(HOME)/.ssh/id_rsa.pub"
# meant for the CI, inspect script & adapt accordingly
build-slate:
bash scripts/slate.sh
# To avoid unintended conflicts with file names, always add to .PHONY
# unless there is a reason not to.
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
.PHONY: check build build_race dist install check_tools get_tools update_tools get_vendor_deps draw_deps test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop
.PHONY: check build build_race dist install check_tools get_tools update_tools get_vendor_deps draw_deps test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate

View File

@@ -269,8 +269,8 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
if appBlockHeight == 0 {
validators := types.TM2PB.Validators(state.Validators)
req := abci.RequestInitChain{
Validators: validators,
AppStateBytes: h.appState,
Validators: validators,
GenesisBytes: h.appState,
}
_, err := proxyApp.Consensus().InitChainSync(req)
if err != nil {
@@ -365,7 +365,8 @@ func (h *Handshaker) replayBlocks(state sm.State, proxyApp proxy.AppConns, appBl
for i := appBlockHeight + 1; i <= finalBlock; i++ {
h.logger.Info("Applying block", "height", i)
block := h.store.LoadBlock(i)
appHash, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger)
appHash, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger, new(types.ValidatorSet))
// TODO: Temporary, see above comment.
if err != nil {
return nil, err
}

View File

@@ -111,7 +111,7 @@ func (wal *baseWAL) OnStop() {
}
// Write is called in newStep and for each receive on the
// peerMsgQueue and the timoutTicker.
// peerMsgQueue and the timeoutTicker.
// NOTE: does not call fsync()
func (wal *baseWAL) Write(msg WALMessage) {
if wal == nil {
@@ -144,13 +144,14 @@ type WALSearchOptions struct {
IgnoreDataCorruptionErrors bool
}
// SearchForEndHeight searches for the EndHeightMessage with the height and
// returns an auto.GroupReader, whenever it was found or not and an error.
// SearchForEndHeight searches for the EndHeightMessage with the given height
// and returns an auto.GroupReader, whenever it was found or not and an error.
// Group reader will be nil if found equals false.
//
// CONTRACT: caller must close group reader.
func (wal *baseWAL) SearchForEndHeight(height int64, options *WALSearchOptions) (gr *auto.GroupReader, found bool, err error) {
var msg *TimedWALMessage
lastHeightFound := int64(-1)
// NOTE: starting from the last file in the group because we're usually
// searching for the last height. See replay.go
@@ -166,17 +167,25 @@ func (wal *baseWAL) SearchForEndHeight(height int64, options *WALSearchOptions)
for {
msg, err = dec.Decode()
if err == io.EOF {
// OPTIMISATION: no need to look for height in older files if we've seen h < height
if lastHeightFound > 0 && lastHeightFound < height {
gr.Close()
return nil, false, nil
}
// check next file
break
}
if options.IgnoreDataCorruptionErrors && IsDataCorruptionError(err) {
wal.Logger.Debug("Corrupted entry. Skipping...", "err", err)
// do nothing
continue
} else if err != nil {
gr.Close()
return nil, false, err
}
if m, ok := msg.Msg.(EndHeightMessage); ok {
lastHeightFound = m.Height
if m.Height == height { // found
wal.Logger.Debug("Found", "height", height, "index", index)
return gr, true, nil
@@ -271,23 +280,17 @@ func (dec *WALDecoder) Decode() (*TimedWALMessage, error) {
b = make([]byte, 4)
_, err = dec.rd.Read(b)
if err == io.EOF {
return nil, err
}
if err != nil {
return nil, fmt.Errorf("failed to read length: %v", err)
}
length := binary.BigEndian.Uint32(b)
if length > maxMsgSizeBytes {
return nil, DataCorruptionError{fmt.Errorf("length %d exceeded maximum possible value of %d bytes", length, maxMsgSizeBytes)}
return nil, fmt.Errorf("length %d exceeded maximum possible value of %d bytes", length, maxMsgSizeBytes)
}
data := make([]byte, length)
_, err = dec.rd.Read(data)
if err == io.EOF {
return nil, err
}
if err != nil {
return nil, fmt.Errorf("failed to read data: %v", err)
}

View File

@@ -183,6 +183,7 @@ Try running these commands:
> commit
-> code: OK
-> data.hex: 0x0000000000000000
> deliver_tx "abc"
-> code: OK
@@ -194,7 +195,7 @@ Try running these commands:
> commit
-> code: OK
-> data.hex: 0x49DFD15CCDACDEAE9728CB01FBB5E8688CA58B91
-> data.hex: 0x0200000000000000
> query "abc"
-> code: OK
@@ -208,7 +209,7 @@ Try running these commands:
> commit
-> code: OK
-> data.hex: 0x70102DB32280373FBF3F9F89DA2A20CE2CD62B0B
-> data.hex: 0x0400000000000000
> query "def"
-> code: OK
@@ -301,6 +302,7 @@ In another window, start the ``abci-cli console``:
> set_option serial on
-> code: OK
-> log: OK (SetOption doesn't return anything.)
> check_tx 0x00
-> code: OK

View File

@@ -1,190 +1,4 @@
RPC
===
Coming soon: RPC docs powered by `slate <https://github.com/lord/slate>`__. Until then, read on.
Tendermint supports the following RPC protocols:
- URI over HTTP
- JSONRPC over HTTP
- JSONRPC over websockets
Tendermint RPC is build using `our own RPC
library <https://github.com/tendermint/tendermint/tree/master/rpc/lib>`__.
Documentation and tests for that library could be found at
``tendermint/rpc/lib`` directory.
Configuration
~~~~~~~~~~~~~
Set the ``laddr`` config parameter under ``[rpc]`` table in the
$TMHOME/config/config.toml file or the ``--rpc.laddr`` command-line flag to the
desired protocol://host:port setting. Default: ``tcp://0.0.0.0:46657``.
Arguments
~~~~~~~~~
Arguments which expect strings or byte arrays may be passed as quoted
strings, like ``"abc"`` or as ``0x``-prefixed strings, like
``0x616263``.
URI/HTTP
~~~~~~~~
Example request:
.. code:: bash
curl -s 'http://localhost:46657/broadcast_tx_sync?tx="abc"' | jq .
Response:
.. code:: json
{
"error": "",
"result": {
"hash": "2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF",
"log": "",
"data": "",
"code": 0
},
"id": "",
"jsonrpc": "2.0"
}
The first entry in the result-array (``96``) is the method this response
correlates with. ``96`` refers to "ResultTypeBroadcastTx", see
`responses.go <https://github.com/tendermint/tendermint/blob/master/rpc/core/types/responses.go>`__
for a complete overview.
JSONRPC/HTTP
~~~~~~~~~~~~
JSONRPC requests can be POST'd to the root RPC endpoint via HTTP (e.g.
``http://localhost:46657/``).
Example request:
.. code:: json
{
"method": "broadcast_tx_sync",
"jsonrpc": "2.0",
"params": [ "abc" ],
"id": "dontcare"
}
JSONRPC/websockets
~~~~~~~~~~~~~~~~~~
JSONRPC requests can be made via websocket. The websocket endpoint is at
``/websocket``, e.g. ``http://localhost:46657/websocket``. Asynchronous
RPC functions like event ``subscribe`` and ``unsubscribe`` are only
available via websockets.
Endpoints
~~~~~~~~~
An HTTP Get request to the root RPC endpoint (e.g.
``http://localhost:46657``) shows a list of available endpoints.
::
Available endpoints:
http://localhost:46657/abci_info
http://localhost:46657/dump_consensus_state
http://localhost:46657/genesis
http://localhost:46657/net_info
http://localhost:46657/num_unconfirmed_txs
http://localhost:46657/health
http://localhost:46657/status
http://localhost:46657/unconfirmed_txs
http://localhost:46657/unsafe_flush_mempool
http://localhost:46657/unsafe_stop_cpu_profiler
http://localhost:46657/validators
Endpoints that require arguments:
http://localhost:46657/abci_query?path=_&data=_&prove=_
http://localhost:46657/block?height=_
http://localhost:46657/blockchain?minHeight=_&maxHeight=_
http://localhost:46657/broadcast_tx_async?tx=_
http://localhost:46657/broadcast_tx_commit?tx=_
http://localhost:46657/broadcast_tx_sync?tx=_
http://localhost:46657/commit?height=_
http://localhost:46657/dial_seeds?seeds=_
http://localhost:46657/dial_peers?peers=_&persistent=_
http://localhost:46657/subscribe?event=_
http://localhost:46657/tx?hash=_&prove=_
http://localhost:46657/unsafe_start_cpu_profiler?filename=_
http://localhost:46657/unsafe_write_heap_profile?filename=_
http://localhost:46657/unsubscribe?event=_
tx
~~
Returns a transaction matching the given transaction hash.
**Parameters**
1. hash - the transaction hash
2. prove - include a proof of the transaction inclusion in the block in
the result (optional, default: false)
**Returns**
- ``proof``: the ``types.TxProof`` object
- ``tx``: ``[]byte`` - the transaction
- ``tx_result``: the ``abci.Result`` object
- ``index``: ``int`` - index of the transaction
- ``height``: ``int`` - height of the block where this transaction was
in
**Example**
.. code:: bash
curl -s 'http://localhost:46657/broadcast_tx_commit?tx="abc"' | jq .
# {
# "error": "",
# "result": {
# "hash": "2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF",
# "log": "",
# "data": "",
# "code": 0
# },
# "id": "",
# "jsonrpc": "2.0"
# }
curl -s 'http://localhost:46657/tx?hash=0x2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF' | jq .
# {
# "error": "",
# "result": {
# "proof": {
# "Proof": {
# "aunts": []
# },
# "Data": "YWJjZA==",
# "RootHash": "2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF",
# "Total": 1,
# "Index": 0
# },
# "tx": "YWJjZA==",
# "tx_result": {
# "log": "",
# "data": "",
# "code": 0
# },
# "index": 0,
# "height": 52
# },
# "id": "",
# "jsonrpc": "2.0"
# }
More Examples
~~~~~~~~~~~~~
See the various bash tests using curl in ``test/``, and examples using
the ``Go`` API in ``rpc/client/``.
The RPC documentation is hosted `here <https://tendermint.github.io/slate>`__ and is generated by the CI from our `Slate repo <https://github.com/tendermint/slate>`__. To update the documentation, edit the relevant ``godoc`` comments in the `rpc/core directory <https://github.com/tendermint/tendermint/tree/develop/rpc/core>`__.

View File

@@ -22,7 +22,7 @@ func TestExample(t *testing.T) {
ch := make(chan interface{}, 1)
err := s.Subscribe(ctx, "example-client", query.MustParse("abci.account.name='John'"), ch)
require.NoError(t, err)
err = s.PublishWithTags(ctx, "Tombstone", pubsub.NewTagMap(map[string]interface{}{"abci.account.name": "John"}))
err = s.PublishWithTags(ctx, "Tombstone", pubsub.NewTagMap(map[string]string{"abci.account.name": "John"}))
require.NoError(t, err)
assertReceive(t, "Tombstone", ch)
}

View File

@@ -38,18 +38,6 @@ var (
ErrAlreadySubscribed = errors.New("already subscribed")
)
// TagMap is used to associate tags to a message.
// They can be queried by subscribers to choose messages they will received.
type TagMap interface {
// Get returns the value for a key, or nil if no value is present.
// The ok result indicates whether value was found in the tags.
Get(key string) (value interface{}, ok bool)
// Len returns the number of tags.
Len() int
}
type tagMap map[string]interface{}
type cmd struct {
op operation
query Query
@@ -80,14 +68,28 @@ type Server struct {
// Option sets a parameter for the server.
type Option func(*Server)
// TagMap is used to associate tags to a message.
// They can be queried by subscribers to choose messages they will received.
type TagMap interface {
// Get returns the value for a key, or nil if no value is present.
// The ok result indicates whether value was found in the tags.
Get(key string) (value string, ok bool)
// Len returns the number of tags.
Len() int
}
type tagMap map[string]string
var _ TagMap = (*tagMap)(nil)
// NewTagMap constructs a new immutable tag set from a map.
func NewTagMap(data map[string]interface{}) TagMap {
func NewTagMap(data map[string]string) TagMap {
return tagMap(data)
}
// Get returns the value for a key, or nil if no value is present.
// The ok result indicates whether value was found in the tags.
func (ts tagMap) Get(key string) (value interface{}, ok bool) {
func (ts tagMap) Get(key string) (value string, ok bool) {
value, ok = ts[key]
return
}
@@ -213,7 +215,7 @@ func (s *Server) UnsubscribeAll(ctx context.Context, clientID string) error {
// Publish publishes the given message. An error will be returned to the caller
// if the context is canceled.
func (s *Server) Publish(ctx context.Context, msg interface{}) error {
return s.PublishWithTags(ctx, msg, NewTagMap(make(map[string]interface{})))
return s.PublishWithTags(ctx, msg, NewTagMap(make(map[string]string)))
}
// PublishWithTags publishes the given message with the set of tags. The set is

View File

@@ -49,14 +49,14 @@ func TestDifferentClients(t *testing.T) {
ch1 := make(chan interface{}, 1)
err := s.Subscribe(ctx, "client-1", query.MustParse("tm.events.type='NewBlock'"), ch1)
require.NoError(t, err)
err = s.PublishWithTags(ctx, "Iceman", pubsub.NewTagMap(map[string]interface{}{"tm.events.type": "NewBlock"}))
err = s.PublishWithTags(ctx, "Iceman", pubsub.NewTagMap(map[string]string{"tm.events.type": "NewBlock"}))
require.NoError(t, err)
assertReceive(t, "Iceman", ch1)
ch2 := make(chan interface{}, 1)
err = s.Subscribe(ctx, "client-2", query.MustParse("tm.events.type='NewBlock' AND abci.account.name='Igor'"), ch2)
require.NoError(t, err)
err = s.PublishWithTags(ctx, "Ultimo", pubsub.NewTagMap(map[string]interface{}{"tm.events.type": "NewBlock", "abci.account.name": "Igor"}))
err = s.PublishWithTags(ctx, "Ultimo", pubsub.NewTagMap(map[string]string{"tm.events.type": "NewBlock", "abci.account.name": "Igor"}))
require.NoError(t, err)
assertReceive(t, "Ultimo", ch1)
assertReceive(t, "Ultimo", ch2)
@@ -64,7 +64,7 @@ func TestDifferentClients(t *testing.T) {
ch3 := make(chan interface{}, 1)
err = s.Subscribe(ctx, "client-3", query.MustParse("tm.events.type='NewRoundStep' AND abci.account.name='Igor' AND abci.invoice.number = 10"), ch3)
require.NoError(t, err)
err = s.PublishWithTags(ctx, "Valeria Richards", pubsub.NewTagMap(map[string]interface{}{"tm.events.type": "NewRoundStep"}))
err = s.PublishWithTags(ctx, "Valeria Richards", pubsub.NewTagMap(map[string]string{"tm.events.type": "NewRoundStep"}))
require.NoError(t, err)
assert.Zero(t, len(ch3))
}
@@ -81,7 +81,7 @@ func TestClientSubscribesTwice(t *testing.T) {
ch1 := make(chan interface{}, 1)
err := s.Subscribe(ctx, clientID, q, ch1)
require.NoError(t, err)
err = s.PublishWithTags(ctx, "Goblin Queen", pubsub.NewTagMap(map[string]interface{}{"tm.events.type": "NewBlock"}))
err = s.PublishWithTags(ctx, "Goblin Queen", pubsub.NewTagMap(map[string]string{"tm.events.type": "NewBlock"}))
require.NoError(t, err)
assertReceive(t, "Goblin Queen", ch1)
@@ -89,7 +89,7 @@ func TestClientSubscribesTwice(t *testing.T) {
err = s.Subscribe(ctx, clientID, q, ch2)
require.Error(t, err)
err = s.PublishWithTags(ctx, "Spider-Man", pubsub.NewTagMap(map[string]interface{}{"tm.events.type": "NewBlock"}))
err = s.PublishWithTags(ctx, "Spider-Man", pubsub.NewTagMap(map[string]string{"tm.events.type": "NewBlock"}))
require.NoError(t, err)
assertReceive(t, "Spider-Man", ch1)
}
@@ -209,7 +209,7 @@ func benchmarkNClients(n int, b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
s.PublishWithTags(ctx, "Gamora", pubsub.NewTagMap(map[string]interface{}{"abci.Account.Owner": "Ivan", "abci.Invoices.Number": i}))
s.PublishWithTags(ctx, "Gamora", pubsub.NewTagMap(map[string]string{"abci.Account.Owner": "Ivan", "abci.Invoices.Number": string(i)}))
}
}
@@ -232,7 +232,7 @@ func benchmarkNClientsOneQuery(n int, b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
s.PublishWithTags(ctx, "Gamora", pubsub.NewTagMap(map[string]interface{}{"abci.Account.Owner": "Ivan", "abci.Invoices.Number": 1}))
s.PublishWithTags(ctx, "Gamora", pubsub.NewTagMap(map[string]string{"abci.Account.Owner": "Ivan", "abci.Invoices.Number": "1"}))
}
}

View File

@@ -11,8 +11,8 @@ import (
func TestEmptyQueryMatchesAnything(t *testing.T) {
q := query.Empty{}
assert.True(t, q.Matches(pubsub.NewTagMap(map[string]interface{}{})))
assert.True(t, q.Matches(pubsub.NewTagMap(map[string]interface{}{"Asher": "Roth"})))
assert.True(t, q.Matches(pubsub.NewTagMap(map[string]interface{}{"Route": 66})))
assert.True(t, q.Matches(pubsub.NewTagMap(map[string]interface{}{"Route": 66, "Billy": "Blue"})))
assert.True(t, q.Matches(pubsub.NewTagMap(map[string]string{})))
assert.True(t, q.Matches(pubsub.NewTagMap(map[string]string{"Asher": "Roth"})))
assert.True(t, q.Matches(pubsub.NewTagMap(map[string]string{"Route": "66"})))
assert.True(t, q.Matches(pubsub.NewTagMap(map[string]string{"Route": "66", "Billy": "Blue"})))
}

View File

@@ -77,6 +77,13 @@ const (
OpContains
)
const (
// DateLayout defines a layout for all dates (`DATE date`)
DateLayout = "2006-01-02"
// TimeLayout defines a layout for all times (`TIME time`)
TimeLayout = time.RFC3339
)
// Conditions returns a list of conditions.
func (q *Query) Conditions() []Condition {
conditions := make([]Condition, 0)
@@ -112,7 +119,7 @@ func (q *Query) Conditions() []Condition {
conditions = append(conditions, Condition{tag, op, valueWithoutSingleQuotes})
case rulenumber:
number := buffer[begin:end]
if strings.Contains(number, ".") { // if it looks like a floating-point number
if strings.ContainsAny(number, ".") { // if it looks like a floating-point number
value, err := strconv.ParseFloat(number, 64)
if err != nil {
panic(fmt.Sprintf("got %v while trying to parse %s as float64 (should never happen if the grammar is correct)", err, number))
@@ -126,7 +133,7 @@ func (q *Query) Conditions() []Condition {
conditions = append(conditions, Condition{tag, op, value})
}
case ruletime:
value, err := time.Parse(time.RFC3339, buffer[begin:end])
value, err := time.Parse(TimeLayout, buffer[begin:end])
if err != nil {
panic(fmt.Sprintf("got %v while trying to parse %s as time.Time / RFC3339 (should never happen if the grammar is correct)", err, buffer[begin:end]))
}
@@ -188,7 +195,7 @@ func (q *Query) Matches(tags pubsub.TagMap) bool {
}
case rulenumber:
number := buffer[begin:end]
if strings.Contains(number, ".") { // if it looks like a floating-point number
if strings.ContainsAny(number, ".") { // if it looks like a floating-point number
value, err := strconv.ParseFloat(number, 64)
if err != nil {
panic(fmt.Sprintf("got %v while trying to parse %s as float64 (should never happen if the grammar is correct)", err, number))
@@ -206,7 +213,7 @@ func (q *Query) Matches(tags pubsub.TagMap) bool {
}
}
case ruletime:
value, err := time.Parse(time.RFC3339, buffer[begin:end])
value, err := time.Parse(TimeLayout, buffer[begin:end])
if err != nil {
panic(fmt.Sprintf("got %v while trying to parse %s as time.Time / RFC3339 (should never happen if the grammar is correct)", err, buffer[begin:end]))
}
@@ -242,9 +249,18 @@ func match(tag string, op Operator, operand reflect.Value, tags pubsub.TagMap) b
switch operand.Kind() {
case reflect.Struct: // time
operandAsTime := operand.Interface().(time.Time)
v, ok := value.(time.Time)
if !ok { // if value from tags is not time.Time
return false
// try our best to convert value from tags to time.Time
var (
v time.Time
err error
)
if strings.ContainsAny(value, "T") {
v, err = time.Parse(TimeLayout, value)
} else {
v, err = time.Parse(DateLayout, value)
}
if err != nil {
panic(fmt.Sprintf("Failed to convert value %v from tag to time.Time: %v", value, err))
}
switch op {
case OpLessEqual:
@@ -262,23 +278,9 @@ func match(tag string, op Operator, operand reflect.Value, tags pubsub.TagMap) b
operandFloat64 := operand.Interface().(float64)
var v float64
// try our best to convert value from tags to float64
switch vt := value.(type) {
case float64:
v = vt
case float32:
v = float64(vt)
case int:
v = float64(vt)
case int8:
v = float64(vt)
case int16:
v = float64(vt)
case int32:
v = float64(vt)
case int64:
v = float64(vt)
default: // fail for all other types
panic(fmt.Sprintf("Incomparable types: %T (%v) vs float64 (%v)", value, value, operandFloat64))
v, err := strconv.ParseFloat(value, 64)
if err != nil {
panic(fmt.Sprintf("Failed to convert value %v from tag to float64: %v", value, err))
}
switch op {
case OpLessEqual:
@@ -295,24 +297,20 @@ func match(tag string, op Operator, operand reflect.Value, tags pubsub.TagMap) b
case reflect.Int64:
operandInt := operand.Interface().(int64)
var v int64
// try our best to convert value from tags to int64
switch vt := value.(type) {
case int64:
v = vt
case int8:
v = int64(vt)
case int16:
v = int64(vt)
case int32:
v = int64(vt)
case int:
v = int64(vt)
case float64:
v = int64(vt)
case float32:
v = int64(vt)
default: // fail for all other types
panic(fmt.Sprintf("Incomparable types: %T (%v) vs int64 (%v)", value, value, operandInt))
// if value looks like float, we try to parse it as float
if strings.ContainsAny(value, ".") {
v1, err := strconv.ParseFloat(value, 64)
if err != nil {
panic(fmt.Sprintf("Failed to convert value %v from tag to float64: %v", value, err))
}
v = int64(v1)
} else {
var err error
// try our best to convert value from tags to int64
v, err = strconv.ParseInt(value, 10, 64)
if err != nil {
panic(fmt.Sprintf("Failed to convert value %v from tag to int64: %v", value, err))
}
}
switch op {
case OpLessEqual:
@@ -327,15 +325,11 @@ func match(tag string, op Operator, operand reflect.Value, tags pubsub.TagMap) b
return v == operandInt
}
case reflect.String:
v, ok := value.(string)
if !ok { // if value from tags is not string
return false
}
switch op {
case OpEqual:
return v == operand.String()
return value == operand.String()
case OpContains:
return strings.Contains(v, operand.String())
return strings.Contains(value, operand.String())
}
default:
panic(fmt.Sprintf("Unknown kind of operand %v", operand.Kind()))

View File

@@ -1,6 +1,7 @@
package query_test
import (
"fmt"
"testing"
"time"
@@ -12,38 +13,37 @@ import (
)
func TestMatches(t *testing.T) {
const shortForm = "2006-Jan-02"
txDate, err := time.Parse(shortForm, "2017-Jan-01")
require.NoError(t, err)
txTime, err := time.Parse(time.RFC3339, "2018-05-03T14:45:00Z")
require.NoError(t, err)
var (
txDate = "2017-01-01"
txTime = "2018-05-03T14:45:00Z"
)
testCases := []struct {
s string
tags map[string]interface{}
tags map[string]string
err bool
matches bool
}{
{"tm.events.type='NewBlock'", map[string]interface{}{"tm.events.type": "NewBlock"}, false, true},
{"tm.events.type='NewBlock'", map[string]string{"tm.events.type": "NewBlock"}, false, true},
{"tx.gas > 7", map[string]interface{}{"tx.gas": 8}, false, true},
{"tx.gas > 7 AND tx.gas < 9", map[string]interface{}{"tx.gas": 8}, false, true},
{"body.weight >= 3.5", map[string]interface{}{"body.weight": 3.5}, false, true},
{"account.balance < 1000.0", map[string]interface{}{"account.balance": 900}, false, true},
{"apples.kg <= 4", map[string]interface{}{"apples.kg": 4.0}, false, true},
{"body.weight >= 4.5", map[string]interface{}{"body.weight": float32(4.5)}, false, true},
{"oranges.kg < 4 AND watermellons.kg > 10", map[string]interface{}{"oranges.kg": 3, "watermellons.kg": 12}, false, true},
{"peaches.kg < 4", map[string]interface{}{"peaches.kg": 5}, false, false},
{"tx.gas > 7", map[string]string{"tx.gas": "8"}, false, true},
{"tx.gas > 7 AND tx.gas < 9", map[string]string{"tx.gas": "8"}, false, true},
{"body.weight >= 3.5", map[string]string{"body.weight": "3.5"}, false, true},
{"account.balance < 1000.0", map[string]string{"account.balance": "900"}, false, true},
{"apples.kg <= 4", map[string]string{"apples.kg": "4.0"}, false, true},
{"body.weight >= 4.5", map[string]string{"body.weight": fmt.Sprintf("%v", float32(4.5))}, false, true},
{"oranges.kg < 4 AND watermellons.kg > 10", map[string]string{"oranges.kg": "3", "watermellons.kg": "12"}, false, true},
{"peaches.kg < 4", map[string]string{"peaches.kg": "5"}, false, false},
{"tx.date > DATE 2017-01-01", map[string]interface{}{"tx.date": time.Now()}, false, true},
{"tx.date = DATE 2017-01-01", map[string]interface{}{"tx.date": txDate}, false, true},
{"tx.date = DATE 2018-01-01", map[string]interface{}{"tx.date": txDate}, false, false},
{"tx.date > DATE 2017-01-01", map[string]string{"tx.date": time.Now().Format(query.DateLayout)}, false, true},
{"tx.date = DATE 2017-01-01", map[string]string{"tx.date": txDate}, false, true},
{"tx.date = DATE 2018-01-01", map[string]string{"tx.date": txDate}, false, false},
{"tx.time >= TIME 2013-05-03T14:45:00Z", map[string]interface{}{"tx.time": time.Now()}, false, true},
{"tx.time = TIME 2013-05-03T14:45:00Z", map[string]interface{}{"tx.time": txTime}, false, false},
{"tx.time >= TIME 2013-05-03T14:45:00Z", map[string]string{"tx.time": time.Now().Format(query.TimeLayout)}, false, true},
{"tx.time = TIME 2013-05-03T14:45:00Z", map[string]string{"tx.time": txTime}, false, false},
{"abci.owner.name CONTAINS 'Igor'", map[string]interface{}{"abci.owner.name": "Igor,Ivan"}, false, true},
{"abci.owner.name CONTAINS 'Igor'", map[string]interface{}{"abci.owner.name": "Pavel,Ivan"}, false, false},
{"abci.owner.name CONTAINS 'Igor'", map[string]string{"abci.owner.name": "Igor,Ivan"}, false, true},
{"abci.owner.name CONTAINS 'Igor'", map[string]string{"abci.owner.name": "Pavel,Ivan"}, false, false},
}
for _, tc := range testCases {

View File

@@ -6,7 +6,7 @@ import (
"sync/atomic"
"time"
"github.com/tendermint/go-crypto"
crypto "github.com/tendermint/go-crypto"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tmlibs/log"

View File

@@ -7,7 +7,7 @@ import (
"sync"
"time"
"github.com/tendermint/go-amino"
amino "github.com/tendermint/go-amino"
cmn "github.com/tendermint/tmlibs/common"
"github.com/tendermint/tendermint/p2p"
@@ -281,6 +281,7 @@ func (r *PEXReactor) receiveRequest(src Peer) error {
// RequestAddrs asks peer for more addresses if we do not already
// have a request out for this peer.
func (r *PEXReactor) RequestAddrs(p Peer) {
r.Logger.Debug("Request addrs", "from", p)
id := string(p.ID())
if r.requestsSent.Has(id) {
return

View File

@@ -59,59 +59,69 @@ func TestPEXReactorAddRemovePeer(t *testing.T) {
assert.Equal(t, size+1, book.Size())
}
func TestPEXReactorRunning(t *testing.T) {
N := 3
switches := make([]*p2p.Switch, N)
// --- FAIL: TestPEXReactorRunning (11.10s)
// pex_reactor_test.go:411: expected all switches to be connected to at
// least one peer (switches: 0 => {outbound: 1, inbound: 0}, 1 =>
// {outbound: 0, inbound: 1}, 2 => {outbound: 0, inbound: 0}, )
//
// EXPLANATION: peers are getting rejected because in switch#addPeer we check
// if any peer (who we already connected to) has the same IP. Even though local
// peers have different IP addresses, they all have the same underlying remote
// IP: 127.0.0.1.
//
// func TestPEXReactorRunning(t *testing.T) {
// N := 3
// switches := make([]*p2p.Switch, N)
// directory to store address books
dir, err := ioutil.TempDir("", "pex_reactor")
require.Nil(t, err)
defer os.RemoveAll(dir) // nolint: errcheck
// // directory to store address books
// dir, err := ioutil.TempDir("", "pex_reactor")
// require.Nil(t, err)
// defer os.RemoveAll(dir) // nolint: errcheck
books := make([]*addrBook, N)
logger := log.TestingLogger()
// books := make([]*addrBook, N)
// logger := log.TestingLogger()
// create switches
for i := 0; i < N; i++ {
switches[i] = p2p.MakeSwitch(config, i, "testing", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch {
books[i] = NewAddrBook(filepath.Join(dir, fmt.Sprintf("addrbook%d.json", i)), false)
books[i].SetLogger(logger.With("pex", i))
sw.SetAddrBook(books[i])
// // create switches
// for i := 0; i < N; i++ {
// switches[i] = p2p.MakeSwitch(config, i, "testing", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch {
// books[i] = NewAddrBook(filepath.Join(dir, fmt.Sprintf("addrbook%d.json", i)), false)
// books[i].SetLogger(logger.With("pex", i))
// sw.SetAddrBook(books[i])
sw.SetLogger(logger.With("pex", i))
// sw.SetLogger(logger.With("pex", i))
r := NewPEXReactor(books[i], &PEXReactorConfig{})
r.SetLogger(logger.With("pex", i))
r.SetEnsurePeersPeriod(250 * time.Millisecond)
sw.AddReactor("pex", r)
// r := NewPEXReactor(books[i], &PEXReactorConfig{})
// r.SetLogger(logger.With("pex", i))
// r.SetEnsurePeersPeriod(250 * time.Millisecond)
// sw.AddReactor("pex", r)
return sw
})
}
// return sw
// })
// }
addOtherNodeAddrToAddrBook := func(switchIndex, otherSwitchIndex int) {
addr := switches[otherSwitchIndex].NodeInfo().NetAddress()
books[switchIndex].AddAddress(addr, addr)
}
// addOtherNodeAddrToAddrBook := func(switchIndex, otherSwitchIndex int) {
// addr := switches[otherSwitchIndex].NodeInfo().NetAddress()
// books[switchIndex].AddAddress(addr, addr)
// }
addOtherNodeAddrToAddrBook(0, 1)
addOtherNodeAddrToAddrBook(1, 0)
addOtherNodeAddrToAddrBook(2, 1)
// addOtherNodeAddrToAddrBook(0, 1)
// addOtherNodeAddrToAddrBook(1, 0)
// addOtherNodeAddrToAddrBook(2, 1)
for i, sw := range switches {
sw.AddListener(p2p.NewDefaultListener("tcp", sw.NodeInfo().ListenAddr, true, logger.With("pex", i)))
// for i, sw := range switches {
// sw.AddListener(p2p.NewDefaultListener("tcp", sw.NodeInfo().ListenAddr, true, logger.With("pex", i)))
err := sw.Start() // start switch and reactors
require.Nil(t, err)
}
// err := sw.Start() // start switch and reactors
// require.Nil(t, err)
// }
assertPeersWithTimeout(t, switches, 10*time.Millisecond, 10*time.Second, N-1)
// assertPeersWithTimeout(t, switches, 10*time.Millisecond, 10*time.Second, N-1)
// stop them
for _, s := range switches {
s.Stop()
}
}
// // stop them
// for _, s := range switches {
// s.Stop()
// }
// }
func TestPEXReactorReceive(t *testing.T) {
r, book := createReactor(&PEXReactorConfig{})

View File

@@ -568,7 +568,7 @@ func (sw *Switch) addPeer(pc peerConn) error {
// and add to our addresses to avoid dialing again
sw.addrBook.RemoveAddress(addr)
sw.addrBook.AddOurAddress(addr)
return ErrSwitchConnectToSelf{}
return ErrSwitchConnectToSelf{addr}
}
// Avoid duplicate

View File

@@ -193,7 +193,7 @@ func TestSwitchFiltersOutItself(t *testing.T) {
// addr should be rejected in addPeer based on the same ID
err := s1.DialPeerWithAddress(rp.Addr(), false)
if assert.Error(t, err) {
assert.EqualValues(t, ErrSwitchConnectToSelf{}, err)
assert.Equal(t, ErrSwitchConnectToSelf{rp.Addr()}.Error(), err.Error())
}
assert.True(t, s1.addrBook.OurAddress(rp.Addr()))

View File

@@ -3,17 +3,16 @@
## Generate markdown for [Slate](https://github.com/tendermint/slate)
We are using [Slate](https://github.com/tendermint/slate) to power our RPC
documentation. If you are changing a comment, make sure to copy the resulting
changes to the slate repo and make a PR
[there](https://github.com/tendermint/slate) as well. For generating markdown
use:
documentation. For generating markdown use:
```shell
go get github.com/melekes/godoc2md
go get github.com/davecheney/godoc2md
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$'
```
For more information see the [CI script for building the Slate docs](/scripts/slate.sh)
## Pagination
Requests that return multiple items will be paginated to 30 items by default.

View File

@@ -7,7 +7,7 @@ Tendermint supports the following RPC protocols:
* JSONRPC over HTTP
* JSONRPC over websockets
Tendermint RPC is built using [our own RPC library](https://github.com/tendermint/tendermint/tree/master/rpc/lib). Documentation and tests for that library could be found at `tendermint/rpc/lib` directory.
Tendermint RPC is built using [our own RPC library](https://github.com/tendermint/tendermint/tree/master/rpc/lib) which contains its own set of documentation and tests.
## Configuration

77
scripts/slate.sh Normal file
View File

@@ -0,0 +1,77 @@
#!/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

View File

@@ -74,7 +74,7 @@ func (blockExec *BlockExecutor) ApplyBlock(s State, blockID types.BlockID, block
return s, ErrInvalidBlock(err)
}
abciResponses, err := execBlockOnProxyApp(blockExec.logger, blockExec.proxyApp, block)
abciResponses, err := execBlockOnProxyApp(blockExec.logger, blockExec.proxyApp, block, s.Validators)
if err != nil {
return s, ErrProxyAppConn(err)
}
@@ -160,7 +160,7 @@ func (blockExec *BlockExecutor) Commit(block *types.Block) ([]byte, error) {
// Executes block's transactions on proxyAppConn.
// Returns a list of transaction results and updates to the validator set
func execBlockOnProxyApp(logger log.Logger, proxyAppConn proxy.AppConnConsensus, block *types.Block) (*ABCIResponses, error) {
func execBlockOnProxyApp(logger log.Logger, proxyAppConn proxy.AppConnConsensus, block *types.Block, vs *types.ValidatorSet) (*ABCIResponses, error) {
var validTxs, invalidTxs = 0, 0
txIndex := 0
@@ -187,10 +187,11 @@ func execBlockOnProxyApp(logger log.Logger, proxyAppConn proxy.AppConnConsensus,
proxyAppConn.SetResponseCallback(proxyCb)
// determine which validators did not sign last block
absentVals := make([]int32, 0)
absentVals := make([][]byte, 0)
for valI, vote := range block.LastCommit.Precommits {
if vote == nil {
absentVals = append(absentVals, int32(valI))
_, val := vs.GetByIndex(valI)
absentVals = append(absentVals, val.PubKey.Bytes())
}
}
@@ -198,6 +199,7 @@ func execBlockOnProxyApp(logger log.Logger, proxyAppConn proxy.AppConnConsensus,
byzantineVals := make([]abci.Evidence, len(block.Evidence.Evidence))
for i, ev := range block.Evidence.Evidence {
byzantineVals[i] = abci.Evidence{
Type: []byte(ev.String()),
PubKey: ev.Address(), // XXX
Height: ev.Height(),
}
@@ -359,8 +361,8 @@ func fireEvents(logger log.Logger, eventBus types.BlockEventPublisher, block *ty
// ExecCommitBlock executes and commits a block on the proxyApp without validating or mutating the state.
// It returns the application root hash (result of abci.Commit).
func ExecCommitBlock(appConnConsensus proxy.AppConnConsensus, block *types.Block, logger log.Logger) ([]byte, error) {
_, err := execBlockOnProxyApp(logger, appConnConsensus, block)
func ExecCommitBlock(appConnConsensus proxy.AppConnConsensus, block *types.Block, logger log.Logger, vs *types.ValidatorSet) ([]byte, error) {
_, err := execBlockOnProxyApp(logger, appConnConsensus, block, vs)
if err != nil {
logger.Error("Error executing block on proxy app", "height", block.Height, "err", err)
return nil, err

View File

@@ -18,7 +18,8 @@ import (
)
var (
privKey = crypto.GenPrivKeyEd25519FromSecret([]byte("execution_test"))
privKey = crypto.GenPrivKeyEd25519FromSecret([]byte("execution_test_1"))
privKey2 = crypto.GenPrivKeyEd25519FromSecret([]byte("execution_test_2"))
chainID = "execution_chain"
testPartSize = 65536
nTxsPerBlock = 10
@@ -64,18 +65,18 @@ func TestBeginBlockAbsentValidators(t *testing.T) {
testCases := []struct {
desc string
lastCommitPrecommits []*types.Vote
expectedAbsentValidators []int32
expectedAbsentValidators [][]byte
}{
{"none absent", []*types.Vote{{ValidatorIndex: 0, Timestamp: now, Type: types.VoteTypePrecommit}, {ValidatorIndex: 1, Timestamp: now}}, []int32{}},
{"one absent", []*types.Vote{{ValidatorIndex: 0, Timestamp: now, Type: types.VoteTypePrecommit}, nil}, []int32{1}},
{"multiple absent", []*types.Vote{nil, nil}, []int32{0, 1}},
{"none absent", []*types.Vote{{ValidatorIndex: 0, Timestamp: now, Type: types.VoteTypePrecommit}, {ValidatorIndex: 1, Timestamp: now}}, [][]byte{}},
{"one absent", []*types.Vote{{ValidatorIndex: 0, Timestamp: now, Type: types.VoteTypePrecommit}, nil}, [][]byte{privKey2.PubKey().Bytes()}},
{"multiple absent", []*types.Vote{nil, nil}, [][]byte{privKey.PubKey().Bytes(), privKey2.PubKey().Bytes()}},
}
for _, tc := range testCases {
lastCommit := &types.Commit{BlockID: prevBlockID, Precommits: tc.lastCommitPrecommits}
block, _ := state.MakeBlock(2, makeTxs(2), lastCommit)
_, err = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger())
_, err = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), state.Validators)
require.Nil(t, err, tc.desc)
// -> app must receive an index of the absent validator
@@ -109,10 +110,10 @@ func TestBeginBlockByzantineValidators(t *testing.T) {
expectedByzantineValidators []abci.Evidence
}{
{"none byzantine", []types.Evidence{}, []abci.Evidence{}},
{"one byzantine", []types.Evidence{ev1}, []abci.Evidence{{ev1.Address(), ev1.Height()}}},
{"one byzantine", []types.Evidence{ev1}, []abci.Evidence{{[]byte(ev1.String()), ev1.Address(), ev1.Height(), int64(0)}}},
{"multiple byzantine", []types.Evidence{ev1, ev2}, []abci.Evidence{
{ev1.Address(), ev1.Height()},
{ev2.Address(), ev2.Height()}}},
{[]byte(ev1.String()), ev1.Address(), ev1.Height(), int64(0)},
{[]byte(ev2.String()), ev2.Address(), ev2.Height(), int64(0)}}},
}
for _, tc := range testCases {
@@ -120,7 +121,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) {
block, _ := state.MakeBlock(10, makeTxs(2), lastCommit)
block.Evidence.Evidence = tc.evidence
_, err = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger())
_, err = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), state.Validators)
require.Nil(t, err, tc.desc)
// -> app must receive an index of the byzantine validator
@@ -142,7 +143,8 @@ func state() State {
s, _ := MakeGenesisState(&types.GenesisDoc{
ChainID: chainID,
Validators: []types.GenesisValidator{
{privKey.PubKey(), 10000, "test"},
{privKey.PubKey(), 10000, "test1"},
{privKey2.PubKey(), 10000, "test2"},
},
AppHash: nil,
})
@@ -161,7 +163,7 @@ var _ abci.Application = (*testApp)(nil)
type testApp struct {
abci.BaseApplication
AbsentValidators []int32
AbsentValidators [][]byte
ByzantineValidators []abci.Evidence
}

View File

@@ -8,7 +8,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/abci/types"
"github.com/tendermint/go-crypto"
crypto "github.com/tendermint/go-crypto"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
@@ -121,7 +121,7 @@ func TestABCIResponsesSaveLoad2(t *testing.T) {
{Code: 383},
{Data: []byte("Gotcha!"),
Tags: []cmn.KVPair{
cmn.KVPair{[]byte("a"), []byte{1}},
cmn.KVPair{[]byte("a"), []byte("1")},
cmn.KVPair{[]byte("build"), []byte("stuff")},
}},
},

View File

@@ -67,7 +67,7 @@ func (b *EventBus) UnsubscribeAll(ctx context.Context, subscriber string) error
func (b *EventBus) Publish(eventType string, eventData TMEventData) error {
// no explicit deadline for publishing events
ctx := context.Background()
b.pubsub.PublishWithTags(ctx, eventData, tmpubsub.NewTagMap(map[string]interface{}{EventTypeKey: eventType}))
b.pubsub.PublishWithTags(ctx, eventData, tmpubsub.NewTagMap(map[string]string{EventTypeKey: eventType}))
return nil
}
@@ -92,7 +92,7 @@ func (b *EventBus) PublishEventTx(event EventDataTx) error {
// no explicit deadline for publishing events
ctx := context.Background()
tags := make(map[string]interface{})
tags := make(map[string]string)
// validate and fill tags from tx result
for _, tag := range event.Result.Tags {
@@ -112,7 +112,7 @@ func (b *EventBus) PublishEventTx(event EventDataTx) error {
tags[TxHashKey] = fmt.Sprintf("%X", event.Tx.Hash())
logIfTagExists(TxHeightKey, tags, b.Logger)
tags[TxHeightKey] = event.Height
tags[TxHeightKey] = fmt.Sprintf("%d", event.Height)
b.pubsub.PublishWithTags(ctx, event, tmpubsub.NewTagMap(tags))
return nil
@@ -160,7 +160,7 @@ func (b *EventBus) PublishEventLock(event EventDataRoundState) error {
return b.Publish(EventLock, event)
}
func logIfTagExists(tag string, tags map[string]interface{}, logger log.Logger) {
func logIfTagExists(tag string, tags map[string]string, logger log.Logger) {
if value, ok := tags[tag]; ok {
logger.Error("Found predefined tag (value will be overwritten)", "tag", tag, "value", value)
}

View File

@@ -23,12 +23,12 @@ func TestEventBusPublishEventTx(t *testing.T) {
defer eventBus.Stop()
tx := Tx("foo")
result := abci.ResponseDeliverTx{Data: []byte("bar"), Tags: []cmn.KVPair{}, Fee: cmn.KI64Pair{Key: []uint8{}, Value: 0}}
result := abci.ResponseDeliverTx{Data: []byte("bar"), Tags: []cmn.KVPair{{[]byte("baz"), []byte("1")}}, Fee: cmn.KI64Pair{Key: []uint8{}, Value: 0}}
txEventsCh := make(chan interface{})
// PublishEventTx adds all these 3 tags, so the query below should work
query := fmt.Sprintf("tm.event='Tx' AND tx.height=1 AND tx.hash='%X'", tx.Hash())
query := fmt.Sprintf("tm.event='Tx' AND tx.height=1 AND tx.hash='%X' AND baz=1", tx.Hash())
err = eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query), txEventsCh)
require.NoError(t, err)

View File

@@ -9,6 +9,11 @@ import (
"github.com/tendermint/tmlibs/merkle"
)
const (
// Evidence type for duplicate vote
DUPLICATE_VOTE = "DUPLICATE_VOTE"
)
// ErrEvidenceInvalid wraps a piece of evidence and the error denoting how or why it is invalid.
type ErrEvidenceInvalid struct {
Evidence Evidence
@@ -35,7 +40,7 @@ type Evidence interface {
Verify(chainID string) error // verify the evidence
Equal(Evidence) bool // check equality of evidence
String() string
String() string // used as type in abci.Evidence
}
func RegisterEvidences(cdc *amino.Codec) {
@@ -54,8 +59,7 @@ type DuplicateVoteEvidence struct {
// String returns a string representation of the evidence.
func (dve *DuplicateVoteEvidence) String() string {
return fmt.Sprintf("VoteA: %v; VoteB: %v", dve.VoteA, dve.VoteB)
return DUPLICATE_VOTE
}
// Height returns the height this evidence refers to.